diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 8df29d8..da381a0 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,5 +18,5 @@ jobs: security-events: write uses: kubescape/workflows/.github/workflows/go-basic-tests.yaml@main with: - GO_VERSION: '1.21' + GO_VERSION: '1.22' secrets: inherit diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ba1e3db..8954388 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,7 +1,7 @@ name: Release-Tag on: push: - branches: [ master, main ] + branches: [ master, main ] paths-ignore: - '**.md' ### Ignore running when .md files change - '**.yaml' ### Ignore running when .yaml files change @@ -17,7 +17,7 @@ jobs: security-events: write uses: kubescape/workflows/.github/workflows/go-basic-tests.yaml@main with: - GO_VERSION: '1.21' + GO_VERSION: '1.22' secrets: inherit release: needs: test @@ -29,4 +29,3 @@ jobs: - uses: rickstaa/action-create-tag@v1 with: tag: "v0.0.${{ github.run_number }}" - \ No newline at end of file diff --git a/go.mod b/go.mod index f23cab9..3f68836 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/kubescape/k8s-interface -go 1.21 - -toolchain go1.21.6 +go 1.22.5 require ( cloud.google.com/go/container v1.24.0 diff --git a/instanceidhandler/interface.go b/instanceidhandler/interface.go index 482b677..eda6741 100644 --- a/instanceidhandler/interface.go +++ b/instanceidhandler/interface.go @@ -3,22 +3,12 @@ package instanceidhandler import "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" type IInstanceID interface { - // GetInstanceType returns the type of the instance ID GetInstanceType() helpers.InstanceType - - GetAPIVersion() string - GetNamespace() string - GetKind() string GetName() string GetContainerName() string - SetAPIVersion(string) - SetNamespace(string) - SetKind(string) - SetName(string) - SetContainerName(string) GetStringFormatted() string GetHashed() string GetLabels() map[string]string // GetSlug returns a string with a human-friendly and Kubernetes-compatible name - GetSlug() (string, error) + GetSlug(noContainer bool) (string, error) } diff --git a/instanceidhandler/v1/containerinstance/helpers.go b/instanceidhandler/v1/containerinstance/helpers.go index 308dd1d..f5f8d0f 100644 --- a/instanceidhandler/v1/containerinstance/helpers.go +++ b/instanceidhandler/v1/containerinstance/helpers.go @@ -3,54 +3,57 @@ package containerinstance import ( "fmt" - "github.com/kubescape/k8s-interface/instanceidhandler" "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" core1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func validateInstanceID(instanceID instanceidhandler.IInstanceID) error { - if instanceID.GetAPIVersion() == "" { +func validateInstanceID(instanceID *InstanceID) error { + if instanceID.ApiVersion == "" { return fmt.Errorf("invalid instanceID: apiVersion cannot be empty") } - if instanceID.GetNamespace() == "" { + if instanceID.Namespace == "" { return fmt.Errorf("invalid instanceID: namespace cannot be empty") } - if instanceID.GetKind() == "" { + if instanceID.Kind == "" { return fmt.Errorf("invalid instanceID: kind cannot be empty") } - if instanceID.GetName() == "" { + if instanceID.Name == "" { return fmt.Errorf("invalid instanceID: name cannot be empty") } - if instanceID.GetContainerName() == "" { + if instanceID.ContainerName == "" { return fmt.Errorf("invalid instanceID: containerName cannot be empty") } + if instanceID.InstanceType == "" { + return fmt.Errorf("invalid instanceID: InstanceType cannot be empty") + } return nil } -func listInstanceIDs(ownerReferences []metav1.OwnerReference, containers []core1.Container, apiVersion, namespace, kind, name string) ([]InstanceID, error) { +func ListInstanceIDs(ownerReference *metav1.OwnerReference, containers []core1.Container, instanceType, apiVersion, namespace, kind, name, alternateName string) ([]InstanceID, error) { + instanceIDs := make([]InstanceID, 0) if len(containers) == 0 { - return nil, fmt.Errorf("failed to validate instance ID: missing containers") + return instanceIDs, nil } - instanceIDs := make([]InstanceID, 0) - parentApiVersion, parentKind, parentName := apiVersion, kind, name - if len(ownerReferences) != 0 && !helpers.IgnoreOwnerReference(ownerReferences[0].Kind) { - parentApiVersion = ownerReferences[0].APIVersion - parentKind = ownerReferences[0].Kind - parentName = ownerReferences[0].Name + if ownerReference != nil && !helpers.IgnoreOwnerReference(ownerReference.Kind) { + parentApiVersion = ownerReference.APIVersion + parentKind = ownerReference.Kind + parentName = ownerReference.Name } for i := range containers { instanceID := InstanceID{ - apiVersion: parentApiVersion, - namespace: namespace, - kind: parentKind, - name: parentName, - containerName: containers[i].Name, + ApiVersion: parentApiVersion, + Namespace: namespace, + Kind: parentKind, + Name: parentName, + AlternateName: alternateName, + ContainerName: containers[i].Name, + InstanceType: instanceType, } if err := validateInstanceID(&instanceID); err != nil { diff --git a/instanceidhandler/v1/containerinstance/helpers_test.go b/instanceidhandler/v1/containerinstance/helpers_test.go index 0ff40ea..ef54193 100644 --- a/instanceidhandler/v1/containerinstance/helpers_test.go +++ b/instanceidhandler/v1/containerinstance/helpers_test.go @@ -8,6 +8,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const container = "container" + func Test_validateInstanceID(t *testing.T) { type args struct { instanceID *InstanceID @@ -28,11 +30,12 @@ func Test_validateInstanceID(t *testing.T) { name: "empty apiVersion", args: args{ instanceID: &InstanceID{ - apiVersion: "", - namespace: "test", - kind: "test", - name: "test", - containerName: "test", + ApiVersion: "", + Namespace: "test", + Kind: "test", + Name: "test", + ContainerName: "test", + InstanceType: container, }, }, wantErr: true, @@ -42,11 +45,12 @@ func Test_validateInstanceID(t *testing.T) { args: args{ instanceID: &InstanceID{ - apiVersion: "test", - namespace: "", - kind: "test", - name: "test", - containerName: "test", + ApiVersion: "test", + Namespace: "", + Kind: "test", + Name: "test", + ContainerName: "test", + InstanceType: container, }, }, wantErr: true, @@ -55,11 +59,12 @@ func Test_validateInstanceID(t *testing.T) { name: "empty kind", args: args{ instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "", - name: "test", - containerName: "test", + ApiVersion: "test", + Namespace: "test", + Kind: "", + Name: "test", + ContainerName: "test", + InstanceType: container, }, }, wantErr: true, @@ -68,11 +73,12 @@ func Test_validateInstanceID(t *testing.T) { name: "empty name", args: args{ instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "", - containerName: "test", + ApiVersion: "test", + Namespace: "test", + Kind: "test", + Name: "", + ContainerName: "test", + InstanceType: container, }, }, wantErr: true, @@ -81,11 +87,26 @@ func Test_validateInstanceID(t *testing.T) { name: "empty containerName", args: args{ instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - containerName: "", + ApiVersion: "test", + Namespace: "test", + Kind: "test", + Name: "test", + ContainerName: "", + InstanceType: container, + }, + }, + wantErr: true, + }, + { + name: "empty InstanceType", + args: args{ + instanceID: &InstanceID{ + ApiVersion: "test", + Namespace: "test", + Kind: "test", + Name: "test", + ContainerName: "test", + InstanceType: "", }, }, wantErr: true, @@ -94,11 +115,12 @@ func Test_validateInstanceID(t *testing.T) { name: "valid instanceID", args: args{ instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - containerName: "test", + ApiVersion: "test", + Namespace: "test", + Kind: "test", + Name: "test", + ContainerName: "test", + InstanceType: container, }, }, wantErr: false, @@ -115,7 +137,7 @@ func Test_validateInstanceID(t *testing.T) { func Test_listInstanceIDs(t *testing.T) { type args struct { - ownerReferences []metav1.OwnerReference + ownerReferences *metav1.OwnerReference containers []core1.Container apiVersion string namespace string @@ -131,7 +153,6 @@ func Test_listInstanceIDs(t *testing.T) { { name: "empty ownerReferences", args: args{ - ownerReferences: []metav1.OwnerReference{}, containers: []core1.Container{ { Name: "test", @@ -144,11 +165,12 @@ func Test_listInstanceIDs(t *testing.T) { }, want: []*InstanceID{ { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - containerName: "test", + ApiVersion: "test", + Namespace: "test", + Kind: "Pod", + Name: "test", + ContainerName: "test", + InstanceType: container, }, }, wantErr: false, @@ -156,25 +178,27 @@ func Test_listInstanceIDs(t *testing.T) { { name: "empty containers", args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.Container{}, - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", + containers: []core1.Container{}, + apiVersion: "test", + namespace: "test", + kind: "test", + name: "test", }, want: []*InstanceID{}, - wantErr: true, + wantErr: false, }, { name: "invalid instanceID", args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.Container{}, - apiVersion: "", - namespace: "test", - kind: "test", - name: "test", + containers: []core1.Container{ + { + Name: "test", + }, + }, + apiVersion: "", + namespace: "test", + kind: "test", + name: "test", }, want: []*InstanceID{}, wantErr: true, @@ -182,7 +206,6 @@ func Test_listInstanceIDs(t *testing.T) { { name: "valid instanceID - Pod", args: args{ - ownerReferences: []metav1.OwnerReference{}, containers: []core1.Container{ { Name: "test", @@ -195,11 +218,12 @@ func Test_listInstanceIDs(t *testing.T) { }, want: []*InstanceID{ { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - containerName: "test", + ApiVersion: "test", + Namespace: "test", + Kind: "Pod", + Name: "test", + ContainerName: "test", + InstanceType: container, }, }, wantErr: false, @@ -207,11 +231,9 @@ func Test_listInstanceIDs(t *testing.T) { { name: "valid instanceID - Node", args: args{ - ownerReferences: []metav1.OwnerReference{ - { - Kind: "Node", - Name: "nodeName", - }, + ownerReferences: &metav1.OwnerReference{ + Kind: "Node", + Name: "nodeName", }, containers: []core1.Container{ { @@ -225,11 +247,12 @@ func Test_listInstanceIDs(t *testing.T) { }, want: []*InstanceID{ { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - containerName: "test", + ApiVersion: "test", + Namespace: "test", + Kind: "Pod", + Name: "podName", + ContainerName: "test", + InstanceType: container, }, }, wantErr: false, @@ -237,12 +260,10 @@ func Test_listInstanceIDs(t *testing.T) { { name: "valid instanceID - multiple containers", args: args{ - ownerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "ReplicaSet", - Name: "OwnerTest", - }, + ownerReferences: &metav1.OwnerReference{ + APIVersion: "apps/v1", + Kind: "ReplicaSet", + Name: "OwnerTest", }, containers: []core1.Container{ { @@ -256,11 +277,12 @@ func Test_listInstanceIDs(t *testing.T) { }, want: []*InstanceID{ { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - containerName: "test", + ApiVersion: "apps/v1", + Namespace: "test", + Kind: "ReplicaSet", + Name: "OwnerTest", + ContainerName: "test", + InstanceType: container, }, }, wantErr: false, @@ -268,12 +290,10 @@ func Test_listInstanceIDs(t *testing.T) { { name: "valid instanceID - Replica", args: args{ - ownerReferences: []metav1.OwnerReference{ - { - Kind: "ReplicaSet", - Name: "OwnerTest", - APIVersion: "apps/v1", - }, + ownerReferences: &metav1.OwnerReference{ + Kind: "ReplicaSet", + Name: "OwnerTest", + APIVersion: "apps/v1", }, containers: []core1.Container{ { @@ -290,18 +310,20 @@ func Test_listInstanceIDs(t *testing.T) { }, want: []*InstanceID{ { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - containerName: "test-0", + ApiVersion: "apps/v1", + Namespace: "test", + Kind: "ReplicaSet", + Name: "OwnerTest", + ContainerName: "test-0", + InstanceType: container, }, { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - containerName: "test-1", + ApiVersion: "apps/v1", + Namespace: "test", + Kind: "ReplicaSet", + Name: "OwnerTest", + ContainerName: "test-1", + InstanceType: container, }, }, wantErr: false, @@ -310,7 +332,7 @@ func Test_listInstanceIDs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := listInstanceIDs(tt.args.ownerReferences, tt.args.containers, tt.args.apiVersion, tt.args.namespace, tt.args.kind, tt.args.name) + got, err := ListInstanceIDs(tt.args.ownerReferences, tt.args.containers, container, tt.args.apiVersion, tt.args.namespace, tt.args.kind, tt.args.name, "") if (err != nil) != tt.wantErr { t.Errorf("listInstanceIDs() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/instanceidhandler/v1/containerinstance/initializers.go b/instanceidhandler/v1/containerinstance/initializers.go index 8297ede..bc023ee 100644 --- a/instanceidhandler/v1/containerinstance/initializers.go +++ b/instanceidhandler/v1/containerinstance/initializers.go @@ -5,41 +5,16 @@ import ( "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-/namespace-/kind-/name-/containerName- func GenerateInstanceIDFromString(input string) (*InstanceID, error) { instanceID := &InstanceID{} + // TODO add case for CronJobs here, or deprecate + // Split the input string by the field separator "/" fields := strings.Split(input, helpers.StringFormatSeparator) if len(fields) != 5 && len(fields) != 6 { @@ -47,18 +22,18 @@ func GenerateInstanceIDFromString(input string) (*InstanceID, error) { } i := 0 - instanceID.apiVersion = strings.TrimPrefix(fields[0], helpers.PrefixApiVersion) + 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] + 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) + 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.InstanceType, instanceID.ContainerName, _ = strings.Cut(fields[4+i], "Name-") if err := validateInstanceID(instanceID); err != nil { return nil, err diff --git a/instanceidhandler/v1/containerinstance/initializers_test.go b/instanceidhandler/v1/containerinstance/initializers_test.go index 6993370..b976166 100644 --- a/instanceidhandler/v1/containerinstance/initializers_test.go +++ b/instanceidhandler/v1/containerinstance/initializers_test.go @@ -1,62 +1,11 @@ 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 @@ -97,11 +46,12 @@ func TestGenerateInstanceIDFromString(t *testing.T) { input: "apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerName-nginx", }, want: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Pod", - name: "nginx", - containerName: "nginx", + ApiVersion: "v1", + Namespace: "default", + Kind: "Pod", + Name: "nginx", + ContainerName: "nginx", + InstanceType: container, }, wantErr: false, }, @@ -111,11 +61,28 @@ func TestGenerateInstanceIDFromString(t *testing.T) { 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", + ApiVersion: "apps/v1", + Namespace: "default", + Kind: "ReplicaSet", + Name: "nginx-1234", + ContainerName: "nginx", + InstanceType: container, + }, + wantErr: false, + }, + { + name: "valid input - CronJob", + args: args{ + input: "apiVersion-batch/v1/namespace-kubescape/kind-Job/name-kubevuln-scheduler-b449cf78f/containerName-kubevuln-scheduler", + }, + want: &InstanceID{ + ApiVersion: "batch/v1", + Namespace: "kubescape", + Kind: "Job", + Name: "kubevuln-scheduler-b449cf78f", // should be kubevuln-scheduler-28677846 + ContainerName: "kubevuln-scheduler", + InstanceType: container, + AlternateName: "", // should be kubevuln-scheduler-b449cf78f }, wantErr: false, }, @@ -127,9 +94,7 @@ func TestGenerateInstanceIDFromString(t *testing.T) { 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) - } + assert.Equalf(t, tt.want, got, "GenerateInstanceIDFromString() = %v, want %v", got, tt.want) }) } } diff --git a/instanceidhandler/v1/containerinstance/instanceidhandler.go b/instanceidhandler/v1/containerinstance/instanceidhandler.go index 2399c02..2b4b983 100644 --- a/instanceidhandler/v1/containerinstance/instanceidhandler.go +++ b/instanceidhandler/v1/containerinstance/instanceidhandler.go @@ -12,69 +12,38 @@ import ( ) // string format: apiVersion-/namespace-/kind-/name-/containerName- -const ( - prefixContainer = "containerName-" - stringFormat = helpers.PrefixApiVersion + "%s" + helpers.StringFormatSeparator + helpers.PrefixNamespace + "%s" + helpers.StringFormatSeparator + helpers.PrefixKind + "%s" + helpers.StringFormatSeparator + helpers.PrefixName + "%s" + helpers.StringFormatSeparator + prefixContainer + "%s" -) - -const InstanceType helpers.InstanceType = "container" // ensure that InstanceID implements IInstanceID -var _ instanceidhandler.IInstanceID = &InstanceID{} +var _ instanceidhandler.IInstanceID = (*InstanceID)(nil) type InstanceID struct { - apiVersion string - namespace string - kind string - name string - containerName string + ApiVersion string + Namespace string + Kind string + Name string + AlternateName string + ContainerName string + InstanceType string } func (id *InstanceID) GetInstanceType() helpers.InstanceType { - return InstanceType -} -func (id *InstanceID) GetAPIVersion() string { - return id.apiVersion -} - -func (id *InstanceID) GetNamespace() string { - return id.namespace -} - -func (id *InstanceID) GetKind() string { - return id.kind + return helpers.InstanceType(id.InstanceType) } func (id *InstanceID) GetName() string { - return id.name + return id.Name } func (id *InstanceID) GetContainerName() string { - return id.containerName -} - -func (id *InstanceID) SetAPIVersion(apiVersion string) { - id.apiVersion = apiVersion -} - -func (id *InstanceID) SetNamespace(namespace string) { - id.namespace = namespace -} - -func (id *InstanceID) SetKind(kind string) { - id.kind = kind -} - -func (id *InstanceID) SetName(name string) { - id.name = name -} - -func (id *InstanceID) SetContainerName(containerName string) { - id.containerName = containerName + return id.ContainerName } func (id *InstanceID) GetStringFormatted() string { - return fmt.Sprintf(stringFormat, id.GetAPIVersion(), id.GetNamespace(), id.GetKind(), id.GetName(), id.GetContainerName()) + stringFormat := helpers.PrefixApiVersion + "%s" + helpers.StringFormatSeparator + helpers.PrefixNamespace + "%s" + helpers.StringFormatSeparator + helpers.PrefixKind + "%s" + helpers.StringFormatSeparator + helpers.PrefixName + "%s" + helpers.StringFormatSeparator + "%sName-%s" + if id.AlternateName != "" { + return fmt.Sprintf(stringFormat, id.ApiVersion, id.Namespace, id.Kind, id.AlternateName, id.InstanceType, id.ContainerName) + } + return fmt.Sprintf(stringFormat, id.ApiVersion, id.Namespace, id.Kind, id.Name, id.InstanceType, id.ContainerName) } func (id *InstanceID) GetHashed() string { @@ -84,17 +53,29 @@ func (id *InstanceID) GetHashed() string { } func (id *InstanceID) GetLabels() map[string]string { - group, version := k8sinterface.SplitApiVersion(id.GetAPIVersion()) + group, version := k8sinterface.SplitApiVersion(id.ApiVersion) return map[string]string{ helpers.ApiGroupMetadataKey: group, helpers.ApiVersionMetadataKey: version, - helpers.NamespaceMetadataKey: id.GetNamespace(), - helpers.KindMetadataKey: id.GetKind(), - helpers.NameMetadataKey: id.GetName(), - helpers.ContainerNameMetadataKey: id.GetContainerName(), + helpers.NamespaceMetadataKey: id.Namespace, + helpers.KindMetadataKey: id.Kind, + helpers.NameMetadataKey: id.Name, + helpers.ContainerNameMetadataKey: id.ContainerName, } } -func (id *InstanceID) GetSlug() (string, error) { - return names.InstanceIDToSlug(id.GetName(), id.GetKind(), id.GetContainerName(), id.GetHashed()) +func (id *InstanceID) GetSlug(noContainer bool) (string, error) { + name := id.Name + kind := id.Kind + containerName := id.ContainerName + hashedID := id.GetHashed() + // use alternate name if present + if len(id.AlternateName) > 0 { + name = id.AlternateName + } + // eventually remove the container name + if noContainer { + containerName = "" + } + return names.InstanceIDToSlug(name, kind, containerName, hashedID) } diff --git a/instanceidhandler/v1/containerinstance/instanceidhandler_test.go b/instanceidhandler/v1/containerinstance/instanceidhandler_test.go index d1849f5..e2042a2 100644 --- a/instanceidhandler/v1/containerinstance/instanceidhandler_test.go +++ b/instanceidhandler/v1/containerinstance/instanceidhandler_test.go @@ -2,20 +2,17 @@ package containerinstance import ( _ "embed" - "fmt" - "reflect" - "strings" "testing" "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" "github.com/kubescape/k8s-interface/names" "github.com/kubescape/k8s-interface/workloadinterface" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var ( - //go:embed testdata/service.json - service string //go:embed testdata/deployment.json deployment string //go:embed testdata/jobPod.json @@ -24,89 +21,37 @@ var ( mockPod string ) -func checkAllsFunctions(object string, apiversion, namespace, kind, name, containerName, formattedString, expectedHash string, expectedLabels map[string]string) error { +func checkAllsFunctions(t *testing.T, object, apiversion, namespace, kind, name, containerName, formattedString, expectedHash string, expectedLabels map[string]string) error { podWorkload, err := workloadinterface.NewWorkload([]byte(object)) - if err != nil { - return fmt.Errorf(err.Error()) - } - podWorkloadInstanceID, err := GenerateInstanceID(podWorkload) - if err != nil { - return fmt.Errorf("TestCreate: GenerateInstanceID - pod instance ID should be created") - } - if len(podWorkloadInstanceID) != 1 { - return fmt.Errorf("TestCreate: should return only one ") - } - - expected := apiversion - if podWorkloadInstanceID[0].GetAPIVersion() != expected { - return fmt.Errorf("TestCreate: GetAPIVersion - wrong , get %s, expected %s", podWorkloadInstanceID[0].GetAPIVersion(), expected) - } - expected = namespace - if podWorkloadInstanceID[0].GetNamespace() != expected { - return fmt.Errorf("TestCreate: GetNamespace - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - expected = kind - if podWorkloadInstanceID[0].GetKind() != expected { - return fmt.Errorf("TestCreate: GetKind - wrong parent kind, get %s, expected %s", podWorkloadInstanceID[0].GetKind(), expected) - } - expected = name - if !strings.Contains(podWorkloadInstanceID[0].GetName(), expected) { - return fmt.Errorf("TestCreate: GetName - wrong parent name, get %s, expected %s", podWorkloadInstanceID[0].GetName(), expected) - } - expected = containerName - if !strings.Contains(podWorkloadInstanceID[0].GetContainerName(), expected) { - return fmt.Errorf("TestCreate: GetContainerName - wrong container name, get %s, expected %s", podWorkloadInstanceID[0].GetContainerName(), expected) - } - expected = formattedString - format := podWorkloadInstanceID[0].GetStringFormatted() - if format != expected { - return fmt.Errorf("TestCreate: GetStringFormatted - fail to format Instance ID in string format, get %s, expected %s", podWorkloadInstanceID[0].GetStringFormatted(), expected) - } - expected = expectedHash - if podWorkloadInstanceID[0].GetHashed() != expected { - return fmt.Errorf("TestCreate: GetHashed - GetHashed, get %s, expected %s", podWorkloadInstanceID[0].GetHashed(), expected) - } - - labels := podWorkloadInstanceID[0].GetLabels() - eq := reflect.DeepEqual(labels, expectedLabels) - if !eq { - return fmt.Errorf("TestCreate: GetLabels - GetLabels failed, get %s, expected %s", podWorkloadInstanceID[0].GetLabels(), expectedLabels) - } - - expected = "123" - podWorkloadInstanceID[0].SetAPIVersion(expected) - if podWorkloadInstanceID[0].GetAPIVersion() != expected { - return fmt.Errorf("TestCreate: SetAPIVersion - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - podWorkloadInstanceID[0].SetNamespace(expected) - if podWorkloadInstanceID[0].GetNamespace() != expected { - return fmt.Errorf("TestCreate: SetNamespace - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - podWorkloadInstanceID[0].SetKind(expected) - if podWorkloadInstanceID[0].GetKind() != expected { - return fmt.Errorf("TestCreate: SetKind - wrong parent kind, get %s, expected %s", podWorkloadInstanceID[0].GetKind(), expected) - } - podWorkloadInstanceID[0].SetName(expected) - if !strings.Contains(podWorkloadInstanceID[0].GetName(), expected) { - return fmt.Errorf("TestCreate: SetName - wrong parent name, get %s, expected %s", podWorkloadInstanceID[0].GetName(), expected) - } - podWorkloadInstanceID[0].SetContainerName(expected) - if !strings.Contains(podWorkloadInstanceID[0].GetContainerName(), expected) { - return fmt.Errorf("TestCreate: SetContainerName - wrong container name, get %s, expected %s", podWorkloadInstanceID[0].GetContainerName(), expected) - } + require.NoError(t, err) + ownerReferences, err := podWorkload.GetOwnerReferences() + require.NoError(t, err) + var ownerReference *metav1.OwnerReference + if len(ownerReferences) > 0 { + ownerReference = &ownerReferences[0] + } + containers, err := podWorkload.GetContainers() + require.NoError(t, err) + podWorkloadInstanceID, err := ListInstanceIDs(ownerReference, containers, container, podWorkload.GetApiVersion(), podWorkload.GetNamespace(), podWorkload.GetKind(), podWorkload.GetName(), "") + require.NoError(t, err) + + assert.Equal(t, 1, len(podWorkloadInstanceID)) + + assert.Equal(t, podWorkloadInstanceID[0].ApiVersion, apiversion) + assert.Equal(t, podWorkloadInstanceID[0].Namespace, namespace) + assert.Equal(t, podWorkloadInstanceID[0].Kind, kind) + assert.Equal(t, podWorkloadInstanceID[0].Name, name) + assert.Equal(t, podWorkloadInstanceID[0].InstanceType, container) + assert.Equal(t, podWorkloadInstanceID[0].ContainerName, containerName) + assert.Equal(t, podWorkloadInstanceID[0].GetStringFormatted(), formattedString) + assert.Equal(t, podWorkloadInstanceID[0].GetHashed(), expectedHash) + + assert.Equal(t, podWorkloadInstanceID[0].GetLabels(), expectedLabels) return nil } func TestInstanceID(t *testing.T) { - serviceWorkload, err := workloadinterface.NewWorkload([]byte(service)) - if err != nil { - t.Fatalf(err.Error()) - } - _, err = GenerateInstanceID(serviceWorkload) - if err == nil { - t.Errorf("can't create instance ID from service") - } expectedLabels := map[string]string{ helpers.ApiGroupMetadataKey: "apps", helpers.ApiVersionMetadataKey: "v1", @@ -116,10 +61,8 @@ func TestInstanceID(t *testing.T) { helpers.ContainerNameMetadataKey: "nginx", } - err = checkAllsFunctions(deployment, "apps/v1", "default", "ReplicaSet", "nginx-84f5585d68", "nginx", "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/containerName-nginx", "57366ade3da2e7ba01f8b78251cb57bd70840939f4f207da91cb092b30c06feb", expectedLabels) - if err != nil { - t.Error(err) - } + err := checkAllsFunctions(t, deployment, "apps/v1", "default", "ReplicaSet", "nginx-84f5585d68", "nginx", "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/containerName-nginx", "57366ade3da2e7ba01f8b78251cb57bd70840939f4f207da91cb092b30c06feb", expectedLabels) + assert.NoError(t, err) expectedLabels = map[string]string{ helpers.ApiGroupMetadataKey: "batch", @@ -129,10 +72,8 @@ func TestInstanceID(t *testing.T) { helpers.NameMetadataKey: "nginx-job", helpers.ContainerNameMetadataKey: "nginx-job", } - err = checkAllsFunctions(jobPod, "batch/v1", "default", "Job", "nginx", "nginx-job", "apiVersion-batch/v1/namespace-default/kind-Job/name-nginx-job/containerName-nginx-job", "1fdef304b3383588f0e8a267914746de2bf03e1652908d57232cd543a87541c5", expectedLabels) - if err != nil { - t.Error(err) - } + err = checkAllsFunctions(t, jobPod, "batch/v1", "default", "Job", "nginx-job", "nginx-job", "apiVersion-batch/v1/namespace-default/kind-Job/name-nginx-job/containerName-nginx-job", "1fdef304b3383588f0e8a267914746de2bf03e1652908d57232cd543a87541c5", expectedLabels) + assert.NoError(t, err) expectedLabels = map[string]string{ helpers.ApiGroupMetadataKey: "", @@ -142,11 +83,8 @@ func TestInstanceID(t *testing.T) { helpers.NameMetadataKey: "nginx", helpers.ContainerNameMetadataKey: "nginx", } - err = checkAllsFunctions(mockPod, "v1", "default", "Pod", "nginx", "nginx", "apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerName-nginx", "1ba506b28f9ee9c7e8a0c98840fe5a1fe21142d225ecc526fbb535d0d6344aaf", expectedLabels) - if err != nil { - t.Error(err) - } - + err = checkAllsFunctions(t, mockPod, "v1", "default", "Pod", "nginx", "nginx", "apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerName-nginx", "1ba506b28f9ee9c7e8a0c98840fe5a1fe21142d225ecc526fbb535d0d6344aaf", expectedLabels) + assert.NoError(t, err) } func TestInstanceIDToDisplayName(t *testing.T) { @@ -159,11 +97,12 @@ func TestInstanceIDToDisplayName(t *testing.T) { { name: "valid instanceID produces matching display name", input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Pod", - name: "reverse-proxy", - containerName: "nginx", + ApiVersion: "v1", + Namespace: "default", + Kind: "Pod", + Name: "reverse-proxy", + ContainerName: "nginx", + InstanceType: container, }, want: "pod-reverse-proxy-nginx-2f07-68bd", wantErr: nil, @@ -171,11 +110,12 @@ func TestInstanceIDToDisplayName(t *testing.T) { { name: "valid instanceID produces matching display name", input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "webapp", - containerName: "leader", + ApiVersion: "v1", + Namespace: "default", + Kind: "Service", + Name: "webapp", + ContainerName: "leader", + InstanceType: container, }, want: "service-webapp-leader-cca3-8ea7", wantErr: nil, @@ -183,10 +123,10 @@ func TestInstanceIDToDisplayName(t *testing.T) { { name: "valid instanceID without container name produces matching display name", input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "webapp", + ApiVersion: "v1", + Namespace: "default", + Kind: "Service", + Name: "webapp", }, want: "service-webapp", wantErr: nil, @@ -194,11 +134,12 @@ func TestInstanceIDToDisplayName(t *testing.T) { { name: "invalid instanceID produces matching error", input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "web/app", - containerName: "leader", + ApiVersion: "v1", + Namespace: "default", + Kind: "Service", + Name: "web/app", + ContainerName: "leader", + InstanceType: container, }, want: "", wantErr: names.ErrInvalidSlug, @@ -207,7 +148,7 @@ func TestInstanceIDToDisplayName(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - got, err := tc.input.GetSlug() + got, err := tc.input.GetSlug(false) assert.Equal(t, tc.want, got) assert.ErrorIs(t, tc.wantErr, err) diff --git a/instanceidhandler/v1/containerinstance/testdata/service.json b/instanceidhandler/v1/containerinstance/testdata/service.json deleted file mode 100644 index f4079ae..0000000 --- a/instanceidhandler/v1/containerinstance/testdata/service.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "creationTimestamp": "2021-12-06T14:01:16Z", - "labels": { - "app": "armo-vuln-scan", - "app.kubernetes.io\/managed-by": "Helm" - }, - "name": "armo-vuln-scan", - "resourceVersion": "351796", - "uid": "12bd4f9f-3ec6-4113-8ec6-0b8a1c772deb" - }, - "spec": { - "clusterIP": "10.107.7.78", - "clusterIPs": [ - "10.107.7.78" - ], - "internalTrafficPolicy": "Cluster", - "ipFamilies": [ - "IPv4" - ], - "ipFamilyPolicy": "SingleStack", - "ports": [ - { - "port": 8080, - "protocol": "TCP", - "targetPort": 8080 - } - ], - "selector": { - "app": "armo-vuln-scan" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - }, - "status": { - "loadBalancer": {} - } -} \ No newline at end of file diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/helpers.go b/instanceidhandler/v1/ephemeralcontainerinstance/helpers.go deleted file mode 100644 index 7149e8d..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/helpers.go +++ /dev/null @@ -1,63 +0,0 @@ -package ephemeralcontainerinstance - -import ( - "fmt" - - "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" - core1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func validateInstanceID(instanceID *InstanceID) error { - if instanceID.GetAPIVersion() == "" { - return fmt.Errorf("invalid instanceID: apiVersion cannot be empty") - } - if instanceID.GetNamespace() == "" { - return fmt.Errorf("invalid instanceID: namespace cannot be empty") - } - if instanceID.GetKind() == "" { - return fmt.Errorf("invalid instanceID: kind cannot be empty") - } - if instanceID.GetName() == "" { - return fmt.Errorf("invalid instanceID: name cannot be empty") - } - if instanceID.GetContainerName() == "" { - return fmt.Errorf("invalid instanceID: containerName cannot be empty") - } - return nil -} - -func listInstanceIDs(ownerReferences []metav1.OwnerReference, ephemeralContainers []core1.EphemeralContainer, apiVersion, namespace, kind, name string) ([]InstanceID, error) { - - if len(ephemeralContainers) == 0 { - return []InstanceID{}, nil // ephemeral containers are optional - } - - instanceIDs := make([]InstanceID, 0) - - parentApiVersion, parentKind, parentName := apiVersion, kind, name - - if len(ownerReferences) != 0 && !helpers.IgnoreOwnerReference(ownerReferences[0].Kind) { - parentApiVersion = ownerReferences[0].APIVersion - parentKind = ownerReferences[0].Kind - parentName = ownerReferences[0].Name - } - - for i := range ephemeralContainers { - instanceID := &InstanceID{ - apiVersion: parentApiVersion, - namespace: namespace, - kind: parentKind, - name: parentName, - ephemeralContainerName: ephemeralContainers[i].Name, - } - - if err := validateInstanceID(instanceID); err != nil { - return nil, fmt.Errorf("failed to validate instance ID: %w", err) - } - - instanceIDs = append(instanceIDs, *instanceID) - } - - return instanceIDs, nil -} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/helpers_test.go b/instanceidhandler/v1/ephemeralcontainerinstance/helpers_test.go deleted file mode 100644 index ce287f1..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/helpers_test.go +++ /dev/null @@ -1,340 +0,0 @@ -package ephemeralcontainerinstance - -import ( - "testing" - - "github.com/stretchr/testify/assert" - core1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func Test_validateInstanceID(t *testing.T) { - type args struct { - instanceID *InstanceID - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "empty instanceID", - args: args{ - instanceID: &InstanceID{}, - }, - wantErr: true, - }, - { - name: "empty apiVersion", - args: args{ - instanceID: &InstanceID{ - apiVersion: "", - namespace: "test", - kind: "test", - name: "test", - ephemeralContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty namespace", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "", - kind: "test", - name: "test", - ephemeralContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty kind", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "", - name: "test", - ephemeralContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty name", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "", - ephemeralContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty ephemeralContainerName", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - ephemeralContainerName: "", - }, - }, - wantErr: true, - }, - { - name: "valid instanceID", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - ephemeralContainerName: "test", - }, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := validateInstanceID(tt.args.instanceID); (err != nil) != tt.wantErr { - t.Errorf("validateInstanceID() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_listInstanceIDs(t *testing.T) { - type args struct { - ownerReferences []metav1.OwnerReference - containers []core1.EphemeralContainer - apiVersion string - namespace string - kind string - name string - } - tests := []struct { - name string - args args - want []*InstanceID - wantErr bool - }{ - { - name: "empty ownerReferences", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.EphemeralContainer{ - { - EphemeralContainerCommon: core1.EphemeralContainerCommon{ - Name: "test", - }, - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - }, - want: []*InstanceID{ - { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - ephemeralContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "empty containers", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.EphemeralContainer{}, - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - }, - want: []*InstanceID{}, - wantErr: false, - }, - { - name: "invalid instanceID", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.EphemeralContainer{}, - apiVersion: "", - namespace: "test", - kind: "test", - name: "test", - }, - want: []*InstanceID{}, - wantErr: false, - }, - { - name: "valid instanceID - Pod", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.EphemeralContainer{ - { - EphemeralContainerCommon: core1.EphemeralContainerCommon{ - Name: "test", - }, - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - }, - want: []*InstanceID{ - { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - ephemeralContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "valid instanceID - Node", - args: args{ - ownerReferences: []metav1.OwnerReference{ - { - Kind: "Node", - Name: "nodeName", - }, - }, - containers: []core1.EphemeralContainer{ - { - EphemeralContainerCommon: core1.EphemeralContainerCommon{ - Name: "test", - }, - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - }, - want: []*InstanceID{ - { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - ephemeralContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "valid instanceID - multiple containers", - args: args{ - ownerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "ReplicaSet", - Name: "OwnerTest", - }, - }, - containers: []core1.EphemeralContainer{ - { - EphemeralContainerCommon: core1.EphemeralContainerCommon{ - Name: "test", - }, - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - }, - want: []*InstanceID{ - { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - ephemeralContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "valid instanceID - Replica", - args: args{ - ownerReferences: []metav1.OwnerReference{ - { - Kind: "ReplicaSet", - Name: "OwnerTest", - APIVersion: "apps/v1", - }, - }, - containers: []core1.EphemeralContainer{ - { - EphemeralContainerCommon: core1.EphemeralContainerCommon{ - Name: "test-0", - }, - }, - { - EphemeralContainerCommon: core1.EphemeralContainerCommon{ - Name: "test-1", - }, - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - }, - want: []*InstanceID{ - { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - ephemeralContainerName: "test-0", - }, - { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - ephemeralContainerName: "test-1", - }, - }, - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := listInstanceIDs(tt.args.ownerReferences, tt.args.containers, tt.args.apiVersion, tt.args.namespace, tt.args.kind, tt.args.name) - if (err != nil) != tt.wantErr { - t.Errorf("listInstanceIDs() error = %v, wantErr %v", err, tt.wantErr) - return - } - if len(tt.want) != len(got) { - t.Errorf("listInstanceIDs() len(tt.want) != len(got): %d != %d", len(tt.want), len(got)) - return - } - - for i := range got { - assert.Equal(t, tt.want[i].GetStringFormatted(), got[i].GetStringFormatted()) - assert.Equal(t, tt.want[i].GetHashed(), got[i].GetHashed()) - } - }) - } -} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/initializers.go b/instanceidhandler/v1/ephemeralcontainerinstance/initializers.go deleted file mode 100644 index 085e398..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/initializers.go +++ /dev/null @@ -1,73 +0,0 @@ -package ephemeralcontainerinstance - -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 - } - - ephemeralContainers, err := w.GetEphemeralContainers() - if err != nil { - return nil, err - } - - return listInstanceIDs(ownerReferences, ephemeralContainers, 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.EphemeralContainers, pod.APIVersion, pod.GetNamespace(), pod.Kind, pod.GetName()) -} - -// GenerateInstanceIDFromString generates instance ID from string -// The string format is: apiVersion-/namespace-/kind-/name-/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.ephemeralContainerName = strings.TrimPrefix(fields[4+i], prefixEphemeralContainer) - - 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 -} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/initializers_test.go b/instanceidhandler/v1/ephemeralcontainerinstance/initializers_test.go deleted file mode 100644 index 0805ad5..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/initializers_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package ephemeralcontainerinstance - -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" -) - -// TestEphemeralGenerateInstanceIDFromString tests the instance id initialization -func TestEphemeralGenerateInstanceIDFromString(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/ephemeralContainerName-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/ephemeralContainerName-nginx", - }, - want: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Pod", - name: "nginx", - ephemeralContainerName: "nginx", - }, - wantErr: false, - }, - { - name: "valid input - ReplicaSet", - args: args{ - input: "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-1234/ephemeralContainerName-nginx", - }, - want: &InstanceID{ - apiVersion: "apps/v1", - namespace: "default", - kind: "ReplicaSet", - name: "nginx-1234", - ephemeralContainerName: "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) - } - }) - } -} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/instanceidhandler.go b/instanceidhandler/v1/ephemeralcontainerinstance/instanceidhandler.go deleted file mode 100644 index 7fb9782..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/instanceidhandler.go +++ /dev/null @@ -1,101 +0,0 @@ -package ephemeralcontainerinstance - -import ( - "crypto/sha256" - "encoding/hex" - "fmt" - - "github.com/kubescape/k8s-interface/instanceidhandler" - "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" - "github.com/kubescape/k8s-interface/k8sinterface" - "github.com/kubescape/k8s-interface/names" -) - -// string format: apiVersion-/namespace-/kind-/name-/ephemeralContainerName- -const ( - prefixEphemeralContainer = "ephemeralContainerName-" - stringFormat = helpers.PrefixApiVersion + "%s" + helpers.StringFormatSeparator + helpers.PrefixNamespace + "%s" + helpers.StringFormatSeparator + helpers.PrefixKind + "%s" + helpers.StringFormatSeparator + helpers.PrefixName + "%s" + helpers.StringFormatSeparator + prefixEphemeralContainer + "%s" -) - -const InstanceType helpers.InstanceType = "ephemeralContainer" - -// ensure that InstanceID implements IInstanceID -var _ instanceidhandler.IInstanceID = &InstanceID{} - -type InstanceID struct { - apiVersion string - namespace string - kind string - name string - ephemeralContainerName string -} - -func (id *InstanceID) GetInstanceType() helpers.InstanceType { - return InstanceType -} - -func (id *InstanceID) GetAPIVersion() string { - return id.apiVersion -} - -func (id *InstanceID) GetNamespace() string { - return id.namespace -} - -func (id *InstanceID) GetKind() string { - return id.kind -} - -func (id *InstanceID) GetName() string { - return id.name -} - -func (id *InstanceID) GetContainerName() string { - return id.ephemeralContainerName -} - -func (id *InstanceID) SetAPIVersion(apiVersion string) { - id.apiVersion = apiVersion -} - -func (id *InstanceID) SetNamespace(namespace string) { - id.namespace = namespace -} - -func (id *InstanceID) SetKind(kind string) { - id.kind = kind -} - -func (id *InstanceID) SetName(name string) { - id.name = name -} - -func (id *InstanceID) SetContainerName(ephemeralContainerName string) { - id.ephemeralContainerName = ephemeralContainerName -} - -func (id *InstanceID) GetStringFormatted() string { - return fmt.Sprintf(stringFormat, id.GetAPIVersion(), id.GetNamespace(), id.GetKind(), id.GetName(), id.GetContainerName()) -} - -func (id *InstanceID) GetHashed() string { - hash := sha256.Sum256([]byte(id.GetStringFormatted())) - str := hex.EncodeToString(hash[:]) - return str -} - -func (id *InstanceID) GetLabels() map[string]string { - group, version := k8sinterface.SplitApiVersion(id.GetAPIVersion()) - return map[string]string{ - helpers.ApiGroupMetadataKey: group, - helpers.ApiVersionMetadataKey: version, - helpers.NamespaceMetadataKey: id.GetNamespace(), - helpers.KindMetadataKey: id.GetKind(), - helpers.NameMetadataKey: id.GetName(), - helpers.EphemeralContainerNameMetadataKey: id.GetContainerName(), - } -} - -func (id *InstanceID) GetSlug() (string, error) { - return names.InstanceIDToSlug(id.GetName(), id.GetKind(), id.GetContainerName(), id.GetHashed()) -} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/instanceidhandler_test.go b/instanceidhandler/v1/ephemeralcontainerinstance/instanceidhandler_test.go deleted file mode 100644 index 03e7baf..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/instanceidhandler_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package ephemeralcontainerinstance - -import ( - _ "embed" - "fmt" - "reflect" - "strings" - "testing" - - "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" - "github.com/kubescape/k8s-interface/names" - "github.com/kubescape/k8s-interface/workloadinterface" - "github.com/stretchr/testify/assert" -) - -var ( - //go:embed testdata/service.json - service string - //go:embed testdata/deployment.json - deployment string - //go:embed testdata/jobPod.json - jobPod string - //go:embed testdata/mockPod.json - mockPod string -) - -func checkAllsFunctions(object string, apiversion, namespace, kind, name, containerName, formattedString, expectedHash string, expectedLabels map[string]string) error { - - podWorkload, err := workloadinterface.NewWorkload([]byte(object)) - if err != nil { - return fmt.Errorf(err.Error()) - } - podWorkloadInstanceID, err := GenerateInstanceID(podWorkload) - if err != nil { - return fmt.Errorf("TestCreate: GenerateInstanceID - pod instance ID should be created") - } - if len(podWorkloadInstanceID) != 1 { - return fmt.Errorf("TestCreate: should return only one ") - } - - expected := apiversion - if podWorkloadInstanceID[0].GetAPIVersion() != expected { - return fmt.Errorf("TestCreate: GetAPIVersion - wrong , get %s, expected %s", podWorkloadInstanceID[0].GetAPIVersion(), expected) - } - expected = namespace - if podWorkloadInstanceID[0].GetNamespace() != expected { - return fmt.Errorf("TestCreate: GetNamespace - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - expected = kind - if podWorkloadInstanceID[0].GetKind() != expected { - return fmt.Errorf("TestCreate: GetKind - wrong parent kind, get %s, expected %s", podWorkloadInstanceID[0].GetKind(), expected) - } - expected = name - if !strings.Contains(podWorkloadInstanceID[0].GetName(), expected) { - return fmt.Errorf("TestCreate: GetName - wrong parent name, get %s, expected %s", podWorkloadInstanceID[0].GetName(), expected) - } - expected = containerName - if !strings.Contains(podWorkloadInstanceID[0].GetContainerName(), expected) { - return fmt.Errorf("TestCreate: GetContainerName - wrong container name, get %s, expected %s", podWorkloadInstanceID[0].GetContainerName(), expected) - } - expected = formattedString - format := podWorkloadInstanceID[0].GetStringFormatted() - if format != expected { - return fmt.Errorf("TestCreate: GetStringFormatted - fail to format Instance ID in string format, get %s, expected %s", podWorkloadInstanceID[0].GetStringFormatted(), expected) - } - expected = expectedHash - if podWorkloadInstanceID[0].GetHashed() != expected { - return fmt.Errorf("TestCreate: GetHashed - GetHashed, get %s, expected %s", podWorkloadInstanceID[0].GetHashed(), expected) - } - - labels := podWorkloadInstanceID[0].GetLabels() - eq := reflect.DeepEqual(labels, expectedLabels) - if !eq { - return fmt.Errorf("TestCreate: GetLabels - GetLabels failed, get %s, expected %s", podWorkloadInstanceID[0].GetLabels(), expectedLabels) - } - - expected = "123" - podWorkloadInstanceID[0].SetAPIVersion(expected) - if podWorkloadInstanceID[0].GetAPIVersion() != expected { - return fmt.Errorf("TestCreate: SetAPIVersion - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - podWorkloadInstanceID[0].SetNamespace(expected) - if podWorkloadInstanceID[0].GetNamespace() != expected { - return fmt.Errorf("TestCreate: SetNamespace - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - podWorkloadInstanceID[0].SetKind(expected) - if podWorkloadInstanceID[0].GetKind() != expected { - return fmt.Errorf("TestCreate: SetKind - wrong parent kind, get %s, expected %s", podWorkloadInstanceID[0].GetKind(), expected) - } - podWorkloadInstanceID[0].SetName(expected) - if !strings.Contains(podWorkloadInstanceID[0].GetName(), expected) { - return fmt.Errorf("TestCreate: SetName - wrong parent name, get %s, expected %s", podWorkloadInstanceID[0].GetName(), expected) - } - podWorkloadInstanceID[0].SetContainerName(expected) - if !strings.Contains(podWorkloadInstanceID[0].GetContainerName(), expected) { - return fmt.Errorf("TestCreate: SetContainerName - wrong container name, get %s, expected %s", podWorkloadInstanceID[0].GetContainerName(), expected) - } - return nil -} - -func TestInstanceID(t *testing.T) { - serviceWorkload, err := workloadinterface.NewWorkload([]byte(service)) - if err != nil { - t.Fatalf(err.Error()) - } - _, err = GenerateInstanceID(serviceWorkload) - if err == nil { - t.Errorf("can't create instance ID from service") - } - expectedLabels := map[string]string{ - helpers.ApiGroupMetadataKey: "apps", - helpers.ApiVersionMetadataKey: "v1", - helpers.NamespaceMetadataKey: "default", - helpers.KindMetadataKey: "ReplicaSet", - helpers.NameMetadataKey: "nginx-84f5585d68", - helpers.EphemeralContainerNameMetadataKey: "nginx", - } - - err = checkAllsFunctions(deployment, "apps/v1", "default", "ReplicaSet", "nginx-84f5585d68", "nginx", "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/ephemeralContainerName-nginx", "71dd1e22d7246a38b30dc1cb974fe2bf56a6dd0a59299c371b85d1684d3d0f6d", expectedLabels) - if err != nil { - t.Error(err) - } - - expectedLabels = map[string]string{ - helpers.ApiGroupMetadataKey: "batch", - helpers.ApiVersionMetadataKey: "v1", - helpers.NamespaceMetadataKey: "default", - helpers.KindMetadataKey: "Job", - helpers.NameMetadataKey: "nginx-job", - helpers.EphemeralContainerNameMetadataKey: "nginx-job", - } - err = checkAllsFunctions(jobPod, "batch/v1", "default", "Job", "nginx", "nginx-job", "apiVersion-batch/v1/namespace-default/kind-Job/name-nginx-job/ephemeralContainerName-nginx-job", "9f451da6492f37b69a61ed95bbb7ef1f4da5a4c191fda69c5fe87ee0b95bc191", expectedLabels) - if err != nil { - t.Error(err) - } - - expectedLabels = map[string]string{ - helpers.ApiGroupMetadataKey: "", - helpers.ApiVersionMetadataKey: "v1", - helpers.NamespaceMetadataKey: "default", - helpers.KindMetadataKey: "Pod", - helpers.NameMetadataKey: "nginx", - helpers.EphemeralContainerNameMetadataKey: "nginx", - } - err = checkAllsFunctions(mockPod, "v1", "default", "Pod", "nginx", "nginx", "apiVersion-v1/namespace-default/kind-Pod/name-nginx/ephemeralContainerName-nginx", "458ef20641136a35dcdbbbc25f90824e9f263faccfaa0da4b0da8c766514fc15", expectedLabels) - if err != nil { - t.Error(err) - } - -} - -func TestInstanceIDToDisplayName(t *testing.T) { - tt := []struct { - name string - input *InstanceID - want string - wantErr error - }{ - { - name: "valid instanceID produces matching display name", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Pod", - name: "reverse-proxy", - ephemeralContainerName: "nginx", - }, - want: "pod-reverse-proxy-nginx-36fe-11b1", - wantErr: nil, - }, - { - name: "valid instanceID produces matching display name", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "webapp", - ephemeralContainerName: "leader", - }, - want: "service-webapp-leader-d90e-d2d8", - wantErr: nil, - }, - { - name: "valid instanceID without container name produces matching display name", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "webapp", - }, - want: "service-webapp", - wantErr: nil, - }, - { - name: "invalid instanceID produces matching error", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "web/app", - ephemeralContainerName: "leader", - }, - want: "", - wantErr: names.ErrInvalidSlug, - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - got, err := tc.input.GetSlug() - - assert.Equal(t, tc.want, got) - assert.ErrorIs(t, tc.wantErr, err) - }) - } -} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/deployment.json b/instanceidhandler/v1/ephemeralcontainerinstance/testdata/deployment.json deleted file mode 100644 index 979b102..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/deployment.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "creationTimestamp": "2023-03-26T09:10:32Z", - "generateName": "nginx-84f5585d68-", - "labels": { - "app": "nginx", - "pod-template-hash": "84f5585d68" - }, - "name": "nginx-84f5585d68-42mxd", - "namespace": "default", - "ownerReferences": [ - { - "apiVersion": "apps/v1", - "blockOwnerDeletion": true, - "controller": true, - "kind": "ReplicaSet", - "name": "nginx-84f5585d68", - "uid": "1747930a-61ba-4ee1-b1a9-838af6cb094d" - } - ], - "resourceVersion": "8051", - "uid": "fa771236-d117-453c-b5dc-af14127cc648" - }, - "spec": { - "ephemeralContainers": [ - { - "env": [ - { - "name": "DEMO_GREETING", - "value": "Hellofromtheenvironment" - } - ], - "image": "nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d", - "imagePullPolicy": "IfNotPresent", - "name": "nginx", - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "volumeMounts": [ - { - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "kube-api-access-g42z4", - "readOnly": true - } - ] - } - ], - "dnsPolicy": "ClusterFirst", - "enableServiceLinks": true, - "nodeName": "minikube", - "preemptionPolicy": "PreemptLowerPriority", - "priority": 0, - "restartPolicy": "Always", - "schedulerName": "default-scheduler", - "securityContext": {}, - "serviceAccount": "default", - "serviceAccountName": "default", - "terminationGracePeriodSeconds": 30, - "tolerations": [ - { - "effect": "NoExecute", - "key": "node.kubernetes.io/not-ready", - "operator": "Exists", - "tolerationSeconds": 300 - }, - { - "effect": "NoExecute", - "key": "node.kubernetes.io/unreachable", - "operator": "Exists", - "tolerationSeconds": 300 - } - ], - "volumes": [ - { - "name": "kube-api-access-g42z4", - "projected": { - "defaultMode": 420, - "sources": [ - { - "serviceAccountToken": { - "expirationSeconds": 3607, - "path": "token" - } - }, - { - "configMap": { - "items": [ - { - "key": "ca.crt", - "path": "ca.crt" - } - ], - "name": "kube-root-ca.crt" - } - }, - { - "downwardAPI": { - "items": [ - { - "fieldRef": { - "apiVersion": "v1", - "fieldPath": "metadata.namespace" - }, - "path": "namespace" - } - ] - } - } - ] - } - } - ] - }, - "status": { - "conditions": [ - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:32Z", - "status": "True", - "type": "Initialized" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:35Z", - "status": "True", - "type": "Ready" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:35Z", - "status": "True", - "type": "ContainersReady" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:32Z", - "status": "True", - "type": "PodScheduled" - } - ], - "containerStatuses": [ - { - "containerID": "docker://f41bb9e0b603517517603bab82762e33a95fb5f8fad62c9152089875e08e0b38", - "image": "sha256:295c7be079025306c4f1d65997fcf7adb411c88f139ad1d34b537164aa060369", - "imageID": "docker-pullable://nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d", - "lastState": {}, - "name": "nginx", - "ready": true, - "restartCount": 0, - "started": true, - "state": { - "running": { - "startedAt": "2023-03-26T09:10:35Z" - } - } - } - ], - "hostIP": "192.168.49.2", - "phase": "Running", - "podIP": "10.244.0.37", - "podIPs": [ - { - "ip": "10.244.0.37" - } - ], - "qosClass": "BestEffort", - "startTime": "2023-03-26T09:10:32Z" - } -} \ No newline at end of file diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/mockPod.json b/instanceidhandler/v1/ephemeralcontainerinstance/testdata/mockPod.json deleted file mode 100644 index 0df3ccf..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/mockPod.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}\n" - }, - "creationTimestamp": "2023-03-26T12:58:59Z", - "name": "nginx", - "namespace": "default", - "resourceVersion": "18987", - "uid": "be4a982c-6c42-4d0f-895a-b5671a6d3a7b" - }, - "spec": { - "ephemeralContainers": [ - { - "image": "nginx", - "imagePullPolicy": "Always", - "name": "nginx", - "ports": [ - { - "containerPort": 80, - "protocol": "TCP" - } - ], - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "volumeMounts": [ - { - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "kube-api-access-87xwk", - "readOnly": true - } - ] - } - ], - "dnsPolicy": "ClusterFirst", - "enableServiceLinks": true, - "nodeName": "minikube", - "preemptionPolicy": "PreemptLowerPriority", - "priority": 0, - "restartPolicy": "Always", - "schedulerName": "default-scheduler", - "securityContext": {}, - "serviceAccount": "default", - "serviceAccountName": "default", - "terminationGracePeriodSeconds": 30, - "tolerations": [ - { - "effect": "NoExecute", - "key": "node.kubernetes.io/not-ready", - "operator": "Exists", - "tolerationSeconds": 300 - }, - { - "effect": "NoExecute", - "key": "node.kubernetes.io/unreachable", - "operator": "Exists", - "tolerationSeconds": 300 - } - ], - "volumes": [ - { - "name": "kube-api-access-87xwk", - "projected": { - "defaultMode": 420, - "sources": [ - { - "serviceAccountToken": { - "expirationSeconds": 3607, - "path": "token" - } - }, - { - "configMap": { - "items": [ - { - "key": "ca.crt", - "path": "ca.crt" - } - ], - "name": "kube-root-ca.crt" - } - }, - { - "downwardAPI": { - "items": [ - { - "fieldRef": { - "apiVersion": "v1", - "fieldPath": "metadata.namespace" - }, - "path": "namespace" - } - ] - } - } - ] - } - } - ] - }, - "status": { - "conditions": [ - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:58:59Z", - "status": "True", - "type": "Initialized" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:59:02Z", - "status": "True", - "type": "Ready" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:59:02Z", - "status": "True", - "type": "ContainersReady" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:58:59Z", - "status": "True", - "type": "PodScheduled" - } - ], - "containerStatuses": [ - { - "containerID": "docker://21c720f6368e7251e539ed56e2fa749b9e28cc92f2e0cb110575daed5863dc24", - "image": "nginx:latest", - "imageID": "docker-pullable://nginx@sha256:f4e3b6489888647ce1834b601c6c06b9f8c03dee6e097e13ed3e28c01ea3ac8c", - "lastState": {}, - "name": "nginx", - "ready": true, - "restartCount": 0, - "started": true, - "state": { - "running": { - "startedAt": "2023-03-26T12:59:02Z" - } - } - } - ], - "hostIP": "192.168.49.2", - "phase": "Running", - "podIP": "10.244.0.40", - "podIPs": [ - { - "ip": "10.244.0.40" - } - ], - "qosClass": "BestEffort", - "startTime": "2023-03-26T12:58:59Z" - } -} \ No newline at end of file diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/service.json b/instanceidhandler/v1/ephemeralcontainerinstance/testdata/service.json deleted file mode 100644 index f4079ae..0000000 --- a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/service.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "creationTimestamp": "2021-12-06T14:01:16Z", - "labels": { - "app": "armo-vuln-scan", - "app.kubernetes.io\/managed-by": "Helm" - }, - "name": "armo-vuln-scan", - "resourceVersion": "351796", - "uid": "12bd4f9f-3ec6-4113-8ec6-0b8a1c772deb" - }, - "spec": { - "clusterIP": "10.107.7.78", - "clusterIPs": [ - "10.107.7.78" - ], - "internalTrafficPolicy": "Cluster", - "ipFamilies": [ - "IPv4" - ], - "ipFamilyPolicy": "SingleStack", - "ports": [ - { - "port": 8080, - "protocol": "TCP", - "targetPort": 8080 - } - ], - "selector": { - "app": "armo-vuln-scan" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - }, - "status": { - "loadBalancer": {} - } -} \ No newline at end of file diff --git a/instanceidhandler/v1/initcontainerinstance/helpers.go b/instanceidhandler/v1/initcontainerinstance/helpers.go deleted file mode 100644 index dbb1177..0000000 --- a/instanceidhandler/v1/initcontainerinstance/helpers.go +++ /dev/null @@ -1,63 +0,0 @@ -package initcontainerinstance - -import ( - "fmt" - - "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" - core1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func validateInstanceID(instanceID *InstanceID) error { - if instanceID.GetAPIVersion() == "" { - return fmt.Errorf("invalid instanceID: apiVersion cannot be empty") - } - if instanceID.GetNamespace() == "" { - return fmt.Errorf("invalid instanceID: namespace cannot be empty") - } - if instanceID.GetKind() == "" { - return fmt.Errorf("invalid instanceID: kind cannot be empty") - } - if instanceID.GetName() == "" { - return fmt.Errorf("invalid instanceID: name cannot be empty") - } - if instanceID.GetContainerName() == "" { - return fmt.Errorf("invalid instanceID: containerName cannot be empty") - } - return nil -} - -func listInstanceIDs(ownerReferences []metav1.OwnerReference, containers []core1.Container, apiVersion, namespace, kind, name string) ([]InstanceID, error) { - - if len(containers) == 0 { - return []InstanceID{}, nil // initContainers are optional - } - - instanceIDs := make([]InstanceID, 0) - - parentApiVersion, parentKind, parentName := apiVersion, kind, name - - 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{ - apiVersion: parentApiVersion, - namespace: namespace, - kind: parentKind, - name: parentName, - initContainerName: containers[i].Name, - } - - if err := validateInstanceID(instanceID); err != nil { - return nil, fmt.Errorf("failed to validate instance ID: %w", err) - } - - instanceIDs = append(instanceIDs, *instanceID) - } - - return instanceIDs, nil -} diff --git a/instanceidhandler/v1/initcontainerinstance/helpers_test.go b/instanceidhandler/v1/initcontainerinstance/helpers_test.go deleted file mode 100644 index 8b232d9..0000000 --- a/instanceidhandler/v1/initcontainerinstance/helpers_test.go +++ /dev/null @@ -1,328 +0,0 @@ -package initcontainerinstance - -import ( - "testing" - - "github.com/stretchr/testify/assert" - core1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func Test_validateInstanceID(t *testing.T) { - type args struct { - instanceID *InstanceID - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "empty instanceID", - args: args{ - instanceID: &InstanceID{}, - }, - wantErr: true, - }, - { - name: "empty apiVersion", - args: args{ - instanceID: &InstanceID{ - apiVersion: "", - namespace: "test", - kind: "test", - name: "test", - initContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty namespace", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "", - kind: "test", - name: "test", - initContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty kind", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "", - name: "test", - initContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty name", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "", - initContainerName: "test", - }, - }, - wantErr: true, - }, - { - name: "empty initContainerName", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - initContainerName: "", - }, - }, - wantErr: true, - }, - { - name: "valid instanceID", - args: args{ - instanceID: &InstanceID{ - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - initContainerName: "test", - }, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := validateInstanceID(tt.args.instanceID); (err != nil) != tt.wantErr { - t.Errorf("validateInstanceID() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_listInstanceIDs(t *testing.T) { - type args struct { - ownerReferences []metav1.OwnerReference - containers []core1.Container - apiVersion string - namespace string - kind string - name string - } - tests := []struct { - name string - args args - want []*InstanceID - wantErr bool - }{ - { - name: "empty ownerReferences", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.Container{ - { - Name: "test", - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - }, - want: []*InstanceID{ - { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - initContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "empty containers", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.Container{}, - apiVersion: "test", - namespace: "test", - kind: "test", - name: "test", - }, - want: []*InstanceID{}, - wantErr: false, - }, - { - name: "invalid instanceID", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.Container{}, - apiVersion: "", - namespace: "test", - kind: "test", - name: "test", - }, - want: []*InstanceID{}, - wantErr: false, - }, - { - name: "valid instanceID - Pod", - args: args{ - ownerReferences: []metav1.OwnerReference{}, - containers: []core1.Container{ - { - Name: "test", - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - }, - want: []*InstanceID{ - { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "test", - initContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "valid instanceID - Node", - args: args{ - ownerReferences: []metav1.OwnerReference{ - { - Kind: "Node", - Name: "nodeName", - }, - }, - containers: []core1.Container{ - { - Name: "test", - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - }, - want: []*InstanceID{ - { - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - initContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "valid instanceID - multiple containers", - args: args{ - ownerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "ReplicaSet", - Name: "OwnerTest", - }, - }, - containers: []core1.Container{ - { - Name: "test", - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - }, - want: []*InstanceID{ - { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - initContainerName: "test", - }, - }, - wantErr: false, - }, - { - name: "valid instanceID - Replica", - args: args{ - ownerReferences: []metav1.OwnerReference{ - { - Kind: "ReplicaSet", - Name: "OwnerTest", - APIVersion: "apps/v1", - }, - }, - containers: []core1.Container{ - { - Name: "test-0", - }, - { - Name: "test-1", - }, - }, - apiVersion: "test", - namespace: "test", - kind: "Pod", - name: "podName", - }, - want: []*InstanceID{ - { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - initContainerName: "test-0", - }, - { - apiVersion: "apps/v1", - namespace: "test", - kind: "ReplicaSet", - name: "OwnerTest", - initContainerName: "test-1", - }, - }, - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := listInstanceIDs(tt.args.ownerReferences, tt.args.containers, tt.args.apiVersion, tt.args.namespace, tt.args.kind, tt.args.name) - if (err != nil) != tt.wantErr { - t.Errorf("listInstanceIDs() error = %v, wantErr %v", err, tt.wantErr) - return - } - if len(tt.want) != len(got) { - t.Errorf("listInstanceIDs() len(tt.want) != len(got): %d != %d", len(tt.want), len(got)) - return - } - - for i := range got { - assert.Equal(t, tt.want[i].GetStringFormatted(), got[i].GetStringFormatted()) - assert.Equal(t, tt.want[i].GetHashed(), got[i].GetHashed()) - } - }) - } -} diff --git a/instanceidhandler/v1/initcontainerinstance/initializers.go b/instanceidhandler/v1/initcontainerinstance/initializers.go deleted file mode 100644 index 64d9feb..0000000 --- a/instanceidhandler/v1/initcontainerinstance/initializers.go +++ /dev/null @@ -1,73 +0,0 @@ -package initcontainerinstance - -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 - } - - initContainers, err := w.GetInitContainers() - if err != nil { - return nil, err - } - - return listInstanceIDs(ownerReferences, initContainers, 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.InitContainers, pod.APIVersion, pod.GetNamespace(), pod.Kind, pod.GetName()) -} - -// GenerateInstanceIDFromString generates instance ID from string -// The string format is: apiVersion-/namespace-/kind-/name-/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.initContainerName = strings.TrimPrefix(fields[4+i], prefixInitContainer) - - 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 -} diff --git a/instanceidhandler/v1/initcontainerinstance/initializers_test.go b/instanceidhandler/v1/initcontainerinstance/initializers_test.go deleted file mode 100644 index 0d3fe4c..0000000 --- a/instanceidhandler/v1/initcontainerinstance/initializers_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package initcontainerinstance - -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/initContainerName-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/initContainerName-nginx", - }, - want: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Pod", - name: "nginx", - initContainerName: "nginx", - }, - wantErr: false, - }, - { - name: "valid input - ReplicaSet", - args: args{ - input: "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-1234/initContainerName-nginx", - }, - want: &InstanceID{ - apiVersion: "apps/v1", - namespace: "default", - kind: "ReplicaSet", - name: "nginx-1234", - initContainerName: "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) - } - }) - } -} diff --git a/instanceidhandler/v1/initcontainerinstance/instanceidhandler.go b/instanceidhandler/v1/initcontainerinstance/instanceidhandler.go deleted file mode 100644 index 3f74cf7..0000000 --- a/instanceidhandler/v1/initcontainerinstance/instanceidhandler.go +++ /dev/null @@ -1,101 +0,0 @@ -package initcontainerinstance - -import ( - "crypto/sha256" - "encoding/hex" - "fmt" - - "github.com/kubescape/k8s-interface/instanceidhandler" - "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" - "github.com/kubescape/k8s-interface/k8sinterface" - "github.com/kubescape/k8s-interface/names" -) - -// string format: apiVersion-/namespace-/kind-/name-/initContainerName- -const ( - prefixInitContainer = "initContainerName-" - stringFormat = helpers.PrefixApiVersion + "%s" + helpers.StringFormatSeparator + helpers.PrefixNamespace + "%s" + helpers.StringFormatSeparator + helpers.PrefixKind + "%s" + helpers.StringFormatSeparator + helpers.PrefixName + "%s" + helpers.StringFormatSeparator + prefixInitContainer + "%s" -) - -const InstanceType helpers.InstanceType = "initContainer" - -// ensure that InstanceID implements IInstanceID -var _ instanceidhandler.IInstanceID = &InstanceID{} - -type InstanceID struct { - apiVersion string - namespace string - kind string - name string - initContainerName string -} - -func (id *InstanceID) GetInstanceType() helpers.InstanceType { - return InstanceType -} - -func (id *InstanceID) GetAPIVersion() string { - return id.apiVersion -} - -func (id *InstanceID) GetNamespace() string { - return id.namespace -} - -func (id *InstanceID) GetKind() string { - return id.kind -} - -func (id *InstanceID) GetName() string { - return id.name -} - -func (id *InstanceID) GetContainerName() string { - return id.initContainerName -} - -func (id *InstanceID) SetAPIVersion(apiVersion string) { - id.apiVersion = apiVersion -} - -func (id *InstanceID) SetNamespace(namespace string) { - id.namespace = namespace -} - -func (id *InstanceID) SetKind(kind string) { - id.kind = kind -} - -func (id *InstanceID) SetName(name string) { - id.name = name -} - -func (id *InstanceID) SetContainerName(initContainerName string) { - id.initContainerName = initContainerName -} - -func (id *InstanceID) GetStringFormatted() string { - return fmt.Sprintf(stringFormat, id.GetAPIVersion(), id.GetNamespace(), id.GetKind(), id.GetName(), id.GetContainerName()) -} - -func (id *InstanceID) GetHashed() string { - hash := sha256.Sum256([]byte(id.GetStringFormatted())) - str := hex.EncodeToString(hash[:]) - return str -} - -func (id *InstanceID) GetLabels() map[string]string { - group, version := k8sinterface.SplitApiVersion(id.GetAPIVersion()) - return map[string]string{ - helpers.ApiGroupMetadataKey: group, - helpers.ApiVersionMetadataKey: version, - helpers.NamespaceMetadataKey: id.GetNamespace(), - helpers.KindMetadataKey: id.GetKind(), - helpers.NameMetadataKey: id.GetName(), - helpers.InitContainerNameMetadataKey: id.GetContainerName(), - } -} - -func (id *InstanceID) GetSlug() (string, error) { - return names.InstanceIDToSlug(id.GetName(), id.GetKind(), id.GetContainerName(), id.GetHashed()) -} diff --git a/instanceidhandler/v1/initcontainerinstance/instanceidhandler_test.go b/instanceidhandler/v1/initcontainerinstance/instanceidhandler_test.go deleted file mode 100644 index 3fa99a7..0000000 --- a/instanceidhandler/v1/initcontainerinstance/instanceidhandler_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package initcontainerinstance - -import ( - _ "embed" - "fmt" - "reflect" - "strings" - "testing" - - "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" - "github.com/kubescape/k8s-interface/names" - "github.com/kubescape/k8s-interface/workloadinterface" - "github.com/stretchr/testify/assert" -) - -var ( - //go:embed testdata/service.json - service string - //go:embed testdata/deployment.json - deployment string - //go:embed testdata/jobPod.json - jobPod string - //go:embed testdata/mockPod.json - mockPod string -) - -func checkAllsFunctions(object string, apiversion, namespace, kind, name, containerName, formattedString, expectedHash string, expectedLabels map[string]string) error { - - podWorkload, err := workloadinterface.NewWorkload([]byte(object)) - if err != nil { - return fmt.Errorf(err.Error()) - } - podWorkloadInstanceID, err := GenerateInstanceID(podWorkload) - if err != nil { - return fmt.Errorf("TestCreate: GenerateInstanceID - pod instance ID should be created") - } - if len(podWorkloadInstanceID) != 1 { - return fmt.Errorf("TestCreate: should return only one ") - } - - expected := apiversion - if podWorkloadInstanceID[0].GetAPIVersion() != expected { - return fmt.Errorf("TestCreate: GetAPIVersion - wrong , get %s, expected %s", podWorkloadInstanceID[0].GetAPIVersion(), expected) - } - expected = namespace - if podWorkloadInstanceID[0].GetNamespace() != expected { - return fmt.Errorf("TestCreate: GetNamespace - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - expected = kind - if podWorkloadInstanceID[0].GetKind() != expected { - return fmt.Errorf("TestCreate: GetKind - wrong parent kind, get %s, expected %s", podWorkloadInstanceID[0].GetKind(), expected) - } - expected = name - if !strings.Contains(podWorkloadInstanceID[0].GetName(), expected) { - return fmt.Errorf("TestCreate: GetName - wrong parent name, get %s, expected %s", podWorkloadInstanceID[0].GetName(), expected) - } - expected = containerName - if !strings.Contains(podWorkloadInstanceID[0].GetContainerName(), expected) { - return fmt.Errorf("TestCreate: GetContainerName - wrong container name, get %s, expected %s", podWorkloadInstanceID[0].GetContainerName(), expected) - } - expected = formattedString - format := podWorkloadInstanceID[0].GetStringFormatted() - if format != expected { - return fmt.Errorf("TestCreate: GetStringFormatted - fail to format Instance ID in string format, get %s, expected %s", podWorkloadInstanceID[0].GetStringFormatted(), expected) - } - expected = expectedHash - if podWorkloadInstanceID[0].GetHashed() != expected { - return fmt.Errorf("TestCreate: GetHashed - GetHashed, get %s, expected %s", podWorkloadInstanceID[0].GetHashed(), expected) - } - - labels := podWorkloadInstanceID[0].GetLabels() - eq := reflect.DeepEqual(labels, expectedLabels) - if !eq { - return fmt.Errorf("TestCreate: GetLabels - GetLabels failed, get %s, expected %s", podWorkloadInstanceID[0].GetLabels(), expectedLabels) - } - - expected = "123" - podWorkloadInstanceID[0].SetAPIVersion(expected) - if podWorkloadInstanceID[0].GetAPIVersion() != expected { - return fmt.Errorf("TestCreate: SetAPIVersion - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - podWorkloadInstanceID[0].SetNamespace(expected) - if podWorkloadInstanceID[0].GetNamespace() != expected { - return fmt.Errorf("TestCreate: SetNamespace - wrong namespace, get %s, expected %s", podWorkloadInstanceID[0].GetNamespace(), expected) - } - podWorkloadInstanceID[0].SetKind(expected) - if podWorkloadInstanceID[0].GetKind() != expected { - return fmt.Errorf("TestCreate: SetKind - wrong parent kind, get %s, expected %s", podWorkloadInstanceID[0].GetKind(), expected) - } - podWorkloadInstanceID[0].SetName(expected) - if !strings.Contains(podWorkloadInstanceID[0].GetName(), expected) { - return fmt.Errorf("TestCreate: SetName - wrong parent name, get %s, expected %s", podWorkloadInstanceID[0].GetName(), expected) - } - podWorkloadInstanceID[0].SetContainerName(expected) - if !strings.Contains(podWorkloadInstanceID[0].GetContainerName(), expected) { - return fmt.Errorf("TestCreate: SetContainerName - wrong container name, get %s, expected %s", podWorkloadInstanceID[0].GetContainerName(), expected) - } - return nil -} - -func TestInstanceID(t *testing.T) { - serviceWorkload, err := workloadinterface.NewWorkload([]byte(service)) - if err != nil { - t.Fatalf(err.Error()) - } - _, err = GenerateInstanceID(serviceWorkload) - if err == nil { - t.Errorf("can't create instance ID from service") - } - expectedLabels := map[string]string{ - helpers.ApiGroupMetadataKey: "apps", - helpers.ApiVersionMetadataKey: "v1", - helpers.NamespaceMetadataKey: "default", - helpers.KindMetadataKey: "ReplicaSet", - helpers.NameMetadataKey: "nginx-84f5585d68", - helpers.InitContainerNameMetadataKey: "nginx", - } - - err = checkAllsFunctions(deployment, "apps/v1", "default", "ReplicaSet", "nginx-84f5585d68", "nginx", "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/initContainerName-nginx", "40e99ccb533d6287efc8950b3259e4f64fb3161555f3f83064d0ecff039a9fb4", expectedLabels) - if err != nil { - t.Error(err) - } - - expectedLabels = map[string]string{ - helpers.ApiGroupMetadataKey: "batch", - helpers.ApiVersionMetadataKey: "v1", - helpers.NamespaceMetadataKey: "default", - helpers.KindMetadataKey: "Job", - helpers.NameMetadataKey: "nginx-job", - helpers.InitContainerNameMetadataKey: "nginx-job", - } - err = checkAllsFunctions(jobPod, "batch/v1", "default", "Job", "nginx", "nginx-job", "apiVersion-batch/v1/namespace-default/kind-Job/name-nginx-job/initContainerName-nginx-job", "d2fadc0d6b2b0b91e5910a528d52ed2762b38d8a68835a78f2988678e4f5a2ae", expectedLabels) - if err != nil { - t.Error(err) - } - - expectedLabels = map[string]string{ - helpers.ApiGroupMetadataKey: "", - helpers.ApiVersionMetadataKey: "v1", - helpers.NamespaceMetadataKey: "default", - helpers.KindMetadataKey: "Pod", - helpers.NameMetadataKey: "nginx", - helpers.InitContainerNameMetadataKey: "nginx", - } - err = checkAllsFunctions(mockPod, "v1", "default", "Pod", "nginx", "nginx", "apiVersion-v1/namespace-default/kind-Pod/name-nginx/initContainerName-nginx", "068cfa194f3be808ca49838cc3ecf4dd0af04586aaf640a98ea1ed2ac78ee328", expectedLabels) - if err != nil { - t.Error(err) - } - -} - -func TestInstanceIDToDisplayName(t *testing.T) { - tt := []struct { - name string - input *InstanceID - want string - wantErr error - }{ - { - name: "valid instanceID produces matching display name", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Pod", - name: "reverse-proxy", - initContainerName: "nginx", - }, - want: "pod-reverse-proxy-nginx-441b-9992", - wantErr: nil, - }, - { - name: "valid instanceID produces matching display name", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "webapp", - initContainerName: "leader", - }, - want: "service-webapp-leader-f0b1-9517", - wantErr: nil, - }, - { - name: "valid instanceID without container name produces matching display name", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "webapp", - }, - want: "service-webapp", - wantErr: nil, - }, - { - name: "invalid instanceID produces matching error", - input: &InstanceID{ - apiVersion: "v1", - namespace: "default", - kind: "Service", - name: "web/app", - initContainerName: "leader", - }, - want: "", - wantErr: names.ErrInvalidSlug, - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - got, err := tc.input.GetSlug() - - assert.Equal(t, tc.want, got) - assert.ErrorIs(t, tc.wantErr, err) - }) - } -} diff --git a/instanceidhandler/v1/initcontainerinstance/testdata/mockPod.json b/instanceidhandler/v1/initcontainerinstance/testdata/mockPod.json deleted file mode 100644 index eceb805..0000000 --- a/instanceidhandler/v1/initcontainerinstance/testdata/mockPod.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}\n" - }, - "creationTimestamp": "2023-03-26T12:58:59Z", - "name": "nginx", - "namespace": "default", - "resourceVersion": "18987", - "uid": "be4a982c-6c42-4d0f-895a-b5671a6d3a7b" - }, - "spec": { - "initContainers": [ - { - "image": "nginx", - "imagePullPolicy": "Always", - "name": "nginx", - "ports": [ - { - "containerPort": 80, - "protocol": "TCP" - } - ], - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "volumeMounts": [ - { - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "kube-api-access-87xwk", - "readOnly": true - } - ] - } - ], - "dnsPolicy": "ClusterFirst", - "enableServiceLinks": true, - "nodeName": "minikube", - "preemptionPolicy": "PreemptLowerPriority", - "priority": 0, - "restartPolicy": "Always", - "schedulerName": "default-scheduler", - "securityContext": {}, - "serviceAccount": "default", - "serviceAccountName": "default", - "terminationGracePeriodSeconds": 30, - "tolerations": [ - { - "effect": "NoExecute", - "key": "node.kubernetes.io/not-ready", - "operator": "Exists", - "tolerationSeconds": 300 - }, - { - "effect": "NoExecute", - "key": "node.kubernetes.io/unreachable", - "operator": "Exists", - "tolerationSeconds": 300 - } - ], - "volumes": [ - { - "name": "kube-api-access-87xwk", - "projected": { - "defaultMode": 420, - "sources": [ - { - "serviceAccountToken": { - "expirationSeconds": 3607, - "path": "token" - } - }, - { - "configMap": { - "items": [ - { - "key": "ca.crt", - "path": "ca.crt" - } - ], - "name": "kube-root-ca.crt" - } - }, - { - "downwardAPI": { - "items": [ - { - "fieldRef": { - "apiVersion": "v1", - "fieldPath": "metadata.namespace" - }, - "path": "namespace" - } - ] - } - } - ] - } - } - ] - }, - "status": { - "conditions": [ - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:58:59Z", - "status": "True", - "type": "Initialized" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:59:02Z", - "status": "True", - "type": "Ready" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:59:02Z", - "status": "True", - "type": "ContainersReady" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:58:59Z", - "status": "True", - "type": "PodScheduled" - } - ], - "containerStatuses": [ - { - "containerID": "docker://21c720f6368e7251e539ed56e2fa749b9e28cc92f2e0cb110575daed5863dc24", - "image": "nginx:latest", - "imageID": "docker-pullable://nginx@sha256:f4e3b6489888647ce1834b601c6c06b9f8c03dee6e097e13ed3e28c01ea3ac8c", - "lastState": {}, - "name": "nginx", - "ready": true, - "restartCount": 0, - "started": true, - "state": { - "running": { - "startedAt": "2023-03-26T12:59:02Z" - } - } - } - ], - "hostIP": "192.168.49.2", - "phase": "Running", - "podIP": "10.244.0.40", - "podIPs": [ - { - "ip": "10.244.0.40" - } - ], - "qosClass": "BestEffort", - "startTime": "2023-03-26T12:58:59Z" - } -} \ No newline at end of file diff --git a/instanceidhandler/v1/initcontainerinstance/testdata/service.json b/instanceidhandler/v1/initcontainerinstance/testdata/service.json deleted file mode 100644 index f4079ae..0000000 --- a/instanceidhandler/v1/initcontainerinstance/testdata/service.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "creationTimestamp": "2021-12-06T14:01:16Z", - "labels": { - "app": "armo-vuln-scan", - "app.kubernetes.io\/managed-by": "Helm" - }, - "name": "armo-vuln-scan", - "resourceVersion": "351796", - "uid": "12bd4f9f-3ec6-4113-8ec6-0b8a1c772deb" - }, - "spec": { - "clusterIP": "10.107.7.78", - "clusterIPs": [ - "10.107.7.78" - ], - "internalTrafficPolicy": "Cluster", - "ipFamilies": [ - "IPv4" - ], - "ipFamilyPolicy": "SingleStack", - "ports": [ - { - "port": 8080, - "protocol": "TCP", - "targetPort": 8080 - } - ], - "selector": { - "app": "armo-vuln-scan" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - }, - "status": { - "loadBalancer": {} - } -} \ No newline at end of file diff --git a/instanceidhandler/v1/initializers.go b/instanceidhandler/v1/initializers.go index 48b28a1..8d831c3 100644 --- a/instanceidhandler/v1/initializers.go +++ b/instanceidhandler/v1/initializers.go @@ -1,102 +1,178 @@ package instanceidhandler import ( + "fmt" + "hash" + "hash/fnv" + "math" + "strconv" + "strings" + "github.com/kubescape/k8s-interface/instanceidhandler" "github.com/kubescape/k8s-interface/instanceidhandler/v1/containerinstance" - "github.com/kubescape/k8s-interface/instanceidhandler/v1/ephemeralcontainerinstance" - "github.com/kubescape/k8s-interface/instanceidhandler/v1/initcontainerinstance" "github.com/kubescape/k8s-interface/workloadinterface" - core1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/dump" + "k8s.io/apimachinery/pkg/util/rand" +) + +const ( + Container = "container" + InitContainer = "initContainer" + EphemeralContainer = "ephemeralContainer" ) // GenerateInstanceID generates instance ID from workload func GenerateInstanceID(w workloadinterface.IWorkload) ([]instanceidhandler.IInstanceID, error) { - c, err := containerinstance.GenerateInstanceID(w) + ownerReferences, err := w.GetOwnerReferences() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get owner references: %v", err) + } + var ownerReference *metav1.OwnerReference + var alternateName string + if len(ownerReferences) > 0 { + ownerReference = &ownerReferences[0] + // if the Pod is created by a CronJob, its parent is a Job named after the CronJob + // with the scheduled timestamp appended to it (unix time in minutes). + // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/cronjob/utils.go#L277 + if ownerReference.Kind == "Job" { + s := strings.Split(ownerReference.Name, "-") + if len(s) > 1 && isUnixTimeInMinutes(s[len(s)-1]) { + // calculate pod template hash + // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/controller_utils.go#L1200 + podTemplateSpecHasher := fnv.New32a() + spec, err := w.GetPodSpec() + if err != nil { + return nil, fmt.Errorf("failed to get pod spec: %v", err) + } + DeepHashObject(podTemplateSpecHasher, spec) + s[len(s)-1] = rand.SafeEncodeString(fmt.Sprint(podTemplateSpecHasher.Sum32())) + alternateName = strings.Join(s, "-") + } + } } - l := convertContainersToIInstanceID(c) - initC, err := initcontainerinstance.GenerateInstanceID(w) + containers, err := w.GetContainers() if err != nil { - return l, err + return nil, err } - l = append(l, convertInitContainersToIInstanceID(initC)...) - - ephemeralC, err := ephemeralcontainerinstance.GenerateInstanceID(w) + c, err := containerinstance.ListInstanceIDs(ownerReference, containers, Container, w.GetApiVersion(), w.GetNamespace(), w.GetKind(), w.GetName(), alternateName) if err != nil { - return l, err + return nil, err } - l = append(l, convertEphemeralContainersToIInstanceID(ephemeralC)...) - - return l, nil -} + initContainers, err := w.GetInitContainers() + if err != nil { + return nil, err + } -// GenerateInstanceIDFromPod generates instance ID from pod -func GenerateInstanceIDFromPod(pod *core1.Pod) ([]instanceidhandler.IInstanceID, error) { + initC, err := containerinstance.ListInstanceIDs(ownerReference, initContainers, InitContainer, w.GetApiVersion(), w.GetNamespace(), w.GetKind(), w.GetName(), alternateName) + if err != nil { + return nil, err + } + c = append(c, initC...) - c, err := containerinstance.GenerateInstanceIDFromPod(pod) + ephemeralContainers, err := w.GetEphemeralContainers() if err != nil { return nil, err } - l := convertContainersToIInstanceID(c) - initC, err := initcontainerinstance.GenerateInstanceIDFromPod(pod) + ephemeralC, err := containerinstance.ListInstanceIDs(ownerReference, convertEphemeralToContainers(ephemeralContainers), EphemeralContainer, w.GetApiVersion(), w.GetNamespace(), w.GetKind(), w.GetName(), alternateName) if err != nil { - return l, err + return nil, err } + c = append(c, ephemeralC...) - l = append(l, convertInitContainersToIInstanceID(initC)...) + return convertContainersToIInstanceID(c), nil +} - ephemeralC, err := ephemeralcontainerinstance.GenerateInstanceIDFromPod(pod) - if err != nil { - return l, err +func DeepHashObject(hasher hash.Hash32, pod *core1.PodSpec) { + // sanitize pod sped + dropProjectedVolumesAndMounts(pod) + dropHostname(pod) + hasher.Reset() + _, _ = fmt.Fprintf(hasher, "%v", dump.ForHash(pod)) +} + +// https://github.com/kubernetes/autoscaler/blob/5077f4817bc3105bd61659950833bd6d66edc556/cluster-autoscaler/utils/utils.go#L76 +func dropProjectedVolumesAndMounts(podSpec *core1.PodSpec) { + projectedVolumeNames := map[string]bool{} + var volumes []core1.Volume + for _, v := range podSpec.Volumes { + if v.Projected == nil { + volumes = append(volumes, v) + } else { + projectedVolumeNames[v.Name] = true + } + } + podSpec.Volumes = volumes + + for i := range podSpec.Containers { + var volumeMounts []core1.VolumeMount + for _, mount := range podSpec.Containers[i].VolumeMounts { + if ok := projectedVolumeNames[mount.Name]; !ok { + volumeMounts = append(volumeMounts, mount) + } + } + podSpec.Containers[i].VolumeMounts = volumeMounts } - l = append(l, convertEphemeralContainersToIInstanceID(ephemeralC)...) + for i := range podSpec.InitContainers { + var volumeMounts []core1.VolumeMount + for _, mount := range podSpec.InitContainers[i].VolumeMounts { + if ok := projectedVolumeNames[mount.Name]; !ok { + volumeMounts = append(volumeMounts, mount) + } + } + podSpec.InitContainers[i].VolumeMounts = volumeMounts + } +} - return l, nil +// https://github.com/kubernetes/autoscaler/blob/5077f4817bc3105bd61659950833bd6d66edc556/cluster-autoscaler/utils/utils.go#L109 +func dropHostname(podSpec *core1.PodSpec) { + podSpec.Hostname = "" } -// GenerateInstanceIDFromString generates instance ID from string -// The string format is: apiVersion-/namespace-/kind-/name-/... -func GenerateInstanceIDFromString(input string) (instanceidhandler.IInstanceID, error) { - if instID, err := containerinstance.GenerateInstanceIDFromString(input); err == nil && instID != nil { - return instID, nil +func isUnixTimeInMinutes(s string) bool { + if i, err := strconv.Atoi(s); err == nil { + return i > 0 && i < math.MaxInt64/60 } - if instID, err := initcontainerinstance.GenerateInstanceIDFromString(input); err == nil && instID != nil { - return instID, nil + return false +} + +// GenerateInstanceIDFromPod generates instance ID from pod +// Deprecated: use GenerateInstanceID instead +func GenerateInstanceIDFromPod(pod *core1.Pod) ([]instanceidhandler.IInstanceID, error) { + unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod) + if err != nil { + return nil, fmt.Errorf("convert pod to unstructured: %v", err) } + w := workloadinterface.NewWorkloadObj(unstructuredObj) + return GenerateInstanceID(w) +} - return ephemeralcontainerinstance.GenerateInstanceIDFromString(input) +// GenerateInstanceIDFromString generates instance ID from string +// The string format is: apiVersion-/namespace-/kind-/name-/... +func GenerateInstanceIDFromString(input string) (instanceidhandler.IInstanceID, error) { + return containerinstance.GenerateInstanceIDFromString(input) } // convert list containerinstance.InstanceID to instanceidhandler.IInstanceID func convertContainersToIInstanceID(l []containerinstance.InstanceID) []instanceidhandler.IInstanceID { - li := []instanceidhandler.IInstanceID{} - for _, i := range l { - c := i - li = append(li, &c) - } - return li -} -func convertInitContainersToIInstanceID(l []initcontainerinstance.InstanceID) []instanceidhandler.IInstanceID { - li := []instanceidhandler.IInstanceID{} - for _, i := range l { - c := i - li = append(li, &c) + li := make([]instanceidhandler.IInstanceID, len(l)) + for i := range l { + li[i] = &l[i] } return li } -func convertEphemeralContainersToIInstanceID(l []ephemeralcontainerinstance.InstanceID) []instanceidhandler.IInstanceID { - li := []instanceidhandler.IInstanceID{} - for _, i := range l { - c := i - li = append(li, &c) +func convertEphemeralToContainers(e []core1.EphemeralContainer) []core1.Container { + c := make([]core1.Container, len(e)) + for i := range e { + c[i] = core1.Container(e[i].EphemeralContainerCommon) } - return li + return c } diff --git a/instanceidhandler/v1/initializers_test.go b/instanceidhandler/v1/initializers_test.go index 11bde61..e12b6dc 100644 --- a/instanceidhandler/v1/initializers_test.go +++ b/instanceidhandler/v1/initializers_test.go @@ -2,39 +2,175 @@ package instanceidhandler import ( _ "embed" + "encoding/json" + "fmt" "testing" + "github.com/kubescape/k8s-interface/instanceidhandler" + "github.com/kubescape/k8s-interface/instanceidhandler/v1/containerinstance" "github.com/kubescape/k8s-interface/workloadinterface" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + core1 "k8s.io/api/core/v1" ) var ( + //go:embed testdata/cronjob.json + cronjob string + //go:embed testdata/cronjob1.json + cronjob1 string + //go:embed testdata/cronjob2.json + cronjob2 string + //go:embed testdata/cronjob3.json + cronjob3 string //go:embed testdata/deployment.json deployment string + //go:embed testdata/jobPod.json + jobPod string + //go:embed testdata/mockPod.json + mockPod string ) +func TestGenerateInstanceID(t *testing.T) { + tests := []struct { + name string + sWorkload string + want []instanceidhandler.IInstanceID + wantSlug string + wantErr assert.ErrorAssertionFunc + }{ + { + name: "deployment", + sWorkload: deployment, + want: []instanceidhandler.IInstanceID{ + &containerinstance.InstanceID{ + ApiVersion: "apps/v1", + Namespace: "default", + Kind: "ReplicaSet", + Name: "nginx-84f5585d68", + ContainerName: "nginx", + InstanceType: Container, + }, + &containerinstance.InstanceID{ + ApiVersion: "apps/v1", + Namespace: "default", + Kind: "ReplicaSet", + Name: "nginx-84f5585d68", + ContainerName: "bla", + InstanceType: InitContainer, + }, + &containerinstance.InstanceID{ + ApiVersion: "apps/v1", + Namespace: "default", + Kind: "ReplicaSet", + Name: "nginx-84f5585d68", + ContainerName: "abc", + InstanceType: EphemeralContainer, + }, + }, + wantSlug: "replicaset-nginx-84f5585d68", + wantErr: assert.NoError, + }, + { + name: "job", + sWorkload: jobPod, + want: []instanceidhandler.IInstanceID{ + &containerinstance.InstanceID{ + ApiVersion: "batch/v1", + Namespace: "default", + Kind: "Job", + Name: "nginx-job", + ContainerName: "nginx-job", + InstanceType: Container, + }, + }, + wantSlug: "job-nginx-job", + wantErr: assert.NoError, + }, + { + name: "cronjob", + sWorkload: cronjob, + want: []instanceidhandler.IInstanceID{ + &containerinstance.InstanceID{ + ApiVersion: "batch/v1", + Namespace: "kubescape", + Kind: "Job", + Name: "kubevuln-scheduler-28677846", + ContainerName: "kubevuln-scheduler", + InstanceType: Container, + AlternateName: "kubevuln-scheduler-b449cf78f", + }, + }, + wantSlug: "job-kubevuln-scheduler-b449cf78f", + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wp, err := workloadinterface.NewWorkload([]byte(tt.sWorkload)) + require.NoError(t, err) + got, err := GenerateInstanceID(wp) + if !tt.wantErr(t, err, fmt.Sprintf("GenerateInstanceID - %s", tt.name)) { + return + } + assert.Equalf(t, tt.want, got, "GenerateInstanceID - %s", tt.name) + instanceID := got[0].(*containerinstance.InstanceID) + // replicate filtered SBOM and application profile slug generation + slug, err := instanceID.GetSlug(true) + assert.NoError(t, err) + assert.Equalf(t, tt.wantSlug, slug, "GenerateInstanceID - %s", tt.name) + }) + } +} + +// TestSameSlug ensures generated slug stays the same for subsequent cronjob runs +func TestSameSlug(t *testing.T) { + for _, s := range []string{cronjob1, cronjob2, cronjob3} { + wp, err := workloadinterface.NewWorkload([]byte(s)) + require.NoError(t, err) + ins, err := GenerateInstanceID(wp) + require.NoError(t, err) + slug, err := ins[0].(*containerinstance.InstanceID).GetSlug(true) + require.NoError(t, err) + assert.Equal(t, "job-hello-65866c76d6", slug) + slugFull, err := ins[0].(*containerinstance.InstanceID).GetSlug(false) + require.NoError(t, err) + assert.Equal(t, "job-hello-65866c76d6-hello-d9a7-58fb", slugFull) + } +} + // Test_InitInstanceID tests the instance id initialization func TestInitInstanceID(t *testing.T) { - wp, err := workloadinterface.NewWorkload([]byte(deployment)) - if err != nil { - t.Fatalf(err.Error()) - } + wp, err := workloadinterface.NewWorkload([]byte(mockPod)) + require.NoError(t, err) insFromWorkload, err := GenerateInstanceID(wp) - if err != nil { - t.Fatalf("can't create instance ID from pod") - } + require.NoError(t, err) + + p := &core1.Pod{} + err = json.Unmarshal([]byte(mockPod), p) + require.NoError(t, err) + insFromPod, err := GenerateInstanceIDFromPod(p) + require.NoError(t, err) - assert.Equal(t, 3, len(insFromWorkload)) + assert.NotEqual(t, 0, len(insFromWorkload)) + assert.Equal(t, len(insFromWorkload), len(insFromPod)) - assert.Equal(t, "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/containerName-nginx", insFromWorkload[0].GetStringFormatted()) - assert.Equal(t, "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/initContainerName-bla", insFromWorkload[1].GetStringFormatted()) - assert.Equal(t, "apiVersion-apps/v1/namespace-default/kind-ReplicaSet/name-nginx-84f5585d68/ephemeralContainerName-abc", insFromWorkload[2].GetStringFormatted()) + for i := range insFromWorkload { + compare(t, insFromWorkload[i].(*containerinstance.InstanceID), insFromPod[i].(*containerinstance.InstanceID)) + } + + insFromString, err := GenerateInstanceIDFromString("apiVersion-v1/namespace-default/kind-Pod/name-nginx/containerName-nginx") //insFromWorkload[0].GetStringFormatted()) + require.NoError(t, err) + compare(t, insFromWorkload[0].(*containerinstance.InstanceID), insFromString.(*containerinstance.InstanceID)) +} - s0, _ := insFromWorkload[0].GetSlug() - assert.Equal(t, "replicaset-nginx-84f5585d68-nginx-5736-6feb", s0) - s1, _ := insFromWorkload[1].GetSlug() - assert.Equal(t, "replicaset-nginx-84f5585d68-bla-c2db-8092", s1) - s2, _ := insFromWorkload[2].GetSlug() - assert.Equal(t, "replicaset-nginx-84f5585d68-abc-725c-ef45", s2) +func compare(t *testing.T, a, b *containerinstance.InstanceID) { + assert.Equal(t, a.GetHashed(), b.GetHashed()) + assert.Equal(t, a.GetStringFormatted(), b.GetStringFormatted()) + assert.Equal(t, a.ApiVersion, b.ApiVersion) + assert.Equal(t, a.Namespace, b.Namespace) + assert.Equal(t, a.Kind, b.Kind) + assert.Equal(t, a.Name, b.Name) + assert.Equal(t, a.ContainerName, b.ContainerName) } diff --git a/instanceidhandler/v1/testdata/cronjob.json b/instanceidhandler/v1/testdata/cronjob.json new file mode 100644 index 0000000..564cfdf --- /dev/null +++ b/instanceidhandler/v1/testdata/cronjob.json @@ -0,0 +1,189 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "creationTimestamp": "2024-07-11T04:06:00Z", + "generateName": "kubevuln-scheduler-28677846-", + "labels": { + "app": "kubevuln-scheduler", + "app.kubernetes.io/name": "kubevuln-scheduler", + "armo.tier": "vuln-scan", + "batch.kubernetes.io/controller-uid": "059940bf-f037-4c0b-9a0d-a6633f761f70", + "batch.kubernetes.io/job-name": "kubevuln-scheduler-28677846", + "controller-uid": "059940bf-f037-4c0b-9a0d-a6633f761f70", + "job-name": "kubevuln-scheduler-28677846", + "kubescape.io/tier": "core" + }, + "name": "kubevuln-scheduler-28677846-pdblz", + "namespace": "kubescape", + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "Job", + "name": "kubevuln-scheduler-28677846", + "uid": "059940bf-f037-4c0b-9a0d-a6633f761f70" + } + ], + "resourceVersion": "93151", + "uid": "45f7b146-be87-4439-94ff-451b06111933" + }, + "spec": { + "automountServiceAccountToken": false, + "containers": [ + { + "args": [ + "-method=post", + "-scheme=http", + "-host=operator:4002", + "-path=v1/triggerAction", + "-headers=Content-Type:application/json", + "-path-body=/home/ks/request-body.json" + ], + "image": "quay.io/kubescape/http-request:v0.2.8", + "imagePullPolicy": "IfNotPresent", + "name": "kubevuln-scheduler", + "resources": { + "limits": { + "cpu": "10m", + "memory": "20Mi" + }, + "requests": { + "cpu": "1m", + "memory": "10Mi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "readOnlyRootFilesystem": true, + "runAsNonRoot": true, + "runAsUser": 100 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/home/ks/request-body.json", + "name": "kubevuln-scheduler", + "readOnly": true, + "subPath": "request-body.json" + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "nodeName": "kind-control-plane", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Never", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "configMap": { + "defaultMode": 420, + "name": "kubevuln-scheduler" + }, + "name": "kubevuln-scheduler" + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-11T04:06:28Z", + "status": "False", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-11T04:06:00Z", + "reason": "PodCompleted", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-11T04:06:27Z", + "reason": "PodCompleted", + "status": "False", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-11T04:06:27Z", + "reason": "PodCompleted", + "status": "False", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-11T04:06:00Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "containerd://916370bb7dbc1a2f4283c84291d54a3f564fa544b6cf82d52addf0feaa7ebacd", + "image": "quay.io/kubescape/http-request:v0.2.8", + "imageID": "quay.io/kubescape/http-request@sha256:7fa45952fe6c5ce0bbd4c2172b56a0e93345d2d7f13750fac1e720548898b44a", + "lastState": {}, + "name": "kubevuln-scheduler", + "ready": false, + "restartCount": 0, + "started": false, + "state": { + "terminated": { + "containerID": "containerd://916370bb7dbc1a2f4283c84291d54a3f564fa544b6cf82d52addf0feaa7ebacd", + "exitCode": 0, + "finishedAt": "2024-07-11T04:06:26Z", + "reason": "Completed", + "startedAt": "2024-07-11T04:06:25Z" + } + } + } + ], + "hostIP": "172.18.0.2", + "hostIPs": [ + { + "ip": "172.18.0.2" + }, + { + "ip": "fc00:f853:ccd:e793::2" + } + ], + "phase": "Succeeded", + "podIP": "10.244.0.65", + "podIPs": [ + { + "ip": "10.244.0.65" + }, + { + "ip": "fd00:10:244::41" + } + ], + "qosClass": "Burstable", + "startTime": "2024-07-11T04:06:00Z" + } +} diff --git a/instanceidhandler/v1/initcontainerinstance/testdata/jobPod.json b/instanceidhandler/v1/testdata/cronjob1.json similarity index 59% rename from instanceidhandler/v1/initcontainerinstance/testdata/jobPod.json rename to instanceidhandler/v1/testdata/cronjob1.json index 116b8a9..e2f90ea 100644 --- a/instanceidhandler/v1/initcontainerinstance/testdata/jobPod.json +++ b/instanceidhandler/v1/testdata/cronjob1.json @@ -2,16 +2,15 @@ "apiVersion": "v1", "kind": "Pod", "metadata": { - "creationTimestamp": "2023-03-26T12:17:45Z", - "finalizers": [ - "batch.kubernetes.io/job-tracking" - ], - "generateName": "nginx-job-", + "creationTimestamp": "2024-07-17T09:41:00Z", + "generateName": "hello-28686821-", "labels": { - "controller-uid": "1ca79890-1432-48fd-8e04-bd6189c194b7", - "job-name": "nginx-job" + "batch.kubernetes.io/controller-uid": "744bf598-0cec-4915-85db-219cef678729", + "batch.kubernetes.io/job-name": "hello-28686821", + "controller-uid": "744bf598-0cec-4915-85db-219cef678729", + "job-name": "hello-28686821" }, - "name": "nginx-job-88b9z", + "name": "hello-28686821-dj5bf", "namespace": "default", "ownerReferences": [ { @@ -19,26 +18,26 @@ "blockOwnerDeletion": true, "controller": true, "kind": "Job", - "name": "nginx-job", - "uid": "1ca79890-1432-48fd-8e04-bd6189c194b7" + "name": "hello-28686821", + "uid": "744bf598-0cec-4915-85db-219cef678729" } ], - "resourceVersion": "17001", - "uid": "324de883-e0cc-4c8e-b0fb-1ed6aa070c88" + "resourceVersion": "91207", + "uid": "800488fb-4118-4d22-b0bb-7f47923d3078" }, "spec": { - "initContainers": [ + "containers": [ { - "image": "nginx", + "image": "hello-world", "imagePullPolicy": "Always", - "name": "nginx-job", + "name": "hello", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "volumeMounts": [ { "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "kube-api-access-8b7q6", + "name": "kube-api-access-22qbv", "readOnly": true } ] @@ -46,10 +45,10 @@ ], "dnsPolicy": "ClusterFirst", "enableServiceLinks": true, - "nodeName": "minikube", + "nodeName": "kind-control-plane", "preemptionPolicy": "PreemptLowerPriority", "priority": 0, - "restartPolicy": "Never", + "restartPolicy": "OnFailure", "schedulerName": "default-scheduler", "securityContext": {}, "serviceAccount": "default", @@ -71,7 +70,7 @@ ], "volumes": [ { - "name": "kube-api-access-8b7q6", + "name": "kube-api-access-22qbv", "projected": { "defaultMode": 420, "sources": [ @@ -114,55 +113,79 @@ "conditions": [ { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:17:45Z", + "lastTransitionTime": "2024-07-17T09:41:05Z", + "status": "False", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-17T09:41:00Z", + "reason": "PodCompleted", "status": "True", "type": "Initialized" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:18:05Z", - "status": "True", + "lastTransitionTime": "2024-07-17T09:41:00Z", + "reason": "PodCompleted", + "status": "False", "type": "Ready" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:18:05Z", - "status": "True", + "lastTransitionTime": "2024-07-17T09:41:00Z", + "reason": "PodCompleted", + "status": "False", "type": "ContainersReady" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:17:45Z", + "lastTransitionTime": "2024-07-17T09:41:00Z", "status": "True", "type": "PodScheduled" } ], "containerStatuses": [ { - "containerID": "docker://3b15c1346e9054ba3da3f87159cc22ede0f0cce8e309f28022055bb3860b500b", - "image": "nginx:latest", - "imageID": "docker-pullable://nginx@sha256:f4e3b6489888647ce1834b601c6c06b9f8c03dee6e097e13ed3e28c01ea3ac8c", + "containerID": "containerd://9c5330335674733a5163e874e007220c541fe8da2e1ba233a7caecbfeab2012c", + "image": "docker.io/library/hello-world:latest", + "imageID": "docker.io/library/hello-world@sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6", "lastState": {}, - "name": "nginx-job", - "ready": true, + "name": "hello", + "ready": false, "restartCount": 0, - "started": true, + "started": false, "state": { - "running": { - "startedAt": "2023-03-26T12:18:03Z" + "terminated": { + "containerID": "containerd://9c5330335674733a5163e874e007220c541fe8da2e1ba233a7caecbfeab2012c", + "exitCode": 0, + "finishedAt": "2024-07-17T09:41:03Z", + "reason": "Completed", + "startedAt": "2024-07-17T09:41:03Z" } } } ], - "hostIP": "192.168.49.2", - "phase": "Running", - "podIP": "10.244.0.38", + "hostIP": "172.18.0.2", + "hostIPs": [ + { + "ip": "172.18.0.2" + }, + { + "ip": "fc00:f853:ccd:e793::2" + } + ], + "phase": "Succeeded", + "podIP": "10.244.0.36", "podIPs": [ { - "ip": "10.244.0.38" + "ip": "10.244.0.36" + }, + { + "ip": "fd00:10:244::24" } ], "qosClass": "BestEffort", - "startTime": "2023-03-26T12:17:45Z" + "startTime": "2024-07-17T09:41:00Z" } -} \ No newline at end of file +} diff --git a/instanceidhandler/v1/initcontainerinstance/testdata/deployment.json b/instanceidhandler/v1/testdata/cronjob2.json similarity index 58% rename from instanceidhandler/v1/initcontainerinstance/testdata/deployment.json rename to instanceidhandler/v1/testdata/cronjob2.json index ef288da..cfe46cd 100644 --- a/instanceidhandler/v1/initcontainerinstance/testdata/deployment.json +++ b/instanceidhandler/v1/testdata/cronjob2.json @@ -2,46 +2,42 @@ "apiVersion": "v1", "kind": "Pod", "metadata": { - "creationTimestamp": "2023-03-26T09:10:32Z", - "generateName": "nginx-84f5585d68-", + "creationTimestamp": "2024-07-17T09:42:00Z", + "generateName": "hello-28686822-", "labels": { - "app": "nginx", - "pod-template-hash": "84f5585d68" + "batch.kubernetes.io/controller-uid": "e6d9be60-4b18-4039-b96b-497db8af2a10", + "batch.kubernetes.io/job-name": "hello-28686822", + "controller-uid": "e6d9be60-4b18-4039-b96b-497db8af2a10", + "job-name": "hello-28686822" }, - "name": "nginx-84f5585d68-42mxd", + "name": "hello-28686822-stmdf", "namespace": "default", "ownerReferences": [ { - "apiVersion": "apps/v1", + "apiVersion": "batch/v1", "blockOwnerDeletion": true, "controller": true, - "kind": "ReplicaSet", - "name": "nginx-84f5585d68", - "uid": "1747930a-61ba-4ee1-b1a9-838af6cb094d" + "kind": "Job", + "name": "hello-28686822", + "uid": "e6d9be60-4b18-4039-b96b-497db8af2a10" } ], - "resourceVersion": "8051", - "uid": "fa771236-d117-453c-b5dc-af14127cc648" + "resourceVersion": "91308", + "uid": "d21cfad9-d2ea-440d-b02b-dedb65d8c00c" }, "spec": { - "initContainers": [ + "containers": [ { - "env": [ - { - "name": "DEMO_GREETING", - "value": "Hellofromtheenvironment" - } - ], - "image": "nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d", - "imagePullPolicy": "IfNotPresent", - "name": "nginx", + "image": "hello-world", + "imagePullPolicy": "Always", + "name": "hello", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "volumeMounts": [ { "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "kube-api-access-g42z4", + "name": "kube-api-access-xvv29", "readOnly": true } ] @@ -49,10 +45,10 @@ ], "dnsPolicy": "ClusterFirst", "enableServiceLinks": true, - "nodeName": "minikube", + "nodeName": "kind-control-plane", "preemptionPolicy": "PreemptLowerPriority", "priority": 0, - "restartPolicy": "Always", + "restartPolicy": "OnFailure", "schedulerName": "default-scheduler", "securityContext": {}, "serviceAccount": "default", @@ -74,7 +70,7 @@ ], "volumes": [ { - "name": "kube-api-access-g42z4", + "name": "kube-api-access-xvv29", "projected": { "defaultMode": 420, "sources": [ @@ -117,55 +113,79 @@ "conditions": [ { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:32Z", + "lastTransitionTime": "2024-07-17T09:42:05Z", + "status": "False", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-17T09:42:00Z", + "reason": "PodCompleted", "status": "True", "type": "Initialized" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:35Z", - "status": "True", + "lastTransitionTime": "2024-07-17T09:42:00Z", + "reason": "PodCompleted", + "status": "False", "type": "Ready" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:35Z", - "status": "True", + "lastTransitionTime": "2024-07-17T09:42:00Z", + "reason": "PodCompleted", + "status": "False", "type": "ContainersReady" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T09:10:32Z", + "lastTransitionTime": "2024-07-17T09:42:00Z", "status": "True", "type": "PodScheduled" } ], "containerStatuses": [ { - "containerID": "docker://f41bb9e0b603517517603bab82762e33a95fb5f8fad62c9152089875e08e0b38", - "image": "sha256:295c7be079025306c4f1d65997fcf7adb411c88f139ad1d34b537164aa060369", - "imageID": "docker-pullable://nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d", + "containerID": "containerd://9456bbb9359d555983fb5a2d925a568b44713e0df7d3b74e605aff91767fc82f", + "image": "docker.io/library/hello-world:latest", + "imageID": "docker.io/library/hello-world@sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6", "lastState": {}, - "name": "nginx", - "ready": true, + "name": "hello", + "ready": false, "restartCount": 0, - "started": true, + "started": false, "state": { - "running": { - "startedAt": "2023-03-26T09:10:35Z" + "terminated": { + "containerID": "containerd://9456bbb9359d555983fb5a2d925a568b44713e0df7d3b74e605aff91767fc82f", + "exitCode": 0, + "finishedAt": "2024-07-17T09:42:03Z", + "reason": "Completed", + "startedAt": "2024-07-17T09:42:03Z" } } } ], - "hostIP": "192.168.49.2", - "phase": "Running", + "hostIP": "172.18.0.2", + "hostIPs": [ + { + "ip": "172.18.0.2" + }, + { + "ip": "fc00:f853:ccd:e793::2" + } + ], + "phase": "Succeeded", "podIP": "10.244.0.37", "podIPs": [ { "ip": "10.244.0.37" + }, + { + "ip": "fd00:10:244::25" } ], "qosClass": "BestEffort", - "startTime": "2023-03-26T09:10:32Z" + "startTime": "2024-07-17T09:42:00Z" } -} \ No newline at end of file +} diff --git a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/jobPod.json b/instanceidhandler/v1/testdata/cronjob3.json similarity index 60% rename from instanceidhandler/v1/ephemeralcontainerinstance/testdata/jobPod.json rename to instanceidhandler/v1/testdata/cronjob3.json index 8376e08..e2d058b 100644 --- a/instanceidhandler/v1/ephemeralcontainerinstance/testdata/jobPod.json +++ b/instanceidhandler/v1/testdata/cronjob3.json @@ -2,16 +2,15 @@ "apiVersion": "v1", "kind": "Pod", "metadata": { - "creationTimestamp": "2023-03-26T12:17:45Z", - "finalizers": [ - "batch.kubernetes.io/job-tracking" - ], - "generateName": "nginx-job-", + "creationTimestamp": "2024-07-17T09:43:00Z", + "generateName": "hello-28686823-", "labels": { - "controller-uid": "1ca79890-1432-48fd-8e04-bd6189c194b7", - "job-name": "nginx-job" + "batch.kubernetes.io/controller-uid": "d9ffd56c-1193-4fa9-9f3d-1afde393afb4", + "batch.kubernetes.io/job-name": "hello-28686823", + "controller-uid": "d9ffd56c-1193-4fa9-9f3d-1afde393afb4", + "job-name": "hello-28686823" }, - "name": "nginx-job-88b9z", + "name": "hello-28686823-kf8c5", "namespace": "default", "ownerReferences": [ { @@ -19,26 +18,26 @@ "blockOwnerDeletion": true, "controller": true, "kind": "Job", - "name": "nginx-job", - "uid": "1ca79890-1432-48fd-8e04-bd6189c194b7" + "name": "hello-28686823", + "uid": "d9ffd56c-1193-4fa9-9f3d-1afde393afb4" } ], - "resourceVersion": "17001", - "uid": "324de883-e0cc-4c8e-b0fb-1ed6aa070c88" + "resourceVersion": "91406", + "uid": "46dcc4d2-28f6-415d-8816-e8cbeee1fb64" }, "spec": { - "ephemeralContainers": [ + "containers": [ { - "image": "nginx", + "image": "hello-world", "imagePullPolicy": "Always", - "name": "nginx-job", + "name": "hello", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "volumeMounts": [ { "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "kube-api-access-8b7q6", + "name": "kube-api-access-pmx9v", "readOnly": true } ] @@ -46,10 +45,10 @@ ], "dnsPolicy": "ClusterFirst", "enableServiceLinks": true, - "nodeName": "minikube", + "nodeName": "kind-control-plane", "preemptionPolicy": "PreemptLowerPriority", "priority": 0, - "restartPolicy": "Never", + "restartPolicy": "OnFailure", "schedulerName": "default-scheduler", "securityContext": {}, "serviceAccount": "default", @@ -71,7 +70,7 @@ ], "volumes": [ { - "name": "kube-api-access-8b7q6", + "name": "kube-api-access-pmx9v", "projected": { "defaultMode": 420, "sources": [ @@ -114,55 +113,79 @@ "conditions": [ { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:17:45Z", + "lastTransitionTime": "2024-07-17T09:43:04Z", + "status": "False", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-07-17T09:43:00Z", + "reason": "PodCompleted", "status": "True", "type": "Initialized" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:18:05Z", - "status": "True", + "lastTransitionTime": "2024-07-17T09:43:00Z", + "reason": "PodCompleted", + "status": "False", "type": "Ready" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:18:05Z", - "status": "True", + "lastTransitionTime": "2024-07-17T09:43:00Z", + "reason": "PodCompleted", + "status": "False", "type": "ContainersReady" }, { "lastProbeTime": null, - "lastTransitionTime": "2023-03-26T12:17:45Z", + "lastTransitionTime": "2024-07-17T09:43:00Z", "status": "True", "type": "PodScheduled" } ], "containerStatuses": [ { - "containerID": "docker://3b15c1346e9054ba3da3f87159cc22ede0f0cce8e309f28022055bb3860b500b", - "image": "nginx:latest", - "imageID": "docker-pullable://nginx@sha256:f4e3b6489888647ce1834b601c6c06b9f8c03dee6e097e13ed3e28c01ea3ac8c", + "containerID": "containerd://b279f211c1a053eecc047b39586b6a6811ece22b5ffd28eb2084d68ba648c7e8", + "image": "docker.io/library/hello-world:latest", + "imageID": "docker.io/library/hello-world@sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6", "lastState": {}, - "name": "nginx-job", - "ready": true, + "name": "hello", + "ready": false, "restartCount": 0, - "started": true, + "started": false, "state": { - "running": { - "startedAt": "2023-03-26T12:18:03Z" + "terminated": { + "containerID": "containerd://b279f211c1a053eecc047b39586b6a6811ece22b5ffd28eb2084d68ba648c7e8", + "exitCode": 0, + "finishedAt": "2024-07-17T09:43:03Z", + "reason": "Completed", + "startedAt": "2024-07-17T09:43:03Z" } } } ], - "hostIP": "192.168.49.2", - "phase": "Running", + "hostIP": "172.18.0.2", + "hostIPs": [ + { + "ip": "172.18.0.2" + }, + { + "ip": "fc00:f853:ccd:e793::2" + } + ], + "phase": "Succeeded", "podIP": "10.244.0.38", "podIPs": [ { "ip": "10.244.0.38" + }, + { + "ip": "fd00:10:244::26" } ], "qosClass": "BestEffort", - "startTime": "2023-03-26T12:17:45Z" + "startTime": "2024-07-17T09:43:00Z" } -} \ No newline at end of file +} diff --git a/instanceidhandler/v1/testdata/service.json b/instanceidhandler/v1/testdata/service.json deleted file mode 100644 index f4079ae..0000000 --- a/instanceidhandler/v1/testdata/service.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "creationTimestamp": "2021-12-06T14:01:16Z", - "labels": { - "app": "armo-vuln-scan", - "app.kubernetes.io\/managed-by": "Helm" - }, - "name": "armo-vuln-scan", - "resourceVersion": "351796", - "uid": "12bd4f9f-3ec6-4113-8ec6-0b8a1c772deb" - }, - "spec": { - "clusterIP": "10.107.7.78", - "clusterIPs": [ - "10.107.7.78" - ], - "internalTrafficPolicy": "Cluster", - "ipFamilies": [ - "IPv4" - ], - "ipFamilyPolicy": "SingleStack", - "ports": [ - { - "port": 8080, - "protocol": "TCP", - "targetPort": 8080 - } - ], - "selector": { - "app": "armo-vuln-scan" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - }, - "status": { - "loadBalancer": {} - } -} \ No newline at end of file diff --git a/names/slugs.go b/names/slugs.go index a900eb9..b8b6897 100644 --- a/names/slugs.go +++ b/names/slugs.go @@ -155,6 +155,7 @@ func sanitizeInstanceIDSlug(instanceIDSlug, containerName, hashedID string) stri // InstanceIDToSlug retuns a human-friendly representation given a description of an instance ID // // If the given inputs would produce an invalid slug, it returns an appropriate error +// Deprecated: use InstanceID.GetSlug instead func InstanceIDToSlug(name, kind, containerName, hashedID string) (string, error) { slug := sanitizeInstanceIDSlug(fmt.Sprintf(instanceIDSlugHashlessFormat, kind, name), containerName, hashedID)