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"
|
2018-12-10 18:42:45 +01:00
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
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/hashicorp/terraform/helper/experiment"
|
2018-12-10 18:42:45 +01:00
|
|
|
"github.com/hashicorp/terraform/plans"
|
|
|
|
"github.com/mitchellh/colorstring"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
2018-12-11 12:49:17 +01:00
|
|
|
func TestResourceChange_primitiveTypes(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
2018-12-10 18:42:45 +01:00
|
|
|
"creation": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-10 18:42:45 +01:00
|
|
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-10 18:42:45 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
2019-01-13 23:51:05 +01:00
|
|
|
- resource "test_instance" "example" {
|
|
|
|
- id = "i-02ae66f368e8518a9" -> null
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-13 23:51:05 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-10 18:42:45 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:49:17 +01:00
|
|
|
"string force-new update": {
|
2018-12-10 18:42:45 +01:00
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
2018-12-11 12:49:17 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
2018-12-10 18:42:45 +01:00
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "ami"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-10 18:42:45 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:49:17 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:49:17 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ more_lines = <<~EOT
|
|
|
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ more_lines = <<~EOT
|
|
|
|
original
|
|
|
|
new line
|
|
|
|
EOT
|
|
|
|
}
|
2018-12-11 12: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"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ more_lines = <<~EOT # forces replacement
|
|
|
|
original
|
|
|
|
+ new line
|
|
|
|
EOT
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:49:17 +01:00
|
|
|
|
|
|
|
// Sensitive
|
|
|
|
|
|
|
|
"creation with sensitive field": {
|
|
|
|
Action: plans.Create,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"password": cty.StringVal("top-secret"),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:49:17 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ id = (known after apply)
|
|
|
|
+ password = (sensitive value)
|
2018-12-10 18:42:45 +01:00
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-23 16:32:13 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-23 16:32:13 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "blah" -> (known after apply)
|
|
|
|
password = (sensitive value)
|
|
|
|
~ str = "before" -> "after"
|
|
|
|
}
|
2019-03-06 01:18:55 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// tainted resources
|
|
|
|
"replace tainted resource": {
|
|
|
|
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.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"},
|
|
|
|
}),
|
|
|
|
Tainted: true,
|
|
|
|
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": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
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"},
|
|
|
|
}),
|
|
|
|
Tainted: false,
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
+ forced = "example" # forces replacement
|
|
|
|
name = "name"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force replacement with empty before value legacy": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
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"},
|
|
|
|
}),
|
|
|
|
Tainted: false,
|
|
|
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
bar = "bar"
|
|
|
|
foo = "foo"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
name = "alice"
|
|
|
|
tags = {
|
|
|
|
"name" = "bob"
|
|
|
|
}
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ {
|
2019-01-15 16:37:03 +01:00
|
|
|
aaa = "value"
|
2018-12-11 12:50:08 +01:00
|
|
|
+ bbb = "new_value"
|
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
|
|
|
- ccc = 5 -> null
|
2018-12-11 12:50:08 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-22 17:49:49 +01:00
|
|
|
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": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "new_value"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "json_field"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
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
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example must be replaced
|
2018-12-11 12:55:59 +01:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ {
|
2019-01-15 16:37:03 +01:00
|
|
|
aaa = "value"
|
2018-12-11 12:55:59 +01:00
|
|
|
+ bbb = "new_value"
|
|
|
|
} # forces replacement
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update (whitespace change)": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "another"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa":"value",
|
|
|
|
"bbb":"another"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode( # whitespace changes
|
|
|
|
{
|
|
|
|
aaa = "value"
|
|
|
|
bbb = "another"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whitespace change)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"json_field": cty.StringVal(`{"aaa": "value", "bbb": "another"}`),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"json_field": cty.StringVal(`{"aaa":"value",
|
|
|
|
"bbb":"another"}`),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"json_field": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "json_field"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:29:36 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-15 16:37:03 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ [
|
|
|
|
"first",
|
|
|
|
"second",
|
|
|
|
- "third",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-15 16:37:03 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ json_field = jsonencode(
|
|
|
|
~ [
|
|
|
|
"first",
|
|
|
|
"second",
|
|
|
|
+ "third",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-15 16:37:03 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-23 14:13:48 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:34:41 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ list_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
"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
|
|
|
"bbbb",
|
|
|
|
+ "cccc",
|
|
|
|
"dddd",
|
|
|
|
"eeee",
|
|
|
|
"ffff",
|
2018-12-11 12:50:08 +01:00
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"list_field": {Type: cty.List(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "list_field"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example must be replaced
|
2018-12-11 12:55:59 +01:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [ # forces replacement
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"list_field": cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
- "aaaa",
|
|
|
|
"bbbb",
|
|
|
|
- "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
|
|
|
"dddd",
|
|
|
|
"eeee",
|
2018-12-11 12:50:08 +01:00
|
|
|
]
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:34:41 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
- "cccc",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:34:41 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ list_field = []
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-21 16:02:15 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-21 16:02:15 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
"aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
+ (known after apply),
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-21 16:02:15 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-21 16:02:15 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ list_field = [
|
|
|
|
"aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
+ (known after apply),
|
|
|
|
+ (known after apply),
|
|
|
|
"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
|
|
|
"dddd",
|
|
|
|
"eeee",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
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)
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
~ tuple_field = [
|
|
|
|
"aaaa",
|
|
|
|
"bbbb",
|
|
|
|
- "dddd",
|
|
|
|
+ "cccc",
|
|
|
|
"eeee",
|
|
|
|
"ffff",
|
2019-01-21 16:02:15 +01:00
|
|
|
]
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:34:41 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ set_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
+ "new-element",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "set_field"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example must be replaced
|
2018-12-11 12:55:59 +01:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [ # forces replacement
|
|
|
|
"aaaa",
|
|
|
|
+ "bbbb",
|
|
|
|
"cccc",
|
|
|
|
]
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("aaaa"),
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"set_field": cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("bbbb"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"set_field": {Type: cty.Set(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
"bbbb",
|
|
|
|
- "cccc",
|
|
|
|
]
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:34:41 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:34:41 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:34:41 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ set_field = []
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-18 14:04:53 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-18 14:04:53 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
- "aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
] -> (known after apply)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-18 14:04:53 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-18 14:04:53 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ set_field = [
|
|
|
|
"aaaa",
|
|
|
|
- "bbbb",
|
|
|
|
~ (known after apply),
|
|
|
|
]
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:15:01 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-14 16:15:01 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
+ map_field = {
|
|
|
|
+ "new-key" = "new-element"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
+ "new-key" = "new-element"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
"a" = "aaaa"
|
|
|
|
+ "b" = "bbbb"
|
|
|
|
"c" = "cccc"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update - insertion": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "map_field"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example must be replaced
|
2018-12-11 12:55:59 +01:00
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = { # forces replacement
|
|
|
|
"a" = "aaaa"
|
|
|
|
+ "b" = "bbbb"
|
|
|
|
"c" = "cccc"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("aaaa"),
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
"c": cty.StringVal("cccc"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.UnknownVal(cty.String),
|
|
|
|
"ami": cty.StringVal("ami-STATIC"),
|
|
|
|
"map_field": cty.MapVal(map[string]cty.Value{
|
|
|
|
"b": cty.StringVal("bbbb"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"map_field": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:50:08 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2018-12-11 12:50:08 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
- "a" = "aaaa" -> null
|
|
|
|
"b" = "bbbb"
|
|
|
|
- "c" = "cccc" -> null
|
|
|
|
}
|
|
|
|
}
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 16:15:01 +01:00
|
|
|
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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-21 16:02:51 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
2019-01-21 16:02:51 +01:00
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
ami = "ami-STATIC"
|
|
|
|
~ id = "i-02ae66f368e8518a9" -> (known after apply)
|
|
|
|
~ map_field = {
|
|
|
|
"a" = "aaaa"
|
|
|
|
~ "b" = "bbbb" -> (known after apply)
|
|
|
|
"c" = "cccc"
|
|
|
|
}
|
|
|
|
}
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 13:56:11 +01:00
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 13:56:11 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2018-12-11 13:56:11 +01:00
|
|
|
root_block_device {
|
|
|
|
volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2018-12-11 12:50:08 +01:00
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
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,
|
|
|
|
})),
|
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"),
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2019-01-14 17:07:04 +01:00
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingList,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ 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,
|
|
|
|
})),
|
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"),
|
|
|
|
"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(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
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},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ root_block_device {
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
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},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2018-12-11 12:50:08 +01:00
|
|
|
~ root_block_device {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:55:59 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (inside block)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
cty.IndexStep{Key: cty.NumberIntVal(0)},
|
|
|
|
cty.GetAttrStep{Name: "volume_type"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
~ root_block_device {
|
|
|
|
~ volume_type = "gp2" -> "different" # forces replacement
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
~ root_block_device { # forces replacement
|
|
|
|
~ volume_type = "gp2" -> "different"
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 12:50:08 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - deletion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.ListVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
2019-03-09 21:14:08 +01:00
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
"new_field": cty.String,
|
|
|
|
})),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
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},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingList,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
- root_block_device {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
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(),
|
|
|
|
Tainted: false,
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
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"),
|
|
|
|
"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"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
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},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ root_block_device {
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
"new_field": cty.StringVal("new_value"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
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},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2018-12-12 15:28:12 +01:00
|
|
|
+ root_block_device {
|
|
|
|
+ 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)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
"root_block_device": cty.SetVal([]cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("different"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(cty.Path{
|
|
|
|
cty.GetAttrStep{Name: "root_block_device"},
|
|
|
|
}),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
2018-12-11 12:55:59 +01:00
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:55:59 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2018-12-12 15:28:12 +01:00
|
|
|
+ root_block_device { # forces replacement
|
|
|
|
+ volume_type = "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"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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,
|
|
|
|
})),
|
2018-12-11 12:50:08 +01:00
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted: false,
|
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},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-12-12 15:28:12 +01:00
|
|
|
Nesting: configschema.NestingSet,
|
2018-12-11 12:50:08 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
- root_block_device {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2019-03-09 21:30:23 +01:00
|
|
|
func TestResourceChange_nestedMap(t *testing.T) {
|
|
|
|
testCases := map[string]testCase{
|
|
|
|
"in-place update - creation": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.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"),
|
|
|
|
"root_block_device": cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"volume_type": cty.StringVal("gp2"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
+ 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"),
|
|
|
|
"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"),
|
|
|
|
"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(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "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
|
|
|
~ root_block_device "a" {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
# (1 unchanged attribute hidden)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2019-03-09 21:30:23 +01:00
|
|
|
~ root_block_device "a" {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - insertion": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.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"),
|
|
|
|
"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(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "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
|
|
|
+ root_block_device "b" {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2019-03-09 21:30:23 +01:00
|
|
|
root_block_device "a" {
|
|
|
|
volume_type = "gp2"
|
|
|
|
}
|
|
|
|
+ root_block_device "b" {
|
|
|
|
+ new_field = "new_value"
|
|
|
|
+ volume_type = "gp2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"force-new update (whole block)": {
|
|
|
|
Action: plans.DeleteThenCreate,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-BEFORE"),
|
|
|
|
"root_block_device": cty.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"),
|
|
|
|
"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")},
|
|
|
|
}),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "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
|
|
|
~ root_block_device "a" { # forces replacement
|
|
|
|
~ volume_type = "gp2" -> "different"
|
|
|
|
}
|
|
|
|
# (1 unchanged block hidden)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
VerboseOutput: ` # test_instance.example must be replaced
|
|
|
|
-/+ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
2019-03-09 21:30:23 +01:00
|
|
|
~ root_block_device "a" { # forces replacement
|
|
|
|
~ volume_type = "gp2" -> "different"
|
|
|
|
}
|
|
|
|
root_block_device "b" {
|
|
|
|
volume_type = "standard"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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.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"),
|
|
|
|
"root_block_device": cty.MapValEmpty(cty.Object(map[string]cty.Type{
|
|
|
|
"volume_type": cty.String,
|
|
|
|
"new_field": cty.String,
|
|
|
|
})),
|
|
|
|
}),
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
|
|
"root_block_device": {
|
|
|
|
Block: configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"volume_type": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"new_field": {
|
|
|
|
Type: cty.String,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Nesting: configschema.NestingMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = "ami-BEFORE" -> "ami-AFTER"
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
|
|
|
|
- root_block_device "a" {
|
|
|
|
- new_field = "new_value" -> null
|
|
|
|
- volume_type = "gp2" -> null
|
|
|
|
}
|
|
|
|
}
|
2019-04-27 17:23:37 +02: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(),
|
|
|
|
Tainted: false,
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
}
|
2019-03-09 21:30:23 +01:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
|
|
|
}},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be created
|
|
|
|
+ resource "test_instance" "example" {
|
|
|
|
+ ami = (sensitive)
|
|
|
|
+ id = "i-02ae66f368e8518a9"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
2020-09-24 19:27:15 +02:00
|
|
|
"in-place update - before sensitive, primitive types": {
|
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-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),
|
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"}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
2020-09-24 19:27:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "special"}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "some_number"}},
|
|
|
|
Marks: cty.NewValueMarks("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
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
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},
|
|
|
|
"special": {Type: cty.Bool, Optional: true},
|
|
|
|
"some_number": {Type: cty.Number, 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
|
|
|
},
|
|
|
|
},
|
|
|
|
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
|
|
|
|
# after applying this change
|
2020-09-24 19:27:15 +02:00
|
|
|
~ ami = (sensitive)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
|
|
|
# after applying this change
|
|
|
|
~ some_number = (sensitive)
|
|
|
|
# Warning: this attribute value will no longer be marked as sensitive
|
|
|
|
# after applying this change
|
|
|
|
~ special = (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
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"in-place update - after sensitive, map type": {
|
|
|
|
Action: plans.Update,
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Before: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"tags": cty.MapVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("anna"),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"tags": cty.MapVal(map[string]cty.Value{
|
|
|
|
"name": cty.StringVal("bob"),
|
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{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "tags"}, cty.IndexStep{Key: cty.StringVal("name")}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
|
|
|
}},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"tags": {Type: cty.Map(cty.String), Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
id = "i-02ae66f368e8518a9"
|
2020-09-23 22:28:55 +02:00
|
|
|
~ tags = {
|
|
|
|
# Warning: this attribute value will be marked as sensitive and will
|
|
|
|
# not display in UI output after applying this change
|
|
|
|
~ "name" = (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
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
"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"),
|
|
|
|
}),
|
|
|
|
After: cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("i-02ae66f368e8518a9"),
|
|
|
|
"ami": cty.StringVal("ami-AFTER"),
|
|
|
|
}),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
|
|
|
}},
|
2020-09-10 22:45:31 +02:00
|
|
|
AfterValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
|
|
|
}},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be updated in-place
|
|
|
|
~ resource "test_instance" "example" {
|
|
|
|
~ ami = (sensitive)
|
|
|
|
id = "i-02ae66f368e8518a9"
|
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"),
|
|
|
|
}),
|
|
|
|
After: cty.NullVal(cty.EmptyObject),
|
|
|
|
BeforeValMarks: []cty.PathValueMarks{
|
|
|
|
{
|
|
|
|
Path: cty.Path{cty.GetAttrStep{Name: "ami"}},
|
|
|
|
Marks: cty.NewValueMarks("sensitive"),
|
|
|
|
}},
|
|
|
|
RequiredReplace: cty.NewPathSet(),
|
|
|
|
Tainted: false,
|
|
|
|
Schema: &configschema.Block{
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedOutput: ` # test_instance.example will be destroyed
|
|
|
|
- resource "test_instance" "example" {
|
2020-09-23 22:28:55 +02:00
|
|
|
- ami = (sensitive) -> null
|
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
|
|
|
- id = "i-02ae66f368e8518a9" -> null
|
2020-09-10 22:45:31 +02:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
runTestCases(t, testCases)
|
|
|
|
}
|
|
|
|
|
2018-12-11 12:49:17 +01:00
|
|
|
type testCase struct {
|
|
|
|
Action plans.Action
|
|
|
|
Mode addrs.ResourceMode
|
|
|
|
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
|
2019-03-06 01:18:55 +01:00
|
|
|
Tainted bool
|
2018-12-11 12:49:17 +01:00
|
|
|
ExpectedOutput string
|
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
|
|
|
|
|
|
|
// This field and all associated values can be removed if the concise diff
|
|
|
|
// experiment succeeds.
|
|
|
|
VerboseOutput string
|
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
|
|
|
|
}
|
|
|
|
before, err := plans.NewDynamicValue(beforeVal, ty)
|
2018-12-10 18:42:45 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
after, err := plans.NewDynamicValue(afterVal, ty)
|
2018-12-10 18:42:45 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
change := &plans.ResourceInstanceChangeSrc{
|
|
|
|
Addr: addrs.Resource{
|
|
|
|
Mode: tc.Mode,
|
|
|
|
Type: "test_instance",
|
|
|
|
Name: "example",
|
|
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
2020-02-13 21:32:58 +01:00
|
|
|
ProviderAddr: addrs.AbsProviderConfig{
|
|
|
|
Provider: addrs.NewLegacyProvider("test"),
|
2020-03-11 19:19:52 +01:00
|
|
|
Module: addrs.RootModule,
|
2020-02-13 21:32:58 +01:00
|
|
|
},
|
2018-12-10 18:42:45 +01:00
|
|
|
ChangeSrc: plans.ChangeSrc{
|
2020-09-10 22:45:31 +02:00
|
|
|
Action: tc.Action,
|
|
|
|
Before: before,
|
|
|
|
After: after,
|
|
|
|
BeforeValMarks: tc.BeforeValMarks,
|
|
|
|
AfterValMarks: tc.AfterValMarks,
|
2018-12-10 18:42:45 +01:00
|
|
|
},
|
|
|
|
RequiredReplace: tc.RequiredReplace,
|
|
|
|
}
|
|
|
|
|
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
|
|
|
experiment.SetEnabled(experiment.X_concise_diff, true)
|
2019-03-06 01:18:55 +01:00
|
|
|
output := ResourceChange(change, tc.Tainted, tc.Schema, color)
|
2018-12-10 18:42:45 +01:00
|
|
|
if output != tc.ExpectedOutput {
|
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
|
|
|
t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.ExpectedOutput)
|
|
|
|
t.Errorf("%s", cmp.Diff(output, tc.ExpectedOutput))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Temporary coverage for verbose diff behaviour. All lines below
|
|
|
|
// in this function can be removed if the concise diff experiment
|
|
|
|
// succeeds.
|
|
|
|
if tc.VerboseOutput == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
experiment.SetEnabled(experiment.X_concise_diff, false)
|
|
|
|
output = ResourceChange(change, tc.Tainted, tc.Schema, color)
|
|
|
|
if output != tc.VerboseOutput {
|
|
|
|
t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.VerboseOutput)
|
|
|
|
t.Errorf("%s", cmp.Diff(output, tc.VerboseOutput))
|
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) {
|
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
|
|
|
experiment.SetEnabled(experiment.X_concise_diff, true)
|
2020-09-04 22:03:48 +02:00
|
|
|
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
|
|
|
|
}
|