[Plannable Import] Implement human-readable plan rendering (#33113)

* [plannable import] embed the resource id within the changes

* add the plannable imports to the json and human plans

* latest importing struct
This commit is contained in:
Liam Cervante
2023-05-03 18:50:04 +02:00
committed by GitHub
parent ddd87994bf
commit 54c1c1162f
6 changed files with 494 additions and 34 deletions

View File

@@ -66,10 +66,11 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRen
willPrintResourceChanges := false
counts := make(map[plans.Action]int)
importingCount := 0
var changes []diff
for _, diff := range diffs.changes {
action := jsonplan.UnmarshalActions(diff.change.Change.Actions)
if action == plans.NoOp && !diff.Moved() {
if action == plans.NoOp && !diff.Moved() && !diff.Importing() {
// Don't show anything for NoOp changes.
continue
}
@@ -80,6 +81,10 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRen
changes = append(changes, diff)
if diff.Importing() {
importingCount++
}
// Don't count move-only changes
if action != plans.NoOp {
willPrintResourceChanges = true
@@ -219,11 +224,20 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRen
}
}
renderer.Streams.Printf(
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to add, %d to change, %d to destroy.\n"),
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
counts[plans.Update],
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete])
if importingCount > 0 {
renderer.Streams.Printf(
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to add, %d to import, %d to change, %d to destroy.\n"),
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
importingCount,
counts[plans.Update],
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete])
} else {
renderer.Streams.Printf(
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to add, %d to change, %d to destroy.\n"),
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
counts[plans.Update],
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete])
}
}
if len(outputs) > 0 {
@@ -335,14 +349,18 @@ func renderHumanDiff(renderer Renderer, diff diff, cause string) (string, bool)
// the computed actions of these.
action := jsonplan.UnmarshalActions(diff.change.Change.Actions)
if action == plans.NoOp && (len(diff.change.PreviousAddress) == 0 || diff.change.PreviousAddress == diff.change.Address) {
if action == plans.NoOp && !diff.Moved() && !diff.Importing() {
// Skip resource changes that have nothing interesting to say.
return "", false
}
var buf bytes.Buffer
buf.WriteString(renderer.Colorize.Color(resourceChangeComment(diff.change, action, cause)))
buf.WriteString(fmt.Sprintf("%s %s %s", renderer.Colorize.Color(format.DiffActionSymbol(action)), resourceChangeHeader(diff.change), diff.diff.RenderHuman(0, computed.NewRenderHumanOpts(renderer.Colorize))))
opts := computed.NewRenderHumanOpts(renderer.Colorize)
opts.ShowUnchangedChildren = diff.Importing()
buf.WriteString(fmt.Sprintf("%s %s %s", renderer.Colorize.Color(format.DiffActionSymbol(action)), resourceChangeHeader(diff.change), diff.diff.RenderHuman(0, opts)))
return buf.String(), true
}
@@ -354,6 +372,9 @@ func resourceChangeComment(resource jsonplan.ResourceChange, action plans.Action
dispAddr = fmt.Sprintf("%s (deposed object %s)", dispAddr, resource.Deposed)
}
var printedMoved bool
var printedImported bool
switch action {
case plans.Create:
buf.WriteString(fmt.Sprintf("[bold] # %s[reset] will be created", dispAddr))
@@ -442,6 +463,12 @@ func resourceChangeComment(resource jsonplan.ResourceChange, action plans.Action
case plans.NoOp:
if len(resource.PreviousAddress) > 0 && resource.PreviousAddress != resource.Address {
buf.WriteString(fmt.Sprintf("[bold] # %s[reset] has moved to [bold]%s[reset]", resource.PreviousAddress, dispAddr))
printedMoved = true
break
}
if resource.Change.Importing != nil {
buf.WriteString(fmt.Sprintf("[bold] # %s[reset] will be imported", dispAddr))
printedImported = true
break
}
fallthrough
@@ -451,9 +478,27 @@ func resourceChangeComment(resource jsonplan.ResourceChange, action plans.Action
}
buf.WriteString("\n")
if len(resource.PreviousAddress) > 0 && resource.PreviousAddress != resource.Address && action != plans.NoOp {
if len(resource.PreviousAddress) > 0 && resource.PreviousAddress != resource.Address && !printedMoved {
buf.WriteString(fmt.Sprintf(" # [reset](moved from %s)\n", resource.PreviousAddress))
}
if resource.Change.Importing != nil && !printedImported {
// We want to make this as forward compatible as possible, and we know
// the ID may be removed from the Importing metadata in favour of
// something else.
// As Importing metadata is loaded from a JSON struct, the effect of it
// being removed in the future will mean this renderer will receive it
// as an empty string
if len(resource.Change.Importing.ID) > 0 {
buf.WriteString(fmt.Sprintf(" # [reset](imported from \"%s\")\n", resource.Change.Importing.ID))
} else {
// This means we're trying to render a plan from a future version
// and we didn't get given the ID. So we'll do our best.
buf.WriteString(" # [reset](will be imported first)\n")
}
}
if resource.Change.Importing != nil && (action == plans.CreateThenDelete || action == plans.DeleteThenCreate) {
buf.WriteString(" # [reset][yellow]Warning: this will destroy the imported resource[reset]\n")
}
return buf.String()
}