-
Notifications
You must be signed in to change notification settings - Fork 4
fix(internal/provider): correctly override from extra_env #44
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
Conversation
extraEnv: map[string]string{ | ||
"FOO": testEnvValue, | ||
"ENVBUILDER_GIT_URL": "https://not.the.real.git/url", | ||
"ENVBUILDER_CACHE_REPO": "not-the-real-cache-repo", | ||
"ENVBUILDER_DEVCONTAINER_DIR": "path/to/.devcontainer", | ||
"ENVBUILDER_DEVCONTAINER_JSON_PATH": "path/to/.devcontainer/devcontainer.json", | ||
"ENVBUILDER_DOCKERFILE_PATH": "path/to/.devcontainer/Dockerfile", | ||
}, |
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 test case should assert the correct behaviour of the provider.
if sa, ok := opt.Value.(*serpent.StringArray); ok { | ||
val = strings.Join(sa.GetSlice(), ",") |
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 if this is due to the incorrect String()
method being called or what; when I stepped through in a debugger I could see the method of a serpent.StringArray
being called. Although I do note that the method in question has a value receiver instead of a pointer receiver.
case "", "false", "0": | ||
// Skip zero values. | ||
continue | ||
} |
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 all of our boolean-ish options are false
by default.
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 a bit flimsy IMHO; could we be more explicit about the types we want to skip, or use reflect to determine if a zero value is set?
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.
Reflect would definitely be an option here; I chose to just hard-code these for simplicity.
// XXX: workaround for serpent behaviour where calling Set() on a | ||
// string slice will append instead of replace: set to empty first. | ||
if key == "ENVBUILDER_IGNORE_PATHS" { | ||
_ = optsMap[key].Set("") | ||
} |
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 explicitly choosing to overwrite instead of append here as I believe the latter will be suprising and unexpected.
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 a chance this could get set to empty but then not replaced?
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.
If somehow ENVBUILDER_IGNORE_PATHS
ended up in extraEnv
but not in optsMap
, which would likely result in a nil pointer error on line 457. I could only see that happening if we removed that option.
This whole options situation is horrible and I agree we need a better way to do it. For now, I'm just focusing on getting it working.
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 overall change LGTM. My main concern is keeping different ENV vars hardcoded instead of a dedicated property/schema file. If these are magic strings, maybe we should create a dedicated file to keep track of them?
Nothing blocking. Thanks for working on this!
return diags | ||
} | ||
|
||
func computeEnvFromOptions(opts eboptions.Options, extraEnv map[string]string) map[string]string { |
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: Maybe we should move computeEnvFromOptions
to a helper function or lib as cached_image_resource.go
is getting larger.
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.
Will address in a follow-up.
overrides["ENVBUILDER_BASE_IMAGE_CACHE_DIR"] = struct{}{} | ||
opts.BaseImageCacheDir = data.BaseImageCacheDir.ValueString() |
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 understand there is no simple way to replace this wall of condition-override-value 's with any dynamic logic?
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.
One option I've been considering is using struct tags to match up the fields between these two. A 'dirty hack' could be using json.Marshal
/ json.Unmarshal
to convert between the two. Some of the subtleties of the Terraform data model might be lost though (e.g. "unknown" versus "zero value").
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.
If there is a chance of losing the information, then let's leave it as is, although I admit it is tempting to make it more dynamic.
}, | ||
{ | ||
name: "all options without extra_env", | ||
data: CachedImageResourceModel{ |
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: would it help if we have some YAML files with test input data? I mean, less Go code...
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 like the idea of using x/tools/txtar to store this.
Danny used it for the provisionerd timings tests (example).
switch key { | ||
|
||
// These options may not be overridden. | ||
case "ENVBUILDER_CACHE_REPO", "ENVBUILDER_GIT_URL": |
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: would love to see these keys defined in some schema instead of hardcoded
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.
LGTM
// XXX: workaround for serpent behaviour where calling Set() on a | ||
// string slice will append instead of replace: set to empty first. | ||
if key == "ENVBUILDER_IGNORE_PATHS" { | ||
_ = optsMap[key].Set("") | ||
} |
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 a chance this could get set to empty but then not replaced?
case "", "false", "0": | ||
// Skip zero values. | ||
continue | ||
} |
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 a bit flimsy IMHO; could we be more explicit about the types we want to skip, or use reflect to determine if a zero value is set?
// tfMapToStringMap converts a types.Map to a map[string]string by calling | ||
// tfValueToString on each element. | ||
func tfMapToStringMap(m types.Map) map[string]string { | ||
res := make(map[string]string) |
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: could we preallocate here based on the length of m
?
Addresses some non-blocking comments from #44: - Extracts some of the functions in cached_image_resource.go to separate internal packages tfutil and imgutil. - Some other functions are extracted to helpers.go. - Extracts non-overridable flags to a package-level variable. - Pre-allocates some slices where possible. - Removes some unused code and renames some existing code for readability
Relates to #43
Our previous logic did not pass options from
extra_env
toenvbuilder.RunCacheProbe
.This fixes the logic and adds more comprehensive tests around the overriding.
Before, we were setting environment variables directly from the data source model but not applying them properly to envbuilder.Options
The new overall flow is:
extra_env
is applied to envbuilder options struct