-
Notifications
You must be signed in to change notification settings - Fork 706
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
Add Helm 3 GetRelease #1406
Add Helm 3 GetRelease #1406
Conversation
As of this commit, the real values.yaml is NOT included in the response to the Dashboard, just a placeholder one.
Files: compatibleFiles(h3c.Files), | ||
Metadata: *h3c.Metadata, | ||
Templates: compatibleTemplates(h3c.Templates), | ||
Values: compatibleConfig(h3c), |
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.
According to comments in the Helm code, "Config is the set of extra Values added to the chart. These values override the default values inside of the chart.".
I guess that means that we should not use compatibleConfig
to set Values
. So Config
should be the subset of Values that we saw somewhere (you know that yaml that was stripped of comments...), but we should also have a compatibleValues()
that returns the actual full Values contents (wherever that is to be found).
https://github.com/helm/helm/blob/master/pkg/release/release.go#L29-L31
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.
You're probably right.
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.
It seems like config
is the final, modified values
. I deployed bitnami/apache
with the replicaCount
and metric.resources.limits
properties manually modified in the GUI, and then compared values.raw
with config.raw
(both from the JSON response):
$ diff json-values.yaml json-config.yaml
27d26
<
51d49
<
55d52
<
58c55
< replicaCount: 1
---
> replicaCount: 5
86d82
<
90d85
<
94d88
<
98d91
<
164d156
<
170d161
<
179c170
< secrets:
---
> secrets:
225,227c216,218
< limits: {}
< # cpu: 100m
< # memory: 128Mi
---
> limits:
> cpu: 100m
> memory: 128Mi
Note that both values.raw
and config.raw
preserved all YAML comments.
Could this be the function to use for getting the Values from a Release? https://github.com/helm/helm/blob/master/pkg/chartutil/values.go#L137 |
They are not in their original raw YAML form, but instead generated from the internal map[string]interface{} representation.
We've now implemented |
I've noticed that, when installing a chart, its |
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.
Thanks Simon. I think the approach here is correct: dashboard expects a json-serialized helm2 release, so you're creating one manually. But it seems this could be simplified by just using the helm2 structs (rather than creating new ones that are compatible) and using the helm2 code (ie. for the yaml serialization for the Raw
field).
See below for more details and let me know if I missed something?
// generatedYamlHeader is prepended to YAML generated from the internal map[string]interface{} representation. | ||
const generatedYamlHeader = "# Not original YAML! Generated from parsed representation." | ||
|
||
type dashboardCompatibleRelease struct { |
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.
Is there an issue using the h2.Release
instead of creating a new struct here? It appears to have the same fields (extra ones serialize with omitempty
):
https://github.com/kubeapps/kubeapps/blob/master/pkg/agent/agent_test.go#L185
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.
We originally wanted to write a function from h3.Release
to h2.Release
, but arrived at the conclusion that it's impossible in general. Can't remember exactly why, but I think there were several reasons. One might be that the respective Metadata
types are not isomorphic. The Values
property in the respective Chart
types also seems problematic.
I've, however, made a best-effort attempt at using Helm 2 types instead of custom ones. It seems to work; see d67e136.
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.
Right, here we're just looking at getting data back to the dashboard for presentation, rather than interacting with helm, so I don't think isomorphism is essential as it would be if we were generating this for use with helm itself. Thanks for the update!
Namespace string `json:"namespace,omitempty"` | ||
} | ||
|
||
type dashboardCompatibleChart struct { |
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.
Same here with https://github.com/helm/helm/blob/release-2.16/pkg/proto/hapi/chart/chart.pb.go#L24 (although I see you're using h3chart.Metadata
below - are they the same?)
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.
No, h2chart.Metadata
and h3chart.Metadata
are different (and not isomorphic).
Values dashboardCompatibleValues `json:"values,omitempty"` | ||
} | ||
|
||
type dashboardCompatibleValues struct { |
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.
...and here https://github.com/helm/helm/blob/release-2.16/pkg/proto/hapi/chart/config.pb.go#L22
(in the helm2 code, the Values
and Config
appear to be the same thing, which looks reflected in you having the same type).
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.
AFAIK, Values
and Config
are not the same thing: Values
is the original values.yaml
for the chart, whereas Config
is the effective set of values used for the release – usually Values
overridden with any custom values set by the user at install time.
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.
Sorry - yes, I meant that they appear to use the same type in helm2 rather than requiring two different but identical types - so Chart.Values
(at https://github.com/helm/helm/blob/release-2.16/pkg/proto/hapi/chart/chart.pb.go#L32) is actually of type *Config
(whereas here we had two identical types with dashboardCompatibleValues
and dashboardCompatibleConfig
I think?)
// valuesToYaml serializes to YAML and prepends an informative header. | ||
// It assumes that the serialization succeeds. | ||
func valuesToYaml(values map[string]interface{}) string { | ||
marshaled, _ := yaml.Marshal(values) |
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.
Why do you want to ignore the error rather than handling it here? This could result in hard-to-discover bugs later (ie. getting an empty values without any known reason why, I think?)
Actually, this looks like another place where we could just use the helm2 pkg? See
where Raw
is set using https://github.com/helm/helm/blob/release-2.16/pkg/chartutil/values.go#L45
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 thought there would never occur any error there, and abandoning that assumption would contaminate the entire file with error
and nil
everywhere without adding any real value (or so I thought).
There should exist a total function from h3.Release
to some Dashboard-compatible type, shouldn't there? (newDashboardCompatibleRelease
is my attempt at constructing such a function.) Maybe @andresmgot's suggestion of storing the YAML separately is the best solution; then we wouldn't need to marshal anything at all.
Finally, I don't see how YAML
could be used here. It works only on members of Values
from Helm 2, but we have a map[string]interface{}
.
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.
In the unlikely case that the map[string]interface{}
couldn't be marshaled, returning an error here would be helpful to identify the issue. I would either log the error (if you want this function to succeed in any case, even if the values cannot be marshalled) or return it and handle it in an upper level.
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.
There should exist a total function from
h3.Release
to some Dashboard-compatible type, shouldn't there? (newDashboardCompatibleRelease
is my attempt at constructing such a function.) Maybe @andresmgot's suggestion of storing the YAML separately is the best solution; then we wouldn't need to marshal anything at all.
Yes, I agree - since the raw config is not stored in the helm3 model at all, yet is an integral part of the Kubeapps UI (which uses the values.yaml as the default config which users update), Kubeapps needs some way to access the raw user config (comments and all) as it was able to with helm2. I'm just not yet sure what the sanest way to do so is. I'm +1 to land this (once conflicts are resolved) and perhaps Andres or I can work on this aspect separately so as to not slow you down?
Finally, I don't see how
YAML
could be used here. It works only on members ofValues
from Helm 2, but we have amap[string]interface{}
.
Values
in the chartutil pkg is exactly that - map[string]interface{}
. Two lines above the definition of func (v Values) YAML()
you'll see:
type Values map[string]interface{}
It's a bit confusing because it's a different type to that used in the struct field Chart.Values
. But as above, this is moot since we really need the raw data with comments.
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'd suggest one of these options for now, to keep the code simple:
- Panic if serialization failed.
- Ignore any serialization error (current solution).
The rationale would be the same no matter which one we choose: This won't hit production anyway (because we need to figure out how to store/access values.yaml
), and it's good enough™ for us to be able to keep working on Helm 3 support.
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.
why don't you log the error instead? A panic
can be disruptive in the whole process and you don't need to return the error and obscure the code if you just log the error.
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.
Sounds reasonable. I'll log it instead.
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.
Thanks for this PR! Apart from the comments of @absoludity I see an important issue: We have no way of restoring the original values used (only the map representation). This means that comments are lost so when comparing these values when upgrading (a feature added in the latest version) the user will see all the comments being deleted.
It seems that information is not being stored by helm so we may want to add that information when creating a release (either as another field of the Secret/ConfigMap) or in a different entity so here we can return that instead of the plain map.
In any case that should not block this PR so lets continue.
// generatedYamlHeader is prepended to YAML generated from the internal map[string]interface{} representation. | ||
const generatedYamlHeader = "# Not original YAML! Generated from parsed representation." |
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 would skip this header, then in the dashboard we can try to recover the comments
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 the header should be there for clarity until we can do something better, such as storing the YAML separately, as you suggested.
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.
Yes, the problem is that it's giving to the user internal information about a problem they may not be aware of. It seems like an error. It's easier to understand that the parsed YAML is what it's stored in the cluster (with no comments).
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 see!
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.
At the end we have a translator package but we need to either do this translation here or in the dashboard so my +1 (with just one comment about the error). I would add a TODO
to remove this package once we drop support for Helm v2.
Thanks! I will let @absoludity do a final review here as well.
// valuesToYaml serializes to YAML and prepends an informative header. | ||
// It assumes that the serialization succeeds. | ||
func valuesToYaml(values map[string]interface{}) string { | ||
marshaled, _ := yaml.Marshal(values) |
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.
In the unlikely case that the map[string]interface{}
couldn't be marshaled, returning an error here would be helpful to identify the issue. I would either log the error (if you want this function to succeed in any case, even if the values cannot be marshalled) or return it and handle it in an upper level.
// valuesToYaml serializes to YAML and prepends an informative header. | ||
// It assumes that the serialization succeeds. | ||
func valuesToYaml(values map[string]interface{}) string { | ||
marshaled, _ := yaml.Marshal(values) |
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.
There should exist a total function from
h3.Release
to some Dashboard-compatible type, shouldn't there? (newDashboardCompatibleRelease
is my attempt at constructing such a function.) Maybe @andresmgot's suggestion of storing the YAML separately is the best solution; then we wouldn't need to marshal anything at all.
Yes, I agree - since the raw config is not stored in the helm3 model at all, yet is an integral part of the Kubeapps UI (which uses the values.yaml as the default config which users update), Kubeapps needs some way to access the raw user config (comments and all) as it was able to with helm2. I'm just not yet sure what the sanest way to do so is. I'm +1 to land this (once conflicts are resolved) and perhaps Andres or I can work on this aspect separately so as to not slow you down?
Finally, I don't see how
YAML
could be used here. It works only on members ofValues
from Helm 2, but we have amap[string]interface{}
.
Values
in the chartutil pkg is exactly that - map[string]interface{}
. Two lines above the definition of func (v Values) YAML()
you'll see:
type Values map[string]interface{}
It's a bit confusing because it's a different type to that used in the struct field Chart.Values
. But as above, this is moot since we really need the raw data with comments.
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.
Thanks for the changes!
Description of the change
This PR implements
GetRelease
using the Helm 3 API.The Dashboard doesn't understand the release format used by Helm 3, so we implemented a compatibility layer that translates Helm 3 releases to a Helm 2-similar format suitable for the Dashboard in its current form.
Benefits
Possible drawbacks
values.yaml
is not included in the response to the Dashboard, just a generated one without comments and formatting. The same goes for the actual config (values.yaml
with release-specific modifications). We didn't know where to find the original files.Applicable issues
Additional information
Don't forget to set
useHelm3
totrue
invalues.yaml
to see the changes.Created in collaboration with @lindhe and @latiif.