github.com/jzbruno/terraform@v0.10.3-0.20180104230435-18975d727047/backend/local/backend_refresh.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/go-multierror" 12 "github.com/hashicorp/terraform/backend" 13 "github.com/hashicorp/terraform/command/clistate" 14 "github.com/hashicorp/terraform/config/module" 15 "github.com/hashicorp/terraform/state" 16 "github.com/hashicorp/terraform/terraform" 17 ) 18 19 func (b *Local) opRefresh( 20 ctx context.Context, 21 op *backend.Operation, 22 runningOp *backend.RunningOperation) { 23 // Check if our state exists if we're performing a refresh operation. We 24 // only do this if we're managing state with this backend. 25 if b.Backend == nil { 26 if _, err := os.Stat(b.StatePath); err != nil { 27 if os.IsNotExist(err) { 28 err = nil 29 } 30 31 if err != nil { 32 runningOp.Err = fmt.Errorf( 33 "There was an error reading the Terraform state that is needed\n"+ 34 "for refreshing. The path and error are shown below.\n\n"+ 35 "Path: %s\n\nError: %s", 36 b.StatePath, err) 37 return 38 } 39 } 40 } 41 42 // If we have no config module given to use, create an empty tree to 43 // avoid crashes when Terraform.Context is initialized. 44 if op.Module == nil { 45 op.Module = module.NewEmptyTree() 46 } 47 48 // Get our context 49 tfCtx, opState, err := b.context(op) 50 if err != nil { 51 runningOp.Err = err 52 return 53 } 54 55 if op.LockState { 56 lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout) 57 defer cancel() 58 59 lockInfo := state.NewLockInfo() 60 lockInfo.Operation = op.Type.String() 61 lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize()) 62 if err != nil { 63 runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err) 64 return 65 } 66 67 defer func() { 68 if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil { 69 runningOp.Err = multierror.Append(runningOp.Err, err) 70 } 71 }() 72 } 73 74 // Set our state 75 runningOp.State = opState.State() 76 if runningOp.State.Empty() || !runningOp.State.HasResources() { 77 if b.CLI != nil { 78 b.CLI.Output(b.Colorize().Color( 79 strings.TrimSpace(refreshNoState) + "\n")) 80 } 81 } 82 83 // Perform the refresh in a goroutine so we can be interrupted 84 var newState *terraform.State 85 var refreshErr error 86 doneCh := make(chan struct{}) 87 go func() { 88 defer close(doneCh) 89 newState, err = tfCtx.Refresh() 90 log.Printf("[INFO] backend/local: plan calling Plan") 91 }() 92 93 select { 94 case <-ctx.Done(): 95 if b.CLI != nil { 96 b.CLI.Output("stopping refresh operation...") 97 } 98 99 // Stop execution 100 go tfCtx.Stop() 101 102 // Wait for completion still 103 <-doneCh 104 case <-doneCh: 105 } 106 107 // write the resulting state to the running op 108 runningOp.State = newState 109 if refreshErr != nil { 110 runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", refreshErr) 111 return 112 } 113 114 // Write and persist the state 115 if err := opState.WriteState(newState); err != nil { 116 runningOp.Err = errwrap.Wrapf("Error writing state: {{err}}", err) 117 return 118 } 119 if err := opState.PersistState(); err != nil { 120 runningOp.Err = errwrap.Wrapf("Error saving state: {{err}}", err) 121 return 122 } 123 } 124 125 const refreshNoState = ` 126 [reset][bold][yellow]Empty or non-existent state file.[reset][yellow] 127 128 Refresh will do nothing. Refresh does not error or return an erroneous 129 exit status because many automation scripts use refresh, plan, then apply 130 and may not have a state file yet for the first run. 131 `