From f2f35265bc75e2256904cfa63df308f7ee21f49c Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Mon, 11 Feb 2019 13:17:03 -0800 Subject: [PATCH] command/show: json output enhancements (#20291) * command/jsonplan: - add variables to plan output - print known planned values for resources Previously, resource attribute values were only displayed if the values were wholly known. Now we will filter the unknown values out of the change and print the known values. * command/jsonstate: added depends_on and tainted fields * command/show: update tests to reflect added fields --- command/jsonplan/plan.go | 39 ++++++++++++++++++- command/jsonplan/values.go | 3 ++ command/jsonstate/state.go | 19 +++++++++ command/show_test.go | 5 ++- .../show-json/basic-create/output.json | 20 ++++++++-- .../show-json/basic-delete/output.json | 5 +++ .../show-json/basic-update/output.json | 5 +++ 7 files changed, 91 insertions(+), 5 deletions(-) diff --git a/command/jsonplan/plan.go b/command/jsonplan/plan.go index 4711707e2..11ceb44a3 100644 --- a/command/jsonplan/plan.go +++ b/command/jsonplan/plan.go @@ -29,6 +29,7 @@ const FormatVersion = "0.1" type plan struct { FormatVersion string `json:"format_version,omitempty"` TerraformVersion string `json:"terraform_version,omitempty"` + Variables variables `json:"variables,omitempty"` PlannedValues stateValues `json:"planned_values,omitempty"` // ResourceChanges are sorted in a user-friendly order that is undefined at // this time, but consistent. @@ -76,6 +77,14 @@ type output struct { Value json.RawMessage `json:"value,omitempty"` } +// variables is the JSON representation of the variables provided to the current +// plan. +type variables map[string]*variable + +type variable struct { + Value json.RawMessage `json:"value,omitempty"` +} + // Marshal returns the json encoding of a terraform plan. func Marshal( config *configs.Config, @@ -87,8 +96,13 @@ func Marshal( output := newPlan() output.TerraformVersion = version.String() + err := output.marshalPlanVariables(p.VariableValues, schemas) + if err != nil { + return nil, fmt.Errorf("error in marshalPlanVariables: %s", err) + } + // output.PlannedValues - err := output.marshalPlannedValues(p.Changes, schemas) + err = output.marshalPlannedValues(p.Changes, schemas) if err != nil { return nil, fmt.Errorf("error in marshalPlannedValues: %s", err) } @@ -122,6 +136,29 @@ func Marshal( return ret, err } +func (p *plan) marshalPlanVariables(vars map[string]plans.DynamicValue, schemas *terraform.Schemas) error { + if len(vars) == 0 { + return nil + } + + p.Variables = make(variables, len(vars)) + + for k, v := range vars { + val, err := v.Decode(cty.DynamicPseudoType) + if err != nil { + return err + } + valJSON, err := ctyjson.Marshal(val, val.Type()) + if err != nil { + return err + } + p.Variables[k] = &variable{ + Value: valJSON, + } + } + return nil +} + func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform.Schemas) error { if changes == nil { // Nothing to do! diff --git a/command/jsonplan/values.go b/command/jsonplan/values.go index 2df050197..7ec437b24 100644 --- a/command/jsonplan/values.go +++ b/command/jsonplan/values.go @@ -166,6 +166,9 @@ func marshalPlanResources(changes *plans.Changes, ris []addrs.AbsResourceInstanc if changeV.After != cty.NilVal { if changeV.After.IsWhollyKnown() { resource.AttributeValues = marshalAttributeValues(changeV.After, schema) + } else { + knowns := omitUnknowns(changeV.After) + resource.AttributeValues = marshalAttributeValues(knowns, schema) } } diff --git a/command/jsonstate/state.go b/command/jsonstate/state.go index c491dc8da..0d1056c85 100644 --- a/command/jsonstate/state.go +++ b/command/jsonstate/state.go @@ -84,6 +84,13 @@ type resource struct { // unknown values are omitted or set to null, making them indistinguishable // from absent values. AttributeValues attributeValues `json:"values,omitempty"` + + // DependsOn contains a list of the resource's dependencies. The entries are + // addresses relative to the containing module. + DependsOn []string `json:"depends_on,omitempty"` + + // Tainted is true if the resource is tainted in terraform state. + Tainted bool `json:"tainted,omitempty"` } // attributeValues is the JSON representation of the attribute values of the @@ -276,6 +283,18 @@ func marshalResources(resources map[string]*states.Resource, schemas *terraform. resource.AttributeValues = marshalAttributeValues(riObj.Value, schema) + if len(riObj.Dependencies) > 0 { + dependencies := make([]string, len(riObj.Dependencies)) + for i, v := range riObj.Dependencies { + dependencies[i] = v.String() + } + resource.DependsOn = dependencies + } + + if riObj.Status == states.ObjectTainted { + resource.Tainted = true + } + ret = append(ret, resource) } diff --git a/command/show_test.go b/command/show_test.go index 5febe8578..2716d1935 100644 --- a/command/show_test.go +++ b/command/show_test.go @@ -312,9 +312,12 @@ func showFixturePlanFile(t *testing.T) string { } // this simplified plan struct allows us to preserve field order when marshaling -// the command output. +// the command output. NOTE: we are leaving "terraform_version" out of this test +// to avoid needing to constantly update the expected output; as a potential +// TODO we could write a jsonplan compare function. type plan struct { FormatVersion string `json:"format_version,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty"` PlannedValues map[string]interface{} `json:"planned_values,omitempty"` ResourceChanges []interface{} `json:"resource_changes,omitempty"` OutputChanges map[string]interface{} `json:"output_changes,omitempty"` diff --git a/command/test-fixtures/show-json/basic-create/output.json b/command/test-fixtures/show-json/basic-create/output.json index 8db83be19..d976addd1 100644 --- a/command/test-fixtures/show-json/basic-create/output.json +++ b/command/test-fixtures/show-json/basic-create/output.json @@ -1,5 +1,10 @@ { "format_version": "0.1", + "variables": { + "test_var": { + "value": "bar" + } + }, "planned_values": { "outputs": { "test": { @@ -16,7 +21,10 @@ "type": "test_instance", "name": "test", "provider_name": "test", - "schema_version": 0 + "schema_version": 0, + "values": { + "ami": "bar" + } }, { "address": "test_instance.test[1]", @@ -25,7 +33,10 @@ "type": "test_instance", "name": "test", "provider_name": "test", - "schema_version": 0 + "schema_version": 0, + "values": { + "ami": "bar" + } }, { "address": "test_instance.test[2]", @@ -34,7 +45,10 @@ "type": "test_instance", "name": "test", "provider_name": "test", - "schema_version": 0 + "schema_version": 0, + "values": { + "ami": "bar" + } } ] } diff --git a/command/test-fixtures/show-json/basic-delete/output.json b/command/test-fixtures/show-json/basic-delete/output.json index 837ed5d01..1b42c9e87 100644 --- a/command/test-fixtures/show-json/basic-delete/output.json +++ b/command/test-fixtures/show-json/basic-delete/output.json @@ -1,5 +1,10 @@ { "format_version": "0.1", + "variables": { + "test_var": { + "value": "bar" + } + }, "planned_values": { "outputs": { "test": { diff --git a/command/test-fixtures/show-json/basic-update/output.json b/command/test-fixtures/show-json/basic-update/output.json index 4f5f115fb..59eadb986 100644 --- a/command/test-fixtures/show-json/basic-update/output.json +++ b/command/test-fixtures/show-json/basic-update/output.json @@ -1,5 +1,10 @@ { "format_version": "0.1", + "variables": { + "test_var": { + "value": "bar" + } + }, "planned_values": { "outputs": { "test": {