From 6ea6eff5bbba011abeabeda8f8d6358ccb8e16a9 Mon Sep 17 00:00:00 2001 From: Jerome Guionnet Date: Mon, 18 Dec 2023 16:47:33 -0800 Subject: [PATCH] Aligned with function patterns and other patterns Added an option for snaphsot directory go mod tidy --- go.mod | 3 +- modules/helm/options.go | 1 + modules/helm/template.go | 81 ++++++++++++------- modules/helm/template_test.go | 17 +++- ...a_remote_example_template_snapshot_test.go | 12 ++- .../helm_keda_remote_example_template_test.go | 2 + 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/go.mod b/go.mod index 30c1704c0..02e367f71 100644 --- a/go.mod +++ b/go.mod @@ -47,9 +47,9 @@ require ( ) require ( + cloud.google.com/go/cloudbuild v1.9.0 github.com/gonvenience/ytbx v1.4.4 github.com/homeport/dyff v1.6.0 - cloud.google.com/go/cloudbuild v1.9.0 github.com/slack-go/slack v0.10.3 gotest.tools/v3 v3.0.3 ) @@ -91,7 +91,6 @@ require ( github.com/gonvenience/term v1.0.2 // indirect github.com/gonvenience/text v1.0.7 // indirect github.com/gonvenience/wrap v1.1.2 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect diff --git a/modules/helm/options.go b/modules/helm/options.go index bafdedea4..da210ef1b 100644 --- a/modules/helm/options.go +++ b/modules/helm/options.go @@ -18,4 +18,5 @@ type Options struct { Logger *logger.Logger // Set a non-default logger that should be used. See the logger package for more info. Use logger.Discard to not print the output while executing the command. ExtraArgs map[string][]string // Extra arguments to pass to the helm install/upgrade/rollback/delete and helm repo add commands. The key signals the command (e.g., install) while the values are the extra arguments to pass through. BuildDependencies bool // If true, helm dependencies will be built before rendering template, installing or upgrade the chart. + SnapshotPath string // The path to the snapshot directory when using snapshot based testing. Empty string means use default ($PWD/__snapshot__). } diff --git a/modules/helm/template.go b/modules/helm/template.go index ff1c82b61..8f0b321db 100644 --- a/modules/helm/template.go +++ b/modules/helm/template.go @@ -12,7 +12,6 @@ import ( "github.com/gruntwork-io/terratest/modules/files" "github.com/gruntwork-io/terratest/modules/testing" - "fmt" "os" "github.com/gonvenience/ytbx" @@ -145,71 +144,94 @@ func UnmarshalK8SYamlE(t testing.TestingT, yamlData string, destinationObj inter // It is one of the two functions needed to implement snapshot based testing for helm. // see https://github.com/gruntwork-io/terratest/issues/1377 // A snapshot is used to compare the current manifests of a chart with the previous manifests. -// A global diff is run against the two snapshost and the number of differences is returned. -func UpdateSnapshot(yamlData string, releaseName string) { +// A global diff is run against the two snapshosts and the number of differences is returned. +func UpdateSnapshot(t testing.TestingT, options *Options, yamlData string, releaseName string) { + require.NoError(t, UpdateSnapshotE(t, options, yamlData, releaseName)) +} + +// UpdateSnapshot creates or updates the k8s manifest snapshot of a chart (e.g bitnami/nginx). +// It is one of the two functions needed to implement snapshot based testing for helm. +// see https://github.com/gruntwork-io/terratest/issues/1377 +// A snapshot is used to compare the current manifests of a chart with the previous manifests. +// A global diff is run against the two snapshosts and the number of differences is returned. +// It will failed the test if there is an error while writing the manifests' snapshot in the file system +func UpdateSnapshotE(t testing.TestingT, options *Options, yamlData string, releaseName string) error { - snapshotDir := "__snapshot__" + var snapshotDir = "__snapshot__" + if options.SnapshotPath != "" { + snapshotDir = options.SnapshotPath + } // Create a directory if not exists if !files.FileExists(snapshotDir) { if err := os.Mkdir(snapshotDir, 0755); err != nil { - fmt.Println("Error creating directory:", err) - return + return errors.WithStackTrace(err) } } - filename := snapshotDir + "/" + releaseName + ".yaml" + filename := filepath.Join(snapshotDir, releaseName+".yaml") // Open a file in write mode file, err := os.Create(filename) if err != nil { - fmt.Println("Error creating file:", err) - return + return errors.WithStackTrace(err) } defer file.Close() // Write the k8s manifest into the file if _, err = file.WriteString(yamlData); err != nil { - fmt.Println("Error writing to file: ", filename, err) - return + return errors.WithStackTrace(err) } - fmt.Println("k8s manifest written into file: ", filename) + if options.Logger != nil { + options.Logger.Logf(t, "helm chart manifest written into file: %s", filename) + } + return nil } // DiffAgainstSnapshot compare the current manifests of a chart (e.g bitnami/nginx) // with the previous manifests stored in the snapshot. // see https://github.com/gruntwork-io/terratest/issues/1377 // It returns the number of difference between the two manifest snaphost or -1 in case of error -func DiffAgainstSnapshot(yamlData string, releaseName string) int { +// It will failed the test if there is an error while reading or writing the two manifests in the file system +func DiffAgainstSnapshot(t testing.TestingT, options *Options, yamlData string, releaseName string) int { + numberOfDiffs, err := DiffAgainstSnapshotE(t, options, yamlData, releaseName) + require.NoError(t, err) + return numberOfDiffs +} + +// DiffAgainstSnapshotE compare the current manifests of a chart (e.g bitnami/nginx) +// with the previous manifests stored in the snapshot. +// see https://github.com/gruntwork-io/terratest/issues/1377 +// It returns the number of difference between the two manifest snaphost or -1 in case of error +func DiffAgainstSnapshotE(t testing.TestingT, options *Options, yamlData string, releaseName string) (int, error) { - snapshotDir := "__snapshot__" + var snapshotDir = "__snapshot__" + if options.SnapshotPath != "" { + snapshotDir = options.SnapshotPath + } // load the yaml snapshot file - snapshot := snapshotDir + "/" + releaseName + ".yaml" + snapshot := filepath.Join(snapshotDir, releaseName+".yaml") from, err := ytbx.LoadFile(snapshot) if err != nil { - fmt.Println("Error opening file:", err) - return -1 + return -1, errors.WithStackTrace(err) } // write the current manifest into a file as `dyff` does not support string input currentManifests := releaseName + ".yaml" file, err := os.Create(currentManifests) if err != nil { - fmt.Println("Error creating file:", err) - return -1 + return -1, errors.WithStackTrace(err) } if _, err = file.WriteString(yamlData); err != nil { - fmt.Println("Error writing to file: ", currentManifests, err) - return -1 + return -1, errors.WithStackTrace(err) } defer file.Close() defer os.Remove(currentManifests) to, err := ytbx.LoadFile(currentManifests) if err != nil { - fmt.Println("Error opening file:", err) - return -1 + return -1, errors.WithStackTrace(err) } // compare the two manifests using `dyff` @@ -218,8 +240,7 @@ func DiffAgainstSnapshot(yamlData string, releaseName string) int { // create a report report, err := dyff.CompareInputFiles(from, to, compOpt) if err != nil { - fmt.Println("Error opening file:", err) - return -1 + return -1, errors.WithStackTrace(err) } // write any difference to stdout @@ -231,8 +252,10 @@ func DiffAgainstSnapshot(yamlData string, releaseName string) int { UseGoPatchPaths: false, } - reportWriter.WriteReport(os.Stdout) - - // return the number of diffs to use in in assertion while testing: 0 = no differences - return len(reportWriter.Diffs) + err = reportWriter.WriteReport(os.Stdout) + if err != nil { + return -1, errors.WithStackTrace(err) + } + // return the number of diffs to use in assertion while testing: 0 = no differences + return len(reportWriter.Diffs), nil } diff --git a/modules/helm/template_test.go b/modules/helm/template_test.go index b9a72f75f..8de17575d 100644 --- a/modules/helm/template_test.go +++ b/modules/helm/template_test.go @@ -17,6 +17,7 @@ import ( appsv1 "k8s.io/api/apps/v1" "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/random" ) @@ -25,7 +26,7 @@ func TestRemoteChartRender(t *testing.T) { const ( remoteChartSource = "https://charts.bitnami.com/bitnami" remoteChartName = "nginx" - remoteChartVersion = "13.2.23" + remoteChartVersion = "13.2.24" ) t.Parallel() @@ -45,6 +46,7 @@ func TestRemoteChartRender(t *testing.T) { "image.tag": remoteChartVersion, }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the template and capture the output. Note that we use the version without `E`, since @@ -86,6 +88,7 @@ func TestRemoteChartRenderDump(t *testing.T) { "image.tag": remoteChartVersion, }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the template and capture the output. Note that we use the version without `E`, since @@ -101,7 +104,11 @@ func TestRemoteChartRenderDump(t *testing.T) { require.Equal(t, namespaceName, deployment.Namespace) // write chart manifest to a local filesystem directory - UpdateSnapshot(output, releaseName) + options = &Options{ + Logger: logger.Default, + SnapshotPath: "__chart_manifests_snapshot__", + } + UpdateSnapshot(t, options, output, releaseName) } // Test that we can diff all the manifest to a local snapshot using a remote chart (e.g bitnami/nginx) @@ -109,7 +116,7 @@ func TestRemoteChartRenderDiff(t *testing.T) { const ( remoteChartSource = "https://charts.bitnami.com/bitnami" remoteChartName = "nginx" - remoteChartVersion = "13.2.23" + remoteChartVersion = "13.2.24" // need to set a fix name for the namespace so it is not flag as a difference namespaceName = "dump-ns" ) @@ -122,6 +129,8 @@ func TestRemoteChartRenderDiff(t *testing.T) { "image.tag": remoteChartVersion, }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, + SnapshotPath: "__chart_manifests_snapshot__", } // Run RenderTemplate to render the template and capture the output. Note that we use the version without `E`, since @@ -134,5 +143,5 @@ func TestRemoteChartRenderDiff(t *testing.T) { UnmarshalK8SYaml(t, output, &deployment) // run the diff and assert there is only one difference: the image name - require.Equal(t, 1, DiffAgainstSnapshot(output, releaseName)) + require.Equal(t, 1, DiffAgainstSnapshot(t, options, output, releaseName)) } diff --git a/test/helm_keda_remote_example_template_snapshot_test.go b/test/helm_keda_remote_example_template_snapshot_test.go index 28ac5bdc7..3efddc837 100644 --- a/test/helm_keda_remote_example_template_snapshot_test.go +++ b/test/helm_keda_remote_example_template_snapshot_test.go @@ -45,6 +45,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedDeploymentDump(t *testing.T) { "resources.metricServer.limits.memory": "1234Mi", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the *remote* template and capture the output. Note that we use the version without `E`, since @@ -68,7 +69,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedDeploymentDump(t *testing.T) { require.Equal(t, expectedMetricsServerReplica, deploymentMetricsServerReplica) // write chart manifest to a local filesystem directory - helm.UpdateSnapshot(output, releaseName) + helm.UpdateSnapshot(t, options, output, releaseName) } // An example of how to verify the rendered template object of a Helm Chart given various inputs. @@ -88,6 +89,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedDeploymentDiff(t *testing.T) { "resources.metricServer.limits.memory": "4321Mi", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the *remote* template and capture the output. Note that we use the version without `E`, since @@ -111,7 +113,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedDeploymentDiff(t *testing.T) { require.Equal(t, expectedMetricsServerReplica, deploymentMetricsServerReplica) // run the diff and assert the number of diffs - require.Equal(t, 4, helm.DiffAgainstSnapshot(output, releaseName)) + require.Equal(t, 4, helm.DiffAgainstSnapshot(t, options, output, releaseName)) } // An example of how to store a snapshot of the current manaifest for future comparison @@ -131,6 +133,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedPackageDump(t *testing.T) { "resources.metricServer.limits.memory": "1234Mi", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the *remote* template and capture the output. Note that we use the version without `E`, since @@ -140,7 +143,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedPackageDump(t *testing.T) { output := helm.RenderRemoteTemplate(t, options, "https://kedacore.github.io/charts", releaseName, []string{}) // write chart manifest to a local filesystem directory - helm.UpdateSnapshot(output, releaseName) + helm.UpdateSnapshot(t, options, output, releaseName) } // An example of how to verify the current helm k8s manifest against a previous snapshot @@ -160,6 +163,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedPackageDiff(t *testing.T) { "resources.metricServer.limits.memory": "4321Mi", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the *remote* template and capture the output. Note that we use the version without `E`, since @@ -169,5 +173,5 @@ func TestHelmKedaRemoteExampleTemplateRenderedPackageDiff(t *testing.T) { output := helm.RenderRemoteTemplate(t, options, "https://kedacore.github.io/charts", releaseName, []string{}) // run the diff and assert the number of diffs matches the number of diffs in the snapshot - require.Equal(t, 18, helm.DiffAgainstSnapshot(output, releaseName)) + require.Equal(t, 18, helm.DiffAgainstSnapshot(t, options, output, releaseName)) } diff --git a/test/helm_keda_remote_example_template_test.go b/test/helm_keda_remote_example_template_test.go index 8eabec7c6..1f66dda40 100644 --- a/test/helm_keda_remote_example_template_test.go +++ b/test/helm_keda_remote_example_template_test.go @@ -46,6 +46,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedDeployment(t *testing.T) { "resources.metricServer.limits.memory": "1234Mi", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the *remote* template and capture the output. Note that we use the version without `E`, since @@ -87,6 +88,7 @@ func TestHelmKedaRemoteExampleTemplateRenderedValuesFileFixtureDeployment(t *tes options := &helm.Options{ ValuesFiles: []string{"./fixtures/helm/keda-values.yaml"}, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + Logger: logger.Discard, } // Run RenderTemplate to render the *remote* template and capture the output. Note that we use the version without `E`, since