github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/backend/local/backend_refresh.go (about)

     1  package local
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/errwrap"
    10  	"github.com/hashicorp/go-multierror"
    11  	"github.com/hashicorp/terraform/backend"
    12  	"github.com/hashicorp/terraform/command/clistate"
    13  	"github.com/hashicorp/terraform/config/module"
    14  	"github.com/hashicorp/terraform/state"
    15  )
    16  
    17  func (b *Local) opRefresh(
    18  	ctx context.Context,
    19  	op *backend.Operation,
    20  	runningOp *backend.RunningOperation) {
    21  	// Check if our state exists if we're performing a refresh operation. We
    22  	// only do this if we're managing state with this backend.
    23  	if b.Backend == nil {
    24  		if _, err := os.Stat(b.StatePath); err != nil {
    25  			if os.IsNotExist(err) {
    26  				err = nil
    27  			}
    28  
    29  			if err != nil {
    30  				runningOp.Err = fmt.Errorf(
    31  					"There was an error reading the Terraform state that is needed\n"+
    32  						"for refreshing. The path and error are shown below.\n\n"+
    33  						"Path: %s\n\nError: %s",
    34  					b.StatePath, err)
    35  				return
    36  			}
    37  		}
    38  	}
    39  
    40  	// If we have no config module given to use, create an empty tree to
    41  	// avoid crashes when Terraform.Context is initialized.
    42  	if op.Module == nil {
    43  		op.Module = module.NewEmptyTree()
    44  	}
    45  
    46  	// Get our context
    47  	tfCtx, opState, err := b.context(op)
    48  	if err != nil {
    49  		runningOp.Err = err
    50  		return
    51  	}
    52  
    53  	if op.LockState {
    54  		lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
    55  		defer cancel()
    56  
    57  		lockInfo := state.NewLockInfo()
    58  		lockInfo.Operation = op.Type.String()
    59  		lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
    60  		if err != nil {
    61  			runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
    62  			return
    63  		}
    64  
    65  		defer func() {
    66  			if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil {
    67  				runningOp.Err = multierror.Append(runningOp.Err, err)
    68  			}
    69  		}()
    70  	}
    71  
    72  	// Set our state
    73  	runningOp.State = opState.State()
    74  	if runningOp.State.Empty() || !runningOp.State.HasResources() {
    75  		if b.CLI != nil {
    76  			b.CLI.Output(b.Colorize().Color(
    77  				strings.TrimSpace(refreshNoState) + "\n"))
    78  		}
    79  	}
    80  
    81  	// Perform operation and write the resulting state to the running op
    82  	newState, err := tfCtx.Refresh()
    83  	runningOp.State = newState
    84  	if err != nil {
    85  		runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", err)
    86  		return
    87  	}
    88  
    89  	// Write and persist the state
    90  	if err := opState.WriteState(newState); err != nil {
    91  		runningOp.Err = errwrap.Wrapf("Error writing state: {{err}}", err)
    92  		return
    93  	}
    94  	if err := opState.PersistState(); err != nil {
    95  		runningOp.Err = errwrap.Wrapf("Error saving state: {{err}}", err)
    96  		return
    97  	}
    98  }
    99  
   100  const refreshNoState = `
   101  [reset][bold][yellow]Empty or non-existent state file.[reset][yellow]
   102  
   103  Refresh will do nothing. Refresh does not error or return an erroneous
   104  exit status because many automation scripts use refresh, plan, then apply
   105  and may not have a state file yet for the first run.
   106  `