From df485e3e39dc9f60faa07d6d88fe24efc31c2ad9 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 8 Feb 2022 10:58:10 -0500 Subject: [PATCH] add relevant_attributes to the json plan format Add the resource instances and individual attributes which may have contributed to the planned changes to the json format of the plan. We use the existing path encoding for individual attributes, which is already used in the replace_paths change field. --- internal/command/jsonplan/plan.go | 79 +++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/internal/command/jsonplan/plan.go b/internal/command/jsonplan/plan.go index 06ed97961012..b86d74900d46 100644 --- a/internal/command/jsonplan/plan.go +++ b/internal/command/jsonplan/plan.go @@ -33,11 +33,12 @@ type plan struct { PlannedValues stateValues `json:"planned_values,omitempty"` // ResourceDrift and ResourceChanges are sorted in a user-friendly order // that is undefined at this time, but consistent. - ResourceDrift []resourceChange `json:"resource_drift,omitempty"` - ResourceChanges []resourceChange `json:"resource_changes,omitempty"` - OutputChanges map[string]change `json:"output_changes,omitempty"` - PriorState json.RawMessage `json:"prior_state,omitempty"` - Config json.RawMessage `json:"configuration,omitempty"` + ResourceDrift []resourceChange `json:"resource_drift,omitempty"` + ResourceChanges []resourceChange `json:"resource_changes,omitempty"` + OutputChanges map[string]change `json:"output_changes,omitempty"` + PriorState json.RawMessage `json:"prior_state,omitempty"` + Config json.RawMessage `json:"configuration,omitempty"` + RelevantAttributes []resourceAttr `json:"relevant_attributes,omitempty"` } func newPlan() *plan { @@ -46,6 +47,13 @@ func newPlan() *plan { } } +// resourceAttr contains the address and attribute of an external for the +// RelevantAttributes in the plan. +type resourceAttr struct { + Resource string `json:"resource"` + Attr json.RawMessage `json:"attribute"` +} + // Change is the representation of a proposed change for an object. type change struct { // Actions are the actions that will be taken on the object selected by the @@ -151,6 +159,10 @@ func Marshal( } } + if err := output.marshalRelevantAttrs(p); err != nil { + return nil, fmt.Errorf("error marshaling relevant attributes for external changes: %s", err) + } + // output.ResourceChanges if p.Changes != nil { output.ResourceChanges, err = output.marshalResourceChanges(p.Changes.Resources, schemas) @@ -482,6 +494,19 @@ func (p *plan) marshalPlannedValues(changes *plans.Changes, schemas *terraform.S return nil } +func (p *plan) marshalRelevantAttrs(plan *plans.Plan) error { + for _, ra := range plan.RelevantAttributes { + addr := ra.Resource.String() + path, err := encodePath(ra.Attr) + if err != nil { + return err + } + + p.RelevantAttributes = append(p.RelevantAttributes, resourceAttr{addr, path}) + } + return nil +} + // omitUnknowns recursively walks the src cty.Value and returns a new cty.Value, // omitting any unknowns. // @@ -655,26 +680,7 @@ func encodePaths(pathSet cty.PathSet) (json.RawMessage, error) { jsonPaths := make([]json.RawMessage, 0, len(pathList)) for _, path := range pathList { - steps := make([]json.RawMessage, 0, len(path)) - for _, step := range path { - switch s := step.(type) { - case cty.IndexStep: - key, err := ctyjson.Marshal(s.Key, s.Key.Type()) - if err != nil { - return nil, fmt.Errorf("Failed to marshal index step key %#v: %s", s.Key, err) - } - steps = append(steps, key) - case cty.GetAttrStep: - name, err := json.Marshal(s.Name) - if err != nil { - return nil, fmt.Errorf("Failed to marshal get attr step name %#v: %s", s.Name, err) - } - steps = append(steps, name) - default: - return nil, fmt.Errorf("Unsupported path step %#v (%t)", step, step) - } - } - jsonPath, err := json.Marshal(steps) + jsonPath, err := encodePath(path) if err != nil { return nil, err } @@ -683,3 +689,26 @@ func encodePaths(pathSet cty.PathSet) (json.RawMessage, error) { return json.Marshal(jsonPaths) } + +func encodePath(path cty.Path) (json.RawMessage, error) { + steps := make([]json.RawMessage, 0, len(path)) + for _, step := range path { + switch s := step.(type) { + case cty.IndexStep: + key, err := ctyjson.Marshal(s.Key, s.Key.Type()) + if err != nil { + return nil, fmt.Errorf("Failed to marshal index step key %#v: %s", s.Key, err) + } + steps = append(steps, key) + case cty.GetAttrStep: + name, err := json.Marshal(s.Name) + if err != nil { + return nil, fmt.Errorf("Failed to marshal get attr step name %#v: %s", s.Name, err) + } + steps = append(steps, name) + default: + return nil, fmt.Errorf("Unsupported path step %#v (%t)", step, step) + } + } + return json.Marshal(steps) +}