Skip to content

Commit c0fabfb

Browse files
migruiz4beltran-rubo
andauthoredDec 24, 2024··
Add support for OCI tarball url (#8184)
### Description of the change This PR aims to fix #8153 by resolving this error: ``` E1219 15:25:15.632218 1 utils.go:1024] Failed to import files, ID=bitnami/nginx, version=17.3.3: Get "oci://registry-1.docker.io/bitnamicharts/nginx:17.3.3": unsupported protocol scheme "oci" ``` ### Benefits Currently, the kubeapps-asset-syncer is unable to fetch the information when the chart URL is stored in OCI. In this PR, the chart tarballUrl is transformed when the oci schema is detected and replaced by its OCI blob URL, so the asset syncer will be able to fetch the chart tarball the same way as it does for non-OCI charts. ### Possible drawbacks None known. ### Applicable issues <!-- Enter any applicable Issues here (You can reference an issue using #) --> - fixes #8153 --------- Signed-off-by: Miguel Ruiz <[email protected]> Co-authored-by: Beltran Rueda <[email protected]>
1 parent 3fb5e43 commit c0fabfb

File tree

5 files changed

+45
-25
lines changed

5 files changed

+45
-25
lines changed
 

‎cmd/asset-syncer/server/utils.go

+27-5
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,33 @@ func (r *HelmRepo) FetchFiles(cv models.ChartVersion, userAgent string, passCred
306306
authorizationHeader = r.AuthorizationHeader
307307
}
308308

309-
return tarutil.FetchChartDetailFromTarballUrl(
310-
chartTarballURL,
311-
userAgent,
312-
authorizationHeader,
313-
r.netClient)
309+
// If URL points to an OCI chart, we transform its URL to its tgz blob URL
310+
if strings.HasPrefix(chartTarballURL, "oci://") {
311+
return FetchChartDetailFromOciUrl(chartTarballURL, userAgent, authorizationHeader, r.netClient)
312+
} else {
313+
return tarutil.FetchChartDetailFromTarballUrl(chartTarballURL, userAgent, authorizationHeader, r.netClient)
314+
}
315+
}
316+
317+
// Fetches helm chart details from an OCI url
318+
func FetchChartDetailFromOciUrl(chartTarballURL string, userAgent string, authz string, netClient *http.Client) (map[string]string, error) {
319+
headers := http.Header{}
320+
if len(userAgent) > 0 {
321+
headers.Add("User-Agent", userAgent)
322+
}
323+
if len(authz) > 0 {
324+
headers.Add("Authorization", authz)
325+
}
326+
327+
puller := &helm.OCIPuller{Resolver: docker.NewResolver(docker.ResolverOptions{Headers: headers, Client: netClient})}
328+
329+
ref := strings.TrimPrefix(strings.TrimSpace(chartTarballURL), "oci://")
330+
chartBuffer, _, err := puller.PullOCIChart(ref)
331+
if err != nil {
332+
return nil, err
333+
}
334+
335+
return tarutil.FetchChartDetailFromTarball(chartBuffer)
314336
}
315337

316338
// TagList represents a list of tags as specified at

‎cmd/kubeapps-apis/plugins/helm/packages/v1alpha1/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,7 @@ func (s *Server) fetchChartWithRegistrySecrets(ctx context.Context, headers http
963963
},
964964
appRepo,
965965
caCertSecret, authSecret,
966-
s.chartClientFactory.New(appRepo.Spec.Type, userAgentString),
966+
s.chartClientFactory.New(tarballURL, userAgentString),
967967
)
968968
if err != nil {
969969
return nil, nil, connect.NewError(connect.CodeInternal, fmt.Errorf("Unable to fetch the chart %s from the namespace %q: %w", chartDetails.ChartName, appRepo.Namespace, err))

‎cmd/kubeapps-apis/plugins/helm/packages/v1alpha1/utils/chart-client.go

+12-14
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,24 @@ func (c *HelmRepoClient) GetChart(details *ChartDetails, repoURL string) (*chart
101101
}
102102

103103
log.Infof("Downloading %s ...", chartURL)
104-
chart, err := fetchChart(c.netClient, chartURL)
104+
chart, err := fetchChart(c.netClient, chartURL.String())
105105
if err != nil {
106106
return nil, err
107107
}
108108

109109
return chart, nil
110110
}
111111

