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