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
24 changes: 17 additions & 7 deletions internal/command/jsonformat/computed/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,21 @@ type RenderHumanOpts struct {
// deleted.
OverrideNullSuffix bool

// OverrideForcesReplacement tells the Renderer to display the
// ForceForcesReplacement tells the Renderer to display the
// `# forces replacement` suffix, even if a diff doesn't have the Replace
// field set.
//
// Some renderers (like the Set renderer) don't display the suffix
// themselves but force their child diffs to display it instead.
OverrideForcesReplacement bool
ForceForcesReplacement bool

// ForbidForcesReplacement is the opposite of ForceForcesReplacement. It
// tells the Renderer to not display the '# forces replacement' suffix, even
// if a diff does have the Replace field set.
//
// Some renderers (like the Unknown renderer) want to capture the
// forceReplacement setting at their level instead of within the children.
ForbidForcesReplacement bool

// ShowUnchangedChildren instructs the Renderer to render all children of a
// given complex change, instead of hiding unchanged items and compressing
Expand Down Expand Up @@ -114,10 +122,12 @@ func (opts RenderHumanOpts) Clone() RenderHumanOpts {
ShowUnchangedChildren: opts.ShowUnchangedChildren,
HideDiffActionSymbols: opts.HideDiffActionSymbols,

// OverrideForcesReplacement is a special case in that it doesn't
// cascade. So each diff should decide independently whether it's direct
// children should override their internal Replace logic, instead of
// an ancestor making the switch and affecting the entire tree.
OverrideForcesReplacement: false,
// ForceForcesReplacement and ForbidForcesReplacement are special cases
// in that they don't cascade. So each diff should decide independently
// whether it's direct children should override their internal Replace
// logic, instead of an ancestor making the switch and affecting the
// entire tree.
ForceForcesReplacement: false,
ForbidForcesReplacement: false,
}
}
17 changes: 15 additions & 2 deletions internal/command/jsonformat/computed/renderers/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (renderer blockRenderer) RenderHuman(diff computed.Diff, indent int, opts c
// know about the replace function if it was set on them
// specifically, and not if it was set for all the blocks.
blockOpts := opts.Clone()
blockOpts.OverrideForcesReplacement = renderer.blocks.ReplaceBlocks[key]
blockOpts.ForceForcesReplacement = renderer.blocks.ReplaceBlocks[key]

for _, warning := range diff.WarningsHuman(indent+1, blockOpts) {
buf.WriteString(fmt.Sprintf("%s%s\n", formatIndent(indent+1), warning))
Expand All @@ -157,18 +157,31 @@ func (renderer blockRenderer) RenderHuman(diff computed.Diff, indent int, opts c
}
sort.Strings(keys)

if renderer.blocks.UnknownBlocks[key] {
renderBlock(computed.NewDiff(Unknown(computed.Diff{}), plans.Create, false), "", opts)
}

for _, innerKey := range keys {
renderBlock(renderer.blocks.MapBlocks[key][innerKey], fmt.Sprintf(" %q", innerKey), opts)
}
case renderer.blocks.IsSetBlock(key):

setOpts := opts.Clone()
setOpts.OverrideForcesReplacement = diff.Replace
setOpts.ForceForcesReplacement = diff.Replace

if renderer.blocks.UnknownBlocks[key] {
renderBlock(computed.NewDiff(Unknown(computed.Diff{}), plans.Create, false), "", opts)
}

for _, block := range renderer.blocks.SetBlocks[key] {
renderBlock(block, "", opts)
}
case renderer.blocks.IsListBlock(key):

if renderer.blocks.UnknownBlocks[key] {
renderBlock(computed.NewDiff(Unknown(computed.Diff{}), plans.Create, false), "", opts)
}

for _, block := range renderer.blocks.ListBlocks[key] {
renderBlock(block, "", opts)
}
Expand Down
13 changes: 9 additions & 4 deletions internal/command/jsonformat/computed/renderers/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Blocks struct {
ReplaceBlocks map[string]bool
BeforeSensitiveBlocks map[string]bool
AfterSensitiveBlocks map[string]bool
UnknownBlocks map[string]bool
}

func (blocks *Blocks) GetAllKeys() []string {
Expand Down Expand Up @@ -67,30 +68,34 @@ func (blocks *Blocks) IsSetBlock(key string) bool {
return ok
}

func (blocks *Blocks) AddSingleBlock(key string, diff computed.Diff, replace, beforeSensitive, afterSensitive bool) {
func (blocks *Blocks) AddSingleBlock(key string, diff computed.Diff, replace, beforeSensitive, afterSensitive, unknown bool) {
blocks.SingleBlocks[key] = diff
blocks.ReplaceBlocks[key] = replace
blocks.BeforeSensitiveBlocks[key] = beforeSensitive
blocks.AfterSensitiveBlocks[key] = afterSensitive
blocks.UnknownBlocks[key] = unknown
}

func (blocks *Blocks) AddAllListBlock(key string, diffs []computed.Diff, replace, beforeSensitive, afterSensitive bool) {
func (blocks *Blocks) AddAllListBlock(key string, diffs []computed.Diff, replace, beforeSensitive, afterSensitive, unknown bool) {
blocks.ListBlocks[key] = diffs
blocks.ReplaceBlocks[key] = replace
blocks.BeforeSensitiveBlocks[key] = beforeSensitive
blocks.AfterSensitiveBlocks[key] = afterSensitive
blocks.UnknownBlocks[key] = unknown
}

func (blocks *Blocks) AddAllSetBlock(key string, diffs []computed.Diff, replace, beforeSensitive, afterSensitive bool) {
func (blocks *Blocks) AddAllSetBlock(key string, diffs []computed.Diff, replace, beforeSensitive, afterSensitive, unknown bool) {
blocks.SetBlocks[key] = diffs
blocks.ReplaceBlocks[key] = replace
blocks.BeforeSensitiveBlocks[key] = beforeSensitive
blocks.AfterSensitiveBlocks[key] = afterSensitive
blocks.UnknownBlocks[key] = unknown
}

func (blocks *Blocks) AddAllMapBlocks(key string, diffs map[string]computed.Diff, replace, beforeSensitive, afterSensitive bool) {
func (blocks *Blocks) AddAllMapBlocks(key string, diffs map[string]computed.Diff, replace, beforeSensitive, afterSensitive, unknown bool) {
blocks.MapBlocks[key] = diffs
blocks.ReplaceBlocks[key] = replace
blocks.BeforeSensitiveBlocks[key] = beforeSensitive
blocks.AfterSensitiveBlocks[key] = afterSensitive
blocks.UnknownBlocks[key] = unknown
}
2 changes: 1 addition & 1 deletion internal/command/jsonformat/computed/renderers/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (renderer mapRenderer) RenderHuman(diff computed.Diff, indent int, opts com

elementOpts := opts.Clone()
elementOpts.OverrideNullSuffix = diff.Action == plans.Delete || renderer.overrideNullSuffix
elementOpts.OverrideForcesReplacement = forcesReplacementChildren
elementOpts.ForceForcesReplacement = forcesReplacementChildren

var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("{%s\n", forcesReplacement(forcesReplacementSelf, opts)))
Expand Down
2 changes: 1 addition & 1 deletion internal/command/jsonformat/computed/renderers/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (renderer setRenderer) RenderHuman(diff computed.Diff, indent int, opts com

elementOpts := opts.Clone()
elementOpts.OverrideNullSuffix = true
elementOpts.OverrideForcesReplacement = displayForcesReplacementInChildren
elementOpts.ForceForcesReplacement = displayForcesReplacementInChildren

unchangedElements := 0

Expand Down
10 changes: 8 additions & 2 deletions internal/command/jsonformat/computed/renderers/unknown.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ func (renderer unknownRenderer) RenderHuman(diff computed.Diff, indent int, opts
return fmt.Sprintf("(known after apply)%s", forcesReplacement(diff.Replace, opts))
}

beforeOpts := opts.Clone()
// Never render null suffix for children of unknown changes.
opts.OverrideNullSuffix = true
return fmt.Sprintf("%s -> (known after apply)%s", renderer.before.RenderHuman(indent, opts), forcesReplacement(diff.Replace, opts))
beforeOpts.OverrideNullSuffix = true
if diff.Replace {
// If we're displaying forces replacement for the overall unknown
// change, then do not display it for the before specifically.
beforeOpts.ForbidForcesReplacement = true
}
return fmt.Sprintf("%s -> (known after apply)%s", renderer.before.RenderHuman(indent, beforeOpts), forcesReplacement(diff.Replace, opts))
}
2 changes: 1 addition & 1 deletion internal/command/jsonformat/computed/renderers/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func nullSuffix(action plans.Action, opts computed.RenderHumanOpts) string {
// forcesReplacement returns the `# forces replacement` suffix if this change is
// driving the entire resource to be replaced.
func forcesReplacement(replace bool, opts computed.RenderHumanOpts) string {
if replace || opts.OverrideForcesReplacement {
if (replace || opts.ForceForcesReplacement) && !opts.ForbidForcesReplacement {
return opts.Colorize.Color(" [red]# forces replacement[reset]")
}
return ""
Expand Down
25 changes: 10 additions & 15 deletions internal/command/jsonformat/differ/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co
ReplaceBlocks: make(map[string]bool),
BeforeSensitiveBlocks: make(map[string]bool),
AfterSensitiveBlocks: make(map[string]bool),
UnknownBlocks: make(map[string]bool),
SingleBlocks: make(map[string]computed.Diff),
ListBlocks: make(map[string][]computed.Diff),
SetBlocks: make(map[string][]computed.Diff),
Expand All @@ -73,46 +74,40 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co
beforeSensitive := childValue.IsBeforeSensitive()
afterSensitive := childValue.IsAfterSensitive()
forcesReplacement := childValue.ReplacePaths.Matches()

if unknown, ok := checkForUnknownBlock(childValue, block); ok {
// An unknown block doesn't render any type information, so we can
// render it as a single block rather than switching on all types.
blocks.AddSingleBlock(key, unknown, forcesReplacement, beforeSensitive, afterSensitive)
continue
}
unknown := childValue.IsUnknown()

switch NestingMode(blockType.NestingMode) {
case nestingModeSet:
diffs, action := computeBlockDiffsAsSet(childValue, blockType.Block)
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !unknown {
// Don't record nil values in blocks.
continue
}
blocks.AddAllSetBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
blocks.AddAllSetBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive, unknown)
current = collections.CompareActions(current, action)
case nestingModeList:
diffs, action := computeBlockDiffsAsList(childValue, blockType.Block)
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !unknown {
// Don't record nil values in blocks.
continue
}
blocks.AddAllListBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
blocks.AddAllListBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive, unknown)
current = collections.CompareActions(current, action)
case nestingModeMap:
diffs, action := computeBlockDiffsAsMap(childValue, blockType.Block)
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !unknown {
// Don't record nil values in blocks.
continue
}
blocks.AddAllMapBlocks(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
blocks.AddAllMapBlocks(key, diffs, forcesReplacement, beforeSensitive, afterSensitive, unknown)
current = collections.CompareActions(current, action)
case nestingModeSingle, nestingModeGroup:
diff := ComputeDiffForBlock(childValue, blockType.Block)
if diff.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
if diff.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !unknown {
// Don't record nil values in blocks.
continue
}
blocks.AddSingleBlock(key, diff, forcesReplacement, beforeSensitive, afterSensitive)
blocks.AddSingleBlock(key, diff, forcesReplacement, beforeSensitive, afterSensitive, unknown)
current = collections.CompareActions(current, diff.Action)
default:
panic("unrecognized nesting mode: " + blockType.NestingMode)
Expand Down
Loading