2018-12-10 18:42:45 +01:00
|
|
|
package format
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
|
|
|
"github.com/hashicorp/terraform/plans"
|
|
|
|
"github.com/mitchellh/colorstring"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
2018-12-11 12:49:17 +01:00
|
|
|
func TestResourceChange_primitiveTypes(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
2018-12-10 18:42:45 +01:00
|
|
|
"creation": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ id = (known after apply)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"deletion": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
}),
|
|
|
|
After: cty.NullVal(cty.EmptyObject),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
|
|
|
- resource "test_instance" "example" {
|
|
|
|
- id = "i-02ae66f368e8518a9" -> null
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:49:17 +01:00
|
|
|
"string in-place update": {
|
2018-12-10 18:42:45 +01:00
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:49:17 +01:00
|
|
|
"string force-new update": {
|
2018-12-10 18:42:45 +01:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
2018-12-11 12:49:17 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
2018-12-10 18:42:45 +01:00
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "ami"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
2018-12-11 12:55:59 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER" # forces replacement
|
2018-12-11 12:49:17 +01:00
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"string in-place update (null values)": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"unchanged": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"unchanged": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"unchanged": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update of multi-line string field": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"more_lines": cty.StringVal(`original
|
|
|
|
`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"more_lines": cty.StringVal(`original
|
|
|
|
new line
|
|
|
|
`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"more_lines": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ more_lines = <<~EOT
|
|
|
|
original
|
|
|
|
+ new line
|
|
|
|
EOT
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:55:59 +01:00
|
|
|
"force-new update of multi-line string field": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"more_lines": cty.StringVal(`original
|
|
|
|
`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"more_lines": cty.StringVal(`original
|
|
|
|
new line
|
|
|
|
`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"more_lines": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "more_lines"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ more_lines = <<~EOT # forces replacement
|
|
|
|
original
|
|
|
|
+ new line
|
|
|
|
EOT
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:49:17 +01:00
|
|
|
|
|
|
|
// Sensitive
|
|
|
|
|
|
|
|
"creation with sensitive field": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"password": cty.StringVal("top-secret"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ id = (known after apply)
|
|
|
|
+ password = (sensitive value)
|
2018-12-10 18:42:45 +01:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-12-11 12:49:17 +01:00
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2018-12-11 12:50:08 +01:00
|
|
|
func TestResourceChange_JSON(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"creation": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{
|
|
|
|
"str": "value",
|
|
|
|
"list":["a","b", 234, true],
|
|
|
|
"obj": {"key": "val"}
|
|
|
|
}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ id = (known after apply)
|
|
|
|
+ json_field = jsonencode(
|
|
|
|
{
|
|
|
|
+ list = [
|
|
|
|
+ "a",
|
|
|
|
+ "b",
|
|
|
|
+ 234,
|
|
|
|
+ true,
|
|
|
|
]
|
|
|
|
+ obj = {
|
|
|
|
+ key = "val"
|
|
|
|
}
|
|
|
|
+ str = "value"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "new_value"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ {
|
|
|
|
- aaa = "value"
|
|
|
|
} -> {
|
|
|
|
+ aaa = "value"
|
|
|
|
+ bbb = "new_value"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "new_value"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "json_field"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ {
|
|
|
|
- aaa = "value"
|
|
|
|
} -> {
|
|
|
|
+ aaa = "value"
|
|
|
|
+ bbb = "new_value"
|
|
|
|
} # forces replacement
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update (whitespace change)": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "another"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa":"value",
|
|
|
|
"bbb":"another"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode( # whitespace changes
|
|
|
|
{
|
|
|
|
aaa = "value"
|
|
|
|
bbb = "another"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whitespace change)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "another"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa":"value",
|
|
|
|
"bbb":"another"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "json_field"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode( # whitespace changes force replacement
|
|
|
|
{
|
|
|
|
aaa = "value"
|
|
|
|
bbb = "another"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_primitiveList(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("new-element"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "list_field"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [ # forces replacement
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
- "aaaa",
|
|
|
|
"bbbb",
|
|
|
|
- "cccc",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_primitiveSet(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("new-element"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "set_field"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [ # forces replacement
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
"bbbb",
|
|
|
|
- "cccc",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_map(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"new-key": cty.StringVal("new-element"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
+ "new-key" = "new-element"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
"a" = "aaaa"
|
|
|
|
+ "b" = "bbbb"
|
|
|
|
"c" = "cccc"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "map_field"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = { # forces replacement
|
|
|
|
"a" = "aaaa"
|
|
|
|
+ "b" = "bbbb"
|
|
|
|
"c" = "cccc"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
- "a" = "aaaa" -> null
|
|
|
|
"b" = "bbbb"
|
|
|
|
- "c" = "cccc" -> null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_nestedList(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
2018-12-11 13:56:11 +01:00
|
|
|
"in-place update - equal": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 13:56:11 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
root_block_device {
|
|
|
|
volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:50:08 +01:00
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListValEmpty(cty.EmptyObject),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ root_block_device {
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
~ root_block_device {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (inside block)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
cty.IndexStep{Key: cty.NumberIntVal(0)},
|
|
|
|
cty.GetAttrStep{Name: "volume_type"},
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
~ root_block_device {
|
|
|
|
~ volume_type = "gp2" -> "different" # forces replacement
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
~ root_block_device { # forces replacement
|
|
|
|
~ volume_type = "gp2" -> "different"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListValEmpty(cty.EmptyObject),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
- root_block_device {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_nestedSet(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.SetValEmpty(cty.EmptyObject),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ root_block_device {
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2018-12-12 15:28:12 +01:00
|
|
|
- root_block_device {
|
|
|
|
- volume_type = "gp2" -> null
|
2018-12-11 12:50:08 +01:00
|
|
|
}
|
2018-12-12 15:28:12 +01:00
|
|
|
+ root_block_device {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
+ volume_type = "gp2"
|
2018-12-11 12:55:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2018-12-12 15:28:12 +01:00
|
|
|
- root_block_device { # forces replacement
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
+ root_block_device { # forces replacement
|
|
|
|
+ volume_type = "different"
|
2018-12-11 12:55:59 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetValEmpty(cty.EmptyObject),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
- root_block_device {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2018-12-11 12:49:17 +01:00
|
|
|
type testCase struct {
|
|
|
|
Action plans.Action
|
|
|
|
Mode addrs.ResourceMode
|
|
|
|
Before cty.Value
|
|
|
|
After cty.Value
|
|
|
|
Schema *configschema.Block
|
|
|
|
RequiredReplace cty.PathSet
|
|
|
|
ExpectedOutput string
|
|
|
|
}
|
|
|
|
|
|
|
|
func runTestCases(t *testing.T, testCases map[string]testCase) {
|
2018-12-10 18:42:45 +01:00
|
|
|
color := &colorstring.Colorize{Colors: colorstring.DefaultColors, Disable: true}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
beforeVal := tc.Before
|
|
|
|
before, err := plans.NewDynamicValue(beforeVal, beforeVal.Type())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
afterVal := tc.After
|
|
|
|
after, err := plans.NewDynamicValue(afterVal, afterVal.Type())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
change := &plans.ResourceInstanceChangeSrc{
|
|
|
|
Addr: addrs.Resource{
|
|
|
|
Mode: tc.Mode,
|
|
|
|
Type: "test_instance",
|
|
|
|
Name: "example",
|
|
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
|
|
ProviderAddr: addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: tc.Action,
|
|
|
|
Before: before,
|
|
|
|
After: after,
|
|
|
|
},
|
|
|
|
RequiredReplace: tc.RequiredReplace,
|
|
|
|
}
|
|
|
|
|
|
|
|
output := ResourceChange(change, tc.Schema, color)
|
|
|
|
if output != tc.ExpectedOutput {
|
|
|
|
t.Fatalf("Unexpected diff.\nExpected:\n%s\nGiven:\n%s\n", tc.ExpectedOutput, output)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|