pull/31206/merge
Benoit Tigeot 11 hours ago committed by GitHub
commit c4b56987ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -17,7 +17,9 @@ limitations under the License.
package action package action
import ( import (
"crypto/sha256"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -138,6 +140,36 @@ func (p *Pull) Run(chartRef string) (string, error) {
return out.String(), err return out.String(), err
} }
// Print a pull summary for repo mode and non-OCI downloads.
// Some repos (e.g., Bitnami) resolve --repo pulls to oci:// refs, but that
// path does not go through the registry client's summary printer, so we emit
// a consistent summary here. We mirror the OCI output format:
//
// Pulled: <chartUrl>:<version>
// Checksum: sha256:<digest>
//
// For HTTP/repo pulls, the digest is the SHA-256 of the downloaded .tgz archive.
// For direct OCI pulls, the registry client already prints "Pulled:" and
// "Digest:" (manifest digest), so we do not print here to avoid duplicates.
if p.RepoURL != "" || !registry.IsOCI(downloadSourceRef) {
base := strings.TrimSuffix(downloadSourceRef, ".tgz")
chart, ver := splitChartNameVersion(base)
tag := chart
if ver != "" {
tag += ":" + ver
} else if p.Version != "" {
tag += ":" + p.Version
}
fmt.Fprintf(&out, "Pulled: %s\n", tag)
if sum, err := sha256File(saved); err == nil {
fmt.Fprintf(&out, "Checksum: sha256:%x\n", sum)
} else {
fmt.Fprintf(&out, "Checksum failed: %v\n", err)
}
}
if p.Verify { if p.Verify {
for name := range v.SignedBy.Identities { for name := range v.SignedBy.Identities {
fmt.Fprintf(&out, "Signed by: %v\n", name) fmt.Fprintf(&out, "Signed by: %v\n", name)
@ -173,3 +205,24 @@ func (p *Pull) Run(chartRef string) (string, error) {
} }
return out.String(), nil return out.String(), nil
} }
func splitChartNameVersion(s string) (name, version string) {
if i := strings.LastIndex(s, "-"); i >= 0 {
return s[:i], s[i+1:]
}
return s, ""
}
func sha256File(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return nil, err
}
return h.Sum(nil), nil
}

@ -0,0 +1,130 @@
/*
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 action
import (
"crypto/sha256"
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/cli"
)
// helm pull testchart --repo=http://127.0.0.1:<port> --version 1.2.3
func TestPull_PrintsSummary_ForHTTPRepo(t *testing.T) {
t.Parallel()
// Minimal chart payload; verification is off so a plain byte buffer is fine.
chartBytes := []byte("dummy-chart-content")
sum := sha256.Sum256(chartBytes)
wantDigest := fmt.Sprintf("sha256:%x", sum)
// Serve a valid index.yaml and the chart archive.
mux := http.NewServeMux()
mux.HandleFunc("/index.yaml", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/yaml")
// Valid index entry requires both name and version.
fmt.Fprintf(w, `apiVersion: v1
entries:
testchart:
- name: testchart
version: 1.2.3
urls:
- testchart-1.2.3.tgz
created: "2020-01-01T00:00:00Z"
`)
})
mux.HandleFunc("/testchart-1.2.3.tgz", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/gzip")
_, _ = w.Write(chartBytes)
})
srv := httptest.NewServer(mux)
defer srv.Close()
settings := cli.New()
settings.RepositoryCache = t.TempDir()
settings.RepositoryConfig = filepath.Join(t.TempDir(), "repositories.yaml")
settings.ContentCache = t.TempDir()
cfg := &Configuration{}
p := NewPull(WithConfig(cfg))
p.Settings = settings
p.DestDir = t.TempDir()
p.RepoURL = srv.URL
p.Version = "1.2.3"
out, err := p.Run("testchart")
require.NoError(t, err, "Pull.Run() should succeed. Output:\n%s", out)
expectedURL := srv.URL + "/testchart:1.2.3"
assert.Contains(t, out, "Pulled: "+expectedURL, "expected Pulled summary in output")
assert.Contains(t, out, "Checksum: "+wantDigest, "expected archive checksum in output")
// Ensure the chart file was saved.
_, statErr := os.Stat(filepath.Join(p.DestDir, "testchart-1.2.3.tgz"))
require.NoError(t, statErr, "expected chart archive to be saved")
}
// helm pull http://127.0.0.1:<port>/directchart-9.9.9.tgz
func TestPull_PrintsSummary_ForDirectHTTPURL(t *testing.T) {
t.Parallel()
chartBytes := []byte("another-dummy-chart")
sum := sha256.Sum256(chartBytes)
wantDigest := fmt.Sprintf("sha256:%x", sum)
mux := http.NewServeMux()
mux.HandleFunc("/directchart-9.9.9.tar.gz", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/gzip")
_, _ = w.Write(chartBytes)
})
srv := httptest.NewServer(mux)
defer srv.Close()
settings := cli.New()
settings.RepositoryCache = t.TempDir()
settings.RepositoryConfig = filepath.Join(t.TempDir(), "repositories.yaml")
settings.ContentCache = t.TempDir()
cfg := &Configuration{}
p := NewPull(WithConfig(cfg))
p.Settings = settings
p.DestDir = t.TempDir()
// Direct HTTP URL (absolute URL). Version is ignored for absolute URLs.
chartURL := srv.URL + "/directchart-9.9.9.tar.gz"
out, err := p.Run(chartURL)
require.NoError(t, err, "Pull.Run() should succeed. Output:\n%s", out)
// Output should reflect name-version.tgz from the URL.
expectedURL := srv.URL + "/directchart:9.9.9"
assert.Contains(t, out, "Pulled: "+expectedURL, "expected Pulled summary in output")
assert.Contains(t, out, "Checksum: "+wantDigest, "expected archive checksum in output")
// Ensure the chart file was saved.
_, statErr := os.Stat(filepath.Join(p.DestDir, "directchart-9.9.9.tar.gz"))
require.NoError(t, statErr, "expected chart archive to be saved")
}

@ -44,7 +44,9 @@ func TestPullCmd(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
helmTestKeyOut := "Signed by: Helm Testing (This key should only be used for testing. DO NOT TRUST.) <helm-testing@helm.sh>\n" + helmTestKeyOut := "Pulled: test/signtest\n" +
"Checksum: sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55\n" +
"Signed by: Helm Testing (This key should only be used for testing. DO NOT TRUST.) <helm-testing@helm.sh>\n" +
"Using Key With Fingerprint: 5E615389B53CA37F0EE60BD3843BBF981FC18762\n" + "Using Key With Fingerprint: 5E615389B53CA37F0EE60BD3843BBF981FC18762\n" +
"Chart Hash Verified: " "Chart Hash Verified: "

Loading…
Cancel
Save