From 397feb8484bd6a1fad5a67f83345ea0979e1c4b9 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Tue, 18 Jun 2024 16:01:52 +0200 Subject: [PATCH 01/14] Add new template and objectkind configuration --- pkg/templates/all/all.go | 1 + .../internal/params/gen-params.go | 72 +++++++++++++++++++ .../internal/params/params.go | 8 +++ .../volumeclaimtemplates/template.go | 57 +++++++++++++++ .../volumeclaimtemplates/template_test.go | 0 5 files changed, 138 insertions(+) create mode 100644 pkg/templates/volumeclaimtemplates/internal/params/gen-params.go create mode 100644 pkg/templates/volumeclaimtemplates/internal/params/params.go create mode 100644 pkg/templates/volumeclaimtemplates/template.go create mode 100644 pkg/templates/volumeclaimtemplates/template_test.go diff --git a/pkg/templates/all/all.go b/pkg/templates/all/all.go index 01f8db3e7..a62c1ea59 100644 --- a/pkg/templates/all/all.go +++ b/pkg/templates/all/all.go @@ -56,6 +56,7 @@ import ( _ "golang.stackrox.io/kube-linter/pkg/templates/targetport" _ "golang.stackrox.io/kube-linter/pkg/templates/unsafeprocmount" _ "golang.stackrox.io/kube-linter/pkg/templates/updateconfig" + _ "golang.stackrox.io/kube-linter/pkg/templates/volumeclaimtemplates" _ "golang.stackrox.io/kube-linter/pkg/templates/wildcardinrules" _ "golang.stackrox.io/kube-linter/pkg/templates/writablehostmount" ) diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go new file mode 100644 index 000000000..d3f80383f --- /dev/null +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go @@ -0,0 +1,72 @@ +// Code generated by kube-linter template codegen. DO NOT EDIT. +// +build !templatecodegen + +package params + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + "golang.stackrox.io/kube-linter/pkg/check" + "golang.stackrox.io/kube-linter/pkg/templates/util" +) + +var ( + // Use some imports in case they don't get used otherwise. + _ = util.MustParseParameterDesc + _ = fmt.Sprintf + + volumeClaimTemplateParamDesc = util.MustParseParameterDesc(`{ + "Name": "Annotation", + "Type": "string", + "Description": " ", + "Examples": null, + "Enum": null, + "SubParameters": null, + "ArrayElemType": "", + "Required": true, + "NoRegex": false, + "NotNegatable": false, + "XXXStructFieldName": "Annotation", + "XXXIsPointer": false +} +`) + + ParamDescs = []check.ParameterDesc{ + volumeClaimTemplateParamDesc, + } +) + +func (p *Params) Validate() error { + var validationErrors []string + if p.Annotation == "" { + validationErrors = append(validationErrors, "required param annotation not found") + } + if len(validationErrors) > 0 { + return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) + } + return nil +} + +// ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, +// validates it, and returns it. +// The return type is interface{} to satisfy the type in the Template struct. +func ParseAndValidate(m map[string]interface{}) (interface{}, error) { + var p Params + if err := util.DecodeMapStructure(m, &p); err != nil { + return nil, err + } + if err := p.Validate(); err != nil { + return nil, err + } + return p, nil +} + +// WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function +// into a typed one. +func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { + return func(paramsInt interface{}) (check.Func, error) { + return f(paramsInt.(Params)) + } +} diff --git a/pkg/templates/volumeclaimtemplates/internal/params/params.go b/pkg/templates/volumeclaimtemplates/internal/params/params.go new file mode 100644 index 000000000..8fa802de9 --- /dev/null +++ b/pkg/templates/volumeclaimtemplates/internal/params/params.go @@ -0,0 +1,8 @@ +package params + +// Params represents the params accepted by this template. +type Params struct { + // Annotation specifies the required annotation to match. + // +required + Annotation string +} diff --git a/pkg/templates/volumeclaimtemplates/template.go b/pkg/templates/volumeclaimtemplates/template.go new file mode 100644 index 000000000..c10c34d69 --- /dev/null +++ b/pkg/templates/volumeclaimtemplates/template.go @@ -0,0 +1,57 @@ +package volumeclaimtemplates + +import ( + "fmt" + + "golang.stackrox.io/kube-linter/pkg/check" + "golang.stackrox.io/kube-linter/pkg/config" + "golang.stackrox.io/kube-linter/pkg/diagnostic" + "golang.stackrox.io/kube-linter/pkg/lintcontext" + "golang.stackrox.io/kube-linter/pkg/objectkinds" + "golang.stackrox.io/kube-linter/pkg/templates" + "golang.stackrox.io/kube-linter/pkg/templates/volumeclaimtemplates/internal/params" + v1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +const ( + templateKey = "statefulset-volumeclaimtemplate-annotation" +) + +func init() { + templates.Register(check.Template{ + HumanName: "StatefulSet VolumeClaimTemplate Annotation", + Key: templateKey, + Description: "Check if StatefulSet's VolumeClaimTemplate contains a specific annotation", + SupportedObjectKinds: config.ObjectKindsDesc{ + ObjectKinds: []string{objectkinds.DeploymentLike}, + }, + Parameters: params.ParamDescs, + ParseAndValidateParams: params.ParseAndValidate, + Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { + return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { + k8sObj, ok := object.K8sObject.(*unstructured.Unstructured) + if !ok { + return nil + } + if k8sObj.GetKind() != "StatefulSet" { + return nil + } + var statefulSet v1.StatefulSet + err := runtime.DefaultUnstructuredConverter.FromUnstructured(k8sObj.UnstructuredContent(), &statefulSet) + if err != nil { + return nil + } + for _, volumeClaimTemplate := range statefulSet.Spec.VolumeClaimTemplates { + if annotationValue, found := volumeClaimTemplate.Annotations[p.Annotation]; found { + return []diagnostic.Diagnostic{{ + Message: fmt.Sprintf("found annotation %q with value %q in VolumeClaimTemplate", p.Annotation, annotationValue), + }} + } + } + return nil + }, nil + } ), + }) +} diff --git a/pkg/templates/volumeclaimtemplates/template_test.go b/pkg/templates/volumeclaimtemplates/template_test.go new file mode 100644 index 000000000..e69de29bb From aaddccda11785428927abf39c886d1340b9f64f0 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Tue, 18 Jun 2024 16:02:13 +0200 Subject: [PATCH 02/14] Add new template and objectkind configuration --- .../yamls/volumeclaimtemplates.yaml | 7 ++++++ pkg/objectkinds/pvc.go | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 pkg/builtinchecks/yamls/volumeclaimtemplates.yaml create mode 100644 pkg/objectkinds/pvc.go diff --git a/pkg/builtinchecks/yamls/volumeclaimtemplates.yaml b/pkg/builtinchecks/yamls/volumeclaimtemplates.yaml new file mode 100644 index 000000000..0ea411619 --- /dev/null +++ b/pkg/builtinchecks/yamls/volumeclaimtemplates.yaml @@ -0,0 +1,7 @@ +name: "volumeclaimtemplates" +description: "Checks StatefulSets for the presence of a specified annotation in their VolumeClaimTemplate." +remediation: "Add an specified annotation to volumeClaimTemplate." +scope: + objectKinds: + - DeploymentLike +template: "volumeclaimtemplates" diff --git a/pkg/objectkinds/pvc.go b/pkg/objectkinds/pvc.go new file mode 100644 index 000000000..4c8b47ba6 --- /dev/null +++ b/pkg/objectkinds/pvc.go @@ -0,0 +1,24 @@ +package objectkinds + +import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + PersistentVolumeClaim = "PersistentVolumeClaim" +) + +var ( + persistentvolumeclaimGVK = v1.SchemeGroupVersion.WithKind("PersistentVolumeClaim") +) + +func init() { + RegisterObjectKind(PersistentVolumeClaim, MatcherFunc(func(gvk schema.GroupVersionKind) bool { + return gvk == persistentvolumeclaimGVK + })) +} + +func GetPersistentVolumeClaimAPIVersion() string { + return persistentvolumeclaimGVK.GroupVersion().String() +} \ No newline at end of file From 99a2adbc195efae42bd8b4ad42000a151aeca0cc Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Wed, 17 Jul 2024 09:14:07 +0200 Subject: [PATCH 03/14] Update new template and add extract file --- docs/generated/templates.md | 20 ++++++ .../yamls/volumeclaimtemplates.yaml | 7 -- pkg/extract/sts_spec.go | 32 +++++++++ pkg/lintcontext/mocks/context.go | 5 ++ .../internal/params/gen-params.go | 8 +-- .../volumeclaimtemplates/template.go | 71 +++++++++---------- .../volumeclaimtemplates/template_test.go | 66 +++++++++++++++++ 7 files changed, 160 insertions(+), 49 deletions(-) delete mode 100644 pkg/builtinchecks/yamls/volumeclaimtemplates.yaml create mode 100644 pkg/extract/sts_spec.go diff --git a/docs/generated/templates.md b/docs/generated/templates.md index 5bb23a908..0a03b1bf7 100644 --- a/docs/generated/templates.md +++ b/docs/generated/templates.md @@ -759,6 +759,26 @@ KubeLinter supports the following templates: **Supported Objects**: DeploymentLike +## StatefulSet VolumeClaimTemplate Annotation + +**Key**: `statefulset-volumeclaimtemplate-annotation` + +**Description**: Check if StatefulSet's VolumeClaimTemplate contains a specific annotation + +**Supported Objects**: DeploymentLike + + +**Parameters**: + +```yaml +- description: Annotation specifies the required annotation to match. + name: annotation + negationAllowed: true + regexAllowed: true + required: true + type: string +``` + ## Target Port **Key**: `target-port` diff --git a/pkg/builtinchecks/yamls/volumeclaimtemplates.yaml b/pkg/builtinchecks/yamls/volumeclaimtemplates.yaml deleted file mode 100644 index 0ea411619..000000000 --- a/pkg/builtinchecks/yamls/volumeclaimtemplates.yaml +++ /dev/null @@ -1,7 +0,0 @@ -name: "volumeclaimtemplates" -description: "Checks StatefulSets for the presence of a specified annotation in their VolumeClaimTemplate." -remediation: "Add an specified annotation to volumeClaimTemplate." -scope: - objectKinds: - - DeploymentLike -template: "volumeclaimtemplates" diff --git a/pkg/extract/sts_spec.go b/pkg/extract/sts_spec.go new file mode 100644 index 000000000..933d218b5 --- /dev/null +++ b/pkg/extract/sts_spec.go @@ -0,0 +1,32 @@ +package extract + +import ( + "reflect" + + "golang.stackrox.io/kube-linter/pkg/k8sutil" + appsV1 "k8s.io/api/apps/v1" +) + +func StatefulSetSpec(obj k8sutil.Object) (appsV1.StatefulSetSpec, bool) { + switch obj := obj.(type) { + case *appsV1.StatefulSet: + return obj.Spec, true + default: + + kind := obj.GetObjectKind().GroupVersionKind().Kind + if kind != "StatefulSet" { + return appsV1.StatefulSetSpec{}, false + } + + objValue := reflect.Indirect(reflect.ValueOf(obj)) + spec := objValue.FieldByName("Spec") + if !spec.IsValid() { + return appsV1.StatefulSetSpec{}, false + } + statefulSetSpec, ok := spec.Interface().(appsV1.StatefulSetSpec) + if ok { + return statefulSetSpec, true + } + return appsV1.StatefulSetSpec{}, false + } +} diff --git a/pkg/lintcontext/mocks/context.go b/pkg/lintcontext/mocks/context.go index 7a3b8eb87..ee6644db5 100644 --- a/pkg/lintcontext/mocks/context.go +++ b/pkg/lintcontext/mocks/context.go @@ -28,3 +28,8 @@ func (l *MockLintContext) InvalidObjects() []lintcontext.InvalidObject { func NewMockContext() *MockLintContext { return &MockLintContext{objects: make(map[string]k8sutil.Object)} } + +// AddObject adds an object to the MockLintContext +func (l *MockLintContext) AddObject(key string, obj k8sutil.Object) { + l.objects[key] = obj +} diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go index d3f80383f..82683ceaa 100644 --- a/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params.go @@ -17,10 +17,10 @@ var ( _ = util.MustParseParameterDesc _ = fmt.Sprintf - volumeClaimTemplateParamDesc = util.MustParseParameterDesc(`{ - "Name": "Annotation", + annotationParamDesc = util.MustParseParameterDesc(`{ + "Name": "annotation", "Type": "string", - "Description": " ", + "Description": "Annotation specifies the required annotation to match.", "Examples": null, "Enum": null, "SubParameters": null, @@ -34,7 +34,7 @@ var ( `) ParamDescs = []check.ParameterDesc{ - volumeClaimTemplateParamDesc, + annotationParamDesc, } ) diff --git a/pkg/templates/volumeclaimtemplates/template.go b/pkg/templates/volumeclaimtemplates/template.go index c10c34d69..c17a9ca5d 100644 --- a/pkg/templates/volumeclaimtemplates/template.go +++ b/pkg/templates/volumeclaimtemplates/template.go @@ -6,52 +6,47 @@ import ( "golang.stackrox.io/kube-linter/pkg/check" "golang.stackrox.io/kube-linter/pkg/config" "golang.stackrox.io/kube-linter/pkg/diagnostic" + "golang.stackrox.io/kube-linter/pkg/extract" "golang.stackrox.io/kube-linter/pkg/lintcontext" "golang.stackrox.io/kube-linter/pkg/objectkinds" "golang.stackrox.io/kube-linter/pkg/templates" "golang.stackrox.io/kube-linter/pkg/templates/volumeclaimtemplates/internal/params" - v1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" ) const ( - templateKey = "statefulset-volumeclaimtemplate-annotation" + templateKey = "statefulset-volumeclaimtemplate-annotation" ) func init() { - templates.Register(check.Template{ - HumanName: "StatefulSet VolumeClaimTemplate Annotation", - Key: templateKey, - Description: "Check if StatefulSet's VolumeClaimTemplate contains a specific annotation", - SupportedObjectKinds: config.ObjectKindsDesc{ - ObjectKinds: []string{objectkinds.DeploymentLike}, - }, - Parameters: params.ParamDescs, - ParseAndValidateParams: params.ParseAndValidate, - Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { - return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { - k8sObj, ok := object.K8sObject.(*unstructured.Unstructured) - if !ok { - return nil - } - if k8sObj.GetKind() != "StatefulSet" { - return nil - } - var statefulSet v1.StatefulSet - err := runtime.DefaultUnstructuredConverter.FromUnstructured(k8sObj.UnstructuredContent(), &statefulSet) - if err != nil { - return nil - } - for _, volumeClaimTemplate := range statefulSet.Spec.VolumeClaimTemplates { - if annotationValue, found := volumeClaimTemplate.Annotations[p.Annotation]; found { - return []diagnostic.Diagnostic{{ - Message: fmt.Sprintf("found annotation %q with value %q in VolumeClaimTemplate", p.Annotation, annotationValue), - }} - } - } - return nil - }, nil - } ), - }) + templates.Register(check.Template{ + HumanName: "StatefulSet VolumeClaimTemplate Annotation", + Key: templateKey, + Description: "Check if StatefulSet's VolumeClaimTemplate contains a specific annotation", + SupportedObjectKinds: config.ObjectKindsDesc{ + ObjectKinds: []string{objectkinds.DeploymentLike}, + }, + Parameters: params.ParamDescs, + ParseAndValidateParams: params.ParseAndValidate, + Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { + return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { + sts, ok := extract.StatefulSetSpec(object.K8sObject) + if !ok { + fmt.Println("failed to extract StatefulSet spec") + return nil + } + + var diagnostics []diagnostic.Diagnostic + + for _, vct := range sts.VolumeClaimTemplates { + if vct.Annotations == nil || vct.Annotations[p.Annotation] == "" { + diagnostics = append(diagnostics, diagnostic.Diagnostic{ + Message: fmt.Sprintf("StatefulSet's VolumeClaimTemplate is missing required annotation: %s", p.Annotation), + }) + } + } + + return diagnostics + }, nil + }), + }) } diff --git a/pkg/templates/volumeclaimtemplates/template_test.go b/pkg/templates/volumeclaimtemplates/template_test.go index e69de29bb..347b54eef 100644 --- a/pkg/templates/volumeclaimtemplates/template_test.go +++ b/pkg/templates/volumeclaimtemplates/template_test.go @@ -0,0 +1,66 @@ +package volumeclaimtemplates + +import ( + "testing" + + "golang.stackrox.io/kube-linter/pkg/lintcontext/mocks" + "golang.stackrox.io/kube-linter/pkg/templates" + "golang.stackrox.io/kube-linter/pkg/templates/volumeclaimtemplates/internal/params" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestStatefulSetVolumeClaimTemplateAnnotation(t *testing.T) { + tests := []struct { + name string + annotation string // Adjusted to match the parameter name in Params + wantDiags int + }{ + {"WithAnnotation", "value", 0}, + {"WithoutAnnotation", "", 1}, // Empty string or any value for negative case + } + + + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sts := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "statefulset"}, + Spec: appsv1.StatefulSetSpec{ + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Annotations: tt.annotations}}, + }, + }, + } + + // Creating mock lint context + mockLintCtx := mocks.NewMockContext() + mockLintCtx.AddObject("statefulset", sts) + + // Fetching template + template, found := templates.Get("statefulset-volumeclaimtemplate-annotation") + if !found { + t.Fatalf("failed to get template") + } + + // Parsing and validating parameters + params, err := params.ParseAndValidate(map[string]interface{}{}) + if err != nil { + t.Fatalf("failed to parse and validate params: %v", err) + } + + // Instantiating check function + checkFunc, err := template.Instantiate(params) + if err != nil { + t.Fatalf("failed to instantiate check function: %v", err) + } + + // Running the check function + diags := checkFunc(mockLintCtx, mockLintCtx.Objects()[0]) + if len(diags) != tt.wantDiags { + t.Errorf("got %d diagnostics, want %d", len(diags), tt.wantDiags) + } + }) + } +} From 0f69ad8af7c7b33143974d63c5d7167e437b8db2 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Wed, 17 Jul 2024 10:14:54 +0200 Subject: [PATCH 04/14] Update test file --- .../volumeclaimtemplates/template.go | 4 - .../volumeclaimtemplates/template_test.go | 185 +++++++++++++----- 2 files changed, 135 insertions(+), 54 deletions(-) diff --git a/pkg/templates/volumeclaimtemplates/template.go b/pkg/templates/volumeclaimtemplates/template.go index c17a9ca5d..d12dd55b3 100644 --- a/pkg/templates/volumeclaimtemplates/template.go +++ b/pkg/templates/volumeclaimtemplates/template.go @@ -31,12 +31,9 @@ func init() { return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { sts, ok := extract.StatefulSetSpec(object.K8sObject) if !ok { - fmt.Println("failed to extract StatefulSet spec") return nil } - var diagnostics []diagnostic.Diagnostic - for _, vct := range sts.VolumeClaimTemplates { if vct.Annotations == nil || vct.Annotations[p.Annotation] == "" { diagnostics = append(diagnostics, diagnostic.Diagnostic{ @@ -44,7 +41,6 @@ func init() { }) } } - return diagnostics }, nil }), diff --git a/pkg/templates/volumeclaimtemplates/template_test.go b/pkg/templates/volumeclaimtemplates/template_test.go index 347b54eef..bc13f77a6 100644 --- a/pkg/templates/volumeclaimtemplates/template_test.go +++ b/pkg/templates/volumeclaimtemplates/template_test.go @@ -12,55 +12,140 @@ import ( ) func TestStatefulSetVolumeClaimTemplateAnnotation(t *testing.T) { - tests := []struct { - name string - annotation string // Adjusted to match the parameter name in Params - wantDiags int - }{ - {"WithAnnotation", "value", 0}, - {"WithoutAnnotation", "", 1}, // Empty string or any value for negative case - } - - - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sts := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{Name: "statefulset"}, - Spec: appsv1.StatefulSetSpec{ - VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ - {ObjectMeta: metav1.ObjectMeta{Annotations: tt.annotations}}, - }, + t.Run("Annotation searched for exists on template, returns no diagnostics", func(t *testing.T) { + + // GIVEN + // Setup a StatefulSet to check with our new linter + sts := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "statefulset"}, + Spec: appsv1.StatefulSetSpec{ + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"valid": "true"}}}, + }, + }, + } + + // SUT Setup + // Creating mock lint context + mockLintCtx := mocks.NewMockContext() + mockLintCtx.AddObject("statefulset", sts) + + // Fetching template + template, found := templates.Get("statefulset-volumeclaimtemplate-annotation") + if !found { + t.Fatalf("failed to get template") + } + + // Parsing and validating parameters + params, err := params.ParseAndValidate(map[string]interface{}{"annotation": "valid"}) + if err != nil { + t.Fatalf("failed to parse and validate params: %v", err) + } + + // Instantiating check function + checkFunc, err := template.Instantiate(params) + if err != nil { + t.Fatalf("failed to instantiate check function: %v", err) + } + + // WHEN + // Running the check function + diags := checkFunc(mockLintCtx, mockLintCtx.Objects()[0]) + + // THEN + if len(diags) != 0 { + t.Errorf("got %d diagnostics, want %d", len(diags), 0) + } + }) + t.Run("Annotation searched for does not exist on template with annotations, returns a diagnostic", func(t *testing.T) { + + // GIVEN + // Setup a StatefulSet to check with our new linter + sts := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "statefulset"}, + Spec: appsv1.StatefulSetSpec{ + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"annotation": "true"}}}, + }, + }, + } + + // SUT Setup + // Creating mock lint context + mockLintCtx := mocks.NewMockContext() + mockLintCtx.AddObject("statefulset", sts) + + // Fetching template + template, found := templates.Get("statefulset-volumeclaimtemplate-annotation") + if !found { + t.Fatalf("failed to get template") + } + + // Parsing and validating parameters + params, err := params.ParseAndValidate(map[string]interface{}{"annotation": "valid"}) + if err != nil { + t.Fatalf("failed to parse and validate params: %v", err) + } + + // Instantiating check function + checkFunc, err := template.Instantiate(params) + if err != nil { + t.Fatalf("failed to instantiate check function: %v", err) + } + + // WHEN + // Running the check function + diags := checkFunc(mockLintCtx, mockLintCtx.Objects()[0]) + + // THEN + if len(diags) != 1 { + t.Errorf("got %d diagnostics, want %d", len(diags), 1) + } + }) + + t.Run("Annotation searched for does not exist on template without annotations, returns a diagnostic", func(t *testing.T) { + + // GIVEN + // Setup a StatefulSet to check with our new linter + sts := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "statefulset"}, + Spec: appsv1.StatefulSetSpec{ + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{}}, }, - } - - // Creating mock lint context - mockLintCtx := mocks.NewMockContext() - mockLintCtx.AddObject("statefulset", sts) - - // Fetching template - template, found := templates.Get("statefulset-volumeclaimtemplate-annotation") - if !found { - t.Fatalf("failed to get template") - } - - // Parsing and validating parameters - params, err := params.ParseAndValidate(map[string]interface{}{}) - if err != nil { - t.Fatalf("failed to parse and validate params: %v", err) - } - - // Instantiating check function - checkFunc, err := template.Instantiate(params) - if err != nil { - t.Fatalf("failed to instantiate check function: %v", err) - } - - // Running the check function - diags := checkFunc(mockLintCtx, mockLintCtx.Objects()[0]) - if len(diags) != tt.wantDiags { - t.Errorf("got %d diagnostics, want %d", len(diags), tt.wantDiags) - } - }) - } + }, + } + + // SUT Setup + // Creating mock lint context + mockLintCtx := mocks.NewMockContext() + mockLintCtx.AddObject("statefulset", sts) + + // Fetching template + template, found := templates.Get("statefulset-volumeclaimtemplate-annotation") + if !found { + t.Fatalf("failed to get template") + } + + // Parsing and validating parameters + params, err := params.ParseAndValidate(map[string]interface{}{"annotation": "valid"}) + if err != nil { + t.Fatalf("failed to parse and validate params: %v", err) + } + + // Instantiating check function + checkFunc, err := template.Instantiate(params) + if err != nil { + t.Fatalf("failed to instantiate check function: %v", err) + } + + // WHEN + // Running the check function + diags := checkFunc(mockLintCtx, mockLintCtx.Objects()[0]) + + // THEN + if len(diags) != 1 { + t.Errorf("got %d diagnostics, want %d", len(diags), 1) + } + }) } From 1be5bfbac91456cdbfd0f368ca71bf60786365ed Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Thu, 18 Jul 2024 08:39:00 +0200 Subject: [PATCH 05/14] Fix EOF --- pkg/objectkinds/pvc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/objectkinds/pvc.go b/pkg/objectkinds/pvc.go index 4c8b47ba6..d86f11e5f 100644 --- a/pkg/objectkinds/pvc.go +++ b/pkg/objectkinds/pvc.go @@ -21,4 +21,4 @@ func init() { func GetPersistentVolumeClaimAPIVersion() string { return persistentvolumeclaimGVK.GroupVersion().String() -} \ No newline at end of file +} From b62c3743f1b6b6dc8c73897978148d9735a59ea8 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Tue, 11 Mar 2025 14:44:02 +0100 Subject: [PATCH 06/14] Run gofmt --- .../volumeclaimtemplates/template.go | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/pkg/templates/volumeclaimtemplates/template.go b/pkg/templates/volumeclaimtemplates/template.go index d12dd55b3..5ac09c330 100644 --- a/pkg/templates/volumeclaimtemplates/template.go +++ b/pkg/templates/volumeclaimtemplates/template.go @@ -14,35 +14,35 @@ import ( ) const ( - templateKey = "statefulset-volumeclaimtemplate-annotation" + templateKey = "statefulset-volumeclaimtemplate-annotation" ) func init() { - templates.Register(check.Template{ - HumanName: "StatefulSet VolumeClaimTemplate Annotation", - Key: templateKey, - Description: "Check if StatefulSet's VolumeClaimTemplate contains a specific annotation", - SupportedObjectKinds: config.ObjectKindsDesc{ - ObjectKinds: []string{objectkinds.DeploymentLike}, - }, - Parameters: params.ParamDescs, - ParseAndValidateParams: params.ParseAndValidate, - Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { - return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { - sts, ok := extract.StatefulSetSpec(object.K8sObject) - if !ok { - return nil - } - var diagnostics []diagnostic.Diagnostic - for _, vct := range sts.VolumeClaimTemplates { - if vct.Annotations == nil || vct.Annotations[p.Annotation] == "" { - diagnostics = append(diagnostics, diagnostic.Diagnostic{ - Message: fmt.Sprintf("StatefulSet's VolumeClaimTemplate is missing required annotation: %s", p.Annotation), - }) - } - } - return diagnostics - }, nil - }), - }) + templates.Register(check.Template{ + HumanName: "StatefulSet VolumeClaimTemplate Annotation", + Key: templateKey, + Description: "Check if StatefulSet's VolumeClaimTemplate contains a specific annotation", + SupportedObjectKinds: config.ObjectKindsDesc{ + ObjectKinds: []string{objectkinds.DeploymentLike}, + }, + Parameters: params.ParamDescs, + ParseAndValidateParams: params.ParseAndValidate, + Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { + return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { + sts, ok := extract.StatefulSetSpec(object.K8sObject) + if !ok { + return nil + } + var diagnostics []diagnostic.Diagnostic + for _, vct := range sts.VolumeClaimTemplates { + if vct.Annotations == nil || vct.Annotations[p.Annotation] == "" { + diagnostics = append(diagnostics, diagnostic.Diagnostic{ + Message: fmt.Sprintf("StatefulSet's VolumeClaimTemplate is missing required annotation: %s", p.Annotation), + }) + } + } + return diagnostics + }, nil + }), + }) } From 786604a79ae129d5e2fe517a23a86501570c768c Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Thu, 10 Apr 2025 13:56:25 +0200 Subject: [PATCH 07/14] Add more tests --- pkg/extract/sts_spec.go | 5 +- pkg/extract/sts_spec_test.go | 53 +++++++++++++++++++ .../internal/params/gen-params_test.go | 42 +++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 pkg/extract/sts_spec_test.go create mode 100644 pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go diff --git a/pkg/extract/sts_spec.go b/pkg/extract/sts_spec.go index 933d218b5..a94b25cb0 100644 --- a/pkg/extract/sts_spec.go +++ b/pkg/extract/sts_spec.go @@ -8,11 +8,14 @@ import ( ) func StatefulSetSpec(obj k8sutil.Object) (appsV1.StatefulSetSpec, bool) { + if obj == nil { + return appsV1.StatefulSetSpec{}, false + } + switch obj := obj.(type) { case *appsV1.StatefulSet: return obj.Spec, true default: - kind := obj.GetObjectKind().GroupVersionKind().Kind if kind != "StatefulSet" { return appsV1.StatefulSetSpec{}, false diff --git a/pkg/extract/sts_spec_test.go b/pkg/extract/sts_spec_test.go new file mode 100644 index 000000000..489be0674 --- /dev/null +++ b/pkg/extract/sts_spec_test.go @@ -0,0 +1,53 @@ +package extract + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.stackrox.io/kube-linter/pkg/k8sutil" + appsV1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestStatefulSetSpec(t *testing.T) { + tests := []struct { + name string + obj k8sutil.Object + expectedResult appsV1.StatefulSetSpec + expectedFound bool + }{ + { + name: "StatefulSet object", + obj: &appsV1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "test-statefulset"}, + Spec: appsV1.StatefulSetSpec{ + Replicas: new(int32), + }, + }, + expectedResult: appsV1.StatefulSetSpec{ + Replicas: new(int32), + }, + expectedFound: true, + }, + { + name: "Non-StatefulSet object (Deployment)", + obj: &appsV1.Deployment{}, // Geçerli bir objeyi başka bir türde veriyoruz + expectedResult: appsV1.StatefulSetSpec{}, + expectedFound: false, + }, + { + name: "Nil object", + obj: nil, // Nil obje durumu + expectedResult: appsV1.StatefulSetSpec{}, + expectedFound: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, found := StatefulSetSpec(tt.obj) + assert.Equal(t, tt.expectedFound, found) + assert.Equal(t, tt.expectedResult, result) + }) + } +} diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go new file mode 100644 index 000000000..5983c999b --- /dev/null +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go @@ -0,0 +1,42 @@ +package params + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestValidate(t *testing.T) { + tests := []struct { + name string + params Params + expectedError error + }{ + { + name: "valid annotation", + params: Params{ + Annotation: "some-annotation", + }, + expectedError: nil, + }, + { + name: "missing annotation", + params: Params{ + Annotation: "", + }, + expectedError: errors.Errorf("invalid parameters: required param annotation not found"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.params.Validate() + if tt.expectedError == nil { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.expectedError.Error()) + } + }) + } +} From a8aff43323cbc85cf5caeb72535f35fe9ab391c8 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Thu, 10 Apr 2025 14:10:40 +0200 Subject: [PATCH 08/14] Fix lint issues --- pkg/extract/sts_spec_test.go | 12 ++++++------ .../internal/params/gen-params_test.go | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/extract/sts_spec_test.go b/pkg/extract/sts_spec_test.go index 489be0674..a829005a4 100644 --- a/pkg/extract/sts_spec_test.go +++ b/pkg/extract/sts_spec_test.go @@ -30,16 +30,16 @@ func TestStatefulSetSpec(t *testing.T) { expectedFound: true, }, { - name: "Non-StatefulSet object (Deployment)", - obj: &appsV1.Deployment{}, // Geçerli bir objeyi başka bir türde veriyoruz + name: "Non-StatefulSet object (Deployment)", + obj: &appsV1.Deployment{}, // Geçerli bir objeyi başka bir türde veriyoruz expectedResult: appsV1.StatefulSetSpec{}, - expectedFound: false, + expectedFound: false, }, { - name: "Nil object", - obj: nil, // Nil obje durumu + name: "Nil object", + obj: nil, // Nil obje durumu expectedResult: appsV1.StatefulSetSpec{}, - expectedFound: false, + expectedFound: false, }, } diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go index 5983c999b..0ae8878a5 100644 --- a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go @@ -9,9 +9,9 @@ import ( func TestValidate(t *testing.T) { tests := []struct { - name string - params Params - expectedError error + name string + params Params + expectedError error }{ { name: "valid annotation", From c9d9a5a56d04c238dae2c7124359c17ce0cc0a56 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Thu, 10 Apr 2025 14:46:17 +0200 Subject: [PATCH 09/14] Update sts spec tests --- pkg/extract/sts_spec_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/extract/sts_spec_test.go b/pkg/extract/sts_spec_test.go index a829005a4..a60b281e1 100644 --- a/pkg/extract/sts_spec_test.go +++ b/pkg/extract/sts_spec_test.go @@ -31,13 +31,24 @@ func TestStatefulSetSpec(t *testing.T) { }, { name: "Non-StatefulSet object (Deployment)", - obj: &appsV1.Deployment{}, // Geçerli bir objeyi başka bir türde veriyoruz + obj: &appsV1.Deployment{}, expectedResult: appsV1.StatefulSetSpec{}, expectedFound: false, }, { name: "Nil object", - obj: nil, // Nil obje durumu + obj: nil, + expectedResult: appsV1.StatefulSetSpec{}, + expectedFound: false, + }, + { + name: "Non-StatefulSet object with Spec (Deployment)", + obj: &appsV1.Deployment{ + ObjectMeta: metav1.ObjectMeta{Name: "test-deployment"}, + Spec: appsV1.DeploymentSpec{ + Replicas: new(int32), + }, + }, expectedResult: appsV1.StatefulSetSpec{}, expectedFound: false, }, From 5204cd249a84b493948751d8aaee298a401c4c05 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Thu, 10 Apr 2025 14:51:36 +0200 Subject: [PATCH 10/14] Update gen-params test --- .../internal/params/gen-params_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go index 0ae8878a5..9c2091cac 100644 --- a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go @@ -27,6 +27,13 @@ func TestValidate(t *testing.T) { }, expectedError: errors.Errorf("invalid parameters: required param annotation not found"), }, + { + name: "valid annotation but with additional invalid param", + params: Params{ + Annotation: "", + }, + expectedError: errors.Errorf("invalid parameters: required param annotation not found"), + }, } for _, tt := range tests { From 439e71ba4ffa7ef1bc6b5dfa00ddfd62964530dd Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Mon, 21 Apr 2025 17:40:38 +0200 Subject: [PATCH 11/14] Update gen-params test file --- .../internal/params/gen-params_test.go | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go index 9c2091cac..c57814b6d 100644 --- a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go @@ -1,10 +1,13 @@ package params import ( + "errors" "testing" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "golang.stackrox.io/kube-linter/pkg/check" + "golang.stackrox.io/kube-linter/pkg/lintcontext" + "golang.stackrox.io/kube-linter/pkg/diagnostic" ) func TestValidate(t *testing.T) { @@ -25,14 +28,7 @@ func TestValidate(t *testing.T) { params: Params{ Annotation: "", }, - expectedError: errors.Errorf("invalid parameters: required param annotation not found"), - }, - { - name: "valid annotation but with additional invalid param", - params: Params{ - Annotation: "", - }, - expectedError: errors.Errorf("invalid parameters: required param annotation not found"), + expectedError: errors.New("invalid parameters: required param annotation not found"), }, } @@ -47,3 +43,51 @@ func TestValidate(t *testing.T) { }) } } + +func TestParseAndValidate(t *testing.T) { + t.Run("valid map input", func(t *testing.T) { + m := map[string]interface{}{ + "annotation": "required-annotation", + } + result, err := ParseAndValidate(m) + assert.NoError(t, err) + + params, ok := result.(Params) + assert.True(t, ok) + assert.Equal(t, "required-annotation", params.Annotation) + }) + + t.Run("missing annotation in map", func(t *testing.T) { + m := map[string]interface{}{} + _, err := ParseAndValidate(m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "required param annotation not found") + }) +} + +func TestWrapInstantiateFunc(t *testing.T) { + mockFunc := func(p Params) (check.Func, error) { + return func(ctx lintcontext.LintContext, obj lintcontext.Object) []diagnostic.Diagnostic { + return []diagnostic.Diagnostic{ + { + Message: "mocked diagnostic", + }, + } + }, nil + } + + wrapped := WrapInstantiateFunc(mockFunc) + + t.Run("wrapped function works", func(t *testing.T) { + params := Params{Annotation: "test-annotation"} + fn, err := wrapped(params) + assert.NoError(t, err) + + var dummyCtx lintcontext.LintContext + var dummyObj lintcontext.Object + + diagnostics := fn(dummyCtx, dummyObj) + assert.Len(t, diagnostics, 1) + assert.Equal(t, "mocked diagnostic", diagnostics[0].Message) + }) +} From 1f928804797d3186e0e1dd1f665d64ad073b24f6 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Mon, 21 Apr 2025 18:07:12 +0200 Subject: [PATCH 12/14] Update sts_spec test file --- pkg/extract/sts_spec_test.go | 129 +++++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/pkg/extract/sts_spec_test.go b/pkg/extract/sts_spec_test.go index a60b281e1..2ec6555a6 100644 --- a/pkg/extract/sts_spec_test.go +++ b/pkg/extract/sts_spec_test.go @@ -2,63 +2,92 @@ package extract import ( "testing" + "time" - "github.com/stretchr/testify/assert" - "golang.stackrox.io/kube-linter/pkg/k8sutil" appsV1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/stretchr/testify/assert" ) +type fakeStatefulSet struct { + metav1.TypeMeta + metav1.ObjectMeta + Spec appsV1.StatefulSetSpec +} + +func (f *fakeStatefulSet) GetObjectKind() schema.ObjectKind { + return &f.TypeMeta +} + +func (f *fakeStatefulSet) DeepCopyObject() runtime.Object { + return &fakeStatefulSet{ + TypeMeta: f.TypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: f.Name, + Namespace: f.Namespace, + }, + Spec: f.Spec, + } +} + +func (f *fakeStatefulSet) GetAnnotations() map[string]string { + return map[string]string{"key": "value"} // Example annotation +} + +func (f *fakeStatefulSet) GetCreationTimestamp() metav1.Time { + return metav1.Time{Time: time.Now()} +} + func TestStatefulSetSpec(t *testing.T) { - tests := []struct { - name string - obj k8sutil.Object - expectedResult appsV1.StatefulSetSpec - expectedFound bool - }{ - { - name: "StatefulSet object", - obj: &appsV1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{Name: "test-statefulset"}, - Spec: appsV1.StatefulSetSpec{ - Replicas: new(int32), - }, - }, - expectedResult: appsV1.StatefulSetSpec{ - Replicas: new(int32), + t.Run("nil object", func(t *testing.T) { + spec, ok := StatefulSetSpec(nil) + assert.False(t, ok) + assert.Equal(t, appsV1.StatefulSetSpec{}, spec) + }) + + t.Run("typed StatefulSet", func(t *testing.T) { + sampleSpec := appsV1.StatefulSetSpec{ + ServiceName: "my-service", + } + obj := &appsV1.StatefulSet{ + Spec: sampleSpec, + } + spec, ok := StatefulSetSpec(obj) + assert.True(t, ok) + assert.Equal(t, sampleSpec, spec) + }) + + t.Run("fallback via reflection", func(t *testing.T) { + sampleSpec := appsV1.StatefulSetSpec{ + ServiceName: "reflected-service", + } + obj := &fakeStatefulSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "StatefulSet", }, - expectedFound: true, - }, - { - name: "Non-StatefulSet object (Deployment)", - obj: &appsV1.Deployment{}, - expectedResult: appsV1.StatefulSetSpec{}, - expectedFound: false, - }, - { - name: "Nil object", - obj: nil, - expectedResult: appsV1.StatefulSetSpec{}, - expectedFound: false, - }, - { - name: "Non-StatefulSet object with Spec (Deployment)", - obj: &appsV1.Deployment{ - ObjectMeta: metav1.ObjectMeta{Name: "test-deployment"}, - Spec: appsV1.DeploymentSpec{ - Replicas: new(int32), - }, + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-statefulset", + Namespace: "default", }, - expectedResult: appsV1.StatefulSetSpec{}, - expectedFound: false, - }, - } + Spec: sampleSpec, + } + spec, ok := StatefulSetSpec(obj) + assert.True(t, ok) + assert.Equal(t, sampleSpec, spec) + }) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, found := StatefulSetSpec(tt.obj) - assert.Equal(t, tt.expectedFound, found) - assert.Equal(t, tt.expectedResult, result) - }) - } + t.Run("wrong kind", func(t *testing.T) { + obj := &fakeStatefulSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + Spec: appsV1.StatefulSetSpec{}, + } + spec, ok := StatefulSetSpec(obj) + assert.False(t, ok) + assert.Equal(t, appsV1.StatefulSetSpec{}, spec) + }) } From 10326600495c4b209a48d01896eaf8b1ec780691 Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Mon, 21 Apr 2025 18:12:32 +0200 Subject: [PATCH 13/14] Formatting --- .../volumeclaimtemplates/internal/params/gen-params_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go index c57814b6d..b583e49a3 100644 --- a/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go +++ b/pkg/templates/volumeclaimtemplates/internal/params/gen-params_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" "golang.stackrox.io/kube-linter/pkg/check" - "golang.stackrox.io/kube-linter/pkg/lintcontext" "golang.stackrox.io/kube-linter/pkg/diagnostic" + "golang.stackrox.io/kube-linter/pkg/lintcontext" ) func TestValidate(t *testing.T) { From 386d67da2a43f34748e8077ab82f0b8e4e9709ad Mon Sep 17 00:00:00 2001 From: Hanife Tastemel Zeray Date: Tue, 22 Apr 2025 08:51:20 +0200 Subject: [PATCH 14/14] Add test for pvc.go --- pkg/objectkinds/pvc_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 pkg/objectkinds/pvc_test.go diff --git a/pkg/objectkinds/pvc_test.go b/pkg/objectkinds/pvc_test.go new file mode 100644 index 000000000..6abba47fa --- /dev/null +++ b/pkg/objectkinds/pvc_test.go @@ -0,0 +1,13 @@ +package objectkinds_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.stackrox.io/kube-linter/pkg/objectkinds" +) + +func TestGetPersistentVolumeClaimAPIVersion(t *testing.T) { + apiVersion := objectkinds.GetPersistentVolumeClaimAPIVersion() + assert.NotEmpty(t, apiVersion) +}