github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/differ/block.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package differ 7 8 import ( 9 "github.com/opentofu/opentofu/internal/command/jsonformat/collections" 10 "github.com/opentofu/opentofu/internal/command/jsonformat/computed" 11 "github.com/opentofu/opentofu/internal/command/jsonformat/computed/renderers" 12 "github.com/opentofu/opentofu/internal/command/jsonformat/structured" 13 "github.com/opentofu/opentofu/internal/command/jsonprovider" 14 "github.com/opentofu/opentofu/internal/plans" 15 ) 16 17 func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) computed.Diff { 18 if sensitive, ok := checkForSensitiveBlock(change, block); ok { 19 return sensitive 20 } 21 22 if unknown, ok := checkForUnknownBlock(change, block); ok { 23 return unknown 24 } 25 26 current := change.GetDefaultActionForIteration() 27 28 blockValue := change.AsMap() 29 30 attributes := make(map[string]computed.Diff) 31 for key, attr := range block.Attributes { 32 childValue := blockValue.GetChild(key) 33 34 if !childValue.RelevantAttributes.MatchesPartial() { 35 // Mark non-relevant attributes as unchanged. 36 childValue = childValue.AsNoOp() 37 } 38 39 // Empty strings in blocks should be considered null for legacy reasons. 40 // The SDK doesn't support null strings yet, so we work around this now. 41 if before, ok := childValue.Before.(string); ok && len(before) == 0 { 42 childValue.Before = nil 43 } 44 if after, ok := childValue.After.(string); ok && len(after) == 0 { 45 childValue.After = nil 46 } 47 48 // Always treat changes to blocks as implicit. 49 childValue.BeforeExplicit = false 50 childValue.AfterExplicit = false 51 52 childChange := ComputeDiffForAttribute(childValue, attr) 53 if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil { 54 // Don't record nil values at all in blocks. 55 continue 56 } 57 58 attributes[key] = childChange 59 current = collections.CompareActions(current, childChange.Action) 60 } 61 62 blocks := renderers.Blocks{ 63 ReplaceBlocks: make(map[string]bool), 64 BeforeSensitiveBlocks: make(map[string]bool), 65 AfterSensitiveBlocks: make(map[string]bool), 66 SingleBlocks: make(map[string]computed.Diff), 67 ListBlocks: make(map[string][]computed.Diff), 68 SetBlocks: make(map[string][]computed.Diff), 69 MapBlocks: make(map[string]map[string]computed.Diff), 70 } 71 72 for key, blockType := range block.BlockTypes { 73 childValue := blockValue.GetChild(key) 74 75 if !childValue.RelevantAttributes.MatchesPartial() { 76 // Mark non-relevant attributes as unchanged. 77 childValue = childValue.AsNoOp() 78 } 79 80 beforeSensitive := childValue.IsBeforeSensitive() 81 afterSensitive := childValue.IsAfterSensitive() 82 forcesReplacement := childValue.ReplacePaths.Matches() 83 84 switch NestingMode(blockType.NestingMode) { 85 case nestingModeSet: 86 diffs, action := computeBlockDiffsAsSet(childValue, blockType.Block) 87 if action == plans.NoOp && childValue.Before == nil && childValue.After == nil { 88 // Don't record nil values in blocks. 89 continue 90 } 91 blocks.AddAllSetBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive) 92 current = collections.CompareActions(current, action) 93 case nestingModeList: 94 diffs, action := computeBlockDiffsAsList(childValue, blockType.Block) 95 if action == plans.NoOp && childValue.Before == nil && childValue.After == nil { 96 // Don't record nil values in blocks. 97 continue 98 } 99 blocks.AddAllListBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive) 100 current = collections.CompareActions(current, action) 101 case nestingModeMap: 102 diffs, action := computeBlockDiffsAsMap(childValue, blockType.Block) 103 if action == plans.NoOp && childValue.Before == nil && childValue.After == nil { 104 // Don't record nil values in blocks. 105 continue 106 } 107 blocks.AddAllMapBlocks(key, diffs, forcesReplacement, beforeSensitive, afterSensitive) 108 current = collections.CompareActions(current, action) 109 case nestingModeSingle, nestingModeGroup: 110 diff := ComputeDiffForBlock(childValue, blockType.Block) 111 if diff.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil { 112 // Don't record nil values in blocks. 113 continue 114 } 115 blocks.AddSingleBlock(key, diff, forcesReplacement, beforeSensitive, afterSensitive) 116 current = collections.CompareActions(current, diff.Action) 117 default: 118 panic("unrecognized nesting mode: " + blockType.NestingMode) 119 } 120 } 121 122 return computed.NewDiff(renderers.Block(attributes, blocks), current, change.ReplacePaths.Matches()) 123 }