Skip to content

Commit 7fe1903

Browse files
authored
Merge pull request #1015 from somtochiama/helm-repo-url
Fix: Normalize helm repository url with query params properly
2 parents a8fc26c + 6f0384c commit 7fe1903

File tree

5 files changed

+59
-38
lines changed

5 files changed

+59
-38
lines changed

controllers/helmchart_controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,10 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
509509
ctxTimeout, cancel := context.WithTimeout(ctx, repo.Spec.Timeout.Duration)
510510
defer cancel()
511511

512-
normalizedURL := repository.NormalizeURL(repo.Spec.URL)
512+
normalizedURL, err := repository.NormalizeURL(repo.Spec.URL)
513+
if err != nil {
514+
return chartRepoConfigErrorReturn(err, obj)
515+
}
513516
// Construct the Getter options from the HelmRepository data
514517
clientOpts := []helmgetter.Option{
515518
helmgetter.WithURL(normalizedURL),
@@ -1021,7 +1024,10 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
10211024
keychain authn.Keychain
10221025
)
10231026

1024-
normalizedURL := repository.NormalizeURL(url)
1027+
normalizedURL, err := repository.NormalizeURL(url)
1028+
if err != nil {
1029+
return nil, err
1030+
}
10251031
obj, err := r.resolveDependencyRepository(ctx, url, namespace)
10261032
if err != nil {
10271033
// Return Kubernetes client errors, but ignore others
@@ -1201,8 +1207,8 @@ func (r *HelmChartReconciler) indexHelmRepositoryByURL(o client.Object) []string
12011207
if !ok {
12021208
panic(fmt.Sprintf("Expected a HelmRepository, got %T", o))
12031209
}
1204-
u := repository.NormalizeURL(repo.Spec.URL)
1205-
if u != "" {
1210+
u, err := repository.NormalizeURL(repo.Spec.URL)
1211+
if u != "" && err == nil {
12061212
return []string{u}
12071213
}
12081214
return nil

internal/helm/chart/dependency_manager.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,10 @@ func (dm *DependencyManager) resolveRepository(url string) (repo repository.Down
266266
dm.mu.Lock()
267267
defer dm.mu.Unlock()
268268

269-
nUrl := repository.NormalizeURL(url)
269+
nUrl, err := repository.NormalizeURL(url)
270+
if err != nil {
271+
return
272+
}
270273
err = repository.ValidateDepURL(nUrl)
271274
if err != nil {
272275
return

internal/helm/repository/chart_repository.go

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"os"
2828
"path"
2929
"sort"
30-
"strings"
3130
"sync"
3231

3332
"github.com/Masterminds/semver/v3"
@@ -271,31 +270,16 @@ func (r *ChartRepository) DownloadChart(chart *repo.ChartVersion) (*bytes.Buffer
271270
// always the correct one to pick, check for updates once in awhile.
272271
// Ref: https://github.com/helm/helm/blob/v3.3.0/pkg/downloader/chart_downloader.go#L241
273272
ref := chart.URLs[0]
274-
u, err := url.Parse(ref)
273+
resolvedUrl, err := repo.ResolveReferenceURL(r.URL, ref)
275274
if err != nil {
276-
err = fmt.Errorf("invalid chart URL format '%s': %w", ref, err)
277275
return nil, err
278276
}
279277

280-
// Prepend the chart repository base URL if the URL is relative
281-
if !u.IsAbs() {
282-
repoURL, err := url.Parse(r.URL)
283-
if err != nil {
284-
err = fmt.Errorf("invalid chart repository URL format '%s': %w", r.URL, err)
285-
return nil, err
286-
}
287-
q := repoURL.Query()
288-
// Trailing slash is required for ResolveReference to work
289-
repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/"
290-
u = repoURL.ResolveReference(u)
291-
u.RawQuery = q.Encode()
292-
}
293-
294278
t := transport.NewOrIdle(r.tlsConfig)
295279
clientOpts := append(r.Options, getter.WithTransport(t))
296280
defer transport.Release(t)
297281

298-
return r.Client.Get(u.String(), clientOpts...)
282+
return r.Client.Get(resolvedUrl, clientOpts...)
299283
}
300284

301285
// CacheIndex attempts to write the index from the remote into a new temporary file

internal/helm/repository/utils.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package repository
1818

1919
import (
2020
"fmt"
21+
"net/url"
2122
"strings"
2223

2324
helmreg "helm.sh/helm/v3/pkg/registry"
@@ -35,17 +36,22 @@ var (
3536
)
3637

3738
// NormalizeURL normalizes a ChartRepository URL by its scheme.
38-
func NormalizeURL(repositoryURL string) string {
39+
func NormalizeURL(repositoryURL string) (string, error) {
3940
if repositoryURL == "" {
40-
return ""
41+
return "", nil
4142
}
42-
43-
if strings.Contains(repositoryURL, helmreg.OCIScheme) {
44-
return strings.TrimRight(repositoryURL, "/")
43+
u, err := url.Parse(repositoryURL)
44+
if err != nil {
45+
return "", err
4546
}
4647

47-
return strings.TrimRight(repositoryURL, "/") + "/"
48+
if u.Scheme == helmreg.OCIScheme {
49+
u.Path = strings.TrimRight(u.Path, "/")
50+
return u.String(), nil
51+
}
4852

53+
u.Path = strings.TrimRight(u.Path, "/") + "/"
54+
return u.String(), nil
4955
}
5056

5157
// ValidateDepURL returns an error if the given depended repository URL declaration is not supported

internal/helm/repository/utils_test.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ import (
2424

2525
func TestNormalizeURL(t *testing.T) {
2626
tests := []struct {
27-
name string
28-
url string
29-
want string
27+
name string
28+
url string
29+
want string
30+
wantErr bool
3031
}{
3132
{
3233
name: "with slash",
@@ -43,11 +44,6 @@ func TestNormalizeURL(t *testing.T) {
4344
url: "http://example.com//",
4445
want: "http://example.com/",
4546
},
46-
{
47-
name: "empty",
48-
url: "",
49-
want: "",
50-
},
5147
{
5248
name: "oci with slash",
5349
url: "oci://example.com/",
@@ -58,12 +54,38 @@ func TestNormalizeURL(t *testing.T) {
5854
url: "oci://example.com//",
5955
want: "oci://example.com",
6056
},
57+
{
58+
name: "url with query",
59+
url: "http://example.com?st=pr",
60+
want: "http://example.com/?st=pr",
61+
},
62+
{
63+
name: "url with slash and query",
64+
url: "http://example.com/?st=pr",
65+
want: "http://example.com/?st=pr",
66+
},
67+
{
68+
name: "empty url",
69+
url: "",
70+
want: "",
71+
},
72+
{
73+
name: "bad url",
74+
url: "://badurl.",
75+
wantErr: true,
76+
},
6177
}
6278
for _, tt := range tests {
6379
t.Run(tt.name, func(t *testing.T) {
6480
g := NewWithT(t)
6581

66-
got := NormalizeURL(tt.url)
82+
got, err := NormalizeURL(tt.url)
83+
if tt.wantErr {
84+
g.Expect(err).To(HaveOccurred())
85+
return
86+
}
87+
88+
g.Expect(err).To(Not(HaveOccurred()))
6789
g.Expect(got).To(Equal(tt.want))
6890
})
6991
}

0 commit comments

Comments
 (0)