2016-03-25 18:17:25 +01:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-10-26 19:08:46 +02:00
|
|
|
"os"
|
2016-03-25 18:17:25 +01:00
|
|
|
"strings"
|
|
|
|
|
2018-10-26 19:08:46 +02:00
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
|
|
"github.com/hashicorp/terraform/backend"
|
|
|
|
"github.com/hashicorp/terraform/command/format"
|
|
|
|
"github.com/hashicorp/terraform/states"
|
2016-03-25 18:17:25 +01:00
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
// StateShowCommand is a Command implementation that shows a single resource.
|
|
|
|
type StateShowCommand struct {
|
2017-03-01 16:10:47 +01:00
|
|
|
Meta
|
2016-03-25 18:17:25 +01:00
|
|
|
StateMeta
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *StateShowCommand) Run(args []string) int {
|
2020-04-01 21:01:08 +02:00
|
|
|
args = c.Meta.process(args)
|
2018-11-21 15:35:27 +01:00
|
|
|
cmdFlags := c.Meta.defaultFlagSet("state show")
|
2018-10-26 19:08:46 +02:00
|
|
|
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
|
2016-03-25 18:17:25 +01:00
|
|
|
if err := cmdFlags.Parse(args); err != nil {
|
2019-08-16 14:31:21 +02:00
|
|
|
c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
|
|
|
|
return 1
|
2016-03-25 18:17:25 +01:00
|
|
|
}
|
|
|
|
args = cmdFlags.Args()
|
2018-10-26 19:08:46 +02:00
|
|
|
if len(args) != 1 {
|
|
|
|
c.Ui.Error("Exactly one argument expected.\n")
|
|
|
|
return cli.RunResultHelp
|
|
|
|
}
|
2016-03-25 18:17:25 +01:00
|
|
|
|
2019-03-05 17:32:11 +01:00
|
|
|
// Check for user-supplied plugin path
|
2020-04-01 21:01:08 +02:00
|
|
|
var err error
|
2019-03-05 17:32:11 +01:00
|
|
|
if c.pluginPath, err = c.loadPluginPath(); err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:45 +01:00
|
|
|
// Load the backend
|
2018-03-28 00:31:05 +02:00
|
|
|
b, backendDiags := c.Backend(nil)
|
|
|
|
if backendDiags.HasErrors() {
|
|
|
|
c.showDiagnostics(backendDiags)
|
2017-01-19 05:50:45 +01:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2018-10-26 19:08:46 +02:00
|
|
|
// We require a local backend
|
|
|
|
local, ok := b.(backend.Local)
|
|
|
|
if !ok {
|
|
|
|
c.Ui.Error(ErrUnsupportedLocalOp)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the address can be parsed
|
|
|
|
addr, addrDiags := addrs.ParseAbsResourceInstanceStr(args[0])
|
|
|
|
if addrDiags.HasErrors() {
|
|
|
|
c.Ui.Error(fmt.Sprintf(errParsingAddress, args[0]))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect the config dir to always be the cwd
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf("Error getting cwd: %s", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the operation (required to get the schemas)
|
|
|
|
opReq := c.Operation(b)
|
2019-10-09 23:29:40 +02:00
|
|
|
opReq.AllowUnsetVariables = true
|
2018-10-26 19:08:46 +02:00
|
|
|
opReq.ConfigDir = cwd
|
2018-11-21 15:35:27 +01:00
|
|
|
|
2018-10-26 19:08:46 +02:00
|
|
|
opReq.ConfigLoader, err = c.initConfigLoader()
|
|
|
|
if err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf("Error initializing config loader: %s", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the context (required to get the schemas)
|
|
|
|
ctx, _, ctxDiags := local.Context(opReq)
|
|
|
|
if ctxDiags.HasErrors() {
|
|
|
|
c.showDiagnostics(ctxDiags)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the schemas from the context
|
|
|
|
schemas := ctx.Schemas()
|
|
|
|
|
2017-01-19 05:50:45 +01:00
|
|
|
// Get the state
|
2017-05-31 02:13:43 +02:00
|
|
|
env := c.Workspace()
|
2018-10-26 19:08:46 +02:00
|
|
|
stateMgr, err := b.StateMgr(env)
|
2017-01-19 05:50:45 +01:00
|
|
|
if err != nil {
|
2018-10-26 19:08:46 +02:00
|
|
|
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
2017-01-19 05:50:45 +01:00
|
|
|
return 1
|
2016-03-25 18:17:25 +01:00
|
|
|
}
|
2018-10-26 19:08:46 +02:00
|
|
|
if err := stateMgr.RefreshState(); err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
|
2017-02-22 05:35:43 +01:00
|
|
|
return 1
|
|
|
|
}
|
2016-03-25 18:17:25 +01:00
|
|
|
|
2018-10-26 19:08:46 +02:00
|
|
|
state := stateMgr.State()
|
|
|
|
if state == nil {
|
2016-03-25 18:17:25 +01:00
|
|
|
c.Ui.Error(fmt.Sprintf(errStateNotFound))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2018-10-26 19:08:46 +02:00
|
|
|
is := state.ResourceInstance(addr)
|
|
|
|
if !is.HasCurrent() {
|
|
|
|
c.Ui.Error(errNoInstanceFound)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2020-02-04 18:07:59 +01:00
|
|
|
// check if the resource has a configured provider, otherwise this will use the default provider
|
|
|
|
rs := state.Resource(addr.ContainingResource())
|
2020-02-13 21:32:58 +01:00
|
|
|
absPc := addrs.AbsProviderConfig{
|
|
|
|
Provider: rs.ProviderConfig.Provider,
|
|
|
|
Alias: rs.ProviderConfig.Alias,
|
2020-03-11 19:19:52 +01:00
|
|
|
Module: addrs.RootModule,
|
2020-02-13 21:32:58 +01:00
|
|
|
}
|
2018-10-26 19:08:46 +02:00
|
|
|
singleInstance := states.NewState()
|
|
|
|
singleInstance.EnsureModule(addr.Module).SetResourceInstanceCurrent(
|
|
|
|
addr.Resource,
|
|
|
|
is.Current,
|
2020-02-04 18:07:59 +01:00
|
|
|
absPc,
|
2018-10-26 19:08:46 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
output := format.State(&format.StateOpts{
|
|
|
|
State: singleInstance,
|
|
|
|
Color: c.Colorize(),
|
|
|
|
Schemas: schemas,
|
|
|
|
})
|
|
|
|
c.Ui.Output(output[strings.Index(output, "#"):])
|
|
|
|
|
|
|
|
return 0
|
2016-03-25 18:17:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *StateShowCommand) Help() string {
|
|
|
|
helpText := `
|
2016-03-25 22:33:31 +01:00
|
|
|
Usage: terraform state show [options] ADDRESS
|
2016-03-25 18:17:25 +01:00
|
|
|
|
|
|
|
Shows the attributes of a resource in the Terraform state.
|
|
|
|
|
|
|
|
This command shows the attributes of a single resource in the Terraform
|
2016-03-25 22:33:31 +01:00
|
|
|
state. The address argument must be used to specify a single resource.
|
2016-03-25 18:17:25 +01:00
|
|
|
You can view the list of available resources with "terraform state list".
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
-state=statefile Path to a Terraform state file to use to look
|
|
|
|
up Terraform-managed resources. By default it will
|
|
|
|
use the state "terraform.tfstate" if it exists.
|
|
|
|
|
|
|
|
`
|
|
|
|
return strings.TrimSpace(helpText)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *StateShowCommand) Synopsis() string {
|
|
|
|
return "Show a resource in the state"
|
|
|
|
}
|
2018-10-26 19:08:46 +02:00
|
|
|
|
|
|
|
const errNoInstanceFound = `No instance found for the given address!
|
|
|
|
|
|
|
|
This command requires that the address references one specific instance.
|
|
|
|
To view the available instances, use "terraform state list". Please modify
|
|
|
|
the address to reference a specific instance.`
|
|
|
|
|
|
|
|
const errParsingAddress = `Error parsing instance address: %s
|
|
|
|
|
|
|
|
This command requires that the address references one specific instance.
|
|
|
|
To view the available instances, use "terraform state list". Please modify
|
|
|
|
the address to reference a specific instance.`
|