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  `