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  `