2018-12-10 18:42:45 +01:00
|
|
|
package format
|
|
|
|
|
|
|
|
import (
|
2020-09-04 22:03:48 +02:00
|
|
|
"fmt"
|
2018-12-10 18:42:45 +01:00
|
|
|
"testing"
|
|
|
|
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
"github.com/google/go-cmp/cmp"
|
2021-05-17 21:00:50 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
2021-05-17 21:17:09 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
2021-06-24 23:53:43 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/lang/marks"
|
2021-05-17 21:33:17 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/plans"
|
2021-05-17 21:43:35 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/states"
|
2018-12-10 18:42:45 +01:00
|
|
|
"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)
|
|
|
|
}
|
2019-03-14 22:20:42 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"creation (null string)": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"string": cty.StringVal("null"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"string": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ string = "null"
|
|
|
|
}
|
2019-11-15 16:25:49 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"creation (null string with extra whitespace)": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"string": cty.StringVal("null "),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"string": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ string = "null "
|
|
|
|
}
|
2018-12-10 18:42:45 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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
|
2019-01-13 23:51:05 +01:00
|
|
|
- resource "test_instance" "example" {
|
|
|
|
- id = "i-02ae66f368e8518a9" -> null
|
|
|
|
}
|
2021-05-13 00:40:55 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"deletion of deposed object": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
DeposedKey: states.DeposedKey("byebye"),
|
|
|
|
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 (deposed object byebye) will be destroyed
|
|
|
|
# (left over from a partially-failed replacement of this instance)
|
|
|
|
- resource "test_instance" "example" {
|
|
|
|
- id = "i-02ae66f368e8518a9" -> null
|
|
|
|
}
|
2019-01-13 23:51:05 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"deletion (empty string)": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"intentionally_long": cty.StringVal(""),
|
|
|
|
}),
|
|
|
|
After: cty.NullVal(cty.EmptyObject),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"intentionally_long": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
2018-12-10 18:42:45 +01:00
|
|
|
- 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": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-10 18:42:45 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
long
|
|
|
|
multi-line
|
|
|
|
string
|
|
|
|
field
|
2018-12-11 12:49:17 +01:00
|
|
|
`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"more_lines": cty.StringVal(`original
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
extremely long
|
|
|
|
multi-line
|
|
|
|
string
|
|
|
|
field
|
2018-12-11 12:49:17 +01:00
|
|
|
`),
|
|
|
|
}),
|
|
|
|
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)
|
2020-08-02 17:33:35 +02:00
|
|
|
~ more_lines = <<-EOT
|
2018-12-11 12:49:17 +01:00
|
|
|
original
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
- long
|
|
|
|
+ extremely long
|
|
|
|
multi-line
|
|
|
|
string
|
|
|
|
field
|
2018-12-11 12:49:17 +01:00
|
|
|
EOT
|
|
|
|
}
|
2019-11-07 00:59:54 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"addition 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.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
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)
|
2020-08-02 17:33:35 +02:00
|
|
|
+ more_lines = <<-EOT
|
2019-11-07 00:59:54 +01:00
|
|
|
original
|
|
|
|
new line
|
|
|
|
EOT
|
|
|
|
}
|
2018-12-11 12:49:17 +01:00
|
|
|
`,
|
|
|
|
},
|
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)
|
2020-08-02 17:33:35 +02:00
|
|
|
~ more_lines = <<-EOT # forces replacement
|
2018-12-11 12:55:59 +01:00
|
|
|
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"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"conn_info": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"user": cty.StringVal("not-secret"),
|
|
|
|
"password": cty.StringVal("top-secret"),
|
|
|
|
}),
|
2018-12-11 12:49:17 +01:00
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
2021-02-18 14:48:52 +01:00
|
|
|
"conn_info": {
|
|
|
|
NestedType: &configschema.Object{
|
|
|
|
Nesting: configschema.NestingSingle,
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"user": {Type: cty.String, Optional: true},
|
|
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-11 12:49:17 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
+ conn_info = {
|
2021-11-19 21:19:08 +01:00
|
|
|
+ password = (sensitive value)
|
|
|
|
+ user = "not-secret"
|
|
|
|
}
|
2021-02-18 14:48:52 +01:00
|
|
|
+ id = (known after apply)
|
|
|
|
+ password = (sensitive value)
|
2018-12-10 18:42:45 +01:00
|
|
|
}
|
2019-01-23 16:32:13 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update with equal sensitive field": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("blah"),
|
|
|
|
"str": cty.StringVal("before"),
|
|
|
|
"password": cty.StringVal("top-secret"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"str": cty.StringVal("after"),
|
|
|
|
"password": cty.StringVal("top-secret"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"str": {Type: cty.String, Optional: true},
|
|
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "blah" -> (known after apply)
|
|
|
|
~ str = "before" -> "after"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-03-06 01:18:55 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
2021-04-28 21:02:34 +02:00
|
|
|
// tainted objects
|
2019-03-06 01:18:55 +01:00
|
|
|
"replace tainted resource": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseTainted,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2019-03-06 01:18:55 +01:00
|
|
|
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.UnknownVal(cty.String),
|
|
|
|
"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 is tainted, so must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER" # forces replacement
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
}
|
2019-03-29 22:52:13 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force replacement with empty before value": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2019-03-29 22:52:13 +01:00
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("name"),
|
|
|
|
"forced": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("name"),
|
|
|
|
"forced": cty.StringVal("example"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"name": {Type: cty.String, Optional: true},
|
|
|
|
"forced": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "forced"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
+ forced = "example" # forces replacement
|
|
|
|
name = "name"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force replacement with empty before value legacy": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2019-03-29 22:52:13 +01:00
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("name"),
|
|
|
|
"forced": cty.StringVal(""),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("name"),
|
|
|
|
"forced": cty.StringVal("example"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"name": {Type: cty.String, Optional: true},
|
|
|
|
"forced": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "forced"},
|
|
|
|
}),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
+ forced = "example" # forces replacement
|
|
|
|
name = "name"
|
|
|
|
}
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"show all identifying attributes even if unchanged": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"bar": cty.StringVal("bar"),
|
|
|
|
"foo": cty.StringVal("foo"),
|
|
|
|
"name": cty.StringVal("alice"),
|
|
|
|
"tags": cty.MapVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("bob"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"bar": cty.StringVal("bar"),
|
|
|
|
"foo": cty.StringVal("foo"),
|
|
|
|
"name": cty.StringVal("alice"),
|
|
|
|
"tags": cty.MapVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("bob"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"bar": {Type: cty.String, Optional: true},
|
|
|
|
"foo": {Type: cty.String, Optional: true},
|
|
|
|
"name": {Type: cty.String, Optional: true},
|
|
|
|
"tags": {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-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
name = "alice"
|
|
|
|
tags = {
|
|
|
|
"name" = "bob"
|
|
|
|
}
|
|
|
|
# (2 unchanged attributes hidden)
|
|
|
|
}
|
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"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2019-01-23 14:13:48 +01:00
|
|
|
"in-place update of object": {
|
2018-12-11 12:50:08 +01:00
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
"json_field": cty.StringVal(`{"aaa": "value","ccc": 5}`),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ {
|
|
|
|
+ bbb = "new_value"
|
|
|
|
- ccc = 5 -> null
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
2019-01-15 16:37:03 +01:00
|
|
|
},
|
|
|
|
"in-place update (from empty tuple)": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": []}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": ["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",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update (to empty tuple)": {
|
|
|
|
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": []}`),
|
|
|
|
}),
|
|
|
|
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",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2019-01-22 17:49:49 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update (tuple of different types)": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": [42, {"foo":"bar"}, "value"]}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": [42, {"foo":"baz"}, "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 = [
|
|
|
|
42,
|
|
|
|
~ {
|
|
|
|
~ foo = "bar" -> "baz"
|
|
|
|
},
|
|
|
|
"value",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ {
|
|
|
|
+ bbb = "new_value"
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
} # 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)": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2019-01-14 16:29:36 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"creation (empty)": {
|
|
|
|
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(`{}`),
|
|
|
|
}),
|
|
|
|
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({})
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
2019-01-15 16:37:03 +01:00
|
|
|
"JSON list item removal": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`["first","second","third"]`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`["first","second"]`),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ [
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
"second",
|
|
|
|
- "third",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
2019-01-15 16:37:03 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"JSON list item addition": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`["first","second"]`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`["first","second","third"]`),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ [
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
"second",
|
|
|
|
+ "third",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
2019-01-15 16:37:03 +01:00
|
|
|
},
|
|
|
|
"JSON list object addition": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"first":"111"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"first":"111","second":"222"}`),
|
|
|
|
}),
|
|
|
|
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(
|
|
|
|
~ {
|
|
|
|
+ second = "222"
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
# (1 unchanged element hidden)
|
2019-01-15 16:37:03 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"JSON object with nested list": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{
|
|
|
|
"Statement": ["first"]
|
|
|
|
}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{
|
|
|
|
"Statement": ["first", "second"]
|
|
|
|
}`),
|
|
|
|
}),
|
|
|
|
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(
|
|
|
|
~ {
|
|
|
|
~ Statement = [
|
|
|
|
"first",
|
|
|
|
+ "second",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2019-03-19 23:21:32 +01:00
|
|
|
"JSON list of objects - adding item": {
|
2019-01-15 16:37:03 +01:00
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`[{"one": "111"}]`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`[{"one": "111"}, {"two": "222"}]`),
|
|
|
|
}),
|
|
|
|
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(
|
|
|
|
~ [
|
|
|
|
{
|
|
|
|
one = "111"
|
|
|
|
},
|
|
|
|
+ {
|
|
|
|
+ two = "222"
|
|
|
|
},
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
2019-03-19 23:21:32 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"JSON list of objects - removing item": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
2019-11-09 01:05:23 +01:00
|
|
|
"json_field": cty.StringVal(`[{"one": "111"}, {"two": "222"}, {"three": "333"}]`),
|
2019-03-19 23:21:32 +01:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
2019-11-09 01:05:23 +01:00
|
|
|
"json_field": cty.StringVal(`[{"one": "111"}, {"three": "333"}]`),
|
2019-03-19 23:21:32 +01:00
|
|
|
}),
|
|
|
|
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(
|
|
|
|
~ [
|
|
|
|
{
|
|
|
|
one = "111"
|
|
|
|
},
|
|
|
|
- {
|
|
|
|
- two = "222"
|
|
|
|
},
|
2019-11-09 01:05:23 +01:00
|
|
|
{
|
|
|
|
three = "333"
|
|
|
|
},
|
2019-03-19 23:21:32 +01:00
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
2019-01-15 16:37:03 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"JSON object with list of objects": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"parent":[{"one": "111"}]}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"parent":[{"one": "111"}, {"two": "222"}]}`),
|
|
|
|
}),
|
|
|
|
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(
|
|
|
|
~ {
|
|
|
|
~ parent = [
|
|
|
|
{
|
|
|
|
one = "111"
|
|
|
|
},
|
|
|
|
+ {
|
|
|
|
+ two = "222"
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"JSON object double nested lists": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"parent":[{"another_list": ["111"]}]}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"parent":[{"another_list": ["111", "222"]}]}`),
|
|
|
|
}),
|
|
|
|
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(
|
|
|
|
~ {
|
|
|
|
~ parent = [
|
|
|
|
~ {
|
|
|
|
~ another_list = [
|
|
|
|
"111",
|
|
|
|
+ "222",
|
|
|
|
]
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2019-01-23 14:13:48 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update from object to tuple": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": [42, {"foo":"bar"}, "value"]}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`["aaa", 42, "something"]`),
|
|
|
|
}),
|
|
|
|
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 = [
|
|
|
|
- 42,
|
|
|
|
- {
|
|
|
|
- foo = "bar"
|
|
|
|
},
|
|
|
|
- "value",
|
|
|
|
]
|
|
|
|
} -> [
|
|
|
|
+ "aaa",
|
|
|
|
+ 42,
|
|
|
|
+ "something",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
2019-01-15 16:37:03 +01:00
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:50:08 +01:00
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2022-03-16 15:34:08 +01:00
|
|
|
func TestResourceChange_listObject(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
// https://github.com/hashicorp/terraform/issues/30641
|
|
|
|
"updating non-identifying attribute": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"accounts": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("1"),
|
|
|
|
"name": cty.StringVal("production"),
|
|
|
|
"status": cty.StringVal("ACTIVE"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("2"),
|
|
|
|
"name": cty.StringVal("staging"),
|
|
|
|
"status": cty.StringVal("ACTIVE"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("3"),
|
|
|
|
"name": cty.StringVal("disaster-recovery"),
|
|
|
|
"status": cty.StringVal("ACTIVE"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"accounts": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("1"),
|
|
|
|
"name": cty.StringVal("production"),
|
|
|
|
"status": cty.StringVal("ACTIVE"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("2"),
|
|
|
|
"name": cty.StringVal("staging"),
|
|
|
|
"status": cty.StringVal("EXPLODED"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("3"),
|
|
|
|
"name": cty.StringVal("disaster-recovery"),
|
|
|
|
"status": cty.StringVal("ACTIVE"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"accounts": {
|
|
|
|
Type: cty.List(cty.Object(map[string]cty.Type{
|
|
|
|
"id": cty.String,
|
|
|
|
"name": cty.String,
|
|
|
|
"status": cty.String,
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ accounts = [
|
|
|
|
{
|
|
|
|
id = "1"
|
|
|
|
name = "production"
|
|
|
|
status = "ACTIVE"
|
|
|
|
},
|
|
|
|
~ {
|
|
|
|
id = "2"
|
|
|
|
name = "staging"
|
|
|
|
~ status = "ACTIVE" -> "EXPLODED"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id = "3"
|
|
|
|
name = "disaster-recovery"
|
|
|
|
status = "ACTIVE"
|
|
|
|
},
|
|
|
|
]
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2018-12-11 12:50:08 +01:00
|
|
|
func TestResourceChange_primitiveList(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
2019-01-14 16:34:41 +01:00
|
|
|
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.NullVal(cty.List(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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ list_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:34:41 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - first addition": {
|
2018-12-11 12:50:08 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
|
|
|
cty.StringVal("ffff"),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
|
|
|
cty.StringVal("ffff"),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
"bbbb",
|
|
|
|
+ "cccc",
|
|
|
|
"dddd",
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [ # forces replacement
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
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"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
- "aaaa",
|
|
|
|
"bbbb",
|
|
|
|
- "cccc",
|
|
|
|
"dddd",
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:34:41 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"creation - empty list": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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 created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ ami = "ami-STATIC"
|
|
|
|
+ id = (known after apply)
|
|
|
|
+ list_field = []
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - full to empty": {
|
|
|
|
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.ListValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
- "cccc",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:34:41 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - null to empty": {
|
|
|
|
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.NullVal(cty.List(cty.String)),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ list_field = []
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-21 16:02:15 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update to unknown element": {
|
|
|
|
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("aaaa"),
|
|
|
|
cty.UnknownVal(cty.String),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
"aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
+ (known after apply),
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-21 16:02:15 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update - two new unknown elements": {
|
|
|
|
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"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
2019-01-21 16:02:15 +01:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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.UnknownVal(cty.String),
|
|
|
|
cty.UnknownVal(cty.String),
|
|
|
|
cty.StringVal("cccc"),
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
2019-01-21 16:02:15 +01:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
"aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
+ (known after apply),
|
|
|
|
+ (known after apply),
|
|
|
|
"cccc",
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_primitiveTuple(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"tuple_field": cty.TupleVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("dddd"),
|
|
|
|
cty.StringVal("eeee"),
|
|
|
|
cty.StringVal("ffff"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"tuple_field": cty.TupleVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
cty.StringVal("eeee"),
|
|
|
|
cty.StringVal("ffff"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Required: true},
|
|
|
|
"tuple_field": {Type: cty.Tuple([]cty.Type{cty.String, cty.String, cty.String, cty.String, cty.String}), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
~ tuple_field = [
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
"bbbb",
|
|
|
|
- "dddd",
|
|
|
|
+ "cccc",
|
|
|
|
"eeee",
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_primitiveSet(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
2019-01-14 16:34:41 +01:00
|
|
|
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.NullVal(cty.Set(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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ set_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:34:41 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - first insertion": {
|
2018-12-11 12:50:08 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
+ "bbbb",
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [ # forces replacement
|
|
|
|
+ "bbbb",
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "cccc",
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:34:41 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"creation - empty set": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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 created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ ami = "ami-STATIC"
|
|
|
|
+ id = (known after apply)
|
|
|
|
+ set_field = []
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - full to empty set": {
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:34:41 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - null to empty set": {
|
|
|
|
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.NullVal(cty.Set(cty.String)),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ set_field = []
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-18 14:04:53 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update to unknown": {
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.UnknownVal(cty.Set(cty.String)),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
] -> (known after apply)
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-18 14:04:53 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update to unknown element": {
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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.UnknownVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "bbbb",
|
|
|
|
~ (known after apply),
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
]
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceChange_map(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
2019-01-14 16:15:01 +01:00
|
|
|
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.NullVal(cty.Map(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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ map_field = {
|
|
|
|
+ "new-key" = "new-element"
|
|
|
|
}
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:15:01 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - first insertion": {
|
2018-12-11 12:50:08 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
+ "new-key" = "new-element"
|
|
|
|
}
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
+ "b" = "bbbb"
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
}
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = { # forces replacement
|
|
|
|
+ "b" = "bbbb"
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
}
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
- "a" = "aaaa" -> null
|
|
|
|
- "c" = "cccc" -> null
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
}
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2019-01-14 16:15:01 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"creation - empty": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapValEmpty(cty.String),
|
|
|
|
}),
|
|
|
|
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 created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ ami = "ami-STATIC"
|
|
|
|
+ id = (known after apply)
|
|
|
|
+ map_field = {}
|
|
|
|
}
|
2019-01-21 16:02:51 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update to unknown element": {
|
|
|
|
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{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.UnknownVal(cty.String),
|
|
|
|
"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
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
~ "b" = "bbbb" -> (known after apply)
|
|
|
|
# (2 unchanged elements hidden)
|
|
|
|
}
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 13:56:11 +01:00
|
|
|
}),
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 13:56:11 +01:00
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchema(configschema.NestingList),
|
2018-12-11 13:56:11 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
# (1 unchanged attribute hidden)
|
2018-12-11 13:56:11 +01:00
|
|
|
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
2018-12-11 13:56:11 +01:00
|
|
|
`,
|
|
|
|
},
|
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{
|
2019-03-09 21:14:08 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
|
2021-03-15 14:31:23 +01:00
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
2021-02-18 14:48:52 +01:00
|
|
|
})}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
2019-01-14 17:07:04 +01:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchema(configschema.NestingList),
|
2019-01-14 17:07:04 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
2021-03-15 14:31:23 +01:00
|
|
|
~ disks = [
|
2021-11-02 16:13:56 +01:00
|
|
|
+ {
|
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
+ size = "50GB"
|
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2019-01-14 17:07:04 +01:00
|
|
|
|
|
|
|
+ root_block_device {}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - first insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
2019-03-09 21:14:08 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2019-01-14 17:07:04 +01:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
2019-01-14 17:07:04 +01:00
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
2018-12-11 12:50:08 +01:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchema(configschema.NestingList),
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
2021-03-15 14:31:23 +01:00
|
|
|
~ disks = [
|
2021-11-02 16:13:56 +01:00
|
|
|
+ {
|
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:50:08 +01:00
|
|
|
|
|
|
|
+ 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"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
2021-05-04 16:23:50 +02:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
2021-02-18 14:48:52 +01:00
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"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"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
2021-05-04 16:23:50 +02:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
2021-02-18 14:48:52 +01:00
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"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(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchemaPlus(configschema.NestingList),
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
~ {
|
2021-11-02 16:13:56 +01:00
|
|
|
+ size = "50GB"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
|
|
|
# (1 unchanged element hidden)
|
2021-02-18 14:48:52 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:50:08 +01:00
|
|
|
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ root_block_device {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
"force-new update (inside blocks)": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:55:59 +01:00
|
|
|
"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"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:55:59 +01:00
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
2021-02-18 14:48:52 +01:00
|
|
|
RequiredReplace: cty.NewPathSet(
|
|
|
|
cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
cty.IndexStep{Key: cty.NumberIntVal(0)},
|
|
|
|
cty.GetAttrStep{Name: "volume_type"},
|
|
|
|
},
|
|
|
|
cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "disks"},
|
|
|
|
cty.IndexStep{Key: cty.NumberIntVal(0)},
|
|
|
|
cty.GetAttrStep{Name: "mount_point"},
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
),
|
2021-04-28 21:02:34 +02:00
|
|
|
Schema: testSchema(configschema.NestingList),
|
2018-12-11 12:55:59 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
~ {
|
2021-11-02 16:13:56 +01:00
|
|
|
~ mount_point = "/var/diska" -> "/var/diskb" # forces replacement
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:55:59 +01:00
|
|
|
|
|
|
|
~ root_block_device {
|
|
|
|
~ volume_type = "gp2" -> "different" # forces replacement
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:55:59 +01:00
|
|
|
"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"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:55:59 +01:00
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
2021-02-18 14:48:52 +01:00
|
|
|
RequiredReplace: cty.NewPathSet(
|
|
|
|
cty.Path{cty.GetAttrStep{Name: "root_block_device"}},
|
|
|
|
cty.Path{cty.GetAttrStep{Name: "disks"}},
|
|
|
|
),
|
2021-04-28 21:02:34 +02:00
|
|
|
Schema: testSchema(configschema.NestingList),
|
2018-12-11 12:55:59 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [ # forces replacement
|
|
|
|
~ {
|
2021-11-02 16:13:56 +01:00
|
|
|
~ mount_point = "/var/diska" -> "/var/diskb"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:55:59 +01:00
|
|
|
|
|
|
|
~ 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"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"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{
|
2019-03-09 21:14:08 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2021-02-18 14:48:52 +01:00
|
|
|
"disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2019-03-09 21:14:08 +01:00
|
|
|
"root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchema(configschema.NestingList),
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-02-18 14:48:52 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
2021-11-02 16:13:56 +01:00
|
|
|
- {
|
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
]
|
2021-02-18 14:48:52 +01:00
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:50:08 +01:00
|
|
|
|
|
|
|
- root_block_device {
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
2019-03-09 21:14:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"with dynamically-typed attribute": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"block": cty.EmptyTupleVal,
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"block": cty.TupleVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"attr": cty.StringVal("foo"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"attr": cty.True,
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"block": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"attr": {Type: cty.DynamicPseudoType, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingList,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
+ block {
|
|
|
|
+ attr = "foo"
|
|
|
|
}
|
|
|
|
+ block {
|
|
|
|
+ attr = true
|
|
|
|
}
|
|
|
|
}
|
2021-03-15 14:31:23 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place sequence update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"list": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("x")}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("y")}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"list": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("y")}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("z")}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"list": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"attr": {
|
|
|
|
Type: cty.String,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingList,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ list {
|
|
|
|
~ attr = "x" -> "y"
|
|
|
|
}
|
|
|
|
~ list {
|
|
|
|
~ attr = "y" -> "z"
|
|
|
|
}
|
|
|
|
}
|
2021-08-18 19:17:03 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - unknown": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"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"),
|
|
|
|
"disks": cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
}))),
|
|
|
|
"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: testSchemaPlus(configschema.NestingList),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
2021-11-02 16:13:56 +01:00
|
|
|
- {
|
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-08-18 19:17:03 +02:00
|
|
|
] -> (known after apply)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2021-11-02 16:13:56 +01:00
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - modification": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskc"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"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"),
|
|
|
|
"disks": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("75GB"),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskc"),
|
|
|
|
"size": cty.StringVal("25GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"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: testSchemaPlus(configschema.NestingList),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
~ {
|
|
|
|
~ size = "50GB" -> "75GB"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
|
|
|
~ {
|
|
|
|
~ size = "50GB" -> "25GB"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2021-08-18 19:17:03 +02:00
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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{
|
2019-03-09 21:14:08 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2019-03-09 21:14:08 +01:00
|
|
|
"root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchema(configschema.NestingSet),
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
+ {
|
2021-11-19 19:06:34 +01:00
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:50:08 +01:00
|
|
|
|
|
|
|
+ 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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
2021-11-19 17:53:36 +01:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("100GB"),
|
|
|
|
}),
|
2021-03-15 14:31:23 +01:00
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
2021-11-19 17:53:36 +01:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("100GB"),
|
|
|
|
}),
|
2021-03-15 14:31:23 +01:00
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
"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(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchemaPlus(configschema.NestingSet),
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
+ {
|
2021-11-19 19:06:34 +01:00
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
+ size = "50GB"
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
- {
|
2021-11-19 19:06:34 +01:00
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
},
|
|
|
|
# (1 unchanged element hidden)
|
2021-03-15 14:31:23 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
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
|
|
|
}
|
2019-05-01 00:29:47 +02:00
|
|
|
- root_block_device {
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2018-12-11 12:55:59 +01:00
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:55:59 +01:00
|
|
|
}),
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diskb"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:55:59 +01:00
|
|
|
}),
|
2021-03-15 14:31:23 +01:00
|
|
|
RequiredReplace: cty.NewPathSet(
|
|
|
|
cty.Path{cty.GetAttrStep{Name: "root_block_device"}},
|
|
|
|
cty.Path{cty.GetAttrStep{Name: "disks"}},
|
|
|
|
),
|
2021-04-28 21:02:34 +02:00
|
|
|
Schema: testSchema(configschema.NestingSet),
|
2018-12-11 12:55:59 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
- { # forces replacement
|
2021-11-19 19:06:34 +01:00
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
+ { # forces replacement
|
2021-11-19 19:06:34 +01:00
|
|
|
+ mount_point = "/var/diskb"
|
|
|
|
+ size = "50GB"
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:55:59 +01:00
|
|
|
|
2018-12-12 15:28:12 +01:00
|
|
|
+ root_block_device { # forces replacement
|
|
|
|
+ volume_type = "different"
|
2018-12-11 12:55:59 +01:00
|
|
|
}
|
2019-05-01 00:29:47 +02:00
|
|
|
- root_block_device { # forces replacement
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
2019-03-09 21:14:08 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
"new_field": cty.String,
|
|
|
|
})),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchemaPlus(configschema.NestingSet),
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
- {
|
2021-11-19 19:06:34 +01:00
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2018-12-11 12:50:08 +01:00
|
|
|
|
|
|
|
- root_block_device {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
2021-08-17 00:13:55 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - empty nested sets": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
}))),
|
|
|
|
"root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
|
|
|
"root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: testSchema(configschema.NestingSet),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
2021-08-18 19:17:03 +02:00
|
|
|
+ disks = [
|
|
|
|
]
|
2021-08-17 00:13:55 +02:00
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
2021-08-17 00:25:16 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - null insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
}))),
|
|
|
|
"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"),
|
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"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: testSchemaPlus(configschema.NestingSet),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
+ disks = [
|
|
|
|
+ {
|
2021-11-19 19:06:34 +01:00
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
+ size = "50GB"
|
|
|
|
},
|
2021-08-17 00:25:16 +02:00
|
|
|
]
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ root_block_device {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
- root_block_device {
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
2021-08-18 19:17:03 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - unknown": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"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"),
|
|
|
|
"disks": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
}))),
|
|
|
|
"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: testSchemaPlus(configschema.NestingSet),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = [
|
|
|
|
- {
|
2021-11-19 19:06:34 +01:00
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-08-18 19:17:03 +02:00
|
|
|
] -> (known after apply)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2019-03-09 21:30:23 +01:00
|
|
|
func TestResourceChange_nestedMap(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
2021-07-21 14:51:35 +02:00
|
|
|
"creation from null": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.NullVal(cty.String),
|
|
|
|
"ami": cty.NullVal(cty.String),
|
|
|
|
"disks": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
}))),
|
|
|
|
"root_block_device": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
}))),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: testSchema(configschema.NestingMap),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
+ ami = "ami-AFTER"
|
|
|
|
+ disks = {
|
|
|
|
+ "disk_a" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
},
|
2021-07-21 14:51:35 +02:00
|
|
|
}
|
|
|
|
+ id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ root_block_device "a" {
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2019-03-09 21:30:23 +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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
})),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchema(configschema.NestingMap),
|
2019-03-09 21:30:23 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
+ "disk_a" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
+ mount_point = "/var/diska"
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
}
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2019-03-09 21:30:23 +01:00
|
|
|
|
|
|
|
+ root_block_device "a" {
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - change attr": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": 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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchemaPlus(configschema.NestingMap),
|
2019-03-09 21:30:23 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
~ "disk_a" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
+ size = "50GB"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
}
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2019-03-09 21:30:23 +01:00
|
|
|
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ root_block_device "a" {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
|
|
|
}
|
2019-03-09 21:30:23 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": 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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
"disk_2": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/disk2"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
"b": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchemaPlus(configschema.NestingMap),
|
2019-03-09 21:30:23 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
+ "disk_2" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
+ mount_point = "/var/disk2"
|
|
|
|
+ size = "50GB"
|
|
|
|
},
|
|
|
|
# (1 unchanged element hidden)
|
2021-03-15 14:31:23 +01:00
|
|
|
}
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2019-03-09 21:30:23 +01:00
|
|
|
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
+ root_block_device "b" {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
2019-03-09 21:30:23 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
2021-04-28 21:02:34 +02:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
2019-03-09 21:30:23 +01:00
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
"b": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("standard"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("100GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
"b": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("standard"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
cty.IndexStep{Key: cty.StringVal("a")},
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
cty.Path{cty.GetAttrStep{Name: "disks"}},
|
|
|
|
),
|
2021-04-28 21:02:34 +02:00
|
|
|
Schema: testSchema(configschema.NestingMap),
|
2019-03-09 21:30:23 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
~ "disk_a" = { # forces replacement
|
2021-11-19 21:19:08 +01:00
|
|
|
~ size = "50GB" -> "100GB"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
}
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2019-03-09 21:30:23 +01:00
|
|
|
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
~ root_block_device "a" { # forces replacement
|
|
|
|
~ volume_type = "gp2" -> "different"
|
|
|
|
}
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
2019-03-09 21:30:23 +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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": 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"),
|
2021-03-15 14:31:23 +01:00
|
|
|
"disks": cty.MapValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
2019-03-09 21:30:23 +01:00
|
|
|
"root_block_device": cty.MapValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
"new_field": cty.String,
|
|
|
|
})),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2021-03-15 14:31:23 +01:00
|
|
|
Schema: testSchemaPlus(configschema.NestingMap),
|
2019-03-09 21:30:23 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2021-03-15 14:31:23 +01:00
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
- "disk_a" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
}
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2019-03-09 21:30:23 +01:00
|
|
|
|
|
|
|
- root_block_device "a" {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
2021-08-18 19:17:03 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - unknown": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": 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"),
|
|
|
|
"disks": cty.UnknownVal(cty.Map(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
}))),
|
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: testSchemaPlus(configschema.NestingMap),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
- "disk_a" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
- mount_point = "/var/diska" -> null
|
|
|
|
- size = "50GB" -> null
|
|
|
|
},
|
2021-08-18 19:17:03 +02:00
|
|
|
} -> (known after apply)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion sensitive": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"disks": cty.MapValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"mount_point": cty.String,
|
|
|
|
"size": cty.String,
|
|
|
|
})),
|
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": 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"),
|
|
|
|
"disks": cty.MapVal(map[string]cty.Value{
|
|
|
|
"disk_a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"mount_point": cty.StringVal("/var/diska"),
|
|
|
|
"size": cty.StringVal("50GB"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "disks"},
|
|
|
|
cty.IndexStep{Key: cty.StringVal("disk_a")},
|
|
|
|
cty.GetAttrStep{Name: "mount_point"},
|
|
|
|
},
|
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: testSchemaPlus(configschema.NestingMap),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
~ disks = {
|
|
|
|
+ "disk_a" = {
|
2021-11-19 21:19:08 +01:00
|
|
|
+ mount_point = (sensitive)
|
|
|
|
+ size = "50GB"
|
|
|
|
},
|
2021-08-18 19:17:03 +02:00
|
|
|
}
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
2019-03-09 21:30:23 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2021-09-23 03:23:35 +02:00
|
|
|
func TestResourceChange_actionReason(t *testing.T) {
|
|
|
|
emptySchema := &configschema.Block{}
|
|
|
|
nullVal := cty.NullVal(cty.EmptyObject)
|
|
|
|
emptyVal := cty.EmptyObjectVal
|
|
|
|
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"delete for no particular reason": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceChangeNoReason,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because of wrong repetition mode (NoKey)": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseWrongRepetition,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
InstanceKey: addrs.NoKey,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
|
|
|
# (because resource uses count or for_each)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because of wrong repetition mode (IntKey)": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseWrongRepetition,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
InstanceKey: addrs.IntKey(1),
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example[1] will be destroyed
|
|
|
|
# (because resource does not use count)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because of wrong repetition mode (StringKey)": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseWrongRepetition,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
InstanceKey: addrs.StringKey("a"),
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example["a"] will be destroyed
|
|
|
|
# (because resource does not use for_each)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because no resource configuration": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseNoResourceConfig,
|
|
|
|
ModuleInst: addrs.RootModuleInstance.Child("foo", addrs.NoKey),
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # module.foo.test_instance.example will be destroyed
|
|
|
|
# (because test_instance.example is not in configuration)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because no module": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseNoModule,
|
|
|
|
ModuleInst: addrs.RootModuleInstance.Child("foo", addrs.IntKey(1)),
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # module.foo[1].test_instance.example will be destroyed
|
|
|
|
# (because module.foo[1] is not in configuration)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because out of range for count": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseCountIndex,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
InstanceKey: addrs.IntKey(1),
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example[1] will be destroyed
|
|
|
|
# (because index [1] is out of range for count)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"delete because out of range for for_each": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
ActionReason: plans.ResourceInstanceDeleteBecauseEachKey,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
InstanceKey: addrs.StringKey("boop"),
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example["boop"] will be destroyed
|
|
|
|
# (because key ["boop"] is not in for_each map)
|
|
|
|
- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace for no particular reason (delete first)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceChangeNoReason,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace for no particular reason (create first)": {
|
|
|
|
Action: plans.CreateThenDelete,
|
|
|
|
ActionReason: plans.ResourceInstanceChangeNoReason,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
+/- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace by request (delete first)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceByRequest,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be replaced, as requested
|
|
|
|
-/+ resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace by request (create first)": {
|
|
|
|
Action: plans.CreateThenDelete,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceByRequest,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be replaced, as requested
|
|
|
|
+/- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace because tainted (delete first)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseTainted,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example is tainted, so must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace because tainted (create first)": {
|
|
|
|
Action: plans.CreateThenDelete,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseTainted,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example is tainted, so must be replaced
|
|
|
|
+/- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace because cannot update (delete first)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
// This one has no special message, because the fuller explanation
|
|
|
|
// typically appears inline as a "# forces replacement" comment.
|
|
|
|
// (not shown here)
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"replace because cannot update (create first)": {
|
|
|
|
Action: plans.CreateThenDelete,
|
|
|
|
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: emptyVal,
|
|
|
|
After: nullVal,
|
|
|
|
Schema: emptySchema,
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
// This one has no special message, because the fuller explanation
|
|
|
|
// typically appears inline as a "# forces replacement" comment.
|
|
|
|
// (not shown here)
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
+/- resource "test_instance" "example" {}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2020-09-10 22:45:31 +02:00
|
|
|
func TestResourceChange_sensitiveVariable(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
"creation": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-123"),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000),
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
cty.StringVal("!"),
|
|
|
|
}),
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_list": cty.ListVal([]cty.Value{
|
2020-09-24 18:41:50 +02:00
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
|
|
|
"another": cty.StringVal("not secret"),
|
|
|
|
}),
|
|
|
|
}),
|
2020-10-02 21:01:17 +02:00
|
|
|
"nested_block_set": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
|
|
|
"another": cty.StringVal("not secret"),
|
|
|
|
}),
|
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(1)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 18:41:50 +02:00
|
|
|
{
|
|
|
|
// Nested blocks/sets will mark the whole set/block as sensitive
|
2020-09-29 18:59:30 +02:00
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_list"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 18:41:50 +02:00
|
|
|
},
|
2020-10-02 21:01:17 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_set"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-02 21:01:17 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
2020-09-24 22:29:30 +02:00
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_whole": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
"map_key": {Type: cty.Map(cty.Number), Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
2020-09-24 18:41:50 +02:00
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_list": {
|
2020-09-24 18:41:50 +02:00
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
"another": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingList,
|
|
|
|
},
|
2020-10-02 21:01:17 +02:00
|
|
|
"nested_block_set": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
"another": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingSet,
|
|
|
|
},
|
2020-09-24 18:41:50 +02:00
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
2020-09-24 22:29:30 +02:00
|
|
|
+ ami = (sensitive)
|
|
|
|
+ id = "i-02ae66f368e8518a9"
|
|
|
|
+ list_field = [
|
|
|
|
+ "hello",
|
|
|
|
+ (sensitive),
|
|
|
|
+ "!",
|
|
|
|
]
|
2020-09-24 22:29:30 +02:00
|
|
|
+ map_key = {
|
|
|
|
+ "breakfast" = 800
|
|
|
|
+ "dinner" = (sensitive)
|
|
|
|
}
|
|
|
|
+ map_whole = (sensitive)
|
2020-09-24 18:41:50 +02:00
|
|
|
|
2020-09-29 18:59:30 +02:00
|
|
|
+ nested_block_list {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
2020-09-24 18:41:50 +02:00
|
|
|
}
|
2020-10-02 21:01:17 +02:00
|
|
|
|
|
|
|
+ nested_block_set {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
"in-place update - before sensitive": {
|
2020-09-10 22:45:31 +02:00
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
2020-09-24 19:27:15 +02:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"special": cty.BoolVal(true),
|
|
|
|
"some_number": cty.NumberIntVal(1),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
cty.StringVal("!"),
|
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-25 19:58:33 +02:00
|
|
|
"nested_block": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
2020-09-29 18:59:30 +02:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"nested_block_set": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
2020-09-25 19:58:33 +02:00
|
|
|
}),
|
|
|
|
}),
|
2020-09-10 22:45:31 +02:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
2020-09-24 19:27:15 +02:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"special": cty.BoolVal(false),
|
|
|
|
"some_number": cty.NumberIntVal(2),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
cty.StringVal("."),
|
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(1900),
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("cereal"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-25 19:58:33 +02:00
|
|
|
"nested_block": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("changed"),
|
2020-09-29 18:59:30 +02:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"nested_block_set": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("changed"),
|
2020-09-25 19:58:33 +02:00
|
|
|
}),
|
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 19:27:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "special"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 19:27:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "some_number"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 19:27:15 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(2)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-25 19:58:33 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-25 19:58:33 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_set"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-29 18:59:30 +02:00
|
|
|
},
|
2020-09-24 19:27:15 +02:00
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
2020-09-24 19:27:15 +02:00
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
2020-09-24 19:27:15 +02:00
|
|
|
"special": {Type: cty.Bool, Optional: true},
|
|
|
|
"some_number": {Type: cty.Number, Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": {Type: cty.Map(cty.Number), Optional: true},
|
|
|
|
"map_whole": {Type: cty.Map(cty.String), Optional: true},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
2020-09-25 19:58:33 +02:00
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
2020-10-02 21:01:17 +02:00
|
|
|
"nested_block": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingList,
|
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_set": {
|
2020-09-25 19:58:33 +02:00
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
Nesting: configschema.NestingSet,
|
2020-09-25 19:58:33 +02:00
|
|
|
},
|
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2020-09-23 22:28:55 +02:00
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-09-24 19:27:15 +02:00
|
|
|
~ ami = (sensitive)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2020-09-24 22:29:30 +02:00
|
|
|
~ list_field = [
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
"friends",
|
|
|
|
- (sensitive),
|
|
|
|
+ ".",
|
|
|
|
]
|
2020-09-24 22:29:30 +02:00
|
|
|
~ map_key = {
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-09-24 22:29:30 +02:00
|
|
|
~ "dinner" = (sensitive)
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
}
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-10-01 20:30:33 +02:00
|
|
|
~ map_whole = (sensitive)
|
2020-09-24 19:27:15 +02:00
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-09-24 19:27:15 +02:00
|
|
|
~ some_number = (sensitive)
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-09-24 19:27:15 +02:00
|
|
|
~ special = (sensitive)
|
2020-09-25 19:58:33 +02:00
|
|
|
|
2020-10-02 21:01:17 +02:00
|
|
|
# Warning: this block will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-10-02 21:01:17 +02:00
|
|
|
~ nested_block {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
|
|
|
|
2020-09-29 18:59:30 +02:00
|
|
|
# Warning: this block will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-09-29 18:59:30 +02:00
|
|
|
~ nested_block_set {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
2020-09-25 19:58:33 +02:00
|
|
|
}
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
"in-place update - after sensitive": {
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_single": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("original"),
|
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("goodbye"),
|
|
|
|
cty.StringVal("friends"),
|
2020-09-10 22:45:31 +02:00
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(700),
|
|
|
|
"dinner": cty.NumberIntVal(2100), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("cereal"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_single": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("changed"),
|
|
|
|
}),
|
2020-09-10 22:45:31 +02:00
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
2020-09-24 22:29:30 +02:00
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "tags"}, cty.IndexStep{Key: cty.StringVal("address")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_single"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-29 18:59:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
2020-09-24 22:29:30 +02:00
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": {Type: cty.Map(cty.Number), Optional: true},
|
|
|
|
"map_whole": {Type: cty.Map(cty.String), Optional: true},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"nested_block_single": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingSingle,
|
|
|
|
},
|
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2020-09-24 22:29:30 +02:00
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
~ list_field = [
|
|
|
|
- "hello",
|
|
|
|
+ (sensitive),
|
|
|
|
"friends",
|
|
|
|
]
|
2020-09-24 22:29:30 +02:00
|
|
|
~ map_key = {
|
|
|
|
~ "breakfast" = 800 -> 700
|
2021-05-18 16:24:31 +02:00
|
|
|
# Warning: this attribute value will be marked as sensitive and will not
|
|
|
|
# display in UI output after applying this change.
|
2020-09-24 22:29:30 +02:00
|
|
|
~ "dinner" = (sensitive)
|
|
|
|
}
|
2021-05-18 16:24:31 +02:00
|
|
|
# Warning: this attribute value will be marked as sensitive and will not
|
|
|
|
# display in UI output after applying this change.
|
2020-10-01 20:30:33 +02:00
|
|
|
~ map_whole = (sensitive)
|
2020-09-29 18:59:30 +02:00
|
|
|
|
2021-05-18 16:24:31 +02:00
|
|
|
# Warning: this block will be marked as sensitive and will not
|
|
|
|
# display in UI output after applying this change.
|
2020-09-29 18:59:30 +02:00
|
|
|
~ nested_block_single {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - both sensitive": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_map": cty.MapVal(map[string]cty.Value{
|
|
|
|
"foo": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("original"),
|
|
|
|
}),
|
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("goodbye"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(1800), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("cereal"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_map": cty.MapVal(map[string]cty.Value{
|
|
|
|
"foo": cty.ObjectVal(map[string]cty.Value{
|
2020-09-29 20:02:43 +02:00
|
|
|
"an_attr": cty.UnknownVal(cty.String),
|
2020-09-29 18:59:30 +02:00
|
|
|
}),
|
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_map"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-29 18:59:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-10 22:45:31 +02:00
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_map"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-29 18:59:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-10 22:45:31 +02:00
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
2020-09-24 22:29:30 +02:00
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": {Type: cty.Map(cty.Number), Optional: true},
|
|
|
|
"map_whole": {Type: cty.Map(cty.String), Optional: true},
|
2020-09-10 22:45:31 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"nested_block_map": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingMap,
|
|
|
|
},
|
|
|
|
},
|
2020-09-10 22:45:31 +02:00
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
2020-09-24 22:29:30 +02:00
|
|
|
~ ami = (sensitive)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
~ list_field = [
|
|
|
|
- (sensitive),
|
|
|
|
+ (sensitive),
|
|
|
|
"friends",
|
|
|
|
]
|
2020-09-24 22:29:30 +02:00
|
|
|
~ map_key = {
|
|
|
|
~ "dinner" = (sensitive)
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
}
|
2020-10-01 20:30:33 +02:00
|
|
|
~ map_whole = (sensitive)
|
2020-09-29 18:59:30 +02:00
|
|
|
|
|
|
|
~ nested_block_map {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
2020-10-13 19:55:16 +02:00
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - value unchanged, sensitivity changes": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"special": cty.BoolVal(true),
|
|
|
|
"some_number": cty.NumberIntVal(1),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
cty.StringVal("!"),
|
|
|
|
}),
|
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
|
|
|
"nested_block": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"nested_block_set": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"special": cty.BoolVal(true),
|
|
|
|
"some_number": cty.NumberIntVal(1),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
cty.StringVal("!"),
|
|
|
|
}),
|
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
|
|
|
"nested_block": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
"nested_block_set": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secretval"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "special"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "some_number"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(2)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_set"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-13 19:55:16 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
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},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
|
|
|
"special": {Type: cty.Bool, Optional: true},
|
|
|
|
"some_number": {Type: cty.Number, Optional: true},
|
|
|
|
"map_key": {Type: cty.Map(cty.Number), Optional: true},
|
|
|
|
"map_whole": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"nested_block": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingList,
|
|
|
|
},
|
|
|
|
"nested_block_set": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingSet,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change. The value is unchanged.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ ami = (sensitive)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
~ list_field = [
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
"friends",
|
|
|
|
- (sensitive),
|
|
|
|
+ "!",
|
|
|
|
]
|
|
|
|
~ map_key = {
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change. The value is unchanged.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ "dinner" = (sensitive)
|
|
|
|
# (1 unchanged element hidden)
|
|
|
|
}
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change. The value is unchanged.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ map_whole = (sensitive)
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change. The value is unchanged.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ some_number = (sensitive)
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change. The value is unchanged.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ special = (sensitive)
|
|
|
|
|
|
|
|
# Warning: this block will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ nested_block {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
|
|
|
|
|
|
|
# Warning: this block will no longer be marked as sensitive
|
2021-05-18 16:24:31 +02:00
|
|
|
# after applying this change.
|
2020-10-13 19:55:16 +02:00
|
|
|
~ nested_block_set {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
2020-09-29 18:59:30 +02:00
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"deletion": {
|
|
|
|
Action: plans.Delete,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2020-09-24 22:29:30 +02:00
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("hello"),
|
|
|
|
cty.StringVal("friends"),
|
|
|
|
}),
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.NumberIntVal(800),
|
|
|
|
"dinner": cty.NumberIntVal(2000), // sensitive key
|
|
|
|
}),
|
|
|
|
"map_whole": cty.MapVal(map[string]cty.Value{
|
|
|
|
"breakfast": cty.StringVal("pizza"),
|
|
|
|
"dinner": cty.StringVal("pizza"),
|
|
|
|
}),
|
2020-09-25 19:58:33 +02:00
|
|
|
"nested_block": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secret"),
|
|
|
|
"another": cty.StringVal("not secret"),
|
|
|
|
}),
|
|
|
|
}),
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_set": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secret"),
|
|
|
|
"another": cty.StringVal("not secret"),
|
|
|
|
}),
|
|
|
|
}),
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
}),
|
|
|
|
After: cty.NullVal(cty.EmptyObject),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(1)}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
2020-09-25 19:58:33 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-25 19:58:33 +02:00
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "nested_block_set"}},
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-09-29 18:59:30 +02:00
|
|
|
},
|
2020-09-24 22:29:30 +02:00
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
2020-09-24 22:29:30 +02:00
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
2020-09-24 22:29:30 +02:00
|
|
|
"map_key": {Type: cty.Map(cty.Number), Optional: true},
|
|
|
|
"map_whole": {Type: cty.Map(cty.String), Optional: true},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
2020-09-25 19:58:33 +02:00
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
2020-09-29 18:59:30 +02:00
|
|
|
"nested_block_set": {
|
2020-09-25 19:58:33 +02:00
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Optional: true},
|
|
|
|
"another": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
2020-09-29 18:59:30 +02:00
|
|
|
Nesting: configschema.NestingSet,
|
2020-09-25 19:58:33 +02:00
|
|
|
},
|
|
|
|
},
|
Store sensitive attribute paths in state (#26338)
* Add creation test and simplify in-place test
* Add deletion test
* Start adding marking from state
Start storing paths that should be marked
when pulled out of state. Implements deep
copy for attr paths. This commit also includes some
comment noise from investigations, and fixing the diff test
* Fix apply stripping marks
* Expand diff tests
* Basic apply test
* Update comments on equality checks to clarify current understanding
* Add JSON serialization for sensitive paths
We need to serialize a slice of cty.Path values to be used to re-mark
the sensitive values of a resource instance when loading the state file.
Paths consist of a list of steps, each of which may be either getting an
attribute value by name, or indexing into a collection by string or
number.
To serialize these without building a complex parser for a compact
string form, we render a nested array of small objects, like so:
[
[
{ type: "get_attr", value: "foo" },
{ type: "index", value: { "type": "number", "value": 2 } }
]
]
The above example is equivalent to a path `foo[2]`.
* Format diffs with map types
Comparisons need unmarked values to operate on,
so create unmarked values for those operations. Additionally,
change diff to cover map types
* Remove debugging printing
* Fix bug with marking non-sensitive values
When pulling a sensitive value from state,
we were previously using those marks to remark
the planned new value, but that new value
might *not* be sensitive, so let's not do that
* Fix apply test
Apply was not passing the second state
through to the third pass at apply
* Consistency in checking for length of paths vs inspecting into value
* In apply, don't mark with before paths
* AttrPaths test coverage for DeepCopy
* Revert format changes
Reverts format changes in format/diff for this
branch so those changes can be discussed on a separate PR
* Refactor name of AttrPaths to AttrSensitivePaths
* Rename AttributePaths/attributePaths for naming consistency
Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
2020-09-24 18:40:17 +02:00
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
|
|
|
- resource "test_instance" "example" {
|
2020-09-24 22:29:30 +02:00
|
|
|
- ami = (sensitive) -> null
|
|
|
|
- id = "i-02ae66f368e8518a9" -> null
|
|
|
|
- list_field = [
|
|
|
|
- "hello",
|
|
|
|
- (sensitive),
|
|
|
|
] -> null
|
2020-09-24 22:29:30 +02:00
|
|
|
- map_key = {
|
|
|
|
- "breakfast" = 800
|
|
|
|
- "dinner" = (sensitive)
|
|
|
|
} -> null
|
|
|
|
- map_whole = (sensitive) -> null
|
2020-09-25 19:58:33 +02:00
|
|
|
|
2020-09-29 18:59:30 +02:00
|
|
|
- nested_block_set {
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
2020-09-25 19:58:33 +02:00
|
|
|
}
|
2020-09-10 22:45:31 +02:00
|
|
|
}
|
2020-10-06 19:05:09 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update with sensitive value forcing replacement": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
2020-10-07 16:50:54 +02:00
|
|
|
"nested_block_set": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("secret"),
|
|
|
|
}),
|
|
|
|
}),
|
2020-10-06 19:05:09 +02:00
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
2020-10-07 16:50:54 +02:00
|
|
|
"nested_block_set": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"an_attr": cty.StringVal("changed"),
|
|
|
|
}),
|
|
|
|
}),
|
2020-10-06 19:05:09 +02:00
|
|
|
}),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
2020-10-07 16:50:54 +02:00
|
|
|
Path: cty.GetAttrPath("ami"),
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-07 16:50:54 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.GetAttrPath("nested_block_set"),
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-06 19:05:09 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
2020-10-07 16:50:54 +02:00
|
|
|
Path: cty.GetAttrPath("ami"),
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-07 16:50:54 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.GetAttrPath("nested_block_set"),
|
2021-06-24 23:53:43 +02:00
|
|
|
Marks: cty.NewValueMarks(marks.Sensitive),
|
2020-10-06 19:05:09 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
2020-10-07 16:50:54 +02:00
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"nested_block_set": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"an_attr": {Type: cty.String, Required: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingSet,
|
|
|
|
},
|
|
|
|
},
|
2020-10-06 19:05:09 +02:00
|
|
|
},
|
2020-10-07 16:50:54 +02:00
|
|
|
RequiredReplace: cty.NewPathSet(
|
|
|
|
cty.GetAttrPath("ami"),
|
|
|
|
cty.GetAttrPath("nested_block_set"),
|
|
|
|
),
|
2020-10-06 19:05:09 +02:00
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = (sensitive) # forces replacement
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2020-10-07 16:50:54 +02:00
|
|
|
|
|
|
|
~ nested_block_set { # forces replacement
|
|
|
|
# At least one attribute in this block is (or was) sensitive,
|
|
|
|
# so its contents will not be displayed.
|
|
|
|
}
|
2020-10-06 19:05:09 +02:00
|
|
|
}
|
2021-05-03 12:48:10 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update with sensitive attribute forcing replacement": {
|
|
|
|
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{
|
|
|
|
"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, Computed: true, Sensitive: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(
|
|
|
|
cty.GetAttrPath("ami"),
|
|
|
|
),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = (sensitive value) # forces replacement
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
2021-05-04 16:23:50 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"update with sensitive nested type attribute forcing replacement": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"conn_info": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"user": cty.StringVal("not-secret"),
|
|
|
|
"password": cty.StringVal("top-secret"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"conn_info": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"user": cty.StringVal("not-secret"),
|
|
|
|
"password": cty.StringVal("new-secret"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"conn_info": {
|
|
|
|
NestedType: &configschema.Object{
|
|
|
|
Nesting: configschema.NestingSingle,
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"user": {Type: cty.String, Optional: true},
|
|
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(
|
|
|
|
cty.GetAttrPath("conn_info"),
|
|
|
|
cty.GetAttrPath("password"),
|
|
|
|
),
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ conn_info = { # forces replacement
|
2021-11-19 21:19:08 +01:00
|
|
|
~ password = (sensitive value)
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
2021-05-04 16:23:50 +02:00
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
2020-09-10 22:45:31 +02:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2021-09-03 18:10:16 +02:00
|
|
|
func TestResourceChange_moved(t *testing.T) {
|
|
|
|
prevRunAddr := addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_instance",
|
|
|
|
Name: "previous",
|
|
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
|
|
|
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"moved and updated": {
|
|
|
|
PrevRunAddr: prevRunAddr,
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("12345"),
|
|
|
|
"foo": cty.StringVal("hello"),
|
|
|
|
"bar": cty.StringVal("baz"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("12345"),
|
|
|
|
"foo": cty.StringVal("hello"),
|
|
|
|
"bar": cty.StringVal("boop"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"foo": {Type: cty.String, Optional: true},
|
|
|
|
"bar": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
2021-09-23 03:23:35 +02:00
|
|
|
# (moved from test_instance.previous)
|
2021-09-03 18:10:16 +02:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ bar = "baz" -> "boop"
|
|
|
|
id = "12345"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"moved without changes": {
|
|
|
|
PrevRunAddr: prevRunAddr,
|
|
|
|
Action: plans.NoOp,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("12345"),
|
|
|
|
"foo": cty.StringVal("hello"),
|
|
|
|
"bar": cty.StringVal("baz"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("12345"),
|
|
|
|
"foo": cty.StringVal("hello"),
|
|
|
|
"bar": cty.StringVal("baz"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"foo": {Type: cty.String, Optional: true},
|
|
|
|
"bar": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
ExpectedOutput: ` # test_instance.previous has moved to test_instance.example
|
|
|
|
resource "test_instance" "example" {
|
|
|
|
id = "12345"
|
|
|
|
# (2 unchanged attributes hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2018-12-11 12:49:17 +01:00
|
|
|
type testCase struct {
|
|
|
|
Action plans.Action
|
2021-04-28 21:02:34 +02:00
|
|
|
ActionReason plans.ResourceInstanceChangeActionReason
|
2021-09-23 03:23:35 +02:00
|
|
|
ModuleInst addrs.ModuleInstance
|
2018-12-11 12:49:17 +01:00
|
|
|
Mode addrs.ResourceMode
|
2021-09-23 03:23:35 +02:00
|
|
|
InstanceKey addrs.InstanceKey
|
2021-05-13 00:40:55 +02:00
|
|
|
DeposedKey states.DeposedKey
|
2018-12-11 12:49:17 +01:00
|
|
|
Before cty.Value
|
2020-09-10 22:45:31 +02:00
|
|
|
BeforeValMarks []cty.PathValueMarks
|
|
|
|
AfterValMarks []cty.PathValueMarks
|
2018-12-11 12:49:17 +01:00
|
|
|
After cty.Value
|
|
|
|
Schema *configschema.Block
|
|
|
|
RequiredReplace cty.PathSet
|
|
|
|
ExpectedOutput string
|
2021-09-03 18:10:16 +02:00
|
|
|
PrevRunAddr addrs.AbsResourceInstance
|
2018-12-11 12:49:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2019-03-09 21:14:08 +01:00
|
|
|
ty := tc.Schema.ImpliedType()
|
|
|
|
|
2018-12-10 18:42:45 +01:00
|
|
|
beforeVal := tc.Before
|
2019-03-09 21:14:08 +01:00
|
|
|
switch { // Some fixups to make the test cases a little easier to write
|
|
|
|
case beforeVal.IsNull():
|
|
|
|
beforeVal = cty.NullVal(ty) // allow mistyped nulls
|
|
|
|
case !beforeVal.IsKnown():
|
|
|
|
beforeVal = cty.UnknownVal(ty) // allow mistyped unknowns
|
|
|
|
}
|
2018-12-10 18:42:45 +01:00
|
|
|
|
|
|
|
afterVal := tc.After
|
2019-03-09 21:14:08 +01:00
|
|
|
switch { // Some fixups to make the test cases a little easier to write
|
|
|
|
case afterVal.IsNull():
|
|
|
|
afterVal = cty.NullVal(ty) // allow mistyped nulls
|
|
|
|
case !afterVal.IsKnown():
|
|
|
|
afterVal = cty.UnknownVal(ty) // allow mistyped unknowns
|
|
|
|
}
|
2018-12-10 18:42:45 +01:00
|
|
|
|
2021-09-03 18:10:16 +02:00
|
|
|
addr := addrs.Resource{
|
|
|
|
Mode: tc.Mode,
|
|
|
|
Type: "test_instance",
|
|
|
|
Name: "example",
|
2021-09-23 03:23:35 +02:00
|
|
|
}.Instance(tc.InstanceKey).Absolute(tc.ModuleInst)
|
2021-09-03 18:10:16 +02:00
|
|
|
|
|
|
|
prevRunAddr := tc.PrevRunAddr
|
|
|
|
// If no previous run address is given, reuse the current address
|
|
|
|
// to make initialization easier
|
|
|
|
if prevRunAddr.Resource.Resource.Type == "" {
|
|
|
|
prevRunAddr = addr
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:24:36 +01:00
|
|
|
change := &plans.ResourceInstanceChange{
|
2021-09-03 18:10:16 +02:00
|
|
|
Addr: addr,
|
|
|
|
PrevRunAddr: prevRunAddr,
|
|
|
|
DeposedKey: tc.DeposedKey,
|
2020-02-13 21:32:58 +01:00
|
|
|
ProviderAddr: addrs.AbsProviderConfig{
|
2020-10-05 14:33:49 +02:00
|
|
|
Provider: addrs.NewDefaultProvider("test"),
|
2020-03-11 19:19:52 +01:00
|
|
|
Module: addrs.RootModule,
|
2020-02-13 21:32:58 +01:00
|
|
|
},
|
2022-02-04 20:24:36 +01:00
|
|
|
Change: plans.Change{
|
|
|
|
Action: tc.Action,
|
|
|
|
Before: beforeVal.MarkWithPaths(tc.BeforeValMarks),
|
|
|
|
After: afterVal.MarkWithPaths(tc.AfterValMarks),
|
2018-12-10 18:42:45 +01:00
|
|
|
},
|
2021-04-28 21:02:34 +02:00
|
|
|
ActionReason: tc.ActionReason,
|
2018-12-10 18:42:45 +01:00
|
|
|
RequiredReplace: tc.RequiredReplace,
|
|
|
|
}
|
|
|
|
|
2021-09-13 23:30:16 +02:00
|
|
|
output := ResourceChange(change, tc.Schema, color, DiffLanguageProposedChange)
|
2021-05-13 00:40:55 +02:00
|
|
|
if diff := cmp.Diff(output, tc.ExpectedOutput); diff != "" {
|
|
|
|
t.Errorf("wrong output\n%s", diff)
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
}
|
2018-12-10 18:42:45 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-09-04 22:03:48 +02:00
|
|
|
|
|
|
|
func TestOutputChanges(t *testing.T) {
|
|
|
|
color := &colorstring.Colorize{Colors: colorstring.DefaultColors, Disable: true}
|
|
|
|
|
|
|
|
testCases := map[string]struct {
|
|
|
|
changes []*plans.OutputChangeSrc
|
|
|
|
output string
|
|
|
|
}{
|
|
|
|
"new output value": {
|
|
|
|
[]*plans.OutputChangeSrc{
|
|
|
|
outputChange(
|
|
|
|
"foo",
|
|
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
|
|
cty.StringVal("bar"),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
},
|
|
|
|
`
|
|
|
|
+ foo = "bar"`,
|
|
|
|
},
|
|
|
|
"removed output": {
|
|
|
|
[]*plans.OutputChangeSrc{
|
|
|
|
outputChange(
|
|
|
|
"foo",
|
|
|
|
cty.StringVal("bar"),
|
|
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
},
|
|
|
|
`
|
|
|
|
- foo = "bar" -> null`,
|
|
|
|
},
|
|
|
|
"single string change": {
|
|
|
|
[]*plans.OutputChangeSrc{
|
|
|
|
outputChange(
|
|
|
|
"foo",
|
|
|
|
cty.StringVal("bar"),
|
|
|
|
cty.StringVal("baz"),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
},
|
|
|
|
`
|
|
|
|
~ foo = "bar" -> "baz"`,
|
|
|
|
},
|
|
|
|
"element added to list": {
|
|
|
|
[]*plans.OutputChangeSrc{
|
|
|
|
outputChange(
|
|
|
|
"foo",
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("alpha"),
|
|
|
|
cty.StringVal("beta"),
|
|
|
|
cty.StringVal("delta"),
|
|
|
|
cty.StringVal("epsilon"),
|
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("alpha"),
|
|
|
|
cty.StringVal("beta"),
|
|
|
|
cty.StringVal("gamma"),
|
|
|
|
cty.StringVal("delta"),
|
|
|
|
cty.StringVal("epsilon"),
|
|
|
|
}),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
},
|
|
|
|
`
|
|
|
|
~ foo = [
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
# (1 unchanged element hidden)
|
2020-09-04 22:03:48 +02:00
|
|
|
"beta",
|
|
|
|
+ "gamma",
|
|
|
|
"delta",
|
command: Add experimental concise diff renderer
When rendering a diff between current state and projected state, we only
show resources and outputs which have changes. However, we show a full
structural diff for these values, which includes all attributes and
blocks for a changed resource or output. The result can be a very long
diff, which makes it difficult to verify what the changed fields are.
This commit adds an experimental concise diff renderer, which suppresses
most unchanged fields, only displaying the most relevant changes and
some identifying context. This means:
- Always show all identifying attributes, initially defined as `id`,
`name`, and `tags`, even if unchanged;
- Only show changed, added, or removed primitive values: `string`,
`number`, or `bool`;
- Only show added or removed elements in unordered collections and
structural types: `map`, `set`, and `object`;
- Show added or removed elements with any surrounding unchanged elements
for sequence types: `list` and `tuple`;
- Only show added or removed nested blocks, or blocks with changed
attributes.
If any attributes, collection elements, or blocks are hidden, a count
is kept and displayed at the end of the parent scope. This ensures that
it is clear that the diff is only displaying a subset of the resource.
The experiment is currently enabled by default, but can be disabled by
setting the TF_X_CONCISE_DIFF environment variable to 0.
2020-08-19 22:47:56 +02:00
|
|
|
# (1 unchanged element hidden)
|
2020-09-04 22:03:48 +02:00
|
|
|
]`,
|
|
|
|
},
|
|
|
|
"multiple outputs changed, one sensitive": {
|
|
|
|
[]*plans.OutputChangeSrc{
|
|
|
|
outputChange(
|
|
|
|
"a",
|
|
|
|
cty.NumberIntVal(1),
|
|
|
|
cty.NumberIntVal(2),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
outputChange(
|
|
|
|
"b",
|
|
|
|
cty.StringVal("hunter2"),
|
|
|
|
cty.StringVal("correct-horse-battery-staple"),
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
outputChange(
|
|
|
|
"c",
|
|
|
|
cty.BoolVal(false),
|
|
|
|
cty.BoolVal(true),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
},
|
|
|
|
`
|
|
|
|
~ a = 1 -> 2
|
|
|
|
~ b = (sensitive value)
|
|
|
|
~ c = false -> true`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
output := OutputChanges(tc.changes, color)
|
|
|
|
if output != tc.output {
|
|
|
|
t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.output)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func outputChange(name string, before, after cty.Value, sensitive bool) *plans.OutputChangeSrc {
|
|
|
|
addr := addrs.AbsOutputValue{
|
|
|
|
OutputValue: addrs.OutputValue{Name: name},
|
|
|
|
}
|
|
|
|
|
|
|
|
change := &plans.OutputChange{
|
|
|
|
Addr: addr, Change: plans.Change{
|
|
|
|
Before: before,
|
|
|
|
After: after,
|
|
|
|
},
|
|
|
|
Sensitive: sensitive,
|
|
|
|
}
|
|
|
|
|
|
|
|
changeSrc, err := change.Encode()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("failed to encode change for %s: %s", addr, err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return changeSrc
|
|
|
|
}
|
2021-02-18 14:48:52 +01:00
|
|
|
|
2021-03-15 14:31:23 +01:00
|
|
|
// A basic test schema using a configurable NestingMode for one (NestedType) attribute and one block
|
|
|
|
func testSchema(nesting configschema.NestingMode) *configschema.Block {
|
|
|
|
return &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"disks": {
|
|
|
|
NestedType: &configschema.Object{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"mount_point": {Type: cty.String, Optional: true},
|
|
|
|
"size": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
Nesting: nesting,
|
2021-02-18 14:48:52 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
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,
|
|
|
|
},
|
2021-02-18 14:48:52 +01:00
|
|
|
},
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
Nesting: nesting,
|
2021-02-18 14:48:52 +01:00
|
|
|
},
|
|
|
|
},
|
2021-03-15 14:31:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// similar to testSchema with the addition of a "new_field" block
|
|
|
|
func testSchemaPlus(nesting configschema.NestingMode) *configschema.Block {
|
|
|
|
return &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"disks": {
|
|
|
|
NestedType: &configschema.Object{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"mount_point": {Type: cty.String, Optional: true},
|
|
|
|
"size": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
Nesting: nesting,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: nesting,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2021-02-18 14:48:52 +01:00
|
|
|
}
|