diff --git a/cmd/uptest/main.go b/cmd/uptest/main.go index bc6c578..d31da67 100644 --- a/cmd/uptest/main.go +++ b/cmd/uptest/main.go @@ -54,7 +54,8 @@ var ( defaultConditions = e2e.Flag("default-conditions", "Comma separated list of default conditions to wait for a successful test.\n"+ "Conditions could be overridden per resource using \"uptest.upbound.io/conditions\" annotation.").Default("Ready").String() - testDir = e2e.Flag("test-directory", "Directory where kuttl test case will be generated and executed.").Envar("UPTEST_TEST_DIR").Default(filepath.Join(os.TempDir(), "uptest-e2e")).String() + skipDelete = e2e.Flag("skip-delete", "Skip the delete step of the test.").Default("false").Bool() + testDir = e2e.Flag("test-directory", "Directory where kuttl test case will be generated and executed.").Envar("UPTEST_TEST_DIR").Default(filepath.Join(os.TempDir(), "uptest-e2e")).String() ) var ( @@ -121,6 +122,7 @@ func e2eTests() { DefaultConditions: strings.Split(*defaultConditions, ","), DefaultTimeout: *defaultTimeout, Directory: *testDir, + SkipDelete: *skipDelete, } kingpin.FatalIfError(internal.RunTest(o), "cannot run e2e tests successfully") diff --git a/internal/config/config.go b/internal/config/config.go index 05e9ede..77101b2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -56,6 +56,8 @@ type AutomatedTest struct { DefaultTimeout int DefaultConditions []string + + SkipDelete bool } // Manifest represents a resource loaded from an example resource manifest file. diff --git a/internal/templates/renderer.go b/internal/templates/renderer.go index 37371bd..7333dc4 100644 --- a/internal/templates/renderer.go +++ b/internal/templates/renderer.go @@ -38,7 +38,7 @@ var fileTemplates = map[string]string{ // Render renders the specified list of resources as a test case // with the specified configuration. -func Render(tc *config.TestCase, resources []config.Resource) (map[string]string, error) { +func Render(tc *config.TestCase, resources []config.Resource, skipDelete bool) (map[string]string, error) { data := struct { Resources []config.Resource TestCase config.TestCase @@ -49,6 +49,11 @@ func Render(tc *config.TestCase, resources []config.Resource) (map[string]string res := make(map[string]string, len(fileTemplates)) for name, tmpl := range fileTemplates { + // Skip templates with names starting with "03-" if skipDelete is true + if skipDelete && strings.HasPrefix(name, "03-") { + continue + } + t, err := template.New(name).Parse(tmpl) if err != nil { return nil, errors.Wrapf(err, "cannot parse template %q", name) diff --git a/internal/templates/renderer_test.go b/internal/templates/renderer_test.go index 6208734..6878aeb 100644 --- a/internal/templates/renderer_test.go +++ b/internal/templates/renderer_test.go @@ -261,7 +261,186 @@ commands: } for name, tc := range tests { t.Run(name, func(t *testing.T) { - got, err := Render(tc.args.tc, tc.args.resources) + got, err := Render(tc.args.tc, tc.args.resources, false) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("Render(...): -want error, +got error:\n%s", diff) + } + if diff := cmp.Diff(tc.want.out, got); diff != "" { + t.Errorf("Render(...): -want, +got:\n%s", diff) + } + }) + } +} + +func TestRenderWithSkipDelete(t *testing.T) { + type args struct { + tc *config.TestCase + resources []config.Resource + } + type want struct { + out map[string]string + err error + } + tests := map[string]struct { + args args + want want + }{ + "SuccessSingleResource": { + args: args{ + tc: &config.TestCase{ + Timeout: 10, + }, + resources: []config.Resource{ + { + Name: "example-bucket", + KindGroup: "s3.aws.upbound.io", + YAML: bucketManifest, + Conditions: []string{"Test"}, + }, + }, + }, + want: want{ + out: map[string]string{ + "00-apply.yaml": "# This file belongs to the resource apply step.\n---\n" + bucketManifest, + "00-assert.yaml": `# This assert file belongs to the resource apply step. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 10 +commands: +- command: ${KUBECTL} annotate managed --all upjet.upbound.io/test=true --overwrite +- script: echo "Dump MR manifests for the apply assertion step:"; ${KUBECTL} get managed -o yaml +- script: echo "Dump Claim manifests for the apply assertion step:" || ${KUBECTL} get claim --all-namespaces -o yaml +- command: ${KUBECTL} wait s3.aws.upbound.io/example-bucket --for=condition=Test --timeout 10s +`, + "01-update.yaml": `# This file belongs to the resource update step. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +`, + "01-assert.yaml": `# This assert file belongs to the resource update step. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 10 +commands: +- script: echo "Dump MR manifests for the update assertion step:"; ${KUBECTL} get managed -o yaml +`, + "02-assert.yaml": `# This assert file belongs to the resource import step. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 10 +commands: +- script: echo "Dump MR manifests for the import assertion step:"; ${KUBECTL} get managed -o yaml +- command: ${KUBECTL} wait s3.aws.upbound.io/example-bucket --for=condition=Test --timeout 10s +- script: new_id="$(${KUBECTL} get s3.aws.upbound.io/example-bucket -o=jsonpath='{.status.atProvider.id}')" && old_id="$(${KUBECTL} get s3.aws.upbound.io/example-bucket -o=jsonpath='{.metadata.annotations.uptest-old-id}')" && [ "$new_id" = "$old_id" ] +`, + "02-import.yaml": `# This file belongs to the resource import step. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +- command: ${KUBECTL} scale deployment crossplane -n upbound-system --replicas=0 +- script: ${KUBECTL} -n upbound-system get deploy --no-headers -o custom-columns=":metadata.name" | grep "provider-" | xargs ${KUBECTL} -n upbound-system scale deploy --replicas=0 +- command: ${KUBECTL} --subresource=status patch s3.aws.upbound.io/example-bucket --type=merge -p '{"status":{"conditions":[]}}' +- script: ${KUBECTL} annotate s3.aws.upbound.io/example-bucket uptest-old-id=$(${KUBECTL} get s3.aws.upbound.io/example-bucket -o=jsonpath='{.status.atProvider.id}') --overwrite +- command: ${KUBECTL} scale deployment crossplane -n upbound-system --replicas=1 +- script: ${KUBECTL} -n upbound-system get deploy --no-headers -o custom-columns=":metadata.name" | grep "provider-" | xargs ${KUBECTL} -n upbound-system scale deploy --replicas=1 +`, + }, + }, + }, + "SuccessMultipleResource": { + args: args{ + tc: &config.TestCase{ + Timeout: 10, + SetupScriptPath: "/tmp/setup.sh", + TeardownScriptPath: "/tmp/teardown.sh", + }, + resources: []config.Resource{ + { + YAML: bucketManifest, + Name: "example-bucket", + KindGroup: "s3.aws.upbound.io", + PreAssertScriptPath: "/tmp/bucket/pre-assert.sh", + PostDeleteScriptPath: "/tmp/bucket/post-delete.sh", + Conditions: []string{"Test"}, + }, + { + YAML: claimManifest, + Name: "test-cluster-claim", + KindGroup: "cluster.gcp.platformref.upbound.io", + Namespace: "upbound-system", + PostAssertScriptPath: "/tmp/claim/post-assert.sh", + PreDeleteScriptPath: "/tmp/claim/pre-delete.sh", + Conditions: []string{"Ready", "Synced"}, + }, + { + YAML: secretManifest, + Name: "test-secret", + KindGroup: "secret.", + Namespace: "upbound-system", + }, + }, + }, + want: want{ + out: map[string]string{ + "00-apply.yaml": `# This file belongs to the resource apply step. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +- command: /tmp/setup.sh +` + "---\n" + bucketManifest + "---\n" + claimManifest + "---\n" + secretManifest, + "00-assert.yaml": `# This assert file belongs to the resource apply step. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 10 +commands: +- command: ${KUBECTL} annotate managed --all upjet.upbound.io/test=true --overwrite +- script: echo "Dump MR manifests for the apply assertion step:"; ${KUBECTL} get managed -o yaml +- script: echo "Dump Claim manifests for the apply assertion step:" || ${KUBECTL} get claim --all-namespaces -o yaml +- command: /tmp/bucket/pre-assert.sh +- command: ${KUBECTL} wait s3.aws.upbound.io/example-bucket --for=condition=Test --timeout 10s +- command: ${KUBECTL} wait cluster.gcp.platformref.upbound.io/test-cluster-claim --for=condition=Ready --timeout 10s --namespace upbound-system +- command: ${KUBECTL} wait cluster.gcp.platformref.upbound.io/test-cluster-claim --for=condition=Synced --timeout 10s --namespace upbound-system +- command: /tmp/claim/post-assert.sh +`, + "01-update.yaml": `# This file belongs to the resource update step. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +`, + "01-assert.yaml": `# This assert file belongs to the resource update step. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 10 +commands: +- script: echo "Dump MR manifests for the update assertion step:"; ${KUBECTL} get managed -o yaml +`, + "02-assert.yaml": `# This assert file belongs to the resource import step. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 10 +commands: +- script: echo "Dump MR manifests for the import assertion step:"; ${KUBECTL} get managed -o yaml +- command: ${KUBECTL} wait s3.aws.upbound.io/example-bucket --for=condition=Test --timeout 10s +- script: new_id="$(${KUBECTL} get s3.aws.upbound.io/example-bucket -o=jsonpath='{.status.atProvider.id}')" && old_id="$(${KUBECTL} get s3.aws.upbound.io/example-bucket -o=jsonpath='{.metadata.annotations.uptest-old-id}')" && [ "$new_id" = "$old_id" ] +`, + "02-import.yaml": `# This file belongs to the resource import step. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +- command: ${KUBECTL} scale deployment crossplane -n upbound-system --replicas=0 +- script: ${KUBECTL} -n upbound-system get deploy --no-headers -o custom-columns=":metadata.name" | grep "provider-" | xargs ${KUBECTL} -n upbound-system scale deploy --replicas=0 +- command: ${KUBECTL} --subresource=status patch s3.aws.upbound.io/example-bucket --type=merge -p '{"status":{"conditions":[]}}' +- script: ${KUBECTL} annotate s3.aws.upbound.io/example-bucket uptest-old-id=$(${KUBECTL} get s3.aws.upbound.io/example-bucket -o=jsonpath='{.status.atProvider.id}') --overwrite +- command: ${KUBECTL} scale deployment crossplane -n upbound-system --replicas=1 +- script: ${KUBECTL} -n upbound-system get deploy --no-headers -o custom-columns=":metadata.name" | grep "provider-" | xargs ${KUBECTL} -n upbound-system scale deploy --replicas=1 +`, + }, + }, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got, err := Render(tc.args.tc, tc.args.resources, true) if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { t.Errorf("Render(...): -want error, +got error:\n%s", diff) } diff --git a/internal/tester.go b/internal/tester.go index 7d89525..b8e5c9a 100644 --- a/internal/tester.go +++ b/internal/tester.go @@ -158,7 +158,7 @@ func (t *tester) writeKuttlFiles() error { return errors.Wrap(err, "cannot build examples config") } - files, err := templates.Render(tc, examples) + files, err := templates.Render(tc, examples, t.options.SkipDelete) if err != nil { return errors.Wrap(err, "cannot render kuttl templates") }