Treat config values that start with '0' as strings (#4393)

When setting structured config values using `--path`, we automatically
treat values that can be converted into an integer via `strconv.Atoi` as
an integer, rather than as a string.

However, this ends up converting values like "0123456" into the integer
123456, stripping the leading 0, which isn't desirable for values like
commit SHAs, etc., where you want to keep the 0 (and keep it a string).

This change makes it so that values starting with 0 are not implicitly
converted to an integer; instead such values will remain a string.
This commit is contained in:
Justin Van Patten 2020-04-14 12:40:22 -07:00 committed by GitHub
parent 9077292d14
commit 33119659e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 204 additions and 0 deletions

View file

@ -28,6 +28,9 @@ CHANGELOG
- Fix Go codegen to emit config packages
[#4388](https://github.com/pulumi/pulumi/pull/4388)
- Treat config values set with `--path` that start with '0' as strings rather than numbers.
[#4393](https://github.com/pulumi/pulumi/pull/4393)
## 1.14.1 (2020-04-13)
- Propagate `additionalSecretOutputs` opt to Read in NodeJS.
[#4307](https://github.com/pulumi/pulumi/pull/4307)

View file

@ -511,6 +511,13 @@ func adjustObjectValue(v Value, path bool) interface{} {
return true
}
// If the value has more than one character and starts with "0", return the value as-is
// so values like "0123456" are saved as a string (without stripping any leading zeros)
// rather than as the integer 123456.
if len(v.value) > 1 && v.value[0] == '0' {
return v.value
}
// If it's convertible to an int, return the int.
i, err := strconv.Atoi(v.value)
if err == nil {

View file

@ -741,6 +741,70 @@ func TestSetSuccess(t *testing.T) {
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":"value"}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("true"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":true}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("false"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":false}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("10"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":10}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("0"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":0}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("-1"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":-1}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("00"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":"00"}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("01"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":"01"}`),
},
},
{
Key: `my:outer.inner`,
Path: true,
Value: NewValue("0123456"),
Expected: Map{
MustMakeKey("my", "outer"): NewObjectValue(`{"inner":"0123456"}`),
},
},
{
Key: `my:array[0]`,
Path: true,
@ -965,6 +1029,14 @@ func TestSetSuccess(t *testing.T) {
MustMakeKey("my", "testKey"): NewObjectValue(`[10]`),
},
},
{
Key: `my:testKey[0]`,
Path: true,
Value: NewValue("0"),
Expected: Map{
MustMakeKey("my", "testKey"): NewObjectValue(`[0]`),
},
},
{
Key: `my:testKey[0]`,
Path: true,
@ -973,6 +1045,30 @@ func TestSetSuccess(t *testing.T) {
MustMakeKey("my", "testKey"): NewObjectValue(`[-1]`),
},
},
{
Key: `my:testKey[0]`,
Path: true,
Value: NewValue("00"),
Expected: Map{
MustMakeKey("my", "testKey"): NewObjectValue(`["00"]`),
},
},
{
Key: `my:testKey[0]`,
Path: true,
Value: NewValue("01"),
Expected: Map{
MustMakeKey("my", "testKey"): NewObjectValue(`["01"]`),
},
},
{
Key: `my:testKey[0]`,
Path: true,
Value: NewValue("0123456"),
Expected: Map{
MustMakeKey("my", "testKey"): NewObjectValue(`["0123456"]`),
},
},
{
Key: `my:key.secure`,
Path: true,

View file

@ -802,6 +802,104 @@ func TestConfigPaths(t *testing.T) {
TopLevelKey: "wayInner",
TopLevelExpectedValue: `{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":false}}}}}}}}}}}`,
},
{
Key: "foo1[0]",
Value: "false",
Path: true,
TopLevelKey: "foo1",
TopLevelExpectedValue: `[false]`,
},
{
Key: "foo2[0]",
Value: "true",
Path: true,
TopLevelKey: "foo2",
TopLevelExpectedValue: `[true]`,
},
{
Key: "foo3[0]",
Value: "10",
Path: true,
TopLevelKey: "foo3",
TopLevelExpectedValue: `[10]`,
},
{
Key: "foo4[0]",
Value: "0",
Path: true,
TopLevelKey: "foo4",
TopLevelExpectedValue: `[0]`,
},
{
Key: "foo5[0]",
Value: "00",
Path: true,
TopLevelKey: "foo5",
TopLevelExpectedValue: `["00"]`,
},
{
Key: "foo6[0]",
Value: "01",
Path: true,
TopLevelKey: "foo6",
TopLevelExpectedValue: `["01"]`,
},
{
Key: "foo7[0]",
Value: "0123456",
Path: true,
TopLevelKey: "foo7",
TopLevelExpectedValue: `["0123456"]`,
},
{
Key: "bar1.inner",
Value: "false",
Path: true,
TopLevelKey: "bar1",
TopLevelExpectedValue: `{"inner":false}`,
},
{
Key: "bar2.inner",
Value: "true",
Path: true,
TopLevelKey: "bar2",
TopLevelExpectedValue: `{"inner":true}`,
},
{
Key: "bar3.inner",
Value: "10",
Path: true,
TopLevelKey: "bar3",
TopLevelExpectedValue: `{"inner":10}`,
},
{
Key: "bar4.inner",
Value: "0",
Path: true,
TopLevelKey: "bar4",
TopLevelExpectedValue: `{"inner":0}`,
},
{
Key: "bar5.inner",
Value: "00",
Path: true,
TopLevelKey: "bar5",
TopLevelExpectedValue: `{"inner":"00"}`,
},
{
Key: "bar6.inner",
Value: "01",
Path: true,
TopLevelKey: "bar6",
TopLevelExpectedValue: `{"inner":"01"}`,
},
{
Key: "bar7.inner",
Value: "0123456",
Path: true,
TopLevelKey: "bar7",
TopLevelExpectedValue: `{"inner":"0123456"}`,
},
// Overwriting a top-level string value is allowed.
{