opentofu/command/state_list.go
Martin Atkins c39905e1a8 command: Fix various issues in the "terraform state ..." subcommands
In earlier refactoring we updated these commands to support the new
address and state types, but attempted to partially retain the old-style
"StateFilter" abstraction that originally lived in the Terraform package,
even though that was no longer being used for any other functionality.

Unfortunately the adaptation of the existing filtering to the new types
wasn't exact and so these commands ended up having a few bugs that were
not covered by the existing tests.

Since the old StateFilter behavior was the source of various misbehavior
anyway, here it's removed altogether and replaced with some simpler
functions in the state_meta.go file that are tailored to the use-cases of
these sub-commands.

As well as just generally behaving more consistently with the other
parts of Terraform that use the new resource address types, this commit
fixes the following bugs:

- A resource address of aws_instance.foo would previously match an
  resource of that type and name in any module, which disagreed with the
  expected interpretation elsewhere of meaning a single resource in the
  root module.

- The "terraform state mv" command was not supporting moves from a single
  resource address to an indexed address and vice-versa, because the old
  logic didn't need to make that distinction while they are two separate
  address types in the new logic. Now we allow resources that do not have
  count/for_each to be treated as if they are instances for the purposes
  of this command, which is a better match for likely user intent and for
  the old behavior.

Finally, we also clean up a little some of the usage output from these
commands, which hasn't been updated for some time and so had both some
stale information and some inaccurate terminology.
2019-03-18 09:19:55 -07:00

138 lines
3.6 KiB
Go

package command
import (
"fmt"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/cli"
)
// StateListCommand is a Command implementation that lists the resources
// within a state file.
type StateListCommand struct {
Meta
StateMeta
}
func (c *StateListCommand) Run(args []string) int {
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.defaultFlagSet("state list")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
lookupId := cmdFlags.String("id", "", "Restrict output to paths with a resource having the specified ID.")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
}
args = cmdFlags.Args()
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
return 1
}
// Get the state
env := c.Workspace()
stateMgr, err := b.StateMgr(env)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return 1
}
if err := stateMgr.RefreshState(); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
return 1
}
state := stateMgr.State()
if state == nil {
c.Ui.Error(fmt.Sprintf(errStateNotFound))
return 1
}
var addrs []addrs.AbsResourceInstance
var diags tfdiags.Diagnostics
if len(args) == 0 {
addrs, diags = c.lookupAllResourceInstanceAddrs(state)
} else {
addrs, diags = c.lookupResourceInstanceAddrs(state, args...)
}
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
for _, addr := range addrs {
if is := state.ResourceInstance(addr); is != nil {
if *lookupId == "" || *lookupId == states.LegacyInstanceObjectID(is.Current) {
c.Ui.Output(addr.String())
}
}
}
c.showDiagnostics(diags)
return 0
}
func (c *StateListCommand) Help() string {
helpText := `
Usage: terraform state list [options] [address...]
List resources in the Terraform state.
This command lists resource instances in the Terraform state. The address
argument can be used to filter the instances by resource or module. If
no pattern is given, all resource instances are listed.
The addresses must either be module addresses or absolute resource
addresses, such as:
aws_instance.example
module.example
module.example.module.child
module.example.aws_instance.example
An error will be returned if any of the resources or modules given as
filter addresses do not exist in the state.
Options:
-state=statefile Path to a Terraform state file to use to look
up Terraform-managed resources. By default, Terraform
will consult the state of the currently-selected
workspace.
-id=ID Filters the results to include only instances whose
resource types have an attribute named "id" whose value
equals the given id string.
`
return strings.TrimSpace(helpText)
}
func (c *StateListCommand) Synopsis() string {
return "List resources in the state"
}
const errStateFilter = `Error filtering state: %[1]s
Please ensure that all your addresses are formatted properly.`
const errStateLoadingState = `Error loading the state: %[1]s
Please ensure that your Terraform state exists and that you've
configured it properly. You can use the "-state" flag to point
Terraform at another state file.`
const errStateNotFound = `No state file was found!
State management commands require a state file. Run this command
in a directory where Terraform has been run or use the -state flag
to point the command to a specific state location.`