112-
func resolveChartURL(indexURL, chartURL string) (string, error) {
112+
func resolveChartURL(indexURL, chartURL string) (*url.URL, error) {
113113
parsedIndexURL, err := url.Parse(strings.TrimSpace(indexURL))
114114
if err != nil {
115-
return "", err
115+
return nil, err
116116
}
117117
parsedChartURL, err := parsedIndexURL.Parse(strings.TrimSpace(chartURL))
118118
if err != nil {
119-
return "", err
119+
return nil, err
120120
}
121-
return parsedChartURL.String(), nil
121+
return parsedChartURL, nil
122122
}
123123

124124
// fetchChart returns the Chart content given an URL
@@ -184,16 +184,15 @@ func (c *OCIRepoClient) GetChart(details *ChartDetails, repoURL string) (*chart.
184184
if c.puller == nil {
185185
return nil, fmt.Errorf("unable to retrieve chart, Init should be called first")
186186
}
187-
parsedURL, err := url.ParseRequestURI(strings.TrimSpace(repoURL))
188-
if err != nil {
189-
return nil, err
187+
if details == nil || details.TarballURL == "" {
188+
return nil, fmt.Errorf("unable to retrieve chart, missing chart details")
190189
}
191-
unescapedChartName, err := url.QueryUnescape(details.ChartName)
190+
chartURL, err := resolveChartURL(repoURL, details.TarballURL)
192191
if err != nil {
193192
return nil, err
194193
}
195194

196-
ref := path.Join(parsedURL.Host, parsedURL.Path, fmt.Sprintf("%s:%s", unescapedChartName, details.Version))
195+
ref := path.Join(chartURL.Host, chartURL.Path)
197196
chartBuffer, _, err := c.puller.PullOCIChart(ref)
198197
if err != nil {
199198
return nil, err
@@ -215,12 +214,11 @@ type ChartClientFactoryInterface interface {
215214
type ChartClientFactory struct{}
216215

217216
// New for ClientResolver
218-
func (c *ChartClientFactory) New(repoType, userAgent string) ChartClient {
217+
func (c *ChartClientFactory) New(tarballUrl string, userAgent string) ChartClient {
219218
var client ChartClient
220-
switch repoType {
221-
case "oci":
219+
if strings.HasPrefix(tarballUrl, "oci://") {
222220
client = NewOCIClient(userAgent)
223-
default:
221+
} else {
224222
client = NewChartClient(userAgent)
225223
}
226224
return client

‎cmd/kubeapps-apis/plugins/helm/packages/v1alpha1/utils/chart-client_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func Test_resolveChartURL(t *testing.T) {
5757
t.Run(tt.name, func(t *testing.T) {
5858
chartURL, err := resolveChartURL(tt.baseURL, tt.chartURL)
5959
assert.NoError(t, err)
60-
assert.Equal(t, chartURL, tt.wantedURL, "url")
60+
assert.Equal(t, chartURL.String(), tt.wantedURL, "url")
6161
})
6262
}
6363
}
@@ -218,7 +218,7 @@ func TestOCIClient(t *testing.T) {
218218
cli := NewOCIClient("foo")
219219
cli.(*OCIRepoClient).puller = &helmfake.OCIPuller{}
220220
_, err := cli.GetChart(nil, "foo")
221-
if !strings.Contains(err.Error(), "invalid URI for request") {
221+
if !strings.Contains(err.Error(), "missing chart details") {
222222
t.Errorf("Unexpected error %v", err)
223223
}
224224
})
@@ -231,7 +231,7 @@ func TestOCIClient(t *testing.T) {
231231
ExpectedName: "foo/bar/nginx:5.1.1",
232232
Content: map[string]*bytes.Buffer{"5.1.1": bytes.NewBuffer(data)},
233233
}
234-
ch, err := cli.GetChart(&ChartDetails{ChartName: "nginx", Version: "5.1.1"}, "http://foo/bar")
234+
ch, err := cli.GetChart(&ChartDetails{ChartName: "nginx", Version: "5.1.1", TarballURL: "oci://foo/bar/nginx:5.1.1"}, "http://foo/bar")
235235
if ch == nil {
236236
t.Errorf("Unexpected error: %s", err)
237237
}
@@ -248,7 +248,7 @@ func TestOCIClient(t *testing.T) {
248248
ExpectedName: "foo/bar/bar/nginx:5.1.1",
249249
Content: map[string]*bytes.Buffer{"5.1.1": bytes.NewBuffer(data)},
250250
}
251-
ch, err := cli.GetChart(&ChartDetails{ChartName: "nginx", Version: "5.1.1"}, "http://foo/bar%2Fbar")
251+
ch, err := cli.GetChart(&ChartDetails{ChartName: "nginx", Version: "5.1.1", TarballURL: "oci://foo/bar%2Fbar/nginx:5.1.1"}, "http://foo/bar%2Fbar")
252252
if ch == nil {
253253
t.Errorf("Unexpected error: %s", err)
254254
}

‎cmd/kubeapps-apis/plugins/helm/packages/v1alpha1/utils/fake/chart.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ func (f *ChartClient) Init(appRepo *appRepov1.AppRepository, caCertSecret *corev
4747
type ChartClientFactory struct{}
4848

4949
// New returns a fake ChartClient
50-
func (c *ChartClientFactory) New(repoType, userAgent string) utils.ChartClient {
50+
func (c *ChartClientFactory) New(tarballURL string, userAgent string) utils.ChartClient {
5151
return &ChartClient{}
5252
}

0 commit comments

Comments
 (0)
Please sign in to comment.