github.com/hartzell/terraform@v0.8.6-0.20180503104400-0cc9e050ecd4/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/terraform/backend"
    12  	"github.com/hashicorp/terraform/config/module"
    13  	"github.com/hashicorp/terraform/terraform"
    14  )
    15  
    16  func (b *Local) opRefresh(
    17  	stopCtx context.Context,
    18  	cancelCtx 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  	// Set our state
    54  	runningOp.State = opState.State()
    55  	if runningOp.State.Empty() || !runningOp.State.HasResources() {
    56  		if b.CLI != nil {
    57  			b.CLI.Output(b.Colorize().Color(
    58  				strings.TrimSpace(refreshNoState) + "\n"))
    59  		}
    60  	}
    61  
    62  	// Perform the refresh in a goroutine so we can be interrupted
    63  	var newState *terraform.State
    64  	var refreshErr error
    65  	doneCh := make(chan struct{})
    66  	go func() {
    67  		defer close(doneCh)
    68  		newState, refreshErr = tfCtx.Refresh()
    69  		log.Printf("[INFO] backend/local: refresh calling Refresh")
    70  	}()
    71  
    72  	if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState) {
    73  		return
    74  	}
    75  
    76  	// write the resulting state to the running op
    77  	runningOp.State = newState
    78  	if refreshErr != nil {
    79  		runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", refreshErr)
    80  		return
    81  	}
    82  
    83  	// Write and persist the state
    84  	if err := opState.WriteState(newState); err != nil {
    85  		runningOp.Err = errwrap.Wrapf("Error writing state: {{err}}", err)
    86  		return
    87  	}
    88  	if err := opState.PersistState(); err != nil {
    89  		runningOp.Err = errwrap.Wrapf("Error saving state: {{err}}", err)
    90  		return
    91  	}
    92  }
    93  
    94  const refreshNoState = `
    95  [reset][bold][yellow]Empty or non-existent state file.[reset][yellow]
    96  
    97  Refresh will do nothing. Refresh does not error or return an erroneous
    98  exit status because many automation scripts use refresh, plan, then apply
    99  and may not have a state file yet for the first run.
   100  `