Skip to content

Commit

Permalink
Support initContainers (#90)
Browse files Browse the repository at this point in the history
* Support initContainers

Signed-off-by: David Wertenteil <dwertent@armosec.io>

* Adding more statuses

Signed-off-by: David Wertenteil <dwertent@armosec.io>

* fixed iteration

Signed-off-by: David Wertenteil <dwertent@armosec.io>

---------

Signed-off-by: David Wertenteil <dwertent@armosec.io>
  • Loading branch information
dwertent committed Jan 24, 2024
1 parent 2d339e8 commit a0ceab8
Show file tree
Hide file tree
Showing 26 changed files with 2,523 additions and 403 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package instanceidhandler
package containerinstance

import (
"fmt"

"github.com/kubescape/k8s-interface/instanceidhandler"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers"
core1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand All @@ -28,32 +28,32 @@ func validateInstanceID(instanceID instanceidhandler.IInstanceID) error {
return nil
}

func listInstanceIDs(ownerReferences []metav1.OwnerReference, containers []core1.Container, apiVersion, namespace, kind, name string) ([]instanceidhandler.IInstanceID, error) {
func listInstanceIDs(ownerReferences []metav1.OwnerReference, containers []core1.Container, apiVersion, namespace, kind, name string) ([]InstanceID, error) {

if len(containers) == 0 {
return nil, fmt.Errorf("failed to validate instance ID: missing containers")
}

instanceIDs := make([]instanceidhandler.IInstanceID, 0)
instanceIDs := make([]InstanceID, 0)

parentApiVersion, parentKind, parentName := apiVersion, kind, name

if len(ownerReferences) != 0 && !ignoreOwnerReference(ownerReferences[0].Kind) {
if len(ownerReferences) != 0 && !helpers.IgnoreOwnerReference(ownerReferences[0].Kind) {
parentApiVersion = ownerReferences[0].APIVersion
parentKind = ownerReferences[0].Kind
parentName = ownerReferences[0].Name
}

for i := range containers {
instanceID := &InstanceID{
instanceID := InstanceID{
apiVersion: parentApiVersion,
namespace: namespace,
kind: parentKind,
name: parentName,
containerName: containers[i].Name,
}

if err := validateInstanceID(instanceID); err != nil {
if err := validateInstanceID(&instanceID); err != nil {
return nil, fmt.Errorf("failed to validate instance ID: %w", err)
}

Expand All @@ -62,14 +62,3 @@ func listInstanceIDs(ownerReferences []metav1.OwnerReference, containers []core1

return instanceIDs, nil
}

// ignoreOwnerReference returns true if the owner reference is a node or a unknown resource (CRD)
func ignoreOwnerReference(ownerKind string) bool {
if ownerKind == "Node" {
return true
}
if _, e := k8sinterface.GetGroupVersionResource(ownerKind); e != nil {
return true
}
return false
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package instanceidhandler
package containerinstance

import (
"testing"
Expand Down Expand Up @@ -327,78 +327,3 @@ func Test_listInstanceIDs(t *testing.T) {
})
}
}

func Test_ignoreOwnerReference(t *testing.T) {
type args struct {
ownerKind string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "ignore - Node",
args: args{
ownerKind: "Node",
},
want: true,
},
{
name: "ignore - CRD",
args: args{
ownerKind: "Bla",
},
want: true,
},
{
name: "not ignore - Pod",
args: args{
ownerKind: "Pod",
},
want: false,
},
{
name: "not ignore",
args: args{
ownerKind: "ReplicaSet",
},
want: false,
},
{
name: "not ignore - StatefulSet",
args: args{
ownerKind: "StatefulSet",
},
want: false,
},
{
name: "not ignore - Job",
args: args{
ownerKind: "Job",
},
want: false,
},
{
name: "not ignore - CronJob",
args: args{
ownerKind: "CronJob",
},
want: false,
},
{
name: "not ignore - DaemonSet",
args: args{
ownerKind: "DaemonSet",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ignoreOwnerReference(tt.args.ownerKind); got != tt.want {
t.Errorf("ignoreOwnerReference() = %v, want %v", got, tt.want)
}
})
}
}
73 changes: 73 additions & 0 deletions instanceidhandler/v1/containerinstance/initializers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package containerinstance

import (
"fmt"
"strings"

"github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"

core1 "k8s.io/api/core/v1"
)

// GenerateInstanceID generates instance ID from workload
func GenerateInstanceID(w workloadinterface.IWorkload) ([]InstanceID, error) {
if w.GetKind() != "Pod" {
return nil, fmt.Errorf("CreateInstanceID: workload kind must be Pod for create instance ID")
}

ownerReferences, err := w.GetOwnerReferences()
if err != nil {
return nil, err
}

containers, err := w.GetContainers()
if err != nil {
return nil, err
}

return listInstanceIDs(ownerReferences, containers, w.GetApiVersion(), w.GetNamespace(), w.GetKind(), w.GetName())
}

// GenerateInstanceIDFromPod generates instance ID from pod
func GenerateInstanceIDFromPod(pod *core1.Pod) ([]InstanceID, error) {
return listInstanceIDs(pod.GetOwnerReferences(), pod.Spec.Containers, pod.APIVersion, pod.GetNamespace(), pod.Kind, pod.GetName())
}

// GenerateInstanceIDFromString generates instance ID from string
// The string format is: apiVersion-<apiVersion>/namespace-<namespace>/kind-<kind>/name-<name>/containerName-<containerName>
func GenerateInstanceIDFromString(input string) (*InstanceID, error) {

instanceID := &InstanceID{}

// Split the input string by the field separator "/"
fields := strings.Split(input, helpers.StringFormatSeparator)
if len(fields) != 5 && len(fields) != 6 {
return nil, fmt.Errorf("invalid format: %s", input)
}

i := 0
instanceID.apiVersion = strings.TrimPrefix(fields[0], helpers.PrefixApiVersion)

// if the apiVersion has a group, e.g. apps/v1
if len(fields) == 6 {
instanceID.apiVersion += helpers.StringFormatSeparator + fields[1]
i += 1
}

instanceID.namespace = strings.TrimPrefix(fields[1+i], helpers.PrefixNamespace)
instanceID.kind = strings.TrimPrefix(fields[2+i], helpers.PrefixKind)
instanceID.name = strings.TrimPrefix(fields[3+i], helpers.PrefixName)
instanceID.containerName = strings.TrimPrefix(fields[4+i], prefixContainer)

if err := validateInstanceID(instanceID); err != nil {
return nil, err
}

// Check if the input string is valid
if instanceID.GetStringFormatted() != input {
return nil, fmt.Errorf("invalid format: %s", input)
}

return instanceID, nil
}
135 changes: 135 additions & 0 deletions instanceidhandler/v1/containerinstance/initializers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package containerinstance

import (
"encoding/json"
"reflect"
"testing"

"github.com/kubescape/k8s-interface/instanceidhandler"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/stretchr/testify/assert"
core1 "k8s.io/api/core/v1"
)

// Test_InitInstanceID tests the instance id initialization
func TestInitInstanceID(t *testing.T) {
wp, err := workloadinterface.NewWorkload([]byte(mockPod))
if err != nil {
t.Fatalf(err.Error())
}
insFromWorkload, err := GenerateInstanceID(wp)
if err != nil {
t.Fatalf("can't create instance ID from pod")
}

p := &core1.Pod{}
if err := json.Unmarshal([]byte(mockPod), p); err != nil {
t.Fatalf(err.Error())
}
insFromPod, err := GenerateInstanceIDFromPod(p)
if err != nil {
t.Fatalf("can't create instance ID from pod")
}

assert.NotEqual(t, 0, len(insFromWorkload))
assert.Equal(t, len(insFromWorkload), len(insFromPod))

for i := range insFromWorkload {
compare(t, &insFromWorkload[i], &insFromPod[i])
}

insFromString, err := GenerateInstanceIDFromString("apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerName-nginx") //insFromWorkload[0].GetStringFormatted())
if err != nil {
t.Fatalf("can't create instance ID from string: %s, error: %s", insFromWorkload[0].GetStringFormatted(), err.Error())
}
compare(t, &insFromWorkload[0], insFromString)

}

func compare(t *testing.T, a, b instanceidhandler.IInstanceID) {
assert.Equal(t, a.GetHashed(), b.GetHashed())
assert.Equal(t, a.GetStringFormatted(), b.GetStringFormatted())

assert.Equal(t, a.GetAPIVersion(), b.GetAPIVersion())
assert.Equal(t, a.GetNamespace(), b.GetNamespace())
assert.Equal(t, a.GetKind(), b.GetKind())
assert.Equal(t, a.GetName(), b.GetName())
assert.Equal(t, a.GetContainerName(), b.GetContainerName())
}

func TestGenerateInstanceIDFromString(t *testing.T) {
type args struct {
input string
}
tests := []struct {
name string
args args
want *InstanceID
wantErr bool
}{
{
name: "empty input",
args: args{
input: "",
},
want: nil,
wantErr: true,
},
{
name: "invalid input",
args: args{
input: "apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerMeme-nginx",
},
want: nil,
wantErr: true,
},
{
name: "invalid input",
args: args{
input: "apiVersion-v1/namespace-default/kind-Pod/name-n/ginx/containerMeme-n/ginx",
},
want: nil,
wantErr: true,
},
{
name: "valid input - Pod",
args: args{
input: "apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerName-nginx",
},
want: &InstanceID{
apiVersion: "v1",
namespace: "default",
kind: "Pod",
name: "nginx",
containerName: "nginx",
},
wantErr: false,
},
{
name: "valid input - ReplicaSet",
args: args{
input: "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-1234/containerName-nginx",
},
want: &InstanceID{
apiVersion: "apps/v1",
namespace: "default",
kind: "ReplicaSet",
name: "nginx-1234",
containerName: "nginx",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GenerateInstanceIDFromString(tt.args.input)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateInstanceIDFromString() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != nil && !reflect.DeepEqual(got, tt.want) {
t.Errorf("GenerateInstanceIDFromString() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit a0ceab8

Please sign in to comment.