github.com/pbthorste/terraform@v0.8.6-0.20170127005045-deb56bd93da2/backend/local/backend_plan.go (about) 1 package local 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "os" 8 "strings" 9 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform/backend" 12 "github.com/hashicorp/terraform/command/format" 13 "github.com/hashicorp/terraform/terraform" 14 ) 15 16 func (b *Local) opPlan( 17 ctx context.Context, 18 op *backend.Operation, 19 runningOp *backend.RunningOperation) { 20 log.Printf("[INFO] backend/local: starting Plan operation") 21 22 if b.CLI != nil && op.Plan != nil { 23 b.CLI.Output(b.Colorize().Color( 24 "[reset][bold][yellow]" + 25 "The plan command received a saved plan file as input. This command\n" + 26 "will output the saved plan. This will not modify the already-existing\n" + 27 "plan. If you wish to generate a new plan, please pass in a configuration\n" + 28 "directory as an argument.\n\n")) 29 } 30 31 // Setup our count hook that keeps track of resource changes 32 countHook := new(CountHook) 33 if b.ContextOpts == nil { 34 b.ContextOpts = new(terraform.ContextOpts) 35 } 36 old := b.ContextOpts.Hooks 37 defer func() { b.ContextOpts.Hooks = old }() 38 b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, countHook) 39 40 // Get our context 41 tfCtx, _, err := b.context(op) 42 if err != nil { 43 runningOp.Err = err 44 return 45 } 46 47 // Setup the state 48 runningOp.State = tfCtx.State() 49 50 // If we're refreshing before plan, perform that 51 if op.PlanRefresh { 52 log.Printf("[INFO] backend/local: plan calling Refresh") 53 54 if b.CLI != nil { 55 b.CLI.Output(b.Colorize().Color(strings.TrimSpace(planRefreshing) + "\n")) 56 } 57 58 _, err := tfCtx.Refresh() 59 if err != nil { 60 runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", err) 61 return 62 } 63 } 64 65 // Perform the plan 66 log.Printf("[INFO] backend/local: plan calling Plan") 67 plan, err := tfCtx.Plan() 68 if err != nil { 69 runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err) 70 return 71 } 72 73 // Record state 74 runningOp.PlanEmpty = plan.Diff.Empty() 75 76 // Save the plan to disk 77 if path := op.PlanOutPath; path != "" { 78 // Write the backend if we have one 79 plan.Backend = op.PlanOutBackend 80 81 log.Printf("[INFO] backend/local: writing plan output to: %s", path) 82 f, err := os.Create(path) 83 if err == nil { 84 err = terraform.WritePlan(plan, f) 85 } 86 f.Close() 87 if err != nil { 88 runningOp.Err = fmt.Errorf("Error writing plan file: %s", err) 89 return 90 } 91 } 92 93 // Perform some output tasks if we have a CLI to output to. 94 if b.CLI != nil { 95 if plan.Diff.Empty() { 96 b.CLI.Output(b.Colorize().Color(strings.TrimSpace(planNoChanges))) 97 return 98 } 99 100 if path := op.PlanOutPath; path == "" { 101 b.CLI.Output(strings.TrimSpace(planHeaderNoOutput) + "\n") 102 } else { 103 b.CLI.Output(fmt.Sprintf( 104 strings.TrimSpace(planHeaderYesOutput)+"\n", 105 path)) 106 } 107 108 b.CLI.Output(format.Plan(&format.PlanOpts{ 109 Plan: plan, 110 Color: b.Colorize(), 111 ModuleDepth: -1, 112 })) 113 114 b.CLI.Output(b.Colorize().Color(fmt.Sprintf( 115 "[reset][bold]Plan:[reset] "+ 116 "%d to add, %d to change, %d to destroy.", 117 countHook.ToAdd+countHook.ToRemoveAndAdd, 118 countHook.ToChange, 119 countHook.ToRemove+countHook.ToRemoveAndAdd))) 120 } 121 } 122 123 const planHeaderNoOutput = ` 124 The Terraform execution plan has been generated and is shown below. 125 Resources are shown in alphabetical order for quick scanning. Green resources 126 will be created (or destroyed and then created if an existing resource 127 exists), yellow resources are being changed in-place, and red resources 128 will be destroyed. Cyan entries are data sources to be read. 129 130 Note: You didn't specify an "-out" parameter to save this plan, so when 131 "apply" is called, Terraform can't guarantee this is what will execute. 132 ` 133 134 const planHeaderYesOutput = ` 135 The Terraform execution plan has been generated and is shown below. 136 Resources are shown in alphabetical order for quick scanning. Green resources 137 will be created (or destroyed and then created if an existing resource 138 exists), yellow resources are being changed in-place, and red resources 139 will be destroyed. Cyan entries are data sources to be read. 140 141 Your plan was also saved to the path below. Call the "apply" subcommand 142 with this plan file and Terraform will exactly execute this execution 143 plan. 144 145 Path: %s 146 ` 147 148 const planNoChanges = ` 149 [reset][bold][green]No changes. Infrastructure is up-to-date.[reset][green] 150 151 This means that Terraform could not detect any differences between your 152 configuration and real physical resources that exist. As a result, Terraform 153 doesn't need to do anything. 154 ` 155 156 const planRefreshing = ` 157 [reset][bold]Refreshing Terraform state in-memory prior to plan...[reset] 158 The refreshed state will be used to calculate this plan, but will not be 159 persisted to local or remote state storage. 160 `