Merge pull request #371 from toVersus/fix/oci-pull-bug
fix: add missing untar flag to pull chart from oci registry
This commit is contained in:
		
						commit
						0341027bc0
					
				|  | @ -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
 | 		// 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
 | 		// https://github.com/helm/helm/releases/tag/v3.7.0
 | ||||||
| 		ociChartURL, ociChartTag := resolveOciChart(chart) | 		ociChartURL, ociChartTag := resolveOciChart(chart) | ||||||
| 		helmArgs = []string{"pull", ociChartURL, "--version", ociChartTag, "--destination", path} | 		helmArgs = []string{"pull", ociChartURL, "--version", ociChartTag, "--destination", path, "--untar"} | ||||||
| 	} else { | 	} else { | ||||||
| 		helmArgs = []string{"chart", "pull", chart} | 		helmArgs = []string{"chart", "pull", chart} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -733,7 +733,7 @@ exec: helm --kube-context dev chart pull chart --untar --untardir /tmp/dir | ||||||
| 			chartPath:   "path1", | 			chartPath:   "path1", | ||||||
| 			chartFlags:  []string{"--untardir", "/tmp/dir"}, | 			chartFlags:  []string{"--untardir", "/tmp/dir"}, | ||||||
| 			listResult: `Pulling repo/helm-charts:0.14.0 | 			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
 | ||||||
| `, | `, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package helmfile | package helmfile | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"bufio" | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
|  | @ -16,6 +17,12 @@ import ( | ||||||
| 	"gopkg.in/yaml.v3" | 	"gopkg.in/yaml.v3" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type ociChart struct { | ||||||
|  | 	name    string | ||||||
|  | 	version string | ||||||
|  | 	digest  string | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestHelmfileTemplateWithBuildCommand(t *testing.T) { | func TestHelmfileTemplateWithBuildCommand(t *testing.T) { | ||||||
| 	type Config struct { | 	type Config struct { | ||||||
| 		LocalDockerRegistry 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`,
 | 			// If localDockerRegistry.enabled is set to `true`,
 | ||||||
| 			// run the docker registry v2 and push the test charts to the registry
 | 			// 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.
 | 			// 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) | 						t.Fatalf("%s is not a directory", c) | ||||||
| 					} | 					} | ||||||
| 					tgzFile := execHelmPackage(t, chartPath) | 					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 := string(got) | ||||||
| 			gotStr = strings.ReplaceAll(gotStr, fmt.Sprintf("chart=%s", wd), "chart=$WD") | 			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=<release_name>, chart=<chart_name>\"") | ||||||
|  | 					} | ||||||
|  | 					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 { | 			if stat, _ := os.Stat(outputFile); stat != nil { | ||||||
| 				want, err := os.ReadFile(outputFile) | 				want, err := os.ReadFile(outputFile) | ||||||
|  | @ -191,6 +244,21 @@ func execHelmPackage(t *testing.T, localChart string) string { | ||||||
| 	return strings.TrimSpace(tgzAbsPath) | 	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 { | func execHelm(t *testing.T, args ...string) string { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | localDockerRegistry: | ||||||
|  |   enabled: true | ||||||
|  |   port: 5000 | ||||||
|  | chartifyTempDir: temp2 | ||||||
|  | helmfileArgs: | ||||||
|  | - template | ||||||
|  | @ -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 | ||||||
|  | @ -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 | ||||||
|  | 
 | ||||||
		Loading…
	
		Reference in New Issue