mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
plannable import: improve gen config human plan output (#33194)
* renderer: remove hard-coded config gen path * mention config gen file in plan next steps
This commit is contained in:
parent
789e30dfc5
commit
b4d1146f58
@ -55,7 +55,6 @@ func (b *Local) opPlan(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(op.GenerateConfigOut) > 0 {
|
if len(op.GenerateConfigOut) > 0 {
|
||||||
|
|
||||||
if op.PlanMode != plans.NormalMode {
|
if op.PlanMode != plans.NormalMode {
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
@ -192,7 +191,7 @@ func (b *Local) opPlan(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write out any generated config, before we render the plan.
|
// Write out any generated config, before we render the plan.
|
||||||
moreDiags = genconfig.MaybeWriteGeneratedConfig(plan, op.GenerateConfigOut)
|
wroteConfig, moreDiags := genconfig.MaybeWriteGeneratedConfig(plan, op.GenerateConfigOut)
|
||||||
diags = diags.Append(moreDiags)
|
diags = diags.Append(moreDiags)
|
||||||
if moreDiags.HasErrors() {
|
if moreDiags.HasErrors() {
|
||||||
op.ReportResult(runningOp, diags)
|
op.ReportResult(runningOp, diags)
|
||||||
@ -209,6 +208,10 @@ func (b *Local) opPlan(
|
|||||||
op.ReportResult(runningOp, diags)
|
op.ReportResult(runningOp, diags)
|
||||||
|
|
||||||
if !runningOp.PlanEmpty {
|
if !runningOp.PlanEmpty {
|
||||||
op.View.PlanNextStep(op.PlanOutPath)
|
if wroteConfig {
|
||||||
|
op.View.PlanNextStep(op.PlanOutPath, op.GenerateConfigOut)
|
||||||
|
} else {
|
||||||
|
op.View.PlanNextStep(op.PlanOutPath, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,7 +469,7 @@ func resourceChangeComment(resource jsonplan.ResourceChange, action plans.Action
|
|||||||
if resource.Change.Importing != nil {
|
if resource.Change.Importing != nil {
|
||||||
buf.WriteString(fmt.Sprintf("[bold] # %s[reset] will be imported", dispAddr))
|
buf.WriteString(fmt.Sprintf("[bold] # %s[reset] will be imported", dispAddr))
|
||||||
if len(resource.Change.GeneratedConfig) > 0 {
|
if len(resource.Change.GeneratedConfig) > 0 {
|
||||||
buf.WriteString("\n #[reset] (config will be written to generated_config.tf)")
|
buf.WriteString("\n #[reset] (config will be generated)")
|
||||||
}
|
}
|
||||||
printedImported = true
|
printedImported = true
|
||||||
break
|
break
|
||||||
|
@ -178,7 +178,7 @@ Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
|
|||||||
Terraform will perform the following actions:
|
Terraform will perform the following actions:
|
||||||
|
|
||||||
# test_resource.resource will be imported
|
# test_resource.resource will be imported
|
||||||
# (config will be written to generated_config.tf)
|
# (config will be generated)
|
||||||
resource "test_resource" "resource" {
|
resource "test_resource" "resource" {
|
||||||
id = "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E"
|
id = "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E"
|
||||||
value = "Hello, World!"
|
value = "Hello, World!"
|
||||||
|
@ -31,7 +31,7 @@ type Operation interface {
|
|||||||
|
|
||||||
PlannedChange(change *plans.ResourceInstanceChangeSrc)
|
PlannedChange(change *plans.ResourceInstanceChangeSrc)
|
||||||
Plan(plan *plans.Plan, schemas *terraform.Schemas)
|
Plan(plan *plans.Plan, schemas *terraform.Schemas)
|
||||||
PlanNextStep(planPath string)
|
PlanNextStep(planPath string, genConfigPath string)
|
||||||
|
|
||||||
Diagnostics(diags tfdiags.Diagnostics)
|
Diagnostics(diags tfdiags.Diagnostics)
|
||||||
}
|
}
|
||||||
@ -135,12 +135,18 @@ func (v *OperationHuman) PlannedChange(change *plans.ResourceInstanceChangeSrc)
|
|||||||
|
|
||||||
// PlanNextStep gives the user some next-steps, unless we're running in an
|
// PlanNextStep gives the user some next-steps, unless we're running in an
|
||||||
// automation tool which is presumed to provide its own UI for further actions.
|
// automation tool which is presumed to provide its own UI for further actions.
|
||||||
func (v *OperationHuman) PlanNextStep(planPath string) {
|
func (v *OperationHuman) PlanNextStep(planPath string, genConfigPath string) {
|
||||||
if v.inAutomation {
|
if v.inAutomation {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v.view.outputHorizRule()
|
v.view.outputHorizRule()
|
||||||
|
|
||||||
|
if genConfigPath != "" {
|
||||||
|
v.view.streams.Printf(
|
||||||
|
"\n"+strings.TrimSpace(format.WordWrap(planHeaderGenConfig, v.view.outputColumns()))+"\n", genConfigPath,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if planPath == "" {
|
if planPath == "" {
|
||||||
v.view.streams.Print(
|
v.view.streams.Print(
|
||||||
"\n" + strings.TrimSpace(format.WordWrap(planHeaderNoOutput, v.view.outputColumns())) + "\n",
|
"\n" + strings.TrimSpace(format.WordWrap(planHeaderNoOutput, v.view.outputColumns())) + "\n",
|
||||||
@ -261,7 +267,7 @@ func (v *OperationJSON) PlannedChange(change *plans.ResourceInstanceChangeSrc) {
|
|||||||
|
|
||||||
// PlanNextStep does nothing for the JSON view as it is a hook for user-facing
|
// PlanNextStep does nothing for the JSON view as it is a hook for user-facing
|
||||||
// output only applicable to human-readable UI.
|
// output only applicable to human-readable UI.
|
||||||
func (v *OperationJSON) PlanNextStep(planPath string) {
|
func (v *OperationJSON) PlanNextStep(planPath string, genConfigPath string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *OperationJSON) Diagnostics(diags tfdiags.Diagnostics) {
|
func (v *OperationJSON) Diagnostics(diags tfdiags.Diagnostics) {
|
||||||
@ -288,3 +294,7 @@ Saved the plan to: %s
|
|||||||
To perform exactly these actions, run the following command to apply:
|
To perform exactly these actions, run the following command to apply:
|
||||||
terraform apply %q
|
terraform apply %q
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const planHeaderGenConfig = `
|
||||||
|
Terraform has generated configuration and written it to %s. Please review the configuration and edit it as necessary before adding it to version control.
|
||||||
|
`
|
||||||
|
@ -450,7 +450,7 @@ func TestOperation_planNextStep(t *testing.T) {
|
|||||||
streams, done := terminal.StreamsForTesting(t)
|
streams, done := terminal.StreamsForTesting(t)
|
||||||
v := NewOperation(arguments.ViewHuman, false, NewView(streams))
|
v := NewOperation(arguments.ViewHuman, false, NewView(streams))
|
||||||
|
|
||||||
v.PlanNextStep(tc.path)
|
v.PlanNextStep(tc.path, "")
|
||||||
|
|
||||||
if got := done(t).Stdout(); !strings.Contains(got, tc.want) {
|
if got := done(t).Stdout(); !strings.Contains(got, tc.want) {
|
||||||
t.Errorf("wrong result\ngot: %q\nwant: %q", got, tc.want)
|
t.Errorf("wrong result\ngot: %q\nwant: %q", got, tc.want)
|
||||||
@ -465,7 +465,7 @@ func TestOperation_planNextStepInAutomation(t *testing.T) {
|
|||||||
streams, done := terminal.StreamsForTesting(t)
|
streams, done := terminal.StreamsForTesting(t)
|
||||||
v := NewOperation(arguments.ViewHuman, true, NewView(streams))
|
v := NewOperation(arguments.ViewHuman, true, NewView(streams))
|
||||||
|
|
||||||
v.PlanNextStep("")
|
v.PlanNextStep("", "")
|
||||||
|
|
||||||
if got := done(t).Stdout(); got != "" {
|
if got := done(t).Stdout(); got != "" {
|
||||||
t.Errorf("unexpected output\ngot: %q", got)
|
t.Errorf("unexpected output\ngot: %q", got)
|
||||||
|
@ -21,22 +21,21 @@ func ValidateTargetFile(out string) tfdiags.Diagnostics {
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaybeWriteGeneratedConfig(plan *plans.Plan, out string) tfdiags.Diagnostics {
|
func MaybeWriteGeneratedConfig(plan *plans.Plan, out string) (wroteConfig bool, diags tfdiags.Diagnostics) {
|
||||||
if len(out) == 0 {
|
if len(out) == 0 {
|
||||||
// No specified out file, so don't write anything.
|
// No specified out file, so don't write anything.
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
diags := ValidateTargetFile(out)
|
diags = ValidateTargetFile(out)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
return diags
|
return false, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
var writer io.Writer
|
var writer io.Writer
|
||||||
|
|
||||||
for _, change := range plan.Changes.Resources {
|
for _, change := range plan.Changes.Resources {
|
||||||
if len(change.GeneratedConfig) > 0 {
|
if len(change.GeneratedConfig) > 0 {
|
||||||
|
|
||||||
if writer == nil {
|
if writer == nil {
|
||||||
// Lazily create the generated file, in case we have no
|
// Lazily create the generated file, in case we have no
|
||||||
// generated config to create.
|
// generated config to create.
|
||||||
@ -47,14 +46,14 @@ func MaybeWriteGeneratedConfig(plan *plans.Plan, out string) tfdiags.Diagnostics
|
|||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
"Failed to create target generated file",
|
"Failed to create target generated file",
|
||||||
fmt.Sprintf("Terraform did not have permission to create the generated file (%s) in the target directory. Please modify permissions over the target directory, and try again.", out)))
|
fmt.Sprintf("Terraform did not have permission to create the generated file (%s) in the target directory. Please modify permissions over the target directory, and try again.", out)))
|
||||||
return diags
|
return false, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
"Failed to create target generated file",
|
"Failed to create target generated file",
|
||||||
fmt.Sprintf("Terraform could not create the generated file (%s) in the target directory: %v. Depending on the error message, this may be a bug in Terraform itself. If so, please report it!", out, err)))
|
fmt.Sprintf("Terraform could not create the generated file (%s) in the target directory: %v. Depending on the error message, this may be a bug in Terraform itself. If so, please report it!", out, err)))
|
||||||
return diags
|
return false, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
header := "# __generated__ by Terraform\n# Please review these resources and move them into your main configuration files.\n"
|
header := "# __generated__ by Terraform\n# Please review these resources and move them into your main configuration files.\n"
|
||||||
@ -74,8 +73,9 @@ func MaybeWriteGeneratedConfig(plan *plans.Plan, out string) tfdiags.Diagnostics
|
|||||||
"Failed to save generated config",
|
"Failed to save generated config",
|
||||||
fmt.Sprintf("Terraform encountered an error while writing generated config: %v. The config for %s must be created manually before applying. Depending on the error message, this may be a bug in Terraform itself. If so, please report it!", err, change.Addr.String())))
|
fmt.Sprintf("Terraform encountered an error while writing generated config: %v. The config for %s must be created manually before applying. Depending on the error message, this may be a bug in Terraform itself. If so, please report it!", err, change.Addr.String())))
|
||||||
}
|
}
|
||||||
|
wroteConfig = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return diags
|
return wroteConfig, diags
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user