From 6bc36d33218eaf1767eb1aeffb6fad47d12c35ff Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 10 May 2019 19:05:00 -0400 Subject: [PATCH] validate integers when using protoV5 The new type system only has a Number type, but helper schema differentiates between Int and Float values. Verify that a new config value is an integer during Validate, because the existing WeakDecode validation will decode a float value into an integer while the config FieldReader will attempt to parse the float exactly. Since we're limiting this to protoV5, we can be certain that any valid config value will be converted to an `int` type by the shims. The only case where an integral float value will appear is if the integer is out of range for the systems `int` type, but we also need to prevent that anyway since it would fail to read in the same manner. --- builtin/providers/test/resource.go | 4 ++++ builtin/providers/test/resource_test.go | 21 +++++++++++++++++++++ helper/schema/schema.go | 23 ++++++++++++++++++----- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/builtin/providers/test/resource.go b/builtin/providers/test/resource.go index 0d6b3590e..09732d421 100644 --- a/builtin/providers/test/resource.go +++ b/builtin/providers/test/resource.go @@ -153,6 +153,10 @@ func testResource() *schema.Resource { Optional: true, Description: "do not set in config", }, + "int": { + Type: schema.TypeInt, + Optional: true, + }, }, } } diff --git a/builtin/providers/test/resource_test.go b/builtin/providers/test/resource_test.go index 725ee8e33..8b78bb415 100644 --- a/builtin/providers/test/resource_test.go +++ b/builtin/providers/test/resource_test.go @@ -1029,3 +1029,24 @@ resource "test_resource" "foo" { }, }) } + +func TestResource_floatInIntAttr(t *testing.T) { + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource" "foo" { + required = "yep" + required_map = { + key = "value" + } + int = 40.2 +} + `), + ExpectError: regexp.MustCompile(`must be a whole number, got 40.2`), + }, + }, + }) +} diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 8cb9bc9f9..140cdff22 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -1731,12 +1731,25 @@ func (m schemaMap) validatePrimitive( } decoded = n case TypeInt: - // Verify that we can parse this as an int - var n int - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} + switch { + case isProto5(): + // We need to verify the type precisely, because WeakDecode will + // decode a float as an integer. + + // the config shims only use int for integral number values + if v, ok := raw.(int); ok { + decoded = v + } else { + return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)} + } + default: + // Verify that we can parse this as an int + var n int + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s: %s", k, err)} + } + decoded = n } - decoded = n case TypeFloat: // Verify that we can parse this as an int var n float64