mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-25 08:21:07 -06:00
json-output: Previous address for resource changes
Configuration-driven moves are represented in the plan file by setting the resource's `PrevRunAddr` to a different value than its `Addr`. For JSON plan output, we here add a new field to resource changes, `previous_address`, which is present and non-empty only if the resource is planned to be moved. Like the CLI UI, refresh-only plans will include move-only changes in the resource drift JSON output. In normal plan mode, these are elided to avoid redundancy with planned changes.
This commit is contained in:
parent
78705f4f10
commit
78c4a8c461
@ -130,9 +130,25 @@ func Marshal(
|
||||
}
|
||||
|
||||
// output.ResourceDrift
|
||||
output.ResourceDrift, err = output.marshalResourceChanges(p.DriftedResources, schemas)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in marshaling resource drift: %s", err)
|
||||
if len(p.DriftedResources) > 0 {
|
||||
// In refresh-only mode, we render all resources marked as drifted,
|
||||
// including those which have moved without other changes. In other plan
|
||||
// modes, move-only changes will be included in the planned changes, so
|
||||
// we skip them here.
|
||||
var driftedResources []*plans.ResourceInstanceChangeSrc
|
||||
if p.UIMode == plans.RefreshOnlyMode {
|
||||
driftedResources = p.DriftedResources
|
||||
} else {
|
||||
for _, dr := range p.DriftedResources {
|
||||
if dr.Action != plans.NoOp {
|
||||
driftedResources = append(driftedResources, dr)
|
||||
}
|
||||
}
|
||||
}
|
||||
output.ResourceDrift, err = output.marshalResourceChanges(driftedResources, schemas)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in marshaling resource drift: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// output.ResourceChanges
|
||||
@ -197,6 +213,9 @@ func (p *plan) marshalResourceChanges(resources []*plans.ResourceInstanceChangeS
|
||||
var r resourceChange
|
||||
addr := rc.Addr
|
||||
r.Address = addr.String()
|
||||
if !addr.Equal(rc.PrevRunAddr) {
|
||||
r.PreviousAddress = rc.PrevRunAddr.String()
|
||||
}
|
||||
|
||||
dataSource := addr.Resource.Resource.Mode == addrs.DataResourceMode
|
||||
// We create "delete" actions for data resources so we can clean up
|
||||
|
@ -48,6 +48,18 @@ type resourceChange struct {
|
||||
// Address is the absolute resource address
|
||||
Address string `json:"address,omitempty"`
|
||||
|
||||
// PreviousAddress is the absolute address that this resource instance had
|
||||
// at the conclusion of a previous run.
|
||||
//
|
||||
// This will typically be omitted, but will be present if the previous
|
||||
// resource instance was subject to a "moved" block that we handled in the
|
||||
// process of creating this plan.
|
||||
//
|
||||
// Note that this behavior diverges from the internal plan data structure,
|
||||
// where the previous address is set equal to the current address in the
|
||||
// common case, rather than being omitted.
|
||||
PreviousAddress string `json:"previous_address,omitempty"`
|
||||
|
||||
// ModuleAddress is the module portion of the above address. Omitted if the
|
||||
// instance is in the root module.
|
||||
ModuleAddress string `json:"module_address,omitempty"`
|
||||
|
22
internal/command/testdata/show-json/moved-drift/main.tf
vendored
Normal file
22
internal/command/testdata/show-json/moved-drift/main.tf
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# In state with `ami = "foo"`, so this should be a regular update. The provider
|
||||
# should not detect changes on refresh.
|
||||
resource "test_instance" "no_refresh" {
|
||||
ami = "bar"
|
||||
}
|
||||
|
||||
# In state with `ami = "refresh-me"`, but the provider will return
|
||||
# `"refreshed"` after the refresh phase. The plan should show the drift
|
||||
# (`"refresh-me"` to `"refreshed"`) and plan the update (`"refreshed"` to
|
||||
# `"baz"`).
|
||||
resource "test_instance" "should_refresh_with_move" {
|
||||
ami = "baz"
|
||||
}
|
||||
|
||||
terraform {
|
||||
experiments = [ config_driven_move ]
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test_instance.should_refresh
|
||||
to = test_instance.should_refresh_with_move
|
||||
}
|
177
internal/command/testdata/show-json/moved-drift/output.json
vendored
Normal file
177
internal/command/testdata/show-json/moved-drift/output.json
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
{
|
||||
"format_version": "0.2",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "test_instance.no_refresh",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "no_refresh",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.should_refresh_with_move",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "should_refresh_with_move",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"resource_drift": [
|
||||
{
|
||||
"address": "test_instance.should_refresh_with_move",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"previous_address": "test_instance.should_refresh",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "should_refresh_with_move",
|
||||
"change": {
|
||||
"actions": [
|
||||
"update"
|
||||
],
|
||||
"before": {
|
||||
"ami": "refresh-me",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after": {
|
||||
"ami": "refreshed",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after_sensitive": {},
|
||||
"after_unknown": {},
|
||||
"before_sensitive": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"resource_changes": [
|
||||
{
|
||||
"address": "test_instance.no_refresh",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "no_refresh",
|
||||
"change": {
|
||||
"actions": [
|
||||
"update"
|
||||
],
|
||||
"before": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after_unknown": {},
|
||||
"after_sensitive": {},
|
||||
"before_sensitive": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.should_refresh_with_move",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"previous_address": "test_instance.should_refresh",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "should_refresh_with_move",
|
||||
"change": {
|
||||
"actions": [
|
||||
"update"
|
||||
],
|
||||
"before": {
|
||||
"ami": "refreshed",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after": {
|
||||
"ami": "baz",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after_unknown": {},
|
||||
"after_sensitive": {},
|
||||
"before_sensitive": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"prior_state": {
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "test_instance.no_refresh",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "no_refresh",
|
||||
"schema_version": 0,
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"values": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.should_refresh_with_move",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "should_refresh_with_move",
|
||||
"schema_version": 0,
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"values": {
|
||||
"ami": "refreshed",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"configuration": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "test_instance.no_refresh",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "no_refresh",
|
||||
"provider_config_key": "test",
|
||||
"schema_version": 0,
|
||||
"expressions": {
|
||||
"ami": {
|
||||
"constant_value": "bar"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.should_refresh_with_move",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "should_refresh_with_move",
|
||||
"provider_config_key": "test",
|
||||
"schema_version": 0,
|
||||
"expressions": {
|
||||
"ami": {
|
||||
"constant_value": "baz"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
38
internal/command/testdata/show-json/moved-drift/terraform.tfstate
vendored
Normal file
38
internal/command/testdata/show-json/moved-drift/terraform.tfstate
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 4,
|
||||
"terraform_version": "0.12.0",
|
||||
"serial": 7,
|
||||
"lineage": "configuredUnchanged",
|
||||
"resources": [
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "no_refresh",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "should_refresh",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"ami": "refresh-me",
|
||||
"id": "placeholder"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
12
internal/command/testdata/show-json/moved/main.tf
vendored
Normal file
12
internal/command/testdata/show-json/moved/main.tf
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
resource "test_instance" "baz" {
|
||||
ami = "baz"
|
||||
}
|
||||
|
||||
terraform {
|
||||
experiments = [ config_driven_move ]
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test_instance.foo
|
||||
to = test_instance.baz
|
||||
}
|
89
internal/command/testdata/show-json/moved/output.json
vendored
Normal file
89
internal/command/testdata/show-json/moved/output.json
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"format_version": "0.2",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "test_instance.baz",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "baz",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"resource_changes": [
|
||||
{
|
||||
"address": "test_instance.baz",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"previous_address": "test_instance.foo",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "baz",
|
||||
"change": {
|
||||
"actions": [
|
||||
"update"
|
||||
],
|
||||
"before": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after": {
|
||||
"ami": "baz",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after_unknown": {},
|
||||
"after_sensitive": {},
|
||||
"before_sensitive": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"prior_state": {
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "test_instance.baz",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "baz",
|
||||
"schema_version": 0,
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"values": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"configuration": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "test_instance.baz",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "baz",
|
||||
"provider_config_key": "test",
|
||||
"schema_version": 0,
|
||||
"expressions": {
|
||||
"ami": {
|
||||
"constant_value": "baz"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
23
internal/command/testdata/show-json/moved/terraform.tfstate
vendored
Normal file
23
internal/command/testdata/show-json/moved/terraform.tfstate
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": 4,
|
||||
"terraform_version": "0.12.0",
|
||||
"serial": 7,
|
||||
"lineage": "configuredUnchanged",
|
||||
"resources": [
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "foo",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -45,32 +45,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"resource_drift": [
|
||||
{
|
||||
"address": "test_instance.test[0]",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"change": {
|
||||
"actions": [
|
||||
"no-op"
|
||||
],
|
||||
"before": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"after": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
},
|
||||
"before_sensitive": {},
|
||||
"after_sensitive": {},
|
||||
"after_unknown": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"resource_changes": [
|
||||
{
|
||||
"address": "test_instance.test[0]",
|
||||
@ -78,6 +52,7 @@
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"previous_address": "test_instance.test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -98,9 +98,15 @@ For ease of consumption by callers, the plan representation includes a partial r
|
||||
{
|
||||
// "address" is the full absolute address of the resource instance this
|
||||
// change applies to, in the same format as addresses in a value
|
||||
// representation
|
||||
// representation.
|
||||
"address": "module.child.aws_instance.foo[0]",
|
||||
|
||||
// "previous_address" is the full absolute address of this resource
|
||||
// instance as it was known after the previous Terraform run.
|
||||
// Included only if the address has changed, e.g. by handling
|
||||
// a "moved" block in the configuration.
|
||||
"previous_address": "module.instances.aws_instance.foo[0]",
|
||||
|
||||
// "module_address", if set, is the module portion of the above address.
|
||||
// Omitted if the instance is in the root module.
|
||||
"module_address": "module.child",
|
||||
|
Loading…
Reference in New Issue
Block a user