-
Notifications
You must be signed in to change notification settings - Fork 705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Load chart and provide access in both v2 and v3 structs. #1362
Changes from 1 commit
755257d
fc975bb
62746f9
5dd2caa
287d876
22164c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,9 +35,12 @@ import ( | |
"github.com/ghodss/yaml" | ||
appRepov1 "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/apis/apprepository/v1alpha1" | ||
appRepoClientSet "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/client/clientset/versioned" | ||
chartv3 "helm.sh/helm/v3/pkg/chart" | ||
helm3loader "helm.sh/helm/v3/pkg/chart/loader" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/helm/pkg/proto/hapi/chart" | ||
helm2loader "k8s.io/helm/pkg/chartutil" | ||
chartv2 "k8s.io/helm/pkg/proto/hapi/chart" | ||
"k8s.io/helm/pkg/repo" | ||
) | ||
|
||
|
@@ -78,31 +81,37 @@ type HTTPClient interface { | |
Do(req *http.Request) (*http.Response, error) | ||
} | ||
|
||
// LoadChart should return a Chart struct from an IOReader | ||
type LoadChart func(in io.Reader) (*chart.Chart, error) | ||
type ChartMultiVersion struct { | ||
V2 *chartv2.Chart | ||
V3 *chartv3.Chart | ||
} | ||
|
||
// LoadChartV2 should return a V2 Chart struct from an IOReader | ||
type LoadChartV2 func(in io.Reader) (*chartv2.Chart, error) | ||
|
||
// LoadChartV3 returns a V3 Chart struct from an IOReader | ||
type LoadChartV3 func(in io.Reader) (*chartv3.Chart, error) | ||
|
||
// Resolver for exposed funcs | ||
type Resolver interface { | ||
ParseDetails(data []byte) (*Details, error) | ||
GetChart(details *Details, netClient HTTPClient) (*chart.Chart, error) | ||
GetChart(details *Details, netClient HTTPClient) (*ChartMultiVersion, error) | ||
InitNetClient(details *Details) (HTTPClient, error) | ||
} | ||
|
||
// Chart struct contains the clients required to retrieve charts info | ||
type Chart struct { | ||
// ChartClient struct contains the clients required to retrieve charts info | ||
type ChartClient struct { | ||
kubeClient kubernetes.Interface | ||
appRepoClient appRepoClientSet.Interface | ||
load LoadChart | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT, the I initially packaged the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it was only for testing purposes. What I don't like about using real data is that you cannot modify the input (so you are only testing the happy path). Since here we are only testing the happy path anyway I am okay with the change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is possible to both use real data and modify the input - I've just not done so here because it wasn't necessary for my simple test. But if we want to in the future, we can extend this with something like the helm tests for the archive loader, in https://github.com/helm/helm/blob/master/pkg/chart/loader/archive_test.go where you generate the archive with the setup for your test. |
||
userAgent string | ||
appRepo *appRepov1.AppRepository | ||
} | ||
|
||
// NewChart returns a new Chart | ||
func NewChart(kubeClient kubernetes.Interface, appRepoClient appRepoClientSet.Interface, load LoadChart, userAgent string) *Chart { | ||
return &Chart{ | ||
// NewChartClient returns a new ChartClient | ||
func NewChartClient(kubeClient kubernetes.Interface, appRepoClient appRepoClientSet.Interface, userAgent string) *ChartClient { | ||
return &ChartClient{ | ||
kubeClient: kubeClient, | ||
appRepoClient: appRepoClient, | ||
load: load, | ||
userAgent: userAgent, | ||
} | ||
} | ||
|
@@ -225,7 +234,7 @@ func findChartInRepoIndex(repoIndex *repo.IndexFile, repoURL, chartName, chartVe | |
} | ||
|
||
// fetchChart returns the Chart content given an URL | ||
func fetchChart(netClient *HTTPClient, chartURL string, load LoadChart) (*chart.Chart, error) { | ||
func fetchChart(netClient *HTTPClient, chartURL string) (*ChartMultiVersion, error) { | ||
req, err := getReq(chartURL) | ||
if err != nil { | ||
return nil, err | ||
|
@@ -239,11 +248,19 @@ func fetchChart(netClient *HTTPClient, chartURL string, load LoadChart) (*chart. | |
if err != nil { | ||
return nil, err | ||
} | ||
return load(bytes.NewReader(data)) | ||
chartV2, err := helm2loader.LoadArchive(bytes.NewReader(data)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
chartV3, err := helm3loader.LoadArchive(bytes.NewReader(data)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd probably do something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does look like it should be DRYer, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hehe, I suspected there could be some black magic like that going on. I'm absolutely fine with the current implementation. 😄 |
||
if err != nil { | ||
return nil, err | ||
} | ||
return &ChartMultiVersion{V2: chartV2, V3: chartV3}, nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would assume this will bite us in the future. There may be cases in the future that a chart is only compatible with the v3 format. Trying to load as a v2 will likely fail. To avoid returning at that point, we can store the errors in the Also, another thing is that the naming can be a bit confusing (even though I think there is not much we can do). The chart APIVersion used in Helm v2 is v1 while the new version introduced in Helm 3 is v2. Because of that, a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we'll definitely need to be able to handle Helm 3-only charts. Would it suffice to have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding the naming: Yes, that is a problem. One solution could be to use names like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm assuming this is transition-only code. Right now they load for both. Later, if/when backwards incompatible changes are added to the chart format, then helm will be at a point where it only supports the new version, so we can update to do the same - I assume?
Ah - I assumed in this test in helm https://github.com/helm/helm/blob/master/pkg/chart/loader/load_test.go#L101 that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep, but that's in the future, right? (I know there are currently different hooks supported, but I didn't think there was a different format right now, as in a chart which wouldn't load in helm2 or the other way around).
Yes, in the future, it'll be no problem for
Yep, so what we're referring to is the struct for a chart in helm2 or the struct for a chart in helm3, so I've updated to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Shouldn't we support v2 of the chart API (i.e. Helm 3) as soon as we claim to have "Helm 3 support"? Such charts are not supported by Helm 2 AFAIK. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would still do that now. Mostly because this will hit us for sure (and better for us to fix it in advance) and also because we may not notice and users could find it instead. It's also an easy fix. Regarding the naming, it's less important. To be explicit we can say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, we definitely need to support the But I don't think we can support
I've updated to handle the case (see the extra tests). Rather than storing the error, I've added a flag This should unblock the helm3 work for now where v1 support is not required (though can still be used so that existing versions of
Yep, I'd already updated to Thanks! |
||
} | ||
|
||
// ParseDetails return Chart details | ||
func (c *Chart) ParseDetails(data []byte) (*Details, error) { | ||
func (c *ChartClient) ParseDetails(data []byte) (*Details, error) { | ||
details := &Details{} | ||
err := json.Unmarshal(data, details) | ||
if err != nil { | ||
|
@@ -278,7 +295,7 @@ func (c *clientWithDefaultHeaders) Do(req *http.Request) (*http.Response, error) | |
|
||
// InitNetClient returns an HTTP client based on the chart details loading a | ||
// custom CA if provided (as a secret) | ||
func (c *Chart) InitNetClient(details *Details) (HTTPClient, error) { | ||
func (c *ChartClient) InitNetClient(details *Details) (HTTPClient, error) { | ||
// Require the SystemCertPool unless the env var is explicitly set. | ||
caCertPool, err := x509.SystemCertPool() | ||
if err != nil { | ||
|
@@ -342,8 +359,9 @@ func (c *Chart) InitNetClient(details *Details) (HTTPClient, error) { | |
}, nil | ||
} | ||
|
||
// GetChart retrieves and loads a Chart from a registry | ||
func (c *Chart) GetChart(details *Details, netClient HTTPClient) (*chart.Chart, error) { | ||
// GetChart retrieves and loads a Chart from a registry in both | ||
// v2 and v3 formats. | ||
func (c *ChartClient) GetChart(details *Details, netClient HTTPClient) (*ChartMultiVersion, error) { | ||
repoURL := c.appRepo.Spec.URL | ||
if repoURL == "" { | ||
// FIXME: Make configurable | ||
|
@@ -363,9 +381,10 @@ func (c *Chart) GetChart(details *Details, netClient HTTPClient) (*chart.Chart, | |
} | ||
|
||
log.Printf("Downloading %s ...", chartURL) | ||
chartRequested, err := fetchChart(&netClient, chartURL, c.load) | ||
chart, err := fetchChart(&netClient, chartURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return chartRequested, nil | ||
|
||
return chart, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,13 +32,15 @@ func (f *FakeChart) ParseDetails(data []byte) (*chartUtils.Details, error) { | |
return details, err | ||
} | ||
|
||
func (f *FakeChart) GetChart(details *chartUtils.Details, netClient chartUtils.HTTPClient) (*chart.Chart, error) { | ||
return &chart.Chart{ | ||
Metadata: &chart.Metadata{ | ||
Name: details.ChartName, | ||
}, | ||
Values: &chart.Config{ | ||
Raw: details.Values, | ||
func (f *FakeChart) GetChart(details *chartUtils.Details, netClient chartUtils.HTTPClient) (*chartUtils.ChartMultiVersion, error) { | ||
return &chartUtils.ChartMultiVersion{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently the |
||
V2: &chart.Chart{ | ||
Metadata: &chart.Metadata{ | ||
Name: details.ChartName, | ||
}, | ||
Values: &chart.Config{ | ||
Raw: details.Values, | ||
}, | ||
}, | ||
}, nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I've mentioned this before, but I found it confusing that we had a struct called
Chart
which wasn't a chart, but a struct containing clients for getting chart details. Hence the rename.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's way less ambiguous