From 0df3c143bbf1cc62a190824ea9675b8a8f07c9ad Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 30 Jun 2023 12:24:57 -0700 Subject: [PATCH] New plans.Quality type for display-relevant facts about a plan This commit replaces the existing jsonformat.PlanRendererOpt type with a new type with identical semantics, located in the plans package. We needed to be able to exchange the facts represented by `jsonformat.PlanRendererOpt` across some package boundaries, but the jsonformat package is implicated in too many dependency chains to be safe for that purpose! So, we had to make a new one. The plans package seems safe to import from all the places that must emit or accept this info, and already contains plans.Mode, which is effectively a sibling of this type. --- internal/command/jsonformat/plan.go | 15 +++++---------- internal/command/jsonformat/renderer.go | 2 +- internal/command/views/operation.go | 6 +++--- internal/command/views/show.go | 6 +++--- internal/plans/quality.go | 20 ++++++++++++++++++++ internal/plans/quality_string.go | 24 ++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 internal/plans/quality.go create mode 100644 internal/plans/quality_string.go diff --git a/internal/command/jsonformat/plan.go b/internal/command/jsonformat/plan.go index cd92e1a44f..7a49ac235b 100644 --- a/internal/command/jsonformat/plan.go +++ b/internal/command/jsonformat/plan.go @@ -19,14 +19,9 @@ import ( "github.com/hashicorp/terraform/internal/plans" ) -type PlanRendererOpt int - const ( detectedDrift string = "drift" proposedChange string = "change" - - Errored PlanRendererOpt = iota - CanNotApply ) type Plan struct { @@ -51,8 +46,8 @@ func (plan Plan) getSchema(change jsonplan.ResourceChange) *jsonprovider.Schema } } -func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRendererOpt) { - checkOpts := func(target PlanRendererOpt) bool { +func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...plans.Quality) { + checkOpts := func(target plans.Quality) bool { for _, opt := range opts { if opt == target { return true @@ -102,7 +97,7 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRen // the plan is "applyable" and, if so, whether it had refresh changes // that we already would've presented above. - if checkOpts(Errored) { + if checkOpts(plans.Errored) { if haveRefreshChanges { renderer.Streams.Print(format.HorizontalRule(renderer.Colorize, renderer.Streams.Stdout.Columns())) renderer.Streams.Println() @@ -143,7 +138,7 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRen ) if haveRefreshChanges { - if !checkOpts(CanNotApply) { + if !checkOpts(plans.NoChanges) { // In this case, applying this plan will not change any // remote objects but _will_ update the state to match what // we detected during refresh, so we'll reassure the user @@ -210,7 +205,7 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...PlanRen } if len(changes) > 0 { - if checkOpts(Errored) { + if checkOpts(plans.Errored) { renderer.Streams.Printf("\nTerraform planned the following actions, but then encountered a problem:\n") } else { renderer.Streams.Printf("\nTerraform will perform the following actions:\n") diff --git a/internal/command/jsonformat/renderer.go b/internal/command/jsonformat/renderer.go index 6c8c1adcc1..9215e0e0ed 100644 --- a/internal/command/jsonformat/renderer.go +++ b/internal/command/jsonformat/renderer.go @@ -82,7 +82,7 @@ type Renderer struct { RunningInAutomation bool } -func (renderer Renderer) RenderHumanPlan(plan Plan, mode plans.Mode, opts ...PlanRendererOpt) { +func (renderer Renderer) RenderHumanPlan(plan Plan, mode plans.Mode, opts ...plans.Quality) { if incompatibleVersions(jsonplan.FormatVersion, plan.PlanFormatVersion) || incompatibleVersions(jsonprovider.FormatVersion, plan.ProviderFormatVersion) { renderer.Streams.Println(format.WordWrap( renderer.Colorize.Color("\n[bold][red]Warning:[reset][bold] This plan was generated using a different version of Terraform, the diff presented here may be missing representations of recent features."), diff --git a/internal/command/views/operation.go b/internal/command/views/operation.go index d5b5566a0f..a2411d4f4a 100644 --- a/internal/command/views/operation.go +++ b/internal/command/views/operation.go @@ -115,12 +115,12 @@ func (v *OperationHuman) Plan(plan *plans.Plan, schemas *terraform.Schemas) { } // Side load some data that we can't extract from the JSON plan. - var opts []jsonformat.PlanRendererOpt + var opts []plans.Quality if !plan.CanApply() { - opts = append(opts, jsonformat.CanNotApply) + opts = append(opts, plans.NoChanges) } if plan.Errored { - opts = append(opts, jsonformat.Errored) + opts = append(opts, plans.Errored) } renderer.RenderHumanPlan(jplan, plan.UIMode, opts...) diff --git a/internal/command/views/show.go b/internal/command/views/show.go index 23f8737597..add3115798 100644 --- a/internal/command/views/show.go +++ b/internal/command/views/show.go @@ -67,12 +67,12 @@ func (v *ShowHuman) Display(config *configs.Config, plan *plans.Plan, stateFile RelevantAttributes: attrs, } - var opts []jsonformat.PlanRendererOpt + var opts []plans.Quality if !plan.CanApply() { - opts = append(opts, jsonformat.CanNotApply) + opts = append(opts, plans.NoChanges) } if plan.Errored { - opts = append(opts, jsonformat.Errored) + opts = append(opts, plans.Errored) } renderer.RenderHumanPlan(jplan, plan.UIMode, opts...) diff --git a/internal/plans/quality.go b/internal/plans/quality.go new file mode 100644 index 0000000000..95646f369e --- /dev/null +++ b/internal/plans/quality.go @@ -0,0 +1,20 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package plans + +// Quality represents facts about the nature of a plan that might be relevant +// when rendering it, like whether it errored or contains no changes. A plan can +// have multiple qualities. +type Quality int + +//go:generate go run golang.org/x/tools/cmd/stringer -type Quality + +const ( + // Errored plans did not successfully complete, and cannot be applied. + Errored Quality = iota + // NoChanges plans won't result in any actions on infrastructure, or any + // semantically meaningful updates to state. They can sometimes still affect + // the format of state if applied. + NoChanges +) diff --git a/internal/plans/quality_string.go b/internal/plans/quality_string.go new file mode 100644 index 0000000000..61a399a1e8 --- /dev/null +++ b/internal/plans/quality_string.go @@ -0,0 +1,24 @@ +// Code generated by "stringer -type Quality"; DO NOT EDIT. + +package plans + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Errored-0] + _ = x[NoChanges-1] +} + +const _Quality_name = "ErroredNoChanges" + +var _Quality_index = [...]uint8{0, 7, 16} + +func (i Quality) String() string { + if i < 0 || i >= Quality(len(_Quality_index)-1) { + return "Quality(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Quality_name[_Quality_index[i]:_Quality_index[i+1]] +}