From 7834cf7190f0569a34cfcd036e23684ac834703e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 2 Nov 2016 13:25:23 -0700 Subject: [PATCH] helper/schema: computed bool fields should not crash Fixes #7715 If a bool field was computed and the raw value was not convertable to a boolean, helper/schema would crash. The correct behavior is to try not to read the raw value when the value is computed and to simply mark that it is computed. This does that (and matches the behavior of the other primitives). --- helper/schema/field_reader.go | 3 ++ helper/schema/field_reader_config_test.go | 65 +++++++++++++++++++++++ helper/schema/schema_test.go | 36 +++++++++++++ 3 files changed, 104 insertions(+) diff --git a/helper/schema/field_reader.go b/helper/schema/field_reader.go index c3a6c76fa0..263308813e 100644 --- a/helper/schema/field_reader.go +++ b/helper/schema/field_reader.go @@ -223,6 +223,9 @@ func stringToPrimitive( returnVal = false break } + if computed { + break + } v, err := strconv.ParseBool(value) if err != nil { diff --git a/helper/schema/field_reader_config_test.go b/helper/schema/field_reader_config_test.go index 9daeab3a01..10037d89e4 100644 --- a/helper/schema/field_reader_config_test.go +++ b/helper/schema/field_reader_config_test.go @@ -49,6 +49,71 @@ func TestConfigFieldReader(t *testing.T) { }) } +// This contains custom table tests for our ConfigFieldReader +func TestConfigFieldReader_custom(t *testing.T) { + schema := map[string]*Schema{ + "bool": &Schema{ + Type: TypeBool, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "basic": { + []string{"bool"}, + FieldReadResult{ + Value: true, + Exists: true, + }, + testConfig(t, map[string]interface{}{ + "bool": true, + }), + false, + }, + + "computed": { + []string{"bool"}, + FieldReadResult{ + Exists: true, + Computed: true, + }, + testConfigInterpolate(t, map[string]interface{}{ + "bool": "${var.foo}", + }, map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeString, + }, + }), + false, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + }) + } +} + func TestConfigFieldReader_DefaultHandling(t *testing.T) { schema := map[string]*Schema{ "strWithDefault": &Schema{ diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 1e2451a694..20720f4fbc 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -2455,6 +2455,42 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, + + // GH-7715 + "computed value for boolean field": { + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeBool, + ForceNew: true, + Computed: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{ + "foo": "${var.foo}", + }, + + ConfigVariables: map[string]ast.Variable{ + "var.foo": interfaceToVariableSwallowError( + config.UnknownVariableValue), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "false", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, } for tn, tc := range cases {