only cache Input "answers", always call Input

While merging the cached Input configs in the correct order prevents
overwriting existing config values, it doesn't prevent an earlier
provider from inserting unwanted values into later provider
configurations.

Diff the key-values returned by Input with the pre-input config, and
store only the "answers" that were added during the Input call.

Always call Input, even if we already have some values, since a
previously cached config may not be complete.
This commit is contained in:
James Bardin 2017-10-16 16:14:55 -04:00
parent d8f4c1f618
commit f08bf76ef2
3 changed files with 32 additions and 35 deletions

View File

@ -1,6 +1,7 @@
package terraform
import (
"errors"
"reflect"
"strings"
"sync"
@ -211,16 +212,25 @@ func TestContext2Input_providerOnce(t *testing.T) {
count := 0
p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) {
count++
return nil, nil
_, set := c.Config["from_input"]
if count == 1 {
if set {
return nil, errors.New("from_input should not be set")
}
c.Config["from_input"] = "x"
}
if count > 1 && !set {
return nil, errors.New("from_input should be set")
}
return c, nil
}
if err := ctx.Input(InputModeStd); err != nil {
t.Fatalf("err: %s", err)
}
if count != 1 {
t.Fatalf("should only be called once: %d", count)
}
}
func TestContext2Input_providerId(t *testing.T) {

View File

@ -99,12 +99,8 @@ type EvalInputProvider struct {
}
func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
// If we already configured this provider, then don't do this again
if v := ctx.ProviderInput(n.Name); v != nil {
return nil, nil
}
rc := *n.Config
orig := rc.DeepCopy()
// Wrap the input into a namespace
input := &PrefixUIInput{
@ -121,27 +117,19 @@ func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
"Error configuring %s: %s", n.Name, err)
}
// Set the input that we received so that child modules don't attempt
// to ask for input again.
// We only store values that have changed through Input.
// The goal is to cache cache input responses, not to provide a complete
// config for other providers.
confMap := make(map[string]interface{})
if config != nil && len(config.Config) > 0 {
// This repository of provider input results on the context doesn't
// retain config.ComputedKeys, so we need to filter those out here
// in order that later users of this data won't try to use the unknown
// value placeholder as if it were a literal value. This map is just
// of known values we've been able to complete so far; dynamic stuff
// will be merged in by EvalBuildProviderConfig on subsequent
// (post-input) walks.
confMap := config.Config
if config.ComputedKeys != nil {
for _, key := range config.ComputedKeys {
delete(confMap, key)
// any values that weren't in the original ResourcConfig will be cached
for k, v := range config.Config {
if _, ok := orig.Config[k]; !ok {
confMap[k] = v
}
}
}
ctx.SetProviderInput(n.Name, confMap)
} else {
ctx.SetProviderInput(n.Name, map[string]interface{}{})
}
return nil, nil
}

View File

@ -137,9 +137,7 @@ func TestEvalInputProvider(t *testing.T) {
}
rawConfig, err := config.NewRawConfig(map[string]interface{}{
"set_in_config": "input",
"set_by_input": "input",
"computed": "fake_computed",
})
if err != nil {
return nil, err
@ -153,6 +151,7 @@ func TestEvalInputProvider(t *testing.T) {
ctx := &MockEvalContext{ProviderProvider: provider}
rawConfig, err := config.NewRawConfig(map[string]interface{}{
"mock_config": "mock",
"set_in_config": "input",
})
if err != nil {
t.Fatalf("NewRawConfig failed: %s", err)
@ -182,12 +181,12 @@ func TestEvalInputProvider(t *testing.T) {
}
inputCfg := ctx.SetProviderInputConfig
// we should only have the value that was set during Input
want := map[string]interface{}{
"set_in_config": "input",
"set_by_input": "input",
// "computed" is omitted because it value isn't known at input time
}
if !reflect.DeepEqual(inputCfg, want) {
t.Errorf("got incorrect input config %#v; want %#v", inputCfg, want)
t.Errorf("got incorrect input config:\n%#v\nwant:\n%#v", inputCfg, want)
}
}