Skip to content

Commit 519a18a

Browse files
author
Liam Cervante
authored
Propagate unknown and sensitive metadata to dynamic attributes (#33057)
* propagate unknown and sensitive metadata to dynamic attributes * update goimports and add some comments
1 parent c81aef3 commit 519a18a

File tree

13 files changed

+456
-220
lines changed

13 files changed

+456
-220
lines changed

internal/command/jsonformat/collections/map.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
type ProcessKey func(key string) computed.Diff
99

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

1919
elements := make(map[string]computed.Diff)
20-
for key := range before {
21-
elements[key] = process(key)
22-
current = CompareActions(current, elements[key].Action)
23-
}
24-
25-
for key := range after {
26-
if _, ok := elements[key]; ok {
27-
// Then we've already processed this key in the before.
28-
continue
29-
}
20+
for _, key := range keys {
3021
elements[key] = process(key)
3122
current = CompareActions(current, elements[key].Action)
3223
}

internal/command/jsonformat/computed/renderers/json.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ func RendererJsonOpts() jsondiff.JsonOpts {
2525
Array: func(elements []computed.Diff, action plans.Action) computed.Diff {
2626
return computed.NewDiff(List(elements), action, false)
2727
},
28+
Unknown: func(diff computed.Diff, action plans.Action) computed.Diff {
29+
return computed.NewDiff(Unknown(diff), action, false)
30+
},
31+
Sensitive: func(diff computed.Diff, beforeSensitive bool, afterSensitive bool, action plans.Action) computed.Diff {
32+
return computed.NewDiff(Sensitive(diff, beforeSensitive, afterSensitive), action, false)
33+
},
2834
TypeChange: func(before, after computed.Diff, action plans.Action) computed.Diff {
2935
return computed.NewDiff(TypeChange(before, after), action, false)
3036
},

internal/command/jsonformat/computed/renderers/primitive.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
1111
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
12+
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
1213
"github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path"
1314
"github.com/hashicorp/terraform/internal/plans"
1415
)
@@ -183,7 +184,17 @@ func (renderer primitiveRenderer) renderStringDiff(diff computed.Diff, indent in
183184
}
184185

185186
func (renderer primitiveRenderer) renderStringDiffAsJson(diff computed.Diff, indent int, opts computed.RenderHumanOpts, before evaluatedString, after evaluatedString) string {
186-
jsonDiff := RendererJsonOpts().Transform(before.Json, after.Json, diff.Action != plans.Create, diff.Action != plans.Delete, attribute_path.AlwaysMatcher())
187+
jsonDiff := RendererJsonOpts().Transform(structured.Change{
188+
BeforeExplicit: diff.Action != plans.Create,
189+
AfterExplicit: diff.Action != plans.Delete,
190+
Before: before.Json,
191+
After: after.Json,
192+
Unknown: false,
193+
BeforeSensitive: false,
194+
AfterSensitive: false,
195+
ReplacePaths: attribute_path.Empty(false),
196+
RelevantAttributes: attribute_path.AlwaysMatcher(),
197+
})
187198

188199
action := diff.Action
189200

internal/command/jsonformat/differ/differ_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,111 @@ func TestRelevantAttributes(t *testing.T) {
25532553
}
25542554
}
25552555

2556+
func TestDynamicPseudoType(t *testing.T) {
2557+
tcs := map[string]struct {
2558+
input structured.Change
2559+
validate renderers.ValidateDiffFunction
2560+
}{
2561+
"after_sensitive_in_dynamic_type": {
2562+
input: structured.Change{
2563+
Before: nil,
2564+
After: map[string]interface{}{
2565+
"key": "value",
2566+
},
2567+
Unknown: false,
2568+
BeforeSensitive: false,
2569+
AfterSensitive: map[string]interface{}{
2570+
"key": true,
2571+
},
2572+
ReplacePaths: attribute_path.Empty(false),
2573+
RelevantAttributes: attribute_path.AlwaysMatcher(),
2574+
},
2575+
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
2576+
"key": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "value", plans.Create, false), false, true, plans.Create, false),
2577+
}, plans.Create, false),
2578+
},
2579+
"before_sensitive_in_dynamic_type": {
2580+
input: structured.Change{
2581+
Before: map[string]interface{}{
2582+
"key": "value",
2583+
},
2584+
After: nil,
2585+
Unknown: false,
2586+
BeforeSensitive: map[string]interface{}{
2587+
"key": true,
2588+
},
2589+
AfterSensitive: false,
2590+
ReplacePaths: attribute_path.Empty(false),
2591+
RelevantAttributes: attribute_path.AlwaysMatcher(),
2592+
},
2593+
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
2594+
"key": renderers.ValidateSensitive(renderers.ValidatePrimitive("value", nil, plans.Delete, false), true, false, plans.Delete, false),
2595+
}, plans.Delete, false),
2596+
},
2597+
"sensitive_in_dynamic_type": {
2598+
input: structured.Change{
2599+
Before: map[string]interface{}{
2600+
"key": "before",
2601+
},
2602+
After: map[string]interface{}{
2603+
"key": "after",
2604+
},
2605+
Unknown: false,
2606+
BeforeSensitive: map[string]interface{}{
2607+
"key": true,
2608+
},
2609+
AfterSensitive: map[string]interface{}{
2610+
"key": true,
2611+
},
2612+
ReplacePaths: attribute_path.Empty(false),
2613+
RelevantAttributes: attribute_path.AlwaysMatcher(),
2614+
},
2615+
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
2616+
"key": renderers.ValidateSensitive(renderers.ValidatePrimitive("before", "after", plans.Update, false), true, true, plans.Update, false),
2617+
}, plans.Update, false),
2618+
},
2619+
"create_unknown_in_dynamic_type": {
2620+
input: structured.Change{
2621+
Before: nil,
2622+
After: map[string]interface{}{},
2623+
Unknown: map[string]interface{}{
2624+
"key": true,
2625+
},
2626+
BeforeSensitive: false,
2627+
AfterSensitive: false,
2628+
ReplacePaths: attribute_path.Empty(false),
2629+
RelevantAttributes: attribute_path.AlwaysMatcher(),
2630+
},
2631+
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
2632+
"key": renderers.ValidateUnknown(nil, plans.Create, false),
2633+
}, plans.Create, false),
2634+
},
2635+
"update_unknown_in_dynamic_type": {
2636+
input: structured.Change{
2637+
Before: map[string]interface{}{
2638+
"key": "before",
2639+
},
2640+
After: map[string]interface{}{},
2641+
Unknown: map[string]interface{}{
2642+
"key": true,
2643+
},
2644+
BeforeSensitive: false,
2645+
AfterSensitive: false,
2646+
ReplacePaths: attribute_path.Empty(false),
2647+
RelevantAttributes: attribute_path.AlwaysMatcher(),
2648+
},
2649+
validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
2650+
"key": renderers.ValidateUnknown(renderers.ValidatePrimitive("before", nil, plans.Delete, false), plans.Update, false),
2651+
}, plans.Update, false),
2652+
},
2653+
}
2654+
for key, tc := range tcs {
2655+
t.Run(key, func(t *testing.T) {
2656+
tc.validate(t, ComputeDiffForType(tc.input, cty.DynamicPseudoType))
2657+
})
2658+
}
2659+
}
2660+
25562661
func TestSpecificCases(t *testing.T) {
25572662
// This is a special test that can contain any combination of individual
25582663
// cases and will execute against them. For testing/fixing specific issues

internal/command/jsonformat/differ/map.go

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,7 @@ import (
1313

1414
func computeAttributeDiffAsMap(change structured.Change, elementType cty.Type) computed.Diff {
1515
mapValue := change.AsMap()
16-
17-
// The jsonplan package will have stripped out unknowns from our after value
18-
// so we're going to add them back in here.
19-
//
20-
// This only affects attributes and not nested attributes or blocks, so we
21-
// only perform this fix in this function and not the equivalent map
22-
// functions for nested attributes and blocks.
23-
24-
// There is actually a difference between a null map and an empty map for
25-
// purposes of calculating a delete, create, or update operation.
26-
27-
var after map[string]interface{}
28-
if mapValue.After != nil {
29-
after = make(map[string]interface{})
30-
}
31-
32-
for key, value := range mapValue.After {
33-
after[key] = value
34-
}
35-
for key := range mapValue.Unknown {
36-
if _, ok := after[key]; ok {
37-
// Then this unknown value was in after, this probably means it has
38-
// a child that is unknown rather than being unknown itself. As
39-
// such, we'll skip over it. Note, it doesn't particularly matter if
40-
// an element is in both places - it's just important we actually
41-
// do cover all the elements. We want a complete union and therefore
42-
// duplicates are no cause for concern as long as we dedupe here.
43-
continue
44-
}
45-
after[key] = nil
46-
}
47-
48-
elements, current := collections.TransformMap(mapValue.Before, after, func(key string) computed.Diff {
16+
elements, current := collections.TransformMap(mapValue.Before, mapValue.After, mapValue.AllKeys(), func(key string) computed.Diff {
4917
value := mapValue.GetChild(key)
5018
if !value.RelevantAttributes.MatchesPartial() {
5119
// Mark non-relevant attributes as unchanged.
@@ -58,7 +26,7 @@ func computeAttributeDiffAsMap(change structured.Change, elementType cty.Type) c
5826

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

7543
func computeBlockDiffsAsMap(change structured.Change, block *jsonprovider.Block) (map[string]computed.Diff, plans.Action) {
7644
mapValue := change.AsMap()
77-
return collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff {
45+
return collections.TransformMap(mapValue.Before, mapValue.After, mapValue.ExplicitKeys(), func(key string) computed.Diff {
7846
value := mapValue.GetChild(key)
7947
if !value.RelevantAttributes.MatchesPartial() {
8048
// Mark non-relevant attributes as unchanged.

internal/command/jsonformat/differ/output.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ func ComputeDiffForOutput(change structured.Change) computed.Diff {
1818
}
1919

2020
jsonOpts := renderers.RendererJsonOpts()
21-
return jsonOpts.Transform(change.Before, change.After, change.BeforeExplicit, change.AfterExplicit, change.RelevantAttributes)
21+
return jsonOpts.Transform(change)
2222
}

internal/command/jsonformat/differ/sensitive.go

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,31 @@ import (
1313
type CreateSensitiveRenderer func(computed.Diff, bool, bool) computed.DiffRenderer
1414

1515
func checkForSensitiveType(change structured.Change, ctype cty.Type) (computed.Diff, bool) {
16-
return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff {
17-
return ComputeDiffForType(value, ctype)
18-
})
16+
return change.CheckForSensitive(
17+
func(value structured.Change) computed.Diff {
18+
return ComputeDiffForType(value, ctype)
19+
}, func(inner computed.Diff, beforeSensitive, afterSensitive bool, action plans.Action) computed.Diff {
20+
return computed.NewDiff(renderers.Sensitive(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches())
21+
},
22+
)
1923
}
2024

2125
func checkForSensitiveNestedAttribute(change structured.Change, attribute *jsonprovider.NestedType) (computed.Diff, bool) {
22-
return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff {
23-
return computeDiffForNestedAttribute(value, attribute)
24-
})
26+
return change.CheckForSensitive(
27+
func(value structured.Change) computed.Diff {
28+
return computeDiffForNestedAttribute(value, attribute)
29+
}, func(inner computed.Diff, beforeSensitive, afterSensitive bool, action plans.Action) computed.Diff {
30+
return computed.NewDiff(renderers.Sensitive(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches())
31+
},
32+
)
2533
}
2634

2735
func checkForSensitiveBlock(change structured.Change, block *jsonprovider.Block) (computed.Diff, bool) {
28-
return checkForSensitive(change, renderers.SensitiveBlock, func(value structured.Change) computed.Diff {
29-
return ComputeDiffForBlock(value, block)
30-
})
31-
}
32-
33-
func checkForSensitive(change structured.Change, create CreateSensitiveRenderer, computedDiff func(value structured.Change) computed.Diff) (computed.Diff, bool) {
34-
beforeSensitive := change.IsBeforeSensitive()
35-
afterSensitive := change.IsAfterSensitive()
36-
37-
if !beforeSensitive && !afterSensitive {
38-
return computed.Diff{}, false
39-
}
40-
41-
// We are still going to give the change the contents of the actual change.
42-
// So we create a new Change with everything matching the current value,
43-
// except for the sensitivity.
44-
//
45-
// The change can choose what to do with this information, in most cases
46-
// it will just be ignored in favour of printing `(sensitive value)`.
47-
48-
value := structured.Change{
49-
BeforeExplicit: change.BeforeExplicit,
50-
AfterExplicit: change.AfterExplicit,
51-
Before: change.Before,
52-
After: change.After,
53-
Unknown: change.Unknown,
54-
BeforeSensitive: false,
55-
AfterSensitive: false,
56-
ReplacePaths: change.ReplacePaths,
57-
RelevantAttributes: change.RelevantAttributes,
58-
}
59-
60-
inner := computedDiff(value)
61-
62-
action := inner.Action
63-
64-
sensitiveStatusChanged := beforeSensitive != afterSensitive
65-
66-
// nullNoOp is a stronger NoOp, where not only is there no change happening
67-
// but the before and after values are not explicitly set and are both
68-
// null. This will override even the sensitive state changing.
69-
nullNoOp := change.Before == nil && !change.BeforeExplicit && change.After == nil && !change.AfterExplicit
70-
71-
if action == plans.NoOp && sensitiveStatusChanged && !nullNoOp {
72-
// Let's override this, since it means the sensitive status has changed
73-
// rather than the actual content of the value.
74-
action = plans.Update
75-
}
76-
77-
return computed.NewDiff(create(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches()), true
36+
return change.CheckForSensitive(
37+
func(value structured.Change) computed.Diff {
38+
return ComputeDiffForBlock(value, block)
39+
}, func(inner computed.Diff, beforeSensitive, afterSensitive bool, action plans.Action) computed.Diff {
40+
return computed.NewDiff(renderers.SensitiveBlock(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches())
41+
},
42+
)
7843
}

0 commit comments

Comments
 (0)