From ac54143db7d8ec8f810156feea3e39a2a1c5209c Mon Sep 17 00:00:00 2001 From: abdouladieng Date: Mon, 14 Sep 2026 14:00:20 +0000 Subject: [PATCH] fix(downloader): do not error for remote chart when local repo cache is missing When resolving an absolute chart URL, scanReposForURL scans known repositories to check if any match the chart URL in order to apply repository-specific TLS or authentication options. Previously, if any repository in repositories.yaml lacked a local cached index file (for example because the cache was cleared or never updated), scanReposForURL immediately aborted with an error ('no cached repo found'). This caused ResolveChartVersion to fail completely, preventing the user from downloading or templating an unrelated remote chart even when fetching directly by URL or when another repository had a valid cache. This change skips repositories whose index cache does not exist (fs.ErrNotExist) rather than returning an early error. If no repository matches, ErrNoOwnerRepo is returned and swallowed by ResolveChartVersion to fall back to the default HTTP client as intended. Closes #11961 Signed-off-by: abdouladieng --- pkg/downloader/chart_downloader.go | 4 ++ pkg/downloader/chart_downloader_test.go | 65 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 712c80ad2..9e09b4216 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -567,6 +567,10 @@ func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry, idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) i, err := repo.LoadIndexFile(idxFile) if err != nil { + if errors.Is(err, fs.ErrNotExist) { + // No cache for this repository; skip and keep looking rather than erroring + continue + } return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'): %w", err) } diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 15e127b8c..f4639a2c6 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -328,6 +328,71 @@ func TestScanReposForURL(t *testing.T) { require.ErrorIs(t, err, ErrNoOwnerRepo) } +func TestScanReposForURLWithEmptyCache(t *testing.T) { + emptyCache := t.TempDir() + c := ChartDownloader{ + Out: os.Stderr, + Verify: VerifyLater, + RepositoryConfig: repoConfig, + RepositoryCache: emptyCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: emptyCache, + }), + } + + u := "http://example.com/alpine-0.2.0.tgz" + rf, err := repo.LoadFile(repoConfig) + require.NoError(t, err) + + _, err = c.scanReposForURL(u, rf) + require.ErrorIs(t, err, ErrNoOwnerRepo) +} + +func TestScanReposForURLWithMissingCacheForUnrelatedRepo(t *testing.T) { + rf := &repo.File{ + APIVersion: "v1", + Repositories: []*repo.Entry{ + {Name: "missing-repo", URL: "http://missing.example.com"}, + {Name: "testing", URL: "http://example.com"}, + }, + } + c := ChartDownloader{ + Out: os.Stderr, + Verify: VerifyLater, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), + } + + u := "http://example.com/alpine-0.2.0.tgz" + entry, err := c.scanReposForURL(u, rf) + require.NoError(t, err) + assert.Equal(t, "testing", entry.Name, "Unexpected repo %q for URL %q", entry.Name, u) +} + +func TestResolveChartVersionWithEmptyCache(t *testing.T) { + emptyCache := t.TempDir() + c := ChartDownloader{ + Out: os.Stderr, + Verify: VerifyLater, + RepositoryConfig: repoConfig, + RepositoryCache: emptyCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: emptyCache, + }), + } + + u := "http://example.com/alpine-0.2.0.tgz" + _, resolvedURL, err := c.ResolveChartVersion(u, "") + require.NoError(t, err) + assert.Equal(t, u, resolvedURL.String()) +} + func TestDownloadToCache(t *testing.T) { srv := repotest.NewTempServer(t, repotest.WithChartSourceGlob("testdata/*.tgz*"),