Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions internal/command/jsonformat/collections/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

type ProcessKey func(key string) computed.Diff

func TransformMap[Input any](before, after map[string]Input, process ProcessKey) (map[string]computed.Diff, plans.Action) {
func TransformMap[Input any](before, after map[string]Input, keys []string, process ProcessKey) (map[string]computed.Diff, plans.Action) {
current := plans.NoOp
if before != nil && after == nil {
current = plans.Delete
Expand All @@ -17,16 +17,7 @@ func TransformMap[Input any](before, after map[string]Input, process ProcessKey)
}

elements := make(map[string]computed.Diff)
for key := range before {
elements[key] = process(key)
current = CompareActions(current, elements[key].Action)
}

for key := range after {
if _, ok := elements[key]; ok {
// Then we've already processed this key in the before.
continue
}
for _, key := range keys {
elements[key] = process(key)
current = CompareActions(current, elements[key].Action)
}
Expand Down
6 changes: 6 additions & 0 deletions internal/command/jsonformat/computed/renderers/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ func RendererJsonOpts() jsondiff.JsonOpts {
Array: func(elements []computed.Diff, action plans.Action) computed.Diff {
return computed.NewDiff(List(elements), action, false)
},
Unknown: func(diff computed.Diff, action plans.Action) computed.Diff {
return computed.NewDiff(Unknown(diff), action, false)
},
Sensitive: func(diff computed.Diff, beforeSensitive bool, afterSensitive bool, action plans.Action) computed.Diff {
return computed.NewDiff(Sensitive(diff, beforeSensitive, afterSensitive), action, false)
},
TypeChange: func(before, after computed.Diff, action plans.Action) computed.Diff {
return computed.NewDiff(TypeChange(before, after), action, false)
},
Expand Down
13 changes: 12 additions & 1 deletion internal/command/jsonformat/computed/renderers/primitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
"github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path"
"github.com/hashicorp/terraform/internal/plans"
)
Expand Down Expand Up @@ -183,7 +184,17 @@ func (renderer primitiveRenderer) renderStringDiff(diff computed.Diff, indent in
}

func (renderer primitiveRenderer) renderStringDiffAsJson(diff computed.Diff, indent int, opts computed.RenderHumanOpts, before evaluatedString, after evaluatedString) string {
jsonDiff := RendererJsonOpts().Transform(before.Json, after.Json, diff.Action != plans.Create, diff.Action != plans.Delete, attribute_path.AlwaysMatcher())
jsonDiff := RendererJsonOpts().Transform(structured.Change{
BeforeExplicit: diff.Action != plans.Create,
AfterExplicit: diff.Action != plans.Delete,
Before: before.Json,
After: after.Json,
Unknown: false,
BeforeSensitive: false,
AfterSensitive: false,
ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
})

action := diff.Action

Expand Down
105 changes: 105 additions & 0 deletions internal/command/jsonformat/differ/differ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,111 @@ func TestRelevantAttributes(t *testing.T) {
}
}

func TestDynamicPseudoType(t *testing.T) {
tcs := map[string]struct {
input structured.Change
validate renderers.ValidateDiffFunction
}{
"after_sensitive_in_dynamic_type": {
input: structured.Change{
Before: nil,
After: map[string]interface{}{
"key": "value",
},
Unknown: false,
BeforeSensitive: false,
AfterSensitive: map[string]interface{}{
"key": true,
},
ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
},
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
"key": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "value", plans.Create, false), false, true, plans.Create, false),
}, plans.Create, false),
},
"before_sensitive_in_dynamic_type": {
input: structured.Change{
Before: map[string]interface{}{
"key": "value",
},
After: nil,
Unknown: false,
BeforeSensitive: map[string]interface{}{
"key": true,
},
AfterSensitive: false,
ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
},
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
"key": renderers.ValidateSensitive(renderers.ValidatePrimitive("value", nil, plans.Delete, false), true, false, plans.Delete, false),
}, plans.Delete, false),
},
"sensitive_in_dynamic_type": {
input: structured.Change{
Before: map[string]interface{}{
"key": "before",
},
After: map[string]interface{}{
"key": "after",
},
Unknown: false,
BeforeSensitive: map[string]interface{}{
"key": true,
},
AfterSensitive: map[string]interface{}{
"key": true,
},
ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
},
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
"key": renderers.ValidateSensitive(renderers.ValidatePrimitive("before", "after", plans.Update, false), true, true, plans.Update, false),
}, plans.Update, false),
},
"create_unknown_in_dynamic_type": {
input: structured.Change{
Before: nil,
After: map[string]interface{}{},
Unknown: map[string]interface{}{
"key": true,
},
BeforeSensitive: false,
AfterSensitive: false,
ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
},
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
"key": renderers.ValidateUnknown(nil, plans.Create, false),
}, plans.Create, false),
},
"update_unknown_in_dynamic_type": {
input: structured.Change{
Before: map[string]interface{}{
"key": "before",
},
After: map[string]interface{}{},
Unknown: map[string]interface{}{
"key": true,
},
BeforeSensitive: false,
AfterSensitive: false,
ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
},
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
"key": renderers.ValidateUnknown(renderers.ValidatePrimitive("before", nil, plans.Delete, false), plans.Update, false),
}, plans.Update, false),
},
}
for key, tc := range tcs {
t.Run(key, func(t *testing.T) {
tc.validate(t, ComputeDiffForType(tc.input, cty.DynamicPseudoType))
})
}
}

func TestSpecificCases(t *testing.T) {
// This is a special test that can contain any combination of individual
// cases and will execute against them. For testing/fixing specific issues
Expand Down
38 changes: 3 additions & 35 deletions internal/command/jsonformat/differ/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,7 @@ import (

func computeAttributeDiffAsMap(change structured.Change, elementType cty.Type) computed.Diff {
mapValue := change.AsMap()

// The jsonplan package will have stripped out unknowns from our after value
// so we're going to add them back in here.
//
// This only affects attributes and not nested attributes or blocks, so we
// only perform this fix in this function and not the equivalent map
// functions for nested attributes and blocks.

// There is actually a difference between a null map and an empty map for
// purposes of calculating a delete, create, or update operation.

var after map[string]interface{}
if mapValue.After != nil {
after = make(map[string]interface{})
}

for key, value := range mapValue.After {
after[key] = value
}
for key := range mapValue.Unknown {
if _, ok := after[key]; ok {
// Then this unknown value was in after, this probably means it has
// a child that is unknown rather than being unknown itself. As
// such, we'll skip over it. Note, it doesn't particularly matter if
// an element is in both places - it's just important we actually
// do cover all the elements. We want a complete union and therefore
// duplicates are no cause for concern as long as we dedupe here.
continue
}
after[key] = nil
}

elements, current := collections.TransformMap(mapValue.Before, after, func(key string) computed.Diff {
elements, current := collections.TransformMap(mapValue.Before, mapValue.After, mapValue.AllKeys(), func(key string) computed.Diff {
value := mapValue.GetChild(key)
if !value.RelevantAttributes.MatchesPartial() {
// Mark non-relevant attributes as unchanged.
Expand All @@ -58,7 +26,7 @@ func computeAttributeDiffAsMap(change structured.Change, elementType cty.Type) c

func computeAttributeDiffAsNestedMap(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff {
mapValue := change.AsMap()
elements, current := collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff {
elements, current := collections.TransformMap(mapValue.Before, mapValue.After, mapValue.ExplicitKeys(), func(key string) computed.Diff {
value := mapValue.GetChild(key)
if !value.RelevantAttributes.MatchesPartial() {
// Mark non-relevant attributes as unchanged.
Expand All @@ -74,7 +42,7 @@ func computeAttributeDiffAsNestedMap(change structured.Change, attributes map[st

func computeBlockDiffsAsMap(change structured.Change, block *jsonprovider.Block) (map[string]computed.Diff, plans.Action) {
mapValue := change.AsMap()
return collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff {
return collections.TransformMap(mapValue.Before, mapValue.After, mapValue.ExplicitKeys(), func(key string) computed.Diff {
value := mapValue.GetChild(key)
if !value.RelevantAttributes.MatchesPartial() {
// Mark non-relevant attributes as unchanged.
Expand Down
2 changes: 1 addition & 1 deletion internal/command/jsonformat/differ/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ func ComputeDiffForOutput(change structured.Change) computed.Diff {
}

jsonOpts := renderers.RendererJsonOpts()
return jsonOpts.Transform(change.Before, change.After, change.BeforeExplicit, change.AfterExplicit, change.RelevantAttributes)
return jsonOpts.Transform(change)
}
77 changes: 21 additions & 56 deletions internal/command/jsonformat/differ/sensitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,66 +13,31 @@ import (
type CreateSensitiveRenderer func(computed.Diff, bool, bool) computed.DiffRenderer

func checkForSensitiveType(change structured.Change, ctype cty.Type) (computed.Diff, bool) {
return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff {
return ComputeDiffForType(value, ctype)
})
return change.CheckForSensitive(
func(value structured.Change) computed.Diff {
return ComputeDiffForType(value, ctype)
}, func(inner computed.Diff, beforeSensitive, afterSensitive bool, action plans.Action) computed.Diff {
return computed.NewDiff(renderers.Sensitive(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches())
},
)
}

func checkForSensitiveNestedAttribute(change structured.Change, attribute *jsonprovider.NestedType) (computed.Diff, bool) {
return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff {
return computeDiffForNestedAttribute(value, attribute)
})
return change.CheckForSensitive(
func(value structured.Change) computed.Diff {
return computeDiffForNestedAttribute(value, attribute)
}, func(inner computed.Diff, beforeSensitive, afterSensitive bool, action plans.Action) computed.Diff {
return computed.NewDiff(renderers.Sensitive(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches())
},
)
}

func checkForSensitiveBlock(change structured.Change, block *jsonprovider.Block) (computed.Diff, bool) {
return checkForSensitive(change, renderers.SensitiveBlock, func(value structured.Change) computed.Diff {
return ComputeDiffForBlock(value, block)
})
}

func checkForSensitive(change structured.Change, create CreateSensitiveRenderer, computedDiff func(value structured.Change) computed.Diff) (computed.Diff, bool) {
beforeSensitive := change.IsBeforeSensitive()
afterSensitive := change.IsAfterSensitive()

if !beforeSensitive && !afterSensitive {
return computed.Diff{}, false
}

// We are still going to give the change the contents of the actual change.
// So we create a new Change with everything matching the current value,
// except for the sensitivity.
//
// The change can choose what to do with this information, in most cases
// it will just be ignored in favour of printing `(sensitive value)`.

value := structured.Change{
BeforeExplicit: change.BeforeExplicit,
AfterExplicit: change.AfterExplicit,
Before: change.Before,
After: change.After,
Unknown: change.Unknown,
BeforeSensitive: false,
AfterSensitive: false,
ReplacePaths: change.ReplacePaths,
RelevantAttributes: change.RelevantAttributes,
}

inner := computedDiff(value)

action := inner.Action

sensitiveStatusChanged := beforeSensitive != afterSensitive

// nullNoOp is a stronger NoOp, where not only is there no change happening
// but the before and after values are not explicitly set and are both
// null. This will override even the sensitive state changing.
nullNoOp := change.Before == nil && !change.BeforeExplicit && change.After == nil && !change.AfterExplicit

if action == plans.NoOp && sensitiveStatusChanged && !nullNoOp {
// Let's override this, since it means the sensitive status has changed
// rather than the actual content of the value.
action = plans.Update
}

return computed.NewDiff(create(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches()), true
return change.CheckForSensitive(
func(value structured.Change) computed.Diff {
return ComputeDiffForBlock(value, block)
}, func(inner computed.Diff, beforeSensitive, afterSensitive bool, action plans.Action) computed.Diff {
return computed.NewDiff(renderers.SensitiveBlock(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches())
},
)
}
Loading