230 lines
7.1 KiB
Go
230 lines
7.1 KiB
Go
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/terraform/backend"
|
|
"github.com/hashicorp/terraform/command/arguments"
|
|
"github.com/hashicorp/terraform/command/views"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// RefreshCommand is a cli.Command implementation that refreshes the state
|
|
// file.
|
|
type RefreshCommand struct {
|
|
Meta
|
|
}
|
|
|
|
func (c *RefreshCommand) Run(rawArgs []string) int {
|
|
// Parse and apply global view arguments
|
|
common, rawArgs := arguments.ParseView(rawArgs)
|
|
c.View.Configure(common)
|
|
|
|
// Parse and validate flags
|
|
args, diags := arguments.ParseRefresh(rawArgs)
|
|
|
|
// Instantiate the view, even if there are flag errors, so that we render
|
|
// diagnostics according to the desired view
|
|
var view views.Refresh
|
|
view = views.NewRefresh(args.ViewType, c.RunningInAutomation, c.View)
|
|
|
|
if diags.HasErrors() {
|
|
view.Diagnostics(diags)
|
|
view.HelpPrompt("refresh")
|
|
return 1
|
|
}
|
|
|
|
// Check for user-supplied plugin path
|
|
var err error
|
|
if c.pluginPath, err = c.loadPluginPath(); err != nil {
|
|
diags = diags.Append(err)
|
|
view.Diagnostics(diags)
|
|
return 1
|
|
}
|
|
|
|
// FIXME: the -input flag value is needed to initialize the backend and the
|
|
// operation, but there is no clear path to pass this value down, so we
|
|
// continue to mutate the Meta object state for now.
|
|
c.Meta.input = args.InputEnabled
|
|
|
|
// FIXME: the -parallelism flag is used to control the concurrency of
|
|
// Terraform operations. At the moment, this value is used both to
|
|
// initialize the backend via the ContextOpts field inside CLIOpts, and to
|
|
// set a largely unused field on the Operation request. Again, there is no
|
|
// clear path to pass this value down, so we continue to mutate the Meta
|
|
// object state for now.
|
|
c.Meta.parallelism = args.Operation.Parallelism
|
|
|
|
// Prepare the backend with the backend-specific arguments
|
|
be, beDiags := c.PrepareBackend(args.State)
|
|
diags = diags.Append(beDiags)
|
|
if diags.HasErrors() {
|
|
view.Diagnostics(diags)
|
|
return 1
|
|
}
|
|
|
|
// Build the operation request
|
|
opReq, opDiags := c.OperationRequest(be, view, args.Operation)
|
|
diags = diags.Append(opDiags)
|
|
if diags.HasErrors() {
|
|
view.Diagnostics(diags)
|
|
return 1
|
|
}
|
|
|
|
// Collect variable value and add them to the operation request
|
|
diags = diags.Append(c.GatherVariables(opReq, args.Vars))
|
|
if diags.HasErrors() {
|
|
view.Diagnostics(diags)
|
|
return 1
|
|
}
|
|
|
|
// Before we delegate to the backend, we'll print any warning diagnostics
|
|
// we've accumulated here, since the backend will start fresh with its own
|
|
// diagnostics.
|
|
view.Diagnostics(diags)
|
|
diags = nil
|
|
|
|
// Perform the operation
|
|
op, err := c.RunOperation(be, opReq)
|
|
if err != nil {
|
|
diags = diags.Append(err)
|
|
view.Diagnostics(diags)
|
|
return 1
|
|
}
|
|
|
|
if op.State != nil {
|
|
view.Outputs(op.State.RootModule().OutputValues)
|
|
}
|
|
|
|
return op.Result.ExitStatus()
|
|
}
|
|
|
|
func (c *RefreshCommand) PrepareBackend(args *arguments.State) (backend.Enhanced, tfdiags.Diagnostics) {
|
|
// FIXME: we need to apply the state arguments to the meta object here
|
|
// because they are later used when initializing the backend. Carving a
|
|
// path to pass these arguments to the functions that need them is
|
|
// difficult but would make their use easier to understand.
|
|
c.Meta.applyStateArguments(args)
|
|
|
|
backendConfig, diags := c.loadBackendConfig(".")
|
|
if diags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
|
|
// Load the backend
|
|
be, beDiags := c.Backend(&BackendOpts{
|
|
Config: backendConfig,
|
|
})
|
|
diags = diags.Append(beDiags)
|
|
if beDiags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
|
|
return be, diags
|
|
}
|
|
|
|
func (c *RefreshCommand) OperationRequest(be backend.Enhanced, view views.Refresh, args *arguments.Operation,
|
|
) (*backend.Operation, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
// Build the operation
|
|
opReq := c.Operation(be)
|
|
opReq.ConfigDir = "."
|
|
opReq.Hooks = view.Hooks()
|
|
opReq.Targets = args.Targets
|
|
opReq.Type = backend.OperationTypeRefresh
|
|
opReq.View = view.Operation()
|
|
// FIXME: this shim is needed until the remote backend is migrated to views
|
|
opReq.ShowDiagnostics = func(vals ...interface{}) {
|
|
var diags tfdiags.Diagnostics
|
|
diags = diags.Append(vals...)
|
|
view.Diagnostics(diags)
|
|
}
|
|
|
|
var err error
|
|
opReq.ConfigLoader, err = c.initConfigLoader()
|
|
if err != nil {
|
|
diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err))
|
|
return nil, diags
|
|
}
|
|
|
|
return opReq, diags
|
|
}
|
|
|
|
func (c *RefreshCommand) GatherVariables(opReq *backend.Operation, args *arguments.Vars) tfdiags.Diagnostics {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
// FIXME the arguments package currently trivially gathers variable related
|
|
// arguments in a heterogenous slice, in order to minimize the number of
|
|
// code paths gathering variables during the transition to this structure.
|
|
// Once all commands that gather variables have been converted to this
|
|
// structure, we could move the variable gathering code to the arguments
|
|
// package directly, removing this shim layer.
|
|
|
|
varArgs := args.All()
|
|
items := make([]rawFlag, len(varArgs))
|
|
for i := range varArgs {
|
|
items[i].Name = varArgs[i].Name
|
|
items[i].Value = varArgs[i].Value
|
|
}
|
|
c.Meta.variableArgs = rawFlags{items: &items}
|
|
opReq.Variables, diags = c.collectVariableValues()
|
|
|
|
return diags
|
|
}
|
|
|
|
func (c *RefreshCommand) Help() string {
|
|
helpText := `
|
|
Usage: terraform refresh [options]
|
|
|
|
Update the state file of your infrastructure with metadata that matches
|
|
the physical resources they are tracking.
|
|
|
|
This will not modify your infrastructure, but it can modify your
|
|
state file to update metadata. This metadata might cause new changes
|
|
to occur when you generate a plan or call apply next.
|
|
|
|
Options:
|
|
|
|
-backup=path Path to backup the existing state file before
|
|
modifying. Defaults to the "-state-out" path with
|
|
".backup" extension. Set to "-" to disable backup.
|
|
|
|
-compact-warnings If Terraform produces any warnings that are not
|
|
accompanied by errors, show them in a more compact form
|
|
that includes only the summary messages.
|
|
|
|
-input=true Ask for input for variables if not directly set.
|
|
|
|
-lock=true Lock the state file when locking is supported.
|
|
|
|
-lock-timeout=0s Duration to retry a state lock.
|
|
|
|
-no-color If specified, output won't contain any color.
|
|
|
|
-state=path Path to read and save state (unless state-out
|
|
is specified). Defaults to "terraform.tfstate".
|
|
|
|
-state-out=path Path to write updated state file. By default, the
|
|
"-state" path will be used.
|
|
|
|
-target=resource Resource to target. Operation will be limited to this
|
|
resource and its dependencies. This flag can be used
|
|
multiple times.
|
|
|
|
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
|
flag can be set multiple times.
|
|
|
|
-var-file=foo Set variables in the Terraform configuration from
|
|
a file. If "terraform.tfvars" or any ".auto.tfvars"
|
|
files are present, they will be automatically loaded.
|
|
|
|
`
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (c *RefreshCommand) Synopsis() string {
|
|
return "Update the state to match remote systems"
|
|
}
|