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 `