-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Use reflection to overlay profile onto config #872
Use reflection to overlay profile onto config #872
Conversation
APIVersion: config.APIVersion, | ||
Kind: config.Kind, | ||
Build: BuildConfig{ | ||
Artifacts: overlayProfileField(config.Build.Artifacts, profile.Build.Artifacts).([]*Artifact), |
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.
this is interesting - if I understand correctly you are avoiding to have to do a recursion/iteration on the profile structure by manually picking fields, right? The problem with this is that when we change the config structure, we will have to touch this code too because it introduces a dependency on the structure ... if we'd add a new field, e.g. config.Build.Foo, tests wouldn't fail, right, so we have to remember to add it here too?
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.
The issue here is that most of the values we're overlaying here are themselves structs (not primitive values), so we can't know which ones to treat as "values" and which ones to treat as "holders of values" (and recurse over them). This solution implicitly chooses the first non-nil value from the profile and uses it as the new value in the final config.
if fieldValue != nil && !reflect.ValueOf(fieldValue).IsNil() {
// use this value and return
}
For example, the BuildConfig
struct (top level in the SkaffoldConfig
) contains the TagPolicy
struct, which itself is composed of several pointers to structs:
type TagPolicy struct {
GitTagger *GitTagger `yaml:"gitCommit"`
ShaTagger *ShaTagger `yaml:"sha256"`
EnvTemplateTagger *EnvTemplateTagger `yaml:"envTemplate"`
DateTimeTagger *DateTimeTagger `yaml:"dateTime"`
}
GitTagger
is technically of type struct
, but we treat it as an actual value. Since we say it's invalid to have multiple values set here, this code just picks the first non-nil one and uses it. This was handled by yaml marshalling before because any value that wasn't set would be set to nil
by marshalling the yaml, but if we add yaml:omitempty
tags to the config this will break.
If we add something to the top level fields in the SkaffoldConfig
, then it's true that tests could still pass and we could potentially miss a silent failure. I'm not sure of a good way to address this apart from either 1) reworking the implementation of the config to not use pointers as a way to implement "one-of" values (see #388), or 2) being more rigorous with our testing when we make changes to the config. WDYT?
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 detailed explanation!
- Can we somehow mark these top level elements as "one-of" and recognize that in the
switch v.Kind()
? it would be cool :) (Add yamltags #388 does it on the "option" level not on the "struct" level, maybe we could sync on this @dlorenc ? ) - I would split the
overlayProfileField
tooverlayOneOfField
andoverlaySpliceField
methods (and maybe later do anoverlayStructField
if necessary) because the fact that the struct fields currently in the profile are "one-of" types has nothing to do inherently with the profile, it is confusing.
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.
Can we somehow mark these top level elements as "one-of" and recognize that in the switch v.Kind()? it would be cool :) (#388 does it on the "option" level not on the "struct" level, maybe we could sync on this @dlorenc ? )
Marking them is doable with YAML tags, it should be relatively easy to add in here. I'll chat with Nick.
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'm not sure we actually want 2) as a feature. I think its better left as a task for kustomize or other tools.
@r2d4 sure, that's just a side effect of this change. We don't have to change anything here if we don't want to |
4330ce9
to
2387af4
Compare
Codecov Report
@@ Coverage Diff @@
## master #872 +/- ##
=======================================
Coverage 38.27% 38.27%
=======================================
Files 56 56
Lines 2576 2576
=======================================
Hits 986 986
Misses 1476 1476
Partials 114 114
Continue to review full report at Codecov.
|
…cursively overlay profiles
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.
only one nit otherwise lgtm
@@ -68,3 +63,71 @@ func profilesByName(profiles []Profile) map[string]Profile { | |||
} | |||
return byName | |||
} | |||
|
|||
func isOneOf(field reflect.StructField) bool { |
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.
nit: I would pull this out into the config package to encourage reuse of the "oneOf" logic instead of having it in v1alpha2
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.
done
This changes the profile code to overlay the profile onto the original config using reflection, rather than relying on yaml unmarshalling. Two reasons why we should do this: