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