diff --git a/config/crds/chainsaw.kyverno.io_teststeps.yaml b/config/crds/chainsaw.kyverno.io_teststeps.yaml index 52ea346de..c6c456464 100644 --- a/config/crds/chainsaw.kyverno.io_teststeps.yaml +++ b/config/crds/chainsaw.kyverno.io_teststeps.yaml @@ -17,8 +17,8 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Configuration is the resource that contains the configuration - used to run tests. + description: TestStep is the resource that contains the testStep used to run + tests. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -33,7 +33,7 @@ spec: metadata: type: object spec: - description: Configuration spec. + description: TestStep spec. properties: apply: items: @@ -67,6 +67,8 @@ spec: type: object type: array type: object + required: + - spec type: object served: true storage: true diff --git a/pkg/apis/v1alpha1/test_step.go b/pkg/apis/v1alpha1/test_step.go index 1789b20db..e08f0e928 100644 --- a/pkg/apis/v1alpha1/test_step.go +++ b/pkg/apis/v1alpha1/test_step.go @@ -9,7 +9,7 @@ import ( // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Cluster -// Configuration is the resource that contains the configuration used to run tests. +// TestStep is the resource that contains the testStep used to run tests. type TestStep struct { metav1.TypeMeta `json:",inline"` @@ -17,7 +17,6 @@ type TestStep struct { // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - // Configuration spec. - // +optional + // TestStep spec. Spec TestStepSpec `json:"spec"` } diff --git a/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml b/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml index 52ea346de..c6c456464 100644 --- a/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml +++ b/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml @@ -17,8 +17,8 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Configuration is the resource that contains the configuration - used to run tests. + description: TestStep is the resource that contains the testStep used to run + tests. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -33,7 +33,7 @@ spec: metadata: type: object spec: - description: Configuration spec. + description: TestStep spec. properties: apply: items: @@ -67,6 +67,8 @@ spec: type: object type: array type: object + required: + - spec type: object served: true storage: true diff --git a/pkg/test/load.go b/pkg/test/load.go index 6611567c5..0ed5a5842 100644 --- a/pkg/test/load.go +++ b/pkg/test/load.go @@ -25,7 +25,7 @@ func Load(path string) ([]*v1alpha1.Test, error) { return nil, err } if len(tests) == 0 { - return nil, fmt.Errorf("found no configuration in %s", path) + return nil, fmt.Errorf("found no test in %s", path) } return tests, nil } diff --git a/pkg/test/load_step.go b/pkg/test/load_step.go new file mode 100644 index 000000000..d1ee3515d --- /dev/null +++ b/pkg/test/load_step.go @@ -0,0 +1,61 @@ +package test + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" + "github.com/kyverno/chainsaw/pkg/data" + "github.com/kyverno/chainsaw/pkg/utils/convert" + "github.com/kyverno/chainsaw/pkg/utils/loader" + yamlutils "github.com/kyverno/chainsaw/pkg/utils/yaml" + "sigs.k8s.io/kubectl-validate/pkg/openapiclient" +) + +var testStep_v1alpha1 = v1alpha1.SchemeGroupVersion.WithKind("TestStep") + +func LoadStep(path string) ([]*v1alpha1.TestStep, error) { + content, err := os.ReadFile(filepath.Clean(path)) + if err != nil { + return nil, err + } + testSteps, err := ParseStep(content) + if err != nil { + return nil, err + } + if len(testSteps) == 0 { + return nil, fmt.Errorf("found no testStep in %s", path) + } + return testSteps, nil +} + +func ParseStep(content []byte) ([]*v1alpha1.TestStep, error) { + documents, err := yamlutils.SplitDocuments(content) + if err != nil { + return nil, err + } + var testSteps []*v1alpha1.TestStep + // TODO: no need to allocate a validator every time + loader, err := loader.New(openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder)) + if err != nil { + return nil, err + } + for _, document := range documents { + gvk, untyped, err := loader.Load(document) + if err != nil { + return nil, err + } + switch gvk { + case testStep_v1alpha1: + testStep, err := convert.To[v1alpha1.TestStep](untyped) + if err != nil { + return nil, err + } + testSteps = append(testSteps, testStep) + default: + return nil, fmt.Errorf("type not supported %s", gvk) + } + } + return testSteps, nil +} diff --git a/pkg/test/load_step_test.go b/pkg/test/load_step_test.go new file mode 100644 index 000000000..74420ce2b --- /dev/null +++ b/pkg/test/load_step_test.go @@ -0,0 +1,132 @@ +package test + +import ( + "path/filepath" + "testing" + + "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestLoadStep(t *testing.T) { + basePath := "../../testdata/test/step" + tests := []struct { + name string + path string + want []*v1alpha1.TestStep + wantErr bool + }{ + { + name: "confimap", + path: filepath.Join(basePath, "configmap.yaml"), + wantErr: true, + }, + { + name: "not found", + path: filepath.Join(basePath, "not-found.yaml"), + wantErr: true, + }, + { + name: "empty", + path: filepath.Join(basePath, "empty.yaml"), + wantErr: true, + }, + { + name: "no spec", + path: filepath.Join(basePath, "no-spec.yaml"), + wantErr: true, + }, + { + name: "invalid testStep", + path: filepath.Join(basePath, "invalid-testStep.yaml"), + wantErr: true, + }, + { + name: "ok", + path: filepath.Join(basePath, "ok.yaml"), + want: []*v1alpha1.TestStep{ + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "chainsaw.kyverno.io/v1alpha1", + Kind: "TestStep", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1", + }, + Spec: v1alpha1.TestStepSpec{ + Apply: []v1alpha1.Apply{ + { + File: "foo.yaml", + }, + }, + Assert: []v1alpha1.Assert{ + { + File: "bar.yaml", + }, + }, + }, + }, + }, + }, + { + name: "mlutiple testStep", + path: filepath.Join(basePath, "multiple-testStep.yaml"), + want: []*v1alpha1.TestStep{ + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "chainsaw.kyverno.io/v1alpha1", + Kind: "TestStep", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1", + }, + Spec: v1alpha1.TestStepSpec{ + Apply: []v1alpha1.Apply{ + { + File: "foo.yaml", + }, + }, + Assert: []v1alpha1.Assert{ + { + File: "bar.yaml", + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "chainsaw.kyverno.io/v1alpha1", + Kind: "TestStep", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-2", + }, + Spec: v1alpha1.TestStepSpec{ + Apply: []v1alpha1.Apply{ + { + File: "bar.yaml", + }, + }, + Assert: []v1alpha1.Assert{ + { + File: "foo.yaml", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := LoadStep(tt.path) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.want, got) + }) + } +} diff --git a/testdata/test/step/configmap.yaml b/testdata/test/step/configmap.yaml new file mode 100644 index 000000000..a1d88436f --- /dev/null +++ b/testdata/test/step/configmap.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: default +data: + foo: bar \ No newline at end of file diff --git a/testdata/test/step/empty.yaml b/testdata/test/step/empty.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/testdata/test/step/invalid-testStep.yaml b/testdata/test/step/invalid-testStep.yaml new file mode 100644 index 000000000..7edbfe028 --- /dev/null +++ b/testdata/test/step/invalid-testStep.yaml @@ -0,0 +1,6 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: TestStep +metadata: + name: test-1 +spec: + - foo: bar \ No newline at end of file diff --git a/testdata/test/step/multiple-testStep.yaml b/testdata/test/step/multiple-testStep.yaml new file mode 100644 index 000000000..2dc735e77 --- /dev/null +++ b/testdata/test/step/multiple-testStep.yaml @@ -0,0 +1,20 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: TestStep +metadata: + name: test-1 +spec: + apply: + - file: foo.yaml + assert: + - file: bar.yaml + +--- +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: TestStep +metadata: + name: test-2 +spec: + apply: + - file: bar.yaml + assert: + - file: foo.yaml diff --git a/testdata/test/step/no-spec.yaml b/testdata/test/step/no-spec.yaml new file mode 100644 index 000000000..f83fc7605 --- /dev/null +++ b/testdata/test/step/no-spec.yaml @@ -0,0 +1,4 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: TestStep +metadata: + name: test-1 diff --git a/testdata/test/step/ok.yaml b/testdata/test/step/ok.yaml new file mode 100644 index 000000000..b4cf1e1a0 --- /dev/null +++ b/testdata/test/step/ok.yaml @@ -0,0 +1,9 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: TestStep +metadata: + name: test-1 +spec: + apply: + - file: foo.yaml + assert: + - file: bar.yaml diff --git a/website/docs/apis/chainsaw.v1alpha1.md b/website/docs/apis/chainsaw.v1alpha1.md index f95a84950..1958abb6b 100644 --- a/website/docs/apis/chainsaw.v1alpha1.md +++ b/website/docs/apis/chainsaw.v1alpha1.md @@ -40,7 +40,7 @@ auto_generated: true ## `TestStep` {#chainsaw-kyverno-io-v1alpha1-TestStep} -

Configuration is the resource that contains the configuration used to run tests.

+

TestStep is the resource that contains the testStep used to run tests.

| Field | Type | Required | Description | @@ -48,7 +48,7 @@ auto_generated: true | `apiVersion` | `string` | :white_check_mark: | `chainsaw.kyverno.io/v1alpha1` | | `kind` | `string` | :white_check_mark: | `TestStep` | | `metadata` | [`meta/v1.ObjectMeta`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#objectmeta-v1-meta) | |

Standard object's metadata.

| -| `spec` | [`TestStepSpec`](#chainsaw-kyverno-io-v1alpha1-TestStepSpec) | |

Configuration spec.

| +| `spec` | [`TestStepSpec`](#chainsaw-kyverno-io-v1alpha1-TestStepSpec) | :white_check_mark: |

TestStep spec.

| ## `Apply` {#chainsaw-kyverno-io-v1alpha1-Apply}