command/jsonplan: sort resources by address (#20113)

* command/jsonplan: sort resources by address
* command/show: extend test case to include resources with count
* command/json*: document resource ordering as consistent but undefined
This commit is contained in:
Kristin Laemmert 2019-01-25 09:17:40 -08:00 committed by GitHub
parent fb0c9714c1
commit 6e057c529e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 13 deletions

View File

@ -5,11 +5,12 @@ import (
"fmt" "fmt"
"sort" "sort"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/zclconf/go-cty/cty"
) )
// Config represents the complete configuration source // Config represents the complete configuration source
@ -30,9 +31,11 @@ type providerConfig struct {
} }
type module struct { type module struct {
Outputs map[string]configOutput `json:"outputs,omitempty"` Outputs map[string]configOutput `json:"outputs,omitempty"`
Resources []resource `json:"resources,omitempty"` // Resources are sorted in a user-friendly order that is undefined at this
ModuleCalls []moduleCall `json:"module_calls,omitempty"` // time, but consistent.
Resources []resource `json:"resources,omitempty"`
ModuleCalls []moduleCall `json:"module_calls,omitempty"`
} }
type moduleCall struct { type moduleCall struct {

View File

@ -3,6 +3,8 @@ package jsonplan
// module is the representation of a module in state. This can be the root // module is the representation of a module in state. This can be the root
// module or a child module. // module or a child module.
type module struct { type module struct {
// Resources are sorted in a user-friendly order that is undefined at this
// time, but consistent.
Resources []resource `json:"resources,omitempty"` Resources []resource `json:"resources,omitempty"`
// Address is the absolute module address, omitted for the root module // Address is the absolute module address, omitted for the root module

View File

@ -6,6 +6,7 @@ import (
"sort" "sort"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/jsonconfig" "github.com/hashicorp/terraform/command/jsonconfig"
@ -15,8 +16,6 @@ import (
"github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statefile"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
ctyjson "github.com/zclconf/go-cty/cty/json"
) )
// FormatVersion represents the version of the json format and will be // FormatVersion represents the version of the json format and will be
@ -27,8 +26,10 @@ const FormatVersion = "0.1"
// Plan is the top-level representation of the json format of a plan. It includes // Plan is the top-level representation of the json format of a plan. It includes
// the complete config and current state. // the complete config and current state.
type plan struct { type plan struct {
FormatVersion string `json:"format_version,omitempty"` FormatVersion string `json:"format_version,omitempty"`
PlannedValues stateValues `json:"planned_values,omitempty"` PlannedValues stateValues `json:"planned_values,omitempty"`
// ResourceChanges are sorted in a user-friendly order that is undefined at
// this time, but consistent.
ResourceChanges []resourceChange `json:"resource_changes,omitempty"` ResourceChanges []resourceChange `json:"resource_changes,omitempty"`
OutputChanges map[string]change `json:"output_changes,omitempty"` OutputChanges map[string]change `json:"output_changes,omitempty"`
PriorState json.RawMessage `json:"prior_state,omitempty"` PriorState json.RawMessage `json:"prior_state,omitempty"`

View File

@ -3,14 +3,15 @@ package jsonplan
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
ctyjson "github.com/zclconf/go-cty/cty/json"
) )
// stateValues is the common representation of resolved values for both the // stateValues is the common representation of resolved values for both the
@ -171,6 +172,10 @@ func marshalPlanResources(changes *plans.Changes, ris []addrs.AbsResourceInstanc
ret = append(ret, resource) ret = append(ret, resource)
} }
sort.Slice(ret, func(i, j int) bool {
return ret[i].Address < ret[j].Address
})
return ret, nil return ret, nil
} }

View File

@ -43,6 +43,8 @@ type output struct {
// module is the representation of a module in state. This can be the root module // module is the representation of a module in state. This can be the root module
// or a child module // or a child module
type module struct { type module struct {
// Resources are sorted in a user-friendly order that is undefined at this
// time, but consistent.
Resources []resource `json:"resources,omitempty"` Resources []resource `json:"resources,omitempty"`
// Address is the absolute module address, omitted for the root module // Address is the absolute module address, omitted for the root module

View File

@ -2,7 +2,8 @@ variable "test_var" {
default = "bar" default = "bar"
} }
resource "test_instance" "test" { resource "test_instance" "test" {
ami = var.test_var ami = var.test_var
count = 3
} }
output "test" { output "test" {

View File

@ -10,7 +10,26 @@
"root_module": { "root_module": {
"resources": [ "resources": [
{ {
"address": "test_instance.test", "address": "test_instance.test[0]",
"index": 0,
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "test",
"schema_version": 0
},
{
"address": "test_instance.test[1]",
"index": 1,
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "test",
"schema_version": 0
},
{
"address": "test_instance.test[2]",
"index": 2,
"mode": "managed", "mode": "managed",
"type": "test_instance", "type": "test_instance",
"name": "test", "name": "test",
@ -22,7 +41,50 @@
}, },
"resource_changes": [ "resource_changes": [
{ {
"address": "test_instance.test", "address": "test_instance.test[0]",
"index": 0,
"mode": "managed",
"type": "test_instance",
"name": "test",
"deposed": true,
"change": {
"actions": [
"create"
],
"before": null,
"after_unknown": {
"ami": false,
"id": true
},
"after": {
"ami": "bar"
}
}
},
{
"address": "test_instance.test[1]",
"index": 1,
"mode": "managed",
"type": "test_instance",
"name": "test",
"deposed": true,
"change": {
"actions": [
"create"
],
"before": null,
"after_unknown": {
"ami": false,
"id": true
},
"after": {
"ami": "bar"
}
}
},
{
"address": "test_instance.test[2]",
"index": 2,
"mode": "managed", "mode": "managed",
"type": "test_instance", "type": "test_instance",
"name": "test", "name": "test",
@ -71,7 +133,9 @@
"name": "test", "name": "test",
"provider_config_key": "provider.test", "provider_config_key": "provider.test",
"schema_version": 0, "schema_version": 0,
"count_expression": {}, "count_expression": {
"constant_value": 3
},
"for_each_expression": {} "for_each_expression": {}
} }
] ]