mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-16 18:35:03 -06:00
This allows a refresh on a non-existent or empty state file. We changed this in 0.9.0 to error which seemed reasonable but it turns out this complicates automation that runs refresh since it now needed to determine if the state file was empty before running. Its easier to just revert this into a warning with exit code zero. The reason this changed is because in 0.8.x and earlier, the output would be simply empty with exit code zero which seemed odd.
104 lines
2.7 KiB
Go
104 lines
2.7 KiB
Go
package local
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/terraform/backend"
|
|
clistate "github.com/hashicorp/terraform/command/state"
|
|
"github.com/hashicorp/terraform/config/module"
|
|
"github.com/hashicorp/terraform/state"
|
|
)
|
|
|
|
func (b *Local) opRefresh(
|
|
ctx context.Context,
|
|
op *backend.Operation,
|
|
runningOp *backend.RunningOperation) {
|
|
// Check if our state exists if we're performing a refresh operation. We
|
|
// only do this if we're managing state with this backend.
|
|
if b.Backend == nil {
|
|
if _, err := os.Stat(b.StatePath); err != nil {
|
|
if os.IsNotExist(err) {
|
|
err = nil
|
|
}
|
|
|
|
if err != nil {
|
|
runningOp.Err = fmt.Errorf(
|
|
"There was an error reading the Terraform state that is needed\n"+
|
|
"for refreshing. The path and error are shown below.\n\n"+
|
|
"Path: %s\n\nError: %s",
|
|
b.StatePath, err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have no config module given to use, create an empty tree to
|
|
// avoid crashes when Terraform.Context is initialized.
|
|
if op.Module == nil {
|
|
op.Module = module.NewEmptyTree()
|
|
}
|
|
|
|
// Get our context
|
|
tfCtx, opState, err := b.context(op)
|
|
if err != nil {
|
|
runningOp.Err = err
|
|
return
|
|
}
|
|
|
|
if op.LockState {
|
|
lockInfo := state.NewLockInfo()
|
|
lockInfo.Operation = op.Type.String()
|
|
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
|
if err != nil {
|
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil {
|
|
runningOp.Err = multierror.Append(runningOp.Err, err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Set our state
|
|
runningOp.State = opState.State()
|
|
if runningOp.State.Empty() || !runningOp.State.HasResources() {
|
|
if b.CLI != nil {
|
|
b.CLI.Output(b.Colorize().Color(
|
|
strings.TrimSpace(refreshNoState) + "\n"))
|
|
}
|
|
}
|
|
|
|
// Perform operation and write the resulting state to the running op
|
|
newState, err := tfCtx.Refresh()
|
|
runningOp.State = newState
|
|
if err != nil {
|
|
runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", err)
|
|
return
|
|
}
|
|
|
|
// Write and persist the state
|
|
if err := opState.WriteState(newState); err != nil {
|
|
runningOp.Err = errwrap.Wrapf("Error writing state: {{err}}", err)
|
|
return
|
|
}
|
|
if err := opState.PersistState(); err != nil {
|
|
runningOp.Err = errwrap.Wrapf("Error saving state: {{err}}", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
const refreshNoState = `
|
|
[reset][bold][yellow]Empty or non-existent state file.[reset][yellow]
|
|
|
|
Refresh will do nothing. Refresh does not error or return an erroneous
|
|
exit status because many automation scripts use refresh, plan, then apply
|
|
and may not have a state file yet for the first run.
|
|
`
|