// Plan implements the TerraformServer interface func (s *Server) Plan(c context.Context, req *pb.PlanRequest) (*pb.PlanResponse, error) { resp := &pb.PlanResponse{ Actions: make(map[string]pb.ResourceAction), } hooks := []terraform.Hook{&PlanHook{ resp: resp, }} ctx, err := s.newContext(req.Config, req.Destroy, nil, req.State, req.Parallelism, hooks) if err != nil { return nil, err } // if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { // resp.Valid = false // resp.Warnings = ws // parseErrors(&resp.Errors, es) // } if err := validateContext(ctx); err != nil { return nil, fmt.Errorf("Error validating context: %v", err) } if req.Refresh { _, err := ctx.Refresh() if err != nil { return nil, fmt.Errorf("Error refreshing state: %v", err) } } plan, err := ctx.Plan() if err != nil { return nil, fmt.Errorf("Error running plan: %v", err) } var b bytes.Buffer err = terraform.WritePlan(plan, &b) if err != nil { return nil, fmt.Errorf("Error writing plan: %v", err) } resp.Plan = b.Bytes() resp.Diff, err = json.Marshal(plan.Diff) if err != nil { return nil, fmt.Errorf("Error marshalling diff: %v", err) } resp.State, err = json.Marshal(plan.State) if err != nil { return nil, fmt.Errorf("Error marshalling refreshed state: %v", err) } return resp, nil }
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.StringVar(&c.Meta.backupPath, "backup", "", "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 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() 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 }