func testPlanFile(t *testing.T, plan *terraform.Plan) string { path := testTempFile(t) f, err := os.Create(path) if err != nil { t.Fatalf("err: %s", err) } defer f.Close() if err := terraform.WritePlan(plan, f); err != nil { t.Fatalf("err: %s", err) } return path }
func (c *PlanCommand) Run(args []string) int { var destroy, refresh, detailed bool var outPath string var moduleDepth int args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("plan") cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") c.addModuleDepthFlag(cmdFlags, &moduleDepth) cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.IntVar( &c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } var path string args = cmdFlags.Args() if len(args) > 1 { c.Ui.Error( "The plan command expects at most one argument with the path\n" + "to a Terraform configuration.\n") cmdFlags.Usage() return 1 } else if len(args) == 1 { path = args[0] } else { var err error path, err = os.Getwd() if err != nil { c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) } } countHook := new(CountHook) c.Meta.extraHooks = []terraform.Hook{countHook} // This is going to keep track of shadow errors var shadowErr error ctx, planned, err := c.Context(contextOpts{ Destroy: destroy, Path: path, StatePath: c.Meta.statePath, Parallelism: c.Meta.parallelism, }) if err != nil { c.Ui.Error(err.Error()) return 1 } if planned { c.Ui.Output(c.Colorize().Color( "[reset][bold][yellow]" + "The plan command received a saved plan file as input. This command\n" + "will output the saved plan. This will not modify the already-existing\n" + "plan. If you wish to generate a new plan, please pass in a configuration\n" + "directory as an argument.\n\n")) // Disable refreshing no matter what since we only want to show the plan refresh = false } err = terraform.SetDebugInfo(DefaultDataDir) if err != nil { c.Ui.Error(err.Error()) return 1 } if err := ctx.Input(c.InputMode()); err != nil { c.Ui.Error(fmt.Sprintf("Error configuring: %s", err)) return 1 } // Record any shadow errors for later if err := ctx.ShadowError(); err != nil { shadowErr = multierror.Append(shadowErr, multierror.Prefix( err, "input operation:")) } if !validateContext(ctx, c.Ui) { return 1 } if refresh { c.Ui.Output("Refreshing Terraform state in-memory prior to plan...") c.Ui.Output("The refreshed state will be used to calculate this plan, but") c.Ui.Output("will not be persisted to local or remote state storage.\n") _, err := ctx.Refresh() if err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) return 1 } c.Ui.Output("") } plan, err := ctx.Plan() if err != nil { c.Ui.Error(fmt.Sprintf("Error running plan: %s", err)) return 1 } if outPath != "" { log.Printf("[INFO] Writing plan output to: %s", outPath) f, err := os.Create(outPath) if err == nil { defer f.Close() err = terraform.WritePlan(plan, f) } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err)) return 1 } } if plan.Diff.Empty() { c.Ui.Output( "No changes. Infrastructure is up-to-date. This means that Terraform\n" + "could not detect any differences between your configuration and\n" + "the real physical resources that exist. As a result, Terraform\n" + "doesn't need to do anything.") return 0 } if outPath == "" { c.Ui.Output(strings.TrimSpace(planHeaderNoOutput) + "\n") } else { c.Ui.Output(fmt.Sprintf( strings.TrimSpace(planHeaderYesOutput)+"\n", outPath)) } c.Ui.Output(FormatPlan(&FormatPlanOpts{ Plan: plan, Color: c.Colorize(), ModuleDepth: moduleDepth, })) c.Ui.Output(c.Colorize().Color(fmt.Sprintf( "[reset][bold]Plan:[reset] "+ "%d to add, %d to change, %d to destroy.", countHook.ToAdd+countHook.ToRemoveAndAdd, countHook.ToChange, countHook.ToRemove+countHook.ToRemoveAndAdd))) // Record any shadow errors for later if err := ctx.ShadowError(); err != nil { shadowErr = multierror.Append(shadowErr, multierror.Prefix( err, "plan operation:")) } // If we have an error in the shadow graph, let the user know. c.outputShadowError(shadowErr, true) if detailed { return 2 } return 0 }
func (c *PlanCommand) Run(args []string) int { var destroy, refresh, detailed bool var outPath string var moduleDepth int args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("plan") cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") c.addModuleDepthFlag(cmdFlags, &moduleDepth) cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.IntVar( &c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } var path string args = cmdFlags.Args() if len(args) > 1 { c.Ui.Error( "The plan command expects at most one argument with the path\n" + "to a Terraform configuration.\n") cmdFlags.Usage() return 1 } else if len(args) == 1 { path = args[0] } else { var err error path, err = os.Getwd() if err != nil { c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) } } countHook := new(CountHook) c.Meta.extraHooks = []terraform.Hook{countHook} ctx, _, err := c.Context(contextOpts{ Destroy: destroy, Path: path, StatePath: c.Meta.statePath, Parallelism: c.Meta.parallelism, }) if err != nil { c.Ui.Error(err.Error()) return 1 } if err := ctx.Input(c.InputMode()); err != nil { c.Ui.Error(fmt.Sprintf("Error configuring: %s", err)) return 1 } if !validateContext(ctx, c.Ui) { return 1 } if refresh { c.Ui.Output("Refreshing Terraform state in-memory prior to plan...") c.Ui.Output("The refreshed state will be used to calculate this plan, but") c.Ui.Output("will not be persisted to local or remote state storage.\n") _, err := ctx.Refresh() if err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) return 1 } c.Ui.Output("") } plan, err := ctx.Plan() if err != nil { c.Ui.Error(fmt.Sprintf("Error running plan: %s", err)) return 1 } if outPath != "" { log.Printf("[INFO] Writing plan output to: %s", outPath) f, err := os.Create(outPath) if err == nil { defer f.Close() err = terraform.WritePlan(plan, f) } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err)) return 1 } } if plan.Diff.Empty() { c.Ui.Output( "No changes. Infrastructure is up-to-date. This means that Terraform\n" + "could not detect any differences between your configuration and\n" + "the real physical resources that exist. As a result, Terraform\n" + "doesn't need to do anything.") return 0 } if outPath == "" { c.Ui.Output(strings.TrimSpace(planHeaderNoOutput) + "\n") } else { c.Ui.Output(fmt.Sprintf( strings.TrimSpace(planHeaderYesOutput)+"\n", outPath)) } c.Ui.Output(FormatPlan(&FormatPlanOpts{ Plan: plan, Color: c.Colorize(), ModuleDepth: moduleDepth, })) c.Ui.Output(c.Colorize().Color(fmt.Sprintf( "[reset][bold]Plan:[reset] "+ "%d to add, %d to change, %d to destroy.", countHook.ToAdd+countHook.ToRemoveAndAdd, countHook.ToChange, countHook.ToRemove+countHook.ToRemoveAndAdd))) if detailed { return 2 } return 0 }
func (c *PlanCommand) Run(args []string) int { var destroy, refresh bool var outPath, statePath, backupPath string args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("plan") cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&backupPath, "backup", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } var path string args = cmdFlags.Args() if len(args) > 1 { c.Ui.Error( "The plan command expects at most one argument with the path\n" + "to a Terraform configuration.\n") cmdFlags.Usage() return 1 } else if len(args) == 1 { path = args[0] } else { var err error path, err = os.Getwd() if err != nil { c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) } } // If the default state path doesn't exist, ignore it. if statePath != "" { if _, err := os.Stat(statePath); err != nil { if os.IsNotExist(err) && statePath == DefaultStateFilename { statePath = "" } } } // If we don't specify a backup path, default to state out with // the extension if backupPath == "" { backupPath = statePath + DefaultBackupExtention } ctx, _, err := c.Context(path, statePath) if err != nil { c.Ui.Error(err.Error()) return 1 } if !validateContext(ctx, c.Ui) { return 1 } if refresh { // Create a backup of the state before updating if backupPath != "-" && c.state != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { err = terraform.WriteState(c.state, f) f.Close() } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) return 1 } } c.Ui.Output("Refreshing Terraform state prior to plan...\n") if _, err := ctx.Refresh(); err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) return 1 } c.Ui.Output("") } plan, err := ctx.Plan(&terraform.PlanOpts{Destroy: destroy}) if err != nil { c.Ui.Error(fmt.Sprintf("Error running plan: %s", err)) return 1 } if plan.Diff.Empty() { c.Ui.Output( "No changes. Infrastructure is up-to-date. This means that Terraform\n" + "could not detect any differences between your configuration and\n" + "the real physical resources that exist. As a result, Terraform\n" + "doesn't need to do anything.") return 0 } if outPath != "" { log.Printf("[INFO] Writing plan output to: %s", outPath) f, err := os.Create(outPath) if err == nil { defer f.Close() err = terraform.WritePlan(plan, f) } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err)) return 1 } } if outPath == "" { c.Ui.Output(strings.TrimSpace(planHeaderNoOutput) + "\n") } else { c.Ui.Output(fmt.Sprintf( strings.TrimSpace(planHeaderYesOutput)+"\n", outPath)) } c.Ui.Output(FormatPlan(plan, c.Colorize())) return 0 }
func (c *PlanCommand) Run(args []string) int { var destroy, refresh bool var outPath string var moduleDepth int args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("plan") cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.IntVar(&moduleDepth, "module-depth", 0, "module-depth") cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } var path string args = cmdFlags.Args() if len(args) > 1 { c.Ui.Error( "The plan command expects at most one argument with the path\n" + "to a Terraform configuration.\n") cmdFlags.Usage() return 1 } else if len(args) == 1 { path = args[0] } else { var err error path, err = os.Getwd() if err != nil { c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) } } ctx, _, err := c.Context(contextOpts{ Path: path, StatePath: c.Meta.statePath, }) if err != nil { c.Ui.Error(err.Error()) return 1 } if !validateContext(ctx, c.Ui) { return 1 } if err := ctx.Input(c.InputMode()); err != nil { c.Ui.Error(fmt.Sprintf("Error configuring: %s", err)) return 1 } if refresh { c.Ui.Output("Refreshing Terraform state prior to plan...\n") state, err := ctx.Refresh() if err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) return 1 } c.Ui.Output("") if state != nil { log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath()) if err := c.Meta.PersistState(state); err != nil { c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err)) return 1 } } } plan, err := ctx.Plan(&terraform.PlanOpts{Destroy: destroy}) if err != nil { c.Ui.Error(fmt.Sprintf("Error running plan: %s", err)) return 1 } if plan.Diff.Empty() { c.Ui.Output( "No changes. Infrastructure is up-to-date. This means that Terraform\n" + "could not detect any differences between your configuration and\n" + "the real physical resources that exist. As a result, Terraform\n" + "doesn't need to do anything.") return 0 } if outPath != "" { log.Printf("[INFO] Writing plan output to: %s", outPath) f, err := os.Create(outPath) if err == nil { defer f.Close() err = terraform.WritePlan(plan, f) } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err)) return 1 } } if outPath == "" { c.Ui.Output(strings.TrimSpace(planHeaderNoOutput) + "\n") } else { c.Ui.Output(fmt.Sprintf( strings.TrimSpace(planHeaderYesOutput)+"\n", outPath)) } c.Ui.Output(FormatPlan(&FormatPlanOpts{ Plan: plan, Color: c.Colorize(), ModuleDepth: moduleDepth, })) return 0 }