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:
parent
9077292d14
commit
33119659e0
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue