diff --git a/pkg/action/pull.go b/pkg/action/pull.go index dd051167b..db06b8a51 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -89,6 +89,7 @@ func (p *Pull) Run(chartRef string) (string, error) { RepositoryConfig: p.Settings.RepositoryConfig, RepositoryCache: p.Settings.RepositoryCache, ContentCache: p.Settings.ContentCache, + Debug: p.Settings.Debug, } if registry.IsOCI(chartRef) { diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index ee4f8abe3..64b369b45 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -85,6 +85,7 @@ type ChartDownloader struct { // Cache specifies the cache implementation to use. Cache Cache + Debug bool } // DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. @@ -110,7 +111,6 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven if err != nil { return "", nil, err } - g, err := c.Getters.ByScheme(u.Scheme) if err != nil { return "", nil, err @@ -139,10 +139,13 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven } } } - if !found { c.Options = append(c.Options, getter.WithAcceptHeader("application/gzip,application/octet-stream")) + if c.Debug { + c.Options = append(c.Options, getter.WithDebug(true)) + } + data, err = g.Get(u.String(), c.Options...) if err != nil { return "", nil, err diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index a2d0f0ee2..f51d0f6a1 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -49,6 +49,7 @@ type getterOptions struct { timeout time.Duration transport *http.Transport artifactType string + Debug bool } // Option allows specifying various settings configurable by the user for overriding the defaults diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index 110f45c54..66ae94d57 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -20,6 +20,7 @@ import ( "crypto/tls" "fmt" "io" + "log/slog" "net/http" "net/url" "sync" @@ -43,6 +44,12 @@ func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error) return g.get(href) } +func WithDebug(debug bool) Option { + return func(o *getterOptions) { + o.Debug = debug + } +} + func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) { // Set a helm specific user agent so that a repo server and metrics can // separate helm calls from other tools interacting with repos. @@ -112,8 +119,12 @@ func NewHTTPGetter(options ...Option) (Getter, error) { func (g *HTTPGetter) httpClient() (*http.Client, error) { if g.opts.transport != nil { + var tr http.RoundTripper = g.opts.transport + if g.opts.Debug { + tr = &debugTransport{transport: tr} + } return &http.Client{ - Transport: g.opts.transport, + Transport: tr, Timeout: g.opts.timeout, }, nil } @@ -122,9 +133,7 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) { g.transport = &http.Transport{ DisableCompression: true, Proxy: http.ProxyFromEnvironment, - // Being nil would cause the tls.Config default to be used - // "NewTLSConfig" modifies an empty TLS config, not the default one - TLSClientConfig: &tls.Config{}, + TLSClientConfig: &tls.Config{}, } }) @@ -151,10 +160,32 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) { } } + var tr http.RoundTripper = g.transport + if g.opts.Debug { + tr = &debugTransport{transport: g.transport} + } + client := &http.Client{ - Transport: g.transport, + Transport: tr, Timeout: g.opts.timeout, } return client, nil } + +type debugTransport struct { + transport http.RoundTripper +} + +func (d *debugTransport) RoundTrip(req *http.Request) (*http.Response, error) { + slog.Debug("http request", "method", req.Method, "url", req.URL.String()) + + resp, err := d.transport.RoundTrip(req) + if err != nil { + slog.Debug("http request failed", "error", err) + return nil, err + } + + slog.Debug("http response", "status", resp.Status) + return resp, nil +} diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go index ef8b87503..2c76afe3f 100644 --- a/pkg/getter/plugingetter.go +++ b/pkg/getter/plugingetter.go @@ -98,8 +98,16 @@ type getterPlugin struct { func (g *getterPlugin) Get(href string, options ...Option) (*bytes.Buffer, error) { opts := convertOptions(g.options, options) + var finalOpts getterOptions + for _, opt := range g.options { + opt(&finalOpts) + } + + env := g.env + if finalOpts.Debug { + env = append(env, "HELM_DEBUG=1") + } - // TODO optimization: pass this along to Get() instead of re-parsing here u, err := url.Parse(href) if err != nil { return nil, err @@ -111,9 +119,7 @@ func (g *getterPlugin) Get(href string, options ...Option) (*bytes.Buffer, error Options: opts, Protocol: u.Scheme, }, - Env: g.env, - // TODO should we pass Stdin, Stdout, and Stderr through Input here to getter plugins? - // Stdout: os.Stdout, + Env: env, } output, err := g.plg.Invoke(context.Background(), input) if err != nil {