pull/32018/merge
Mehrab Sandhu 2 days ago committed by GitHub
commit 441e6a12bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -51,6 +51,9 @@ type Release struct {
// ApplyMethod stores whether server-side or client-side apply was used for the release
// Unset (empty string) should be treated as the default of client-side apply
ApplyMethod string `json:"apply_method,omitempty"` // "ssa" | "csa"
// Source records where the chart was fetched from.
// Nil for releases created before this field was added.
Source *common.ChartSource `json:"source,omitempty"`
}
// SetStatus is a helper for setting the status on a release.

@ -153,6 +153,9 @@ type ChartPathOptions struct {
// registryClient provides a registry client but is not added with
// options from a flag
registryClient *registry.Client
// resolvedSource is populated by LocateChart with chart provenance info.
resolvedSource *rcommon.ChartSource
}
// NewInstall creates a new Install object with the given configuration.
@ -669,6 +672,7 @@ func (i *Install) createRelease(chrt *chart.Chart, rawVals map[string]any, label
Version: 1,
Labels: labels,
ApplyMethod: string(determineReleaseSSApplyMethod(i.ServerSideApply)),
Source: i.resolvedSource,
}
return r
@ -980,6 +984,19 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
return "", err
}
// Capture source provenance from downloader or fallback context
if dl.ResolvedSource != nil {
c.resolvedSource = dl.ResolvedSource
}
if c.resolvedSource == nil && c.RepoURL != "" {
c.resolvedSource = &rcommon.ChartSource{RepoURL: c.RepoURL}
}
// Fallback for direct OCI references (e.g. oci://registry.com/chart:tag)
// where RepoURL is empty and the downloader may not have populated it.
if c.resolvedSource == nil && registry.IsOCI(name) {
c.resolvedSource = &rcommon.ChartSource{RegistryRef: name}
}
lname, err := filepath.Abs(filename)
if err != nil {
return filename, err

@ -329,6 +329,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[str
Hooks: hooks,
Labels: mergeCustomLabels(lastRelease.Labels, u.Labels),
ApplyMethod: string(determineReleaseSSApplyMethod(serverSideApply)),
Source: u.resolvedSource,
}
if len(notesTxt) > 0 {

@ -36,6 +36,7 @@ import (
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/provenance"
"helm.sh/helm/v4/pkg/registry"
rcommon "helm.sh/helm/v4/pkg/release/common"
"helm.sh/helm/v4/pkg/repo/v1"
)
@ -85,6 +86,10 @@ type ChartDownloader struct {
// Cache specifies the cache implementation to use.
Cache Cache
// ResolvedSource is populated after a successful download with the chart's
// source provenance (OCI ref and digest).
ResolvedSource *rcommon.ChartSource
}
// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file.
@ -145,6 +150,15 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
}
}
// Populate source provenance for cached OCI charts.
// The ref and digest are already known from ResolveChartVersion.
if found && u.Scheme == registry.OCIScheme {
c.ResolvedSource = &rcommon.ChartSource{
RegistryRef: ref,
Digest: hash,
}
}
if !found {
c.Options = append(c.Options, getter.WithAcceptHeader("application/gzip,application/octet-stream"))
@ -152,6 +166,14 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
if err != nil {
return "", nil, err
}
// Capture source provenance via duck-typing
if u.Scheme == registry.OCIScheme {
c.ResolvedSource = &rcommon.ChartSource{RegistryRef: ref}
if dg, ok := g.(interface{ Digest() string }); ok {
c.ResolvedSource.Digest = dg.Digest()
}
}
}
name := filepath.Base(u.Path)
@ -253,6 +275,14 @@ func (c *ChartDownloader) DownloadToCache(ref, version string) (string, *provena
pth, err = c.Cache.Get(digest32, CacheChart)
if err == nil {
slog.Debug("found chart in cache", "id", digestString)
// Populate source provenance for cached OCI charts.
if u.Scheme == registry.OCIScheme {
c.ResolvedSource = &rcommon.ChartSource{
RegistryRef: ref,
Digest: digestString,
}
}
}
}
if len(digest) == 0 || err != nil {
@ -267,6 +297,14 @@ func (c *ChartDownloader) DownloadToCache(ref, version string) (string, *provena
return "", nil, gerr
}
// Capture source provenance via duck-typing
if u.Scheme == registry.OCIScheme {
c.ResolvedSource = &rcommon.ChartSource{RegistryRef: ref}
if dg, ok := g.(interface{ Digest() string }); ok {
c.ResolvedSource.Digest = dg.Digest()
}
}
// Generate the digest
if len(digest) == 0 {
digest32 = sha256.Sum256(data.Bytes())

@ -36,6 +36,7 @@ type OCIGetter struct {
opts getterOptions
transport *http.Transport
once sync.Once
digest string
}
// Get performs a Get from repo.Getter and returns the body.
@ -82,6 +83,7 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
if err != nil {
return nil, err
}
g.digest = result.Manifest.Digest
if requestingProv {
return bytes.NewBuffer(result.Prov.Data), nil
@ -89,6 +91,11 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
return bytes.NewBuffer(result.Chart.Data), nil
}
// Digest returns the OCI manifest digest from the most recent pull.
func (g *OCIGetter) Digest() string {
return g.digest
}
// NewOCIGetter constructs a valid http/https client as a Getter
func NewOCIGetter(ops ...Option) (Getter, error) {
var client OCIGetter

@ -0,0 +1,23 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
// ChartSource records where a chart was fetched from.
type ChartSource struct {
RepoURL string `json:"repo_url,omitempty"`
RegistryRef string `json:"registry_ref,omitempty"`
Digest string `json:"digest,omitempty"`
}

@ -51,6 +51,9 @@ type Release struct {
// ApplyMethod stores whether server-side or client-side apply was used for the release
// Unset (empty string) should be treated as the default of client-side apply
ApplyMethod string `json:"apply_method,omitempty"` // "ssa" | "csa"
// Source records where the chart was fetched from.
// Nil for releases created before this field was added.
Source *common.ChartSource `json:"source,omitempty"`
}
// SetStatus is a helper for setting the status on a release.

Loading…
Cancel
Save