Skip to content

Commit f915dd0

Browse files
committed
don't fetch tags when exact version is used in HelmRepository
Taking this shortcut has two benefits: 1. It allows charts to be fetched from AWS's public container registry at public.ecr.aws 2. It makes reconciling a HelmChart faster by skipping one or more potentially expensive API calls to the registry. I adapted the unit tests to the new behavior that the OCIChartRepository doesn't fail anymore for the case where a specific chart version has been requested that doesn't actually exist in the registry. refs #845 Signed-off-by: Max Jonas Werner <max@e13.dev> fix tests
1 parent cf1a27c commit f915dd0

File tree

4 files changed

+84
-47
lines changed

4 files changed

+84
-47
lines changed

internal/helm/chart/builder_remote_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,14 @@ func (m *mockRegistryClient) Logout(url string, opts ...registry.LogoutOption) e
6464
type mockIndexChartGetter struct {
6565
IndexResponse []byte
6666
ChartResponse []byte
67+
ErrorResponse error
6768
requestedURL string
6869
}
6970

7071
func (g *mockIndexChartGetter) Get(u string, _ ...helmgetter.Option) (*bytes.Buffer, error) {
72+
if g.ErrorResponse != nil {
73+
return nil, g.ErrorResponse
74+
}
7175
g.requestedURL = u
7276
r := g.ChartResponse
7377
if strings.HasSuffix(u, "index.yaml") {
@@ -248,6 +252,15 @@ func TestRemoteBuilder_BuildFromOCIChatRepository(t *testing.T) {
248252
RegistryClient: registryClient,
249253
}
250254
}
255+
mockRepoWithoutChart := func() *repository.OCIChartRepository {
256+
return &repository.OCIChartRepository{
257+
URL: *u,
258+
Client: &mockIndexChartGetter{
259+
ErrorResponse: fmt.Errorf("chart doesn't exist"),
260+
},
261+
RegistryClient: registryClient,
262+
}
263+
}
251264

252265
tests := []struct {
253266
name string
@@ -278,8 +291,8 @@ func TestRemoteBuilder_BuildFromOCIChatRepository(t *testing.T) {
278291
{
279292
name: "chart version not in repository",
280293
reference: RemoteReference{Name: "grafana", Version: "1.1.1"},
281-
repository: mockRepo(),
282-
wantErr: "failed to get chart version for remote reference",
294+
repository: mockRepoWithoutChart(),
295+
wantErr: "failed to download chart for remote reference",
283296
},
284297
{
285298
name: "invalid version metadata",
@@ -334,7 +347,7 @@ func TestRemoteBuilder_BuildFromOCIChatRepository(t *testing.T) {
334347
cb, err := b.Build(context.TODO(), tt.reference, targetPath, tt.buildOpts)
335348

336349
if tt.wantErr != "" {
337-
g.Expect(err).To(HaveOccurred())
350+
g.Expect(err).To(HaveOccurred(), "expected error '%s'", tt.wantErr)
338351
g.Expect(err.Error()).To(ContainSubstring(tt.wantErr))
339352
g.Expect(cb).To(BeZero())
340353
return

internal/helm/chart/dependency_manager_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,8 @@ func TestDependencyManager_addRemoteOCIDependency(t *testing.T) {
604604
},
605605
wantFunc: func(g *WithT, c *helmchart.Chart) {
606606
g.Expect(c.Dependencies()).To(HaveLen(1))
607+
dep := c.Dependencies()[0]
608+
g.Expect(dep).NotTo(BeNil())
607609
},
608610
},
609611
{
@@ -633,9 +635,7 @@ func TestDependencyManager_addRemoteOCIDependency(t *testing.T) {
633635
Scheme: "oci",
634636
Host: "example.com",
635637
},
636-
Client: &mockGetter{
637-
Response: chartB,
638-
},
638+
Client: &mockGetter{},
639639
RegistryClient: &mockTagsGetter{
640640
tags: map[string][]string{
641641
"helmchart": {"0.1.0"},
@@ -648,7 +648,7 @@ func TestDependencyManager_addRemoteOCIDependency(t *testing.T) {
648648
Version: "0.2.0",
649649
Repository: "oci://example.com",
650650
},
651-
wantErr: "could not locate a version matching provided version string 0.2.0",
651+
wantErr: "failed to load downloaded archive of version '0.2.0'",
652652
},
653653
{
654654
name: "chart load error",

internal/helm/repository/oci_chart_repository.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,25 @@ func NewOCIChartRepository(repositoryURL string, chartRepoOpts ...OCIChartReposi
131131
// stable version will be returned and prerelease versions will be ignored.
132132
// adapted from https://github.com/helm/helm/blob/49819b4ef782e80b0c7f78c30bd76b51ebb56dc8/pkg/downloader/chart_downloader.go#L162
133133
func (r *OCIChartRepository) GetChartVersion(name, ver string) (*repo.ChartVersion, error) {
134-
// Find chart versions matching the given name.
135-
// Either in an index file or from a registry.
134+
136135
cpURL := r.URL
137136
cpURL.Path = path.Join(cpURL.Path, name)
137+
138+
// if ver is a valid semver version, take a shortcut here so we don't need to list all tags which can be an
139+
// expensive operation.
140+
if _, err := version.ParseVersion(ver); err == nil {
141+
return &repo.ChartVersion{
142+
URLs: []string{fmt.Sprintf("%s:%s", cpURL.String(), ver)},
143+
Metadata: &chart.Metadata{
144+
Name: name,
145+
Version: ver,
146+
},
147+
}, nil
148+
}
149+
150+
// ver doesn't denote a concrete version so we interpret it as a semver range and try to find the best-matching
151+
// version from the list of tags in the registry.
152+
138153
cvs, err := r.getTags(cpURL.String())
139154
if err != nil {
140155
return nil, fmt.Errorf("could not get tags for %q: %s", name, err)

internal/helm/repository/oci_chart_repository_test.go

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -118,67 +118,76 @@ func TestOCIChartRepository_Get(t *testing.T) {
118118
testURL := "oci://localhost:5000/my_repo"
119119

120120
testCases := []struct {
121-
name string
122-
url string
123-
version string
124-
expected string
125-
expectedErr string
121+
name string
122+
registryClient RegistryClient
123+
url string
124+
version string
125+
expected string
126+
expectedErr string
126127
}{
127128
{
128-
name: "should return latest stable version",
129-
version: "",
130-
url: testURL,
131-
expected: "1.0.0",
129+
name: "should return latest stable version",
130+
registryClient: registryClient,
131+
version: "",
132+
url: testURL,
133+
expected: "1.0.0",
132134
},
133135
{
134-
name: "should return latest stable version (asterisk)",
135-
version: "*",
136-
url: testURL,
137-
expected: "1.0.0",
136+
name: "should return latest stable version (asterisk)",
137+
registryClient: registryClient,
138+
version: "*",
139+
url: testURL,
140+
expected: "1.0.0",
138141
},
139142
{
140-
name: "should return latest stable version (semver range)",
141-
version: ">=0.1.5",
142-
url: testURL,
143-
expected: "1.0.0",
143+
name: "should return latest stable version (semver range)",
144+
registryClient: registryClient,
145+
version: ">=0.1.5",
146+
url: testURL,
147+
expected: "1.0.0",
144148
},
145149
{
146-
name: "should return 0.2.0 (semver range)",
147-
version: "0.2.x",
148-
url: testURL,
149-
expected: "0.2.0",
150+
name: "should return 0.2.0 (semver range)",
151+
registryClient: registryClient,
152+
version: "0.2.x",
153+
url: testURL,
154+
expected: "0.2.0",
150155
},
151156
{
152-
name: "should return a perfect match",
153-
version: "0.1.0",
154-
url: testURL,
155-
expected: "0.1.0",
157+
name: "should return a perfect match",
158+
registryClient: nil,
159+
version: "0.1.0",
160+
url: testURL,
161+
expected: "0.1.0",
156162
},
157163
{
158-
name: "should return 0.10.0",
159-
version: "0.*",
160-
url: testURL,
161-
expected: "0.10.0",
164+
name: "should return 0.10.0",
165+
registryClient: registryClient,
166+
version: "0.*",
167+
url: testURL,
168+
expected: "0.10.0",
162169
},
163170
{
164-
name: "should an error for unfunfilled range",
165-
version: ">2.0.0",
166-
url: testURL,
167-
expectedErr: "could not locate a version matching provided version string >2.0.0",
171+
name: "should an error for unfulfilled range",
172+
registryClient: registryClient,
173+
version: ">2.0.0",
174+
url: testURL,
175+
expectedErr: "could not locate a version matching provided version string >2.0.0",
168176
},
169177
{
170-
name: "shouldn't error out with trailing slash",
171-
version: "",
172-
url: "oci://localhost:5000/my_repo/",
173-
expected: "1.0.0",
178+
name: "shouldn't error out with trailing slash",
179+
registryClient: registryClient,
180+
version: "",
181+
url: "oci://localhost:5000/my_repo/",
182+
expected: "1.0.0",
174183
},
175184
}
176185

177186
for _, tc := range testCases {
178187

179188
t.Run(tc.name, func(t *testing.T) {
180189
g := NewWithT(t)
181-
r, err := NewOCIChartRepository(tc.url, WithOCIRegistryClient(registryClient), WithOCIGetter(providers))
190+
r, err := NewOCIChartRepository(tc.url, WithOCIRegistryClient(tc.registryClient), WithOCIGetter(providers))
182191
g.Expect(err).ToNot(HaveOccurred())
183192
g.Expect(r).ToNot(BeNil())
184193

0 commit comments

Comments
 (0)