From e8a7e23d4d7d52089779e5dd2c8125c11bdfe07a Mon Sep 17 00:00:00 2001 From: Tsubasa Nagasawa Date: Sun, 18 Sep 2022 17:17:08 +0900 Subject: [PATCH 1/2] chore: add e2e test case for helm template with oci based helm chart Signed-off-by: Tsubasa Nagasawa --- test/e2e/template/helmfile/snapshot_test.go | 70 ++++++++++++++++++- .../snapshot/oci_chart_pull/config.yaml | 6 ++ .../snapshot/oci_chart_pull/input.yaml | 19 +++++ .../snapshot/oci_chart_pull/output.yaml | 15 ++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/config.yaml create mode 100644 test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/input.yaml create mode 100644 test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/output.yaml diff --git a/test/e2e/template/helmfile/snapshot_test.go b/test/e2e/template/helmfile/snapshot_test.go index eb565f79..cc94e0c3 100644 --- a/test/e2e/template/helmfile/snapshot_test.go +++ b/test/e2e/template/helmfile/snapshot_test.go @@ -1,6 +1,7 @@ package helmfile import ( + "bufio" "context" "fmt" "os" @@ -16,6 +17,12 @@ import ( "gopkg.in/yaml.v3" ) +type ociChart struct { + name string + version string + digest string +} + func TestHelmfileTemplateWithBuildCommand(t *testing.T) { type Config struct { LocalDockerRegistry struct { @@ -95,6 +102,8 @@ func TestHelmfileTemplateWithBuildCommand(t *testing.T) { } }) + // ociCharts holds a list of chart name, version and digest distributed by local oci registry. + ociCharts := []ociChart{} // If localDockerRegistry.enabled is set to `true`, // run the docker registry v2 and push the test charts to the registry // so that it can be accessed by helm and helmfile as a oci registry based chart repository. @@ -126,7 +135,20 @@ func TestHelmfileTemplateWithBuildCommand(t *testing.T) { t.Fatalf("%s is not a directory", c) } tgzFile := execHelmPackage(t, chartPath) - _ = execHelm(t, "push", tgzFile, fmt.Sprintf("oci://localhost:%d/myrepo", hostPort)) + + // Extract chart version from the name of chart package archival + chartName := c.Name() + chartNameWithVersion := strings.TrimSuffix(filepath.Base(tgzFile), filepath.Ext(tgzFile)) + chartVersion := strings.TrimPrefix(chartNameWithVersion, fmt.Sprintf("%s-", chartName)) + + chartDigest, err := execHelmPush(t, tgzFile, fmt.Sprintf("oci://localhost:%d/myrepo", hostPort)) + require.NoError(t, err, "Unable to run helm push to local registry: %v", err) + + ociCharts = append(ociCharts, ociChart{ + name: chartName, + version: chartVersion, + digest: chartDigest, + }) } } @@ -155,6 +177,37 @@ func TestHelmfileTemplateWithBuildCommand(t *testing.T) { gotStr := string(got) gotStr = strings.ReplaceAll(gotStr, fmt.Sprintf("chart=%s", wd), "chart=$WD") + // OCI based helm charts are pulled and exported under temporary directory. + // We are not sure the exact name of the temporary directory generated by helmfile, + // so redact its base directory name with $TMP. + if config.LocalDockerRegistry.Enabled { + var releaseName, chartPath string + sc := bufio.NewScanner(strings.NewReader(gotStr)) + for sc.Scan() { + if !strings.HasPrefix(sc.Text(), "Templating ") { + continue + } + releaseChartStr := strings.TrimPrefix(sc.Text(), "Templating ") + releaseChartParts := strings.Split(releaseChartStr, ", ") + if len(releaseChartParts) != 2 { + t.Fatal("Found unexpected log output of templating oci based helm chart, want=\"Templating release=, chart=\"") + } + releaseNamePart, chartPathPart := releaseChartParts[0], releaseChartParts[1] + releaseName = strings.TrimPrefix(releaseNamePart, "release=") + chartPath = chartPathPart + } + for _, ociChart := range ociCharts { + chartPathWithoutTempDirBase := fmt.Sprintf("/%s/%s/%s/%s", releaseName, ociChart.name, ociChart.version, ociChart.name) + var chartPathBase string + if strings.HasSuffix(chartPath, chartPathWithoutTempDirBase) { + chartPathBase = strings.TrimSuffix(chartPath, chartPathWithoutTempDirBase) + } + if len(chartPathBase) != 0 { + gotStr = strings.ReplaceAll(gotStr, chartPathBase, "chart=$TMP") + } + gotStr = strings.ReplaceAll(gotStr, fmt.Sprintf("Digest: %s", ociChart.digest), "Digest: $DIGEST") + } + } if stat, _ := os.Stat(outputFile); stat != nil { want, err := os.ReadFile(outputFile) @@ -191,6 +244,21 @@ func execHelmPackage(t *testing.T, localChart string) string { return strings.TrimSpace(tgzAbsPath) } +// execHelmPush pushes helm package to oci based helm repository, +// then returns its digest. +func execHelmPush(t *testing.T, tgzPath, remoteUrl string) (string, error) { + t.Helper() + + out := execHelm(t, "push", tgzPath, remoteUrl) + sc := bufio.NewScanner(strings.NewReader(out)) + for sc.Scan() { + if strings.HasPrefix(sc.Text(), "Digest:") { + return strings.TrimPrefix(sc.Text(), "Digest: "), nil + } + } + return "", fmt.Errorf("Unable to find chart digest from output string of helm push") +} + func execHelm(t *testing.T, args ...string) string { t.Helper() diff --git a/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/config.yaml b/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/config.yaml new file mode 100644 index 00000000..55a14538 --- /dev/null +++ b/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/config.yaml @@ -0,0 +1,6 @@ +localDockerRegistry: + enabled: true + port: 5000 +chartifyTempDir: temp2 +helmfileArgs: +- template diff --git a/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/input.yaml b/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/input.yaml new file mode 100644 index 00000000..2bb1dcc7 --- /dev/null +++ b/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/input.yaml @@ -0,0 +1,19 @@ +repositories: +- name: myrepo + url: localhost:5000/myrepo + oci: true + +releases: +- name: foo + chart: myrepo/raw + version: 0.1.0 + values: + - templates: + - | + apiVersion: v1 + kind: ConfigMap + metadata: + name: {{`{{ .Release.Name }}`}} + namespace: {{`{{ .Release.Namespace }}`}} + data: + foo: FOO diff --git a/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/output.yaml b/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/output.yaml new file mode 100644 index 00000000..10ccf024 --- /dev/null +++ b/test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull/output.yaml @@ -0,0 +1,15 @@ +Pulling localhost:5000/myrepo/raw:0.1.0 +Pulled: localhost:5000/myrepo/raw:0.1.0 +Digest: $DIGEST + +Templating release=foo, chart=$TMP/foo/raw/0.1.0/raw +--- +# Source: raw/templates/resources.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo + namespace: default +data: + foo: FOO + From 7b40cefdda6c1a240efbed66973f8adb089e9568 Mon Sep 17 00:00:00 2001 From: Tsubasa Nagasawa Date: Sun, 18 Sep 2022 17:22:46 +0900 Subject: [PATCH 2/2] fix: add missing untar flag to pull chart from oci registry Signed-off-by: Tsubasa Nagasawa --- pkg/helmexec/exec.go | 2 +- pkg/helmexec/exec_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/helmexec/exec.go b/pkg/helmexec/exec.go index e53e92a2..6ac2aba3 100644 --- a/pkg/helmexec/exec.go +++ b/pkg/helmexec/exec.go @@ -466,7 +466,7 @@ func (helm *execer) ChartPull(chart string, path string, flags ...string) error // in the 3.7.0 version, the chart pull has been replaced with helm pull // https://github.com/helm/helm/releases/tag/v3.7.0 ociChartURL, ociChartTag := resolveOciChart(chart) - helmArgs = []string{"pull", ociChartURL, "--version", ociChartTag, "--destination", path} + helmArgs = []string{"pull", ociChartURL, "--version", ociChartTag, "--destination", path, "--untar"} } else { helmArgs = []string{"chart", "pull", chart} } diff --git a/pkg/helmexec/exec_test.go b/pkg/helmexec/exec_test.go index 5dfa43e7..2b56f011 100644 --- a/pkg/helmexec/exec_test.go +++ b/pkg/helmexec/exec_test.go @@ -733,7 +733,7 @@ exec: helm --kube-context dev chart pull chart --untar --untardir /tmp/dir chartPath: "path1", chartFlags: []string{"--untardir", "/tmp/dir"}, listResult: `Pulling repo/helm-charts:0.14.0 -exec: helm --kube-context dev pull oci://repo/helm-charts --version 0.14.0 --destination path1 --untardir /tmp/dir +exec: helm --kube-context dev pull oci://repo/helm-charts --version 0.14.0 --destination path1 --untar --untardir /tmp/dir `, }, }