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.
This commit is contained in:
James Bardin 2019-05-10 19:05:00 -04:00
parent 28b2383eac
commit 6bc36d3321
3 changed files with 43 additions and 5 deletions

View File

@ -153,6 +153,10 @@ func testResource() *schema.Resource {
Optional: true, Optional: true,
Description: "do not set in config", Description: "do not set in config",
}, },
"int": {
Type: schema.TypeInt,
Optional: true,
},
}, },
} }
} }

View File

@ -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`),
},
},
})
}

View File

@ -1731,12 +1731,25 @@ func (m schemaMap) validatePrimitive(
} }
decoded = n decoded = n
case TypeInt: case TypeInt:
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 // Verify that we can parse this as an int
var n int var n int
if err := mapstructure.WeakDecode(raw, &n); err != nil { if err := mapstructure.WeakDecode(raw, &n); err != nil {
return nil, []error{fmt.Errorf("%s: %s", k, err)} return nil, []error{fmt.Errorf("%s: %s", k, err)}
} }
decoded = n decoded = n
}
case TypeFloat: case TypeFloat:
// Verify that we can parse this as an int // Verify that we can parse this as an int
var n float64 var n float64