kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/backend/local/backend_refresh.go (about)

     1  package local
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  
     9  	"kubeform.dev/terraform-backend-sdk/backend"
    10  	"kubeform.dev/terraform-backend-sdk/states"
    11  	"kubeform.dev/terraform-backend-sdk/states/statemgr"
    12  	"kubeform.dev/terraform-backend-sdk/tfdiags"
    13  )
    14  
    15  func (b *Local) opRefresh(
    16  	stopCtx context.Context,
    17  	cancelCtx context.Context,
    18  	op *backend.Operation,
    19  	runningOp *backend.RunningOperation) {
    20  
    21  	var diags tfdiags.Diagnostics
    22  
    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  				diags = diags.Append(tfdiags.Sourceless(
    33  					tfdiags.Error,
    34  					"Cannot read state file",
    35  					fmt.Sprintf("Failed to read %s: %s", b.StatePath, err),
    36  				))
    37  				op.ReportResult(runningOp, diags)
    38  				return
    39  			}
    40  		}
    41  	}
    42  
    43  	// Refresh now happens via a plan, so we need to ensure this is enabled
    44  	op.PlanRefresh = true
    45  
    46  	// Get our context
    47  	lr, _, opState, contextDiags := b.localRun(op)
    48  	diags = diags.Append(contextDiags)
    49  	if contextDiags.HasErrors() {
    50  		op.ReportResult(runningOp, diags)
    51  		return
    52  	}
    53  
    54  	// the state was locked during succesfull context creation; unlock the state
    55  	// when the operation completes
    56  	defer func() {
    57  		diags := op.StateLocker.Unlock()
    58  		if diags.HasErrors() {
    59  			op.View.Diagnostics(diags)
    60  			runningOp.Result = backend.OperationFailure
    61  		}
    62  	}()
    63  
    64  	// If we succeed then we'll overwrite this with the resulting state below,
    65  	// but otherwise the resulting state is just the input state.
    66  	runningOp.State = lr.InputState
    67  	if !runningOp.State.HasResources() {
    68  		diags = diags.Append(tfdiags.Sourceless(
    69  			tfdiags.Warning,
    70  			"Empty or non-existent state",
    71  			"There are currently no resources tracked in the state, so there is nothing to refresh.",
    72  		))
    73  	}
    74  
    75  	// Perform the refresh in a goroutine so we can be interrupted
    76  	var newState *states.State
    77  	var refreshDiags tfdiags.Diagnostics
    78  	doneCh := make(chan struct{})
    79  	go func() {
    80  		defer close(doneCh)
    81  		newState, refreshDiags = lr.Core.Refresh(lr.Config, lr.InputState, lr.PlanOpts)
    82  		log.Printf("[INFO] backend/local: refresh calling Refresh")
    83  	}()
    84  
    85  	if b.opWait(doneCh, stopCtx, cancelCtx, lr.Core, opState, op.View) {
    86  		return
    87  	}
    88  
    89  	// Write the resulting state to the running op
    90  	runningOp.State = newState
    91  	diags = diags.Append(refreshDiags)
    92  	if refreshDiags.HasErrors() {
    93  		op.ReportResult(runningOp, diags)
    94  		return
    95  	}
    96  
    97  	err := statemgr.WriteAndPersist(opState, newState)
    98  	if err != nil {
    99  		diags = diags.Append(fmt.Errorf("failed to write state: %w", err))
   100  		op.ReportResult(runningOp, diags)
   101  		return
   102  	}
   103  
   104  	// Show any remaining warnings before exiting
   105  	op.ReportResult(runningOp, diags)
   106  }