mirror of
https://github.com/helmfile/helmfile.git
synced 2026-09-30 10:51:41 +02:00
fix: support OCI chart digest syntax (@sha256:...) (#2398)
fix: support OCI chart digest syntax in chart URLs and version fields Helm supports pinning OCI chart images by digest (@sha256:...), version tag (:version), or both (:version@sha256:digest) since helm/helm#12690. Helmfile failed to parse these formats, incorrectly constructing helm commands and losing version/digest information embedded in chart URLs. Root causes: - resolveOciChart() used last ":" to find version tag, but sha256:abc contains ":", so digest URLs were split incorrectly - getOCIQualifiedChartName() included :version and @digest in chartName with no parsing of either source - appendChartVersionFlags() passed release.Version verbatim to --version flag, including any digest suffix - ChartPull() discarded the tag from resolveOciChart but did not preserve digest in the URL This commit adds parseOCIChartRef() and parseVersionDigest() utilities, then updates the OCI chart handling pipeline so that: - Digests are preserved in the chart URL passed to helm pull - Version tags are extracted cleanly for the --version flag - Both chart URL and version field are parsed for version/digest info Fixes #2097 Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
This commit is contained in:
+10
-2
@@ -962,8 +962,16 @@ func (helm *execer) IsVersionAtLeast(versionStr string) bool {
|
||||
}
|
||||
|
||||
func resolveOciChart(ociChart string) (ociChartURL, ociChartTag string) {
|
||||
// Split off digest (e.g., @sha256:abc) first so the colon in sha256:
|
||||
// does not confuse the version tag search below.
|
||||
var digest string
|
||||
if atIdx := strings.Index(ociChart, "@"); atIdx >= 0 {
|
||||
digest = ociChart[atIdx:] // includes the "@"
|
||||
ociChart = ociChart[:atIdx]
|
||||
}
|
||||
|
||||
var urlTagIndex int
|
||||
// Get the last : index
|
||||
// Get the last : index in the pre-digest part
|
||||
// e.g.,
|
||||
// 1. registry:443/helm-charts
|
||||
// 2. registry/helm-charts:latest
|
||||
@@ -975,7 +983,7 @@ func resolveOciChart(ociChart string) (ociChartURL, ociChartTag string) {
|
||||
urlTagIndex = strings.LastIndex(ociChart, ":")
|
||||
ociChartTag = ociChart[urlTagIndex+1:]
|
||||
}
|
||||
ociChartURL = fmt.Sprintf("oci://%s", ociChart[:urlTagIndex])
|
||||
ociChartURL = fmt.Sprintf("oci://%s%s", ociChart[:urlTagIndex], digest)
|
||||
return ociChartURL, ociChartTag
|
||||
}
|
||||
|
||||
|
||||
@@ -1381,6 +1381,30 @@ func Test_resolveOciChart(t *testing.T) {
|
||||
ociChartURL: "oci://chart:5000/nginx",
|
||||
ociChartTag: "",
|
||||
},
|
||||
{
|
||||
name: "digest only",
|
||||
chartPath: "ghcr.io/nginxinc/charts/nginx-ingress@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
||||
ociChartURL: "oci://ghcr.io/nginxinc/charts/nginx-ingress@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
||||
ociChartTag: "",
|
||||
},
|
||||
{
|
||||
name: "version and digest",
|
||||
chartPath: "ghcr.io/nginxinc/charts/nginx-ingress:2.0.0@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
||||
ociChartURL: "oci://ghcr.io/nginxinc/charts/nginx-ingress@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
||||
ociChartTag: "2.0.0",
|
||||
},
|
||||
{
|
||||
name: "port with digest",
|
||||
chartPath: "registry:5000/chart@sha256:abc123",
|
||||
ociChartURL: "oci://registry:5000/chart@sha256:abc123",
|
||||
ociChartTag: "",
|
||||
},
|
||||
{
|
||||
name: "port with version and digest",
|
||||
chartPath: "registry:5000/chart:1.0.0@sha256:abc123",
|
||||
ociChartURL: "oci://registry:5000/chart@sha256:abc123",
|
||||
ociChartTag: "1.0.0",
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
tt := tests[i]
|
||||
|
||||
@@ -83,6 +83,51 @@ func TestOCIChartVersionHandling(t *testing.T) {
|
||||
expectedError: false,
|
||||
expectedQualifiedChart: "",
|
||||
},
|
||||
{
|
||||
name: "OCI chart with digest in version field",
|
||||
chart: "oci://registry.example.com/my-chart",
|
||||
version: "1.2.3@sha256:abc123def456",
|
||||
helmVersion: "3.18.0",
|
||||
expectedVersion: "1.2.3",
|
||||
expectedError: false,
|
||||
expectedQualifiedChart: "registry.example.com/my-chart@sha256:abc123def456",
|
||||
},
|
||||
{
|
||||
name: "OCI chart with digest-only in version field",
|
||||
chart: "oci://registry.example.com/my-chart",
|
||||
version: "@sha256:abc123def456",
|
||||
helmVersion: "3.18.0",
|
||||
expectedVersion: "",
|
||||
expectedError: false,
|
||||
expectedQualifiedChart: "registry.example.com/my-chart@sha256:abc123def456",
|
||||
},
|
||||
{
|
||||
name: "OCI chart with version tag in URL",
|
||||
chart: "oci://registry.example.com/my-chart:1.2.3",
|
||||
version: "",
|
||||
helmVersion: "3.18.0",
|
||||
expectedVersion: "1.2.3",
|
||||
expectedError: false,
|
||||
expectedQualifiedChart: "registry.example.com/my-chart",
|
||||
},
|
||||
{
|
||||
name: "OCI chart with digest in URL",
|
||||
chart: "oci://registry.example.com/my-chart@sha256:abc123def456",
|
||||
version: "",
|
||||
helmVersion: "3.18.0",
|
||||
expectedVersion: "",
|
||||
expectedError: false,
|
||||
expectedQualifiedChart: "registry.example.com/my-chart@sha256:abc123def456",
|
||||
},
|
||||
{
|
||||
name: "OCI chart with version and digest in URL",
|
||||
chart: "oci://registry.example.com/my-chart:1.2.3@sha256:abc123def456",
|
||||
version: "",
|
||||
helmVersion: "3.18.0",
|
||||
expectedVersion: "1.2.3",
|
||||
expectedError: false,
|
||||
expectedQualifiedChart: "registry.example.com/my-chart@sha256:abc123def456",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseOCIChartRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
chartURL string
|
||||
expectedBase string
|
||||
expectedVer string
|
||||
expectedDigest string
|
||||
}{
|
||||
{
|
||||
name: "plain OCI URL",
|
||||
chartURL: "oci://registry/chart",
|
||||
expectedBase: "oci://registry/chart",
|
||||
expectedVer: "",
|
||||
expectedDigest: "",
|
||||
},
|
||||
{
|
||||
name: "OCI URL with version",
|
||||
chartURL: "oci://registry/chart:2.0.0",
|
||||
expectedBase: "oci://registry/chart",
|
||||
expectedVer: "2.0.0",
|
||||
expectedDigest: "",
|
||||
},
|
||||
{
|
||||
name: "OCI URL with digest",
|
||||
chartURL: "oci://registry/chart@sha256:abc",
|
||||
expectedBase: "oci://registry/chart",
|
||||
expectedVer: "",
|
||||
expectedDigest: "sha256:abc",
|
||||
},
|
||||
{
|
||||
name: "OCI URL with version and digest",
|
||||
chartURL: "oci://reg/chart:2.0@sha256:abc",
|
||||
expectedBase: "oci://reg/chart",
|
||||
expectedVer: "2.0",
|
||||
expectedDigest: "sha256:abc",
|
||||
},
|
||||
{
|
||||
name: "OCI URL with port, version, and digest",
|
||||
chartURL: "oci://reg:5000/chart:1.0@sha256:a",
|
||||
expectedBase: "oci://reg:5000/chart",
|
||||
expectedVer: "1.0",
|
||||
expectedDigest: "sha256:a",
|
||||
},
|
||||
{
|
||||
name: "OCI URL with port only",
|
||||
chartURL: "oci://reg:5000/chart",
|
||||
expectedBase: "oci://reg:5000/chart",
|
||||
expectedVer: "",
|
||||
expectedDigest: "",
|
||||
},
|
||||
{
|
||||
name: "OCI URL with port and digest, no version",
|
||||
chartURL: "oci://reg:5000/chart@sha256:abc",
|
||||
expectedBase: "oci://reg:5000/chart",
|
||||
expectedVer: "",
|
||||
expectedDigest: "sha256:abc",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
base, ver, digest := parseOCIChartRef(tt.chartURL)
|
||||
assert.Equal(t, tt.expectedBase, base, "baseURL mismatch")
|
||||
assert.Equal(t, tt.expectedVer, ver, "version mismatch")
|
||||
assert.Equal(t, tt.expectedDigest, digest, "digest mismatch")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVersionDigest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
expectedVer string
|
||||
expectedDigest string
|
||||
}{
|
||||
{
|
||||
name: "version only",
|
||||
version: "2.0.0",
|
||||
expectedVer: "2.0.0",
|
||||
expectedDigest: "",
|
||||
},
|
||||
{
|
||||
name: "version with digest",
|
||||
version: "2.0.0@sha256:abc",
|
||||
expectedVer: "2.0.0",
|
||||
expectedDigest: "sha256:abc",
|
||||
},
|
||||
{
|
||||
name: "digest only",
|
||||
version: "@sha256:abc",
|
||||
expectedVer: "",
|
||||
expectedDigest: "sha256:abc",
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
version: "",
|
||||
expectedVer: "",
|
||||
expectedDigest: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ver, digest := parseVersionDigest(tt.version)
|
||||
assert.Equal(t, tt.expectedVer, ver, "version mismatch")
|
||||
assert.Equal(t, tt.expectedDigest, digest, "digest mismatch")
|
||||
})
|
||||
}
|
||||
}
|
||||
+113
-8
@@ -3513,8 +3513,13 @@ func (st *HelmState) appendTakeOwnershipFlagsForDiff(flags []string, release *Re
|
||||
}
|
||||
|
||||
func (st *HelmState) appendChartVersionFlags(flags []string, release *ReleaseSpec) []string {
|
||||
if release.Version != "" {
|
||||
flags = append(flags, "--version", release.Version)
|
||||
version := release.Version
|
||||
// Strip OCI digest from version (digest is handled in chart URL, not --version flag)
|
||||
if idx := strings.Index(version, "@"); idx >= 0 {
|
||||
version = version[:idx]
|
||||
}
|
||||
if version != "" {
|
||||
flags = append(flags, "--version", version)
|
||||
}
|
||||
|
||||
if st.isDevelopment(release) {
|
||||
@@ -4807,7 +4812,17 @@ func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helm
|
||||
flags = st.appendVerifyFlags(flags, release)
|
||||
flags = st.appendKeyringFlags(flags, release)
|
||||
flags = st.appendChartDownloadFlags(flags, release)
|
||||
flags = st.appendChartVersionFlags(flags, release)
|
||||
// Use the clean chartVersion (without digest) from getOCIQualifiedChartName
|
||||
// rather than appendChartVersionFlags which uses release.Version verbatim.
|
||||
// The digest is already embedded in qualifiedChartName.
|
||||
// When a digest is present, omit --version: the digest is the authoritative
|
||||
// content identifier, and passing both causes errors in some Helm versions.
|
||||
if chartVersion != "" && !strings.Contains(qualifiedChartName, "@") {
|
||||
flags = append(flags, "--version", chartVersion)
|
||||
}
|
||||
if st.isDevelopment(release) {
|
||||
flags = append(flags, "--devel")
|
||||
}
|
||||
|
||||
if err := helm.ChartPull(qualifiedChartName, chartPath, flags...); err != nil {
|
||||
lockResult.Release(st.logger)
|
||||
@@ -4850,6 +4865,48 @@ func (st *HelmState) IsOCIChart(chart string) bool {
|
||||
return repo.OCI
|
||||
}
|
||||
|
||||
// parseOCIChartRef parses an OCI chart URL into base URL, version tag, and digest.
|
||||
// Examples:
|
||||
//
|
||||
// oci://registry/chart → (oci://registry/chart, "", "")
|
||||
// oci://registry/chart:2.0.0 → (oci://registry/chart, "2.0.0", "")
|
||||
// oci://registry/chart@sha256:a → (oci://registry/chart, "", "sha256:a")
|
||||
// oci://registry/chart:2.0@sha256:a → (oci://registry/chart, "2.0", "sha256:a")
|
||||
// oci://reg:5000/chart:1.0@sha256:a → (oci://reg:5000/chart, "1.0", "sha256:a")
|
||||
func parseOCIChartRef(chartURL string) (baseURL, version, digest string) {
|
||||
// Split off digest first (everything after @)
|
||||
if atIdx := strings.Index(chartURL, "@"); atIdx >= 0 {
|
||||
digest = chartURL[atIdx+1:]
|
||||
chartURL = chartURL[:atIdx]
|
||||
}
|
||||
|
||||
// Find version tag: last ":" that comes after the last "/"
|
||||
lastSlash := strings.LastIndex(chartURL, "/")
|
||||
lastColon := strings.LastIndex(chartURL, ":")
|
||||
if lastColon > lastSlash {
|
||||
version = chartURL[lastColon+1:]
|
||||
baseURL = chartURL[:lastColon]
|
||||
} else {
|
||||
baseURL = chartURL
|
||||
}
|
||||
|
||||
return baseURL, version, digest
|
||||
}
|
||||
|
||||
// parseVersionDigest splits a version string that may contain an OCI digest.
|
||||
// Examples:
|
||||
//
|
||||
// "2.0.0" → ("2.0.0", "")
|
||||
// "2.0.0@sha256:abc" → ("2.0.0", "sha256:abc")
|
||||
// "@sha256:abc" → ("", "sha256:abc")
|
||||
// "" → ("", "")
|
||||
func parseVersionDigest(version string) (ver, digest string) {
|
||||
if atIdx := strings.Index(version, "@"); atIdx >= 0 {
|
||||
return version[:atIdx], version[atIdx+1:]
|
||||
}
|
||||
return version, ""
|
||||
}
|
||||
|
||||
func (st *HelmState) getOCIQualifiedChartName(release *ReleaseSpec) (string, string, string, error) {
|
||||
// For issue #2247: Don't default to "latest" - use empty string to let Helm pull the latest version
|
||||
// Only use the version explicitly provided by the user
|
||||
@@ -4867,21 +4924,69 @@ func (st *HelmState) getOCIQualifiedChartName(release *ReleaseSpec) (string, str
|
||||
// Reject explicit "latest" for OCI charts (issue #1047, #2247)
|
||||
// This only applies if user explicitly specified "latest", not when version is omitted
|
||||
// We reject for all Helm versions to ensure consistent behavior
|
||||
if release.Version == "latest" {
|
||||
// Strip any digest suffix before checking (e.g. "latest@sha256:..." is still invalid)
|
||||
versionForCheck, _ := parseVersionDigest(release.Version)
|
||||
if versionForCheck == "latest" {
|
||||
return "", "", "", fmt.Errorf("the version for OCI charts should be semver compliant, the latest tag is not supported")
|
||||
}
|
||||
|
||||
var qualifiedChartName, chartName string
|
||||
if strings.HasPrefix(release.Chart, "oci://") {
|
||||
parts := strings.Split(release.Chart, "/")
|
||||
// Parse version and digest from the chart URL
|
||||
baseURL, versionInURL, digestInURL := parseOCIChartRef(release.Chart)
|
||||
|
||||
// Parse version and digest from the version field
|
||||
versionInField, digestInField := parseVersionDigest(chartVersion)
|
||||
|
||||
// Merge: version field takes precedence; fall back to URL-embedded version
|
||||
finalVersion := versionInField
|
||||
if finalVersion == "" && versionInURL != "" {
|
||||
finalVersion = versionInURL
|
||||
}
|
||||
|
||||
// Merge: URL-embedded digest takes precedence; fall back to version field digest
|
||||
finalDigest := digestInURL
|
||||
if finalDigest == "" {
|
||||
finalDigest = digestInField
|
||||
}
|
||||
|
||||
// Extract chart name from base URL (last path segment)
|
||||
parts := strings.Split(baseURL, "/")
|
||||
chartName = parts[len(parts)-1]
|
||||
qualifiedChartName = strings.Replace(fmt.Sprintf("%s:%s", release.Chart, chartVersion), "oci://", "", 1)
|
||||
|
||||
// Build qualifiedChartName: base (without oci:// prefix) + digest or version
|
||||
base := strings.TrimPrefix(baseURL, "oci://")
|
||||
if finalDigest != "" {
|
||||
// Digest present — put it in the URL; version goes through --version flag only
|
||||
qualifiedChartName = fmt.Sprintf("%s@%s", base, finalDigest)
|
||||
} else if versionInURL == "" && finalVersion != "" {
|
||||
// Version from field only (backward compatible format)
|
||||
qualifiedChartName = fmt.Sprintf("%s:%s", base, finalVersion)
|
||||
} else {
|
||||
// Version came from URL (handled via --version flag) or no version at all
|
||||
qualifiedChartName = base
|
||||
}
|
||||
|
||||
chartVersion = finalVersion
|
||||
} else {
|
||||
var repo *RepositorySpec
|
||||
repo, chartName = st.GetRepositoryAndNameFromChartName(release.Chart)
|
||||
qualifiedChartName = fmt.Sprintf("%s/%s:%s", repo.URL, chartName, chartVersion)
|
||||
|
||||
// Handle digest in version field for repo-aliased OCI charts too
|
||||
base := fmt.Sprintf("%s/%s", repo.URL, chartName)
|
||||
finalVersion, digest := parseVersionDigest(chartVersion)
|
||||
|
||||
switch {
|
||||
case digest != "":
|
||||
qualifiedChartName = fmt.Sprintf("%s@%s", base, digest)
|
||||
case finalVersion != "":
|
||||
qualifiedChartName = fmt.Sprintf("%s:%s", base, finalVersion)
|
||||
default:
|
||||
qualifiedChartName = base
|
||||
}
|
||||
|
||||
chartVersion = finalVersion
|
||||
}
|
||||
qualifiedChartName = strings.TrimSuffix(qualifiedChartName, ":")
|
||||
|
||||
return qualifiedChartName, chartName, chartVersion, nil
|
||||
}
|
||||
|
||||
@@ -3701,6 +3701,212 @@ func TestGetOCIQualifiedChartName(t *testing.T) {
|
||||
{"registry/chart-path/chart-name", "chart-name", ""},
|
||||
},
|
||||
},
|
||||
// Digest in version field
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry/chart-path/chart-name",
|
||||
Version: "2.0.0@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", "2.0.0"},
|
||||
},
|
||||
},
|
||||
// Digest-only in version field
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry/chart-path/chart-name",
|
||||
Version: "@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", ""},
|
||||
},
|
||||
},
|
||||
// Version tag in URL
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry/chart-path/chart-name:2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name", "chart-name", "2.0.0"},
|
||||
},
|
||||
},
|
||||
// Digest in URL
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry/chart-path/chart-name@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", ""},
|
||||
},
|
||||
},
|
||||
// Version + digest in URL
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry/chart-path/chart-name:2.0.0@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", "2.0.0"},
|
||||
},
|
||||
},
|
||||
// Port with digest in URL
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry:5000/chart-path/chart-name@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry:5000/chart-path/chart-name@sha256:abc123", "chart-name", ""},
|
||||
},
|
||||
},
|
||||
// Digest in URL + version field
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci://registry/chart-path/chart-name@sha256:abc123",
|
||||
Version: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", "2.0.0"},
|
||||
},
|
||||
},
|
||||
// Repo-aliased OCI chart with digest in version
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{
|
||||
{
|
||||
Name: "oci-repo",
|
||||
URL: "registry/chart-path",
|
||||
OCI: true,
|
||||
},
|
||||
},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci-repo/chart-name",
|
||||
Version: "2.0.0@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", "2.0.0"},
|
||||
},
|
||||
},
|
||||
// Repo-aliased OCI chart with digest-only version
|
||||
{
|
||||
state: HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: []RepositorySpec{
|
||||
{
|
||||
Name: "oci-repo",
|
||||
URL: "registry/chart-path",
|
||||
OCI: true,
|
||||
},
|
||||
},
|
||||
Releases: []ReleaseSpec{
|
||||
{
|
||||
Chart: "oci-repo/chart-name",
|
||||
Version: "@sha256:abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
helmVersion: "3.13.3",
|
||||
expected: []struct {
|
||||
qualifiedChartName string
|
||||
chartName string
|
||||
chartVersion string
|
||||
}{
|
||||
{"registry/chart-path/chart-name@sha256:abc123", "chart-name", ""},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user