github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/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/states"
    13  	"github.com/hashicorp/terraform/states/statemgr"
    14  	"github.com/hashicorp/terraform/tfdiags"
    15  )
    16  
    17  func (b *Local) opRefresh(
    18  	stopCtx context.Context,
    19  	cancelCtx context.Context,
    20  	op *backend.Operation,
    21  	runningOp *backend.RunningOperation) {
    22  
    23  	var diags tfdiags.Diagnostics
    24  
    25  	// Check if our state exists if we're performing a refresh operation. We
    26  	// only do this if we're managing state with this backend.
    27  	if b.Backend == nil {
    28  		if _, err := os.Stat(b.StatePath); err != nil {
    29  			if os.IsNotExist(err) {
    30  				err = nil
    31  			}
    32  
    33  			if err != nil {
    34  				diags = diags.Append(tfdiags.Sourceless(
    35  					tfdiags.Error,
    36  					"Cannot read state file",
    37  					fmt.Sprintf("Failed to read %s: %s", b.StatePath, err),
    38  				))
    39  				b.ReportResult(runningOp, diags)
    40  				return
    41  			}
    42  		}
    43  	}
    44  
    45  	// Get our context
    46  	tfCtx, _, opState, contextDiags := b.context(op)
    47  	diags = diags.Append(contextDiags)
    48  	if contextDiags.HasErrors() {
    49  		b.ReportResult(runningOp, diags)
    50  		return
    51  	}
    52  
    53  	// Set our state
    54  	runningOp.State = opState.State()
    55  	if !runningOp.State.HasResources() {
    56  		if b.CLI != nil {
    57  			diags = diags.Append(tfdiags.Sourceless(
    58  				tfdiags.Warning,
    59  				"Empty or non-existent state",
    60  				"There are currently no resources tracked in the state, so there is nothing to refresh.",
    61  			))
    62  			b.CLI.Output(b.Colorize().Color(strings.TrimSpace(refreshNoState) + "\n"))
    63  		}
    64  	}
    65  
    66  	// Perform the refresh in a goroutine so we can be interrupted
    67  	var newState *states.State
    68  	var refreshDiags tfdiags.Diagnostics
    69  	doneCh := make(chan struct{})
    70  	go func() {
    71  		defer close(doneCh)
    72  		newState, refreshDiags = tfCtx.Refresh()
    73  		log.Printf("[INFO] backend/local: refresh calling Refresh")
    74  	}()
    75  
    76  	if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState) {
    77  		return
    78  	}
    79  
    80  	// write the resulting state to the running op
    81  	runningOp.State = newState
    82  	diags = diags.Append(refreshDiags)
    83  	if refreshDiags.HasErrors() {
    84  		b.ReportResult(runningOp, diags)
    85  		return
    86  	}
    87  
    88  	err := statemgr.WriteAndPersist(opState, newState)
    89  	if err != nil {
    90  		diags = diags.Append(errwrap.Wrapf("Failed to write state: {{err}}", err))
    91  		b.ReportResult(runningOp, diags)
    92  		return
    93  	}
    94  }
    95  
    96  const refreshNoState = `
    97  [reset][bold][yellow]Empty or non-existent state file.[reset][yellow]
    98  
    99  Refresh will do nothing. Refresh does not error or return an erroneous
   100  exit status because many automation scripts use refresh, plan, then apply
   101  and may not have a state file yet for the first run.
   102  `