Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Allow dynamic configuration as a default for input variables #2889

Merged
merged 17 commits into from
Jan 10, 2022

Conversation

mitchellh
Copy link
Contributor

@mitchellh mitchellh commented Jan 7, 2022

This allows the default value for a variable stanza to use the configdynamic() function to source default values from any supported config source. You can now, for example, source data from Vault to use within your waypoint.hcl file. Previously, configdynamic could only be used to set application configuration (env vars and files that appear only to your app).

One limitation (by design): This only works with remote runners. This is the only way we can guarantee consistency in plugin versions and protects against some secrets spilling onto local machines. However, you may use a local runner if you manually override the default value with something like -var or a wpvars file. You only cannot use local mode if the dynamic value must be fetched.

null Config Source

This PR also introduces a new plugin null with a single component for now: a config sourcer. This is used for testing but will be generally available for people to use. I think this is useful for experimentation and learning how config sourcing works without having to deal with the complexities of a remote system.

I mixed it with this PR because I used this to manually test (in addition to the unit tests) that this works.

Known Issues or Improvements

It's becoming harder and harder to know what variable values are used for a job. This exacerbates that but doesn't introduce this problem. In the future, we want to send back information about where variable values are coming from (and perhaps what they are).

Copy link
Contributor

@izaaklauer izaaklauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! The null config sourcer makes testing this way easier. Looking forward to running a test from the stories pulling an arn in from TFC too.

// For dynamic types we don't do conversion because we don't yet
// have the value. For now we require v.Type to be string so that
// the user isn't surprised by anything. Users can use explicit
// type conversion such as `tonumber`.
Copy link
Contributor

@izaaklauer izaaklauer Jan 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering how you'd handle types - requiring an hcl conversion is a good idea for now I think, and we can do fancier typing in the future if we want to. I was kind of dreading handling the object type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think strings are the way to go.

For numerics, HCL is arbitrary precision numbers too so any floats will work fine and easy to convert.
For boolean, easy to use tobool (uses strconv.parseBool)
For objects, easy to use yamldecode jsondecode jsonnetdecode etc. depending on format, and easy to add new format support if they have it.

@@ -18,6 +18,7 @@ import (
"github.com/hashicorp/waypoint-plugin-sdk/component"
"github.com/hashicorp/waypoint-plugin-sdk/datadir"
"github.com/hashicorp/waypoint-plugin-sdk/terminal"
"github.com/hashicorp/waypoint/internal/appconfig"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puritanical point: Now that the configsourcers are called in the variable system, it feels a little weird for their core logic to still live inside the appconfig package. It feels better for me for that logic to live somewhere else, and be invoked both inside variables and appconfig. Not a big deal though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just a package naming thing? I think the role of appconfig package is still totally right, we can maybe just rename the package to something like externalconfig or something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better example of the weirdness is that the variable system has to pretend that it's receiving config files (here and here), when really it's just getting string values back from the configsourcer plugins.

I think we might eventually want a new externalconfig or dynamicvalues package that invokes the plugins, and both the variables and appconfig could use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the files thing is weird, @evanphx noted that too. I think if we just change it to have a way to accept strings directly I still think this package is the right structure. It is requesting a "config var" in my mind.


// Build our watcher
ch := make(chan *appconfig.UpdatedConfig)
w, err := appconfig.NewWatcher(append(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity: If we wind up having to do this again down the road, it might make sense to make a "one-shot" configsourcer invoke mode, so you don't need to spool up a whole watcher and associated channels to get one value from the plugins. The watcher might even be able to call that one-shot logic internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, on my mind too.

Comment on lines +196 to +199
// If we're a local runner, we can't support this. We don't
// support dynamic config sourcing on local runners since it
// requires config sourcing plugins, auth, etc.
if r.local {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this ok for now. I'd be surprised if there are many users sophisticated enough to be using these dynamic variables, but haven't set up the remote server/runner infra yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, so I think this is good forever. This just gives people a nice error message.

//
// We also do this if the runner is NOT local, in case it is processing
// jobs that are using config variables with dynamic defaults.
_, isLocal := record.Kind.(*pb.Runner_Local_)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little spooky that this security check depends on the runner not lying to us about what type it is. I think we'll address this in the future with a more formal runner registration/approval process though?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep!

Comment on lines +526 to +532
// Do not allow dynamic values as default values since they aren't valid.
// Dynamic values should be evaluated and overridden by LoadDynamicDefaults
// and provided via pbvars. If not, then an unset error will be created.
if def.Default != nil && def.Default.Value.Type() == dynamic.Type {
continue
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah not a big deal, just makes UX nicer. Since the dynamic type is an encapsulated value it can't be used for anything so its basically a poison pill. If anyone tried to use it for any values it'd just give an obscure HCL type error message. This just helps prevent that.

@denysvitali
Copy link
Contributor

I seem to understand that this is a fix for #1553, is that correct?

Copy link
Contributor

@evanphx evanphx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice and tidy! One request to inform the user if the appconfig watcher takes more than a second or two when trying to resolve the dynamic values.

Comment on lines +467 to +470
// This is set to true on purpose because it forces the appconfig
// watcher to give us an easier to consume format (struct vs
// array of key=value strings for env vars).
NameIsPath: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point, we should make that flatten logic that NameIsPath implies selectable more obviously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agreed, a bit of a hack.

internal/config/variables/variables.go Outdated Show resolved Hide resolved
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants