github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/backend/local/backend_apply.go (about) 1 package local 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 8 "github.com/hashicorp/errwrap" 9 "github.com/hashicorp/go-multierror" 10 "github.com/hashicorp/terraform/backend" 11 clistate "github.com/hashicorp/terraform/command/state" 12 "github.com/hashicorp/terraform/terraform" 13 ) 14 15 func (b *Local) opApply( 16 ctx context.Context, 17 op *backend.Operation, 18 runningOp *backend.RunningOperation) { 19 log.Printf("[INFO] backend/local: starting Apply operation") 20 21 // Setup our count hook that keeps track of resource changes 22 countHook := new(CountHook) 23 stateHook := new(StateHook) 24 if b.ContextOpts == nil { 25 b.ContextOpts = new(terraform.ContextOpts) 26 } 27 old := b.ContextOpts.Hooks 28 defer func() { b.ContextOpts.Hooks = old }() 29 b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, countHook, stateHook) 30 31 // Get our context 32 tfCtx, opState, err := b.context(op) 33 if err != nil { 34 runningOp.Err = err 35 return 36 } 37 38 // If we're locking state, unlock when we're done 39 if op.LockState { 40 defer func() { 41 if err := clistate.Unlock(opState, b.CLI, b.Colorize()); err != nil { 42 runningOp.Err = multierror.Append(runningOp.Err, err) 43 } 44 }() 45 } 46 47 // Setup the state 48 runningOp.State = tfCtx.State() 49 50 // If we weren't given a plan, then we refresh/plan 51 if op.Plan == nil { 52 // If we're refreshing before apply, perform that 53 if op.PlanRefresh { 54 log.Printf("[INFO] backend/local: apply calling Refresh") 55 _, err := tfCtx.Refresh() 56 if err != nil { 57 runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", err) 58 return 59 } 60 } 61 62 // Perform the plan 63 log.Printf("[INFO] backend/local: apply calling Plan") 64 if _, err := tfCtx.Plan(); err != nil { 65 runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err) 66 return 67 } 68 } 69 70 // Setup our hook for continuous state updates 71 stateHook.State = opState 72 73 // Start the apply in a goroutine so that we can be interrupted. 74 var applyState *terraform.State 75 var applyErr error 76 doneCh := make(chan struct{}) 77 go func() { 78 defer close(doneCh) 79 applyState, applyErr = tfCtx.Apply() 80 81 /* 82 // Record any shadow errors for later 83 if err := ctx.ShadowError(); err != nil { 84 shadowErr = multierror.Append(shadowErr, multierror.Prefix( 85 err, "apply operation:")) 86 } 87 */ 88 }() 89 90 // Wait for the apply to finish or for us to be interrupted so 91 // we can handle it properly. 92 err = nil 93 select { 94 case <-ctx.Done(): 95 if b.CLI != nil { 96 b.CLI.Output("Interrupt received. Gracefully shutting down...") 97 } 98 99 // Stop execution 100 go tfCtx.Stop() 101 102 // Wait for completion still 103 <-doneCh 104 case <-doneCh: 105 } 106 107 // Store the final state 108 runningOp.State = applyState 109 110 // Persist the state 111 if err := opState.WriteState(applyState); err != nil { 112 runningOp.Err = fmt.Errorf("Failed to save state: %s", err) 113 return 114 } 115 if err := opState.PersistState(); err != nil { 116 runningOp.Err = fmt.Errorf("Failed to save state: %s", err) 117 return 118 } 119 120 if applyErr != nil { 121 runningOp.Err = fmt.Errorf( 122 "Error applying plan:\n\n"+ 123 "%s\n\n"+ 124 "Terraform does not automatically rollback in the face of errors.\n"+ 125 "Instead, your Terraform state file has been partially updated with\n"+ 126 "any resources that successfully completed. Please address the error\n"+ 127 "above and apply again to incrementally change your infrastructure.", 128 multierror.Flatten(applyErr)) 129 return 130 } 131 132 // If we have a UI, output the results 133 if b.CLI != nil { 134 if op.Destroy { 135 b.CLI.Output(b.Colorize().Color(fmt.Sprintf( 136 "[reset][bold][green]\n"+ 137 "Destroy complete! Resources: %d destroyed.", 138 countHook.Removed))) 139 } else { 140 b.CLI.Output(b.Colorize().Color(fmt.Sprintf( 141 "[reset][bold][green]\n"+ 142 "Apply complete! Resources: %d added, %d changed, %d destroyed.", 143 countHook.Added, 144 countHook.Changed, 145 countHook.Removed))) 146 } 147 148 if countHook.Added > 0 || countHook.Changed > 0 { 149 b.CLI.Output(b.Colorize().Color(fmt.Sprintf( 150 "[reset]\n"+ 151 "The state of your infrastructure has been saved to the path\n"+ 152 "below. This state is required to modify and destroy your\n"+ 153 "infrastructure, so keep it safe. To inspect the complete state\n"+ 154 "use the `terraform show` command.\n\n"+ 155 "State path: %s", 156 b.StateOutPath))) 157 } 158 } 159 }