github.com/kevinklinger/open_terraform@v1.3.6/noninternal/command/state_show.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/kevinklinger/open_terraform/noninternal/addrs"
     9  	"github.com/kevinklinger/open_terraform/noninternal/backend"
    10  	"github.com/kevinklinger/open_terraform/noninternal/command/format"
    11  	"github.com/kevinklinger/open_terraform/noninternal/states"
    12  	"github.com/mitchellh/cli"
    13  )
    14  
    15  // StateShowCommand is a Command implementation that shows a single resource.
    16  type StateShowCommand struct {
    17  	Meta
    18  	StateMeta
    19  }
    20  
    21  func (c *StateShowCommand) Run(args []string) int {
    22  	args = c.Meta.process(args)
    23  	cmdFlags := c.Meta.defaultFlagSet("state show")
    24  	cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
    25  	if err := cmdFlags.Parse(args); err != nil {
    26  		c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
    27  		return 1
    28  	}
    29  	args = cmdFlags.Args()
    30  	if len(args) != 1 {
    31  		c.Ui.Error("Exactly one argument expected.\n")
    32  		return cli.RunResultHelp
    33  	}
    34  
    35  	// Check for user-supplied plugin path
    36  	var err error
    37  	if c.pluginPath, err = c.loadPluginPath(); err != nil {
    38  		c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err))
    39  		return 1
    40  	}
    41  
    42  	// Load the backend
    43  	b, backendDiags := c.Backend(nil)
    44  	if backendDiags.HasErrors() {
    45  		c.showDiagnostics(backendDiags)
    46  		return 1
    47  	}
    48  
    49  	// We require a local backend
    50  	local, ok := b.(backend.Local)
    51  	if !ok {
    52  		c.Ui.Error(ErrUnsupportedLocalOp)
    53  		return 1
    54  	}
    55  
    56  	// This is a read-only command
    57  	c.ignoreRemoteVersionConflict(b)
    58  
    59  	// Check if the address can be parsed
    60  	addr, addrDiags := addrs.ParseAbsResourceInstanceStr(args[0])
    61  	if addrDiags.HasErrors() {
    62  		c.Ui.Error(fmt.Sprintf(errParsingAddress, args[0]))
    63  		return 1
    64  	}
    65  
    66  	// We expect the config dir to always be the cwd
    67  	cwd, err := os.Getwd()
    68  	if err != nil {
    69  		c.Ui.Error(fmt.Sprintf("Error getting cwd: %s", err))
    70  		return 1
    71  	}
    72  
    73  	// Build the operation (required to get the schemas)
    74  	opReq := c.Operation(b)
    75  	opReq.AllowUnsetVariables = true
    76  	opReq.ConfigDir = cwd
    77  
    78  	opReq.ConfigLoader, err = c.initConfigLoader()
    79  	if err != nil {
    80  		c.Ui.Error(fmt.Sprintf("Error initializing config loader: %s", err))
    81  		return 1
    82  	}
    83  
    84  	// Get the context (required to get the schemas)
    85  	lr, _, ctxDiags := local.LocalRun(opReq)
    86  	if ctxDiags.HasErrors() {
    87  		c.showDiagnostics(ctxDiags)
    88  		return 1
    89  	}
    90  
    91  	// Get the schemas from the context
    92  	schemas, diags := lr.Core.Schemas(lr.Config, lr.InputState)
    93  	if diags.HasErrors() {
    94  		c.showDiagnostics(diags)
    95  		return 1
    96  	}
    97  
    98  	// Get the state
    99  	env, err := c.Workspace()
   100  	if err != nil {
   101  		c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err))
   102  		return 1
   103  	}
   104  	stateMgr, err := b.StateMgr(env)
   105  	if err != nil {
   106  		c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
   107  		return 1
   108  	}
   109  	if err := stateMgr.RefreshState(); err != nil {
   110  		c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
   111  		return 1
   112  	}
   113  
   114  	state := stateMgr.State()
   115  	if state == nil {
   116  		c.Ui.Error(errStateNotFound)
   117  		return 1
   118  	}
   119  
   120  	is := state.ResourceInstance(addr)
   121  	if !is.HasCurrent() {
   122  		c.Ui.Error(errNoInstanceFound)
   123  		return 1
   124  	}
   125  
   126  	// check if the resource has a configured provider, otherwise this will use the default provider
   127  	rs := state.Resource(addr.ContainingResource())
   128  	absPc := addrs.AbsProviderConfig{
   129  		Provider: rs.ProviderConfig.Provider,
   130  		Alias:    rs.ProviderConfig.Alias,
   131  		Module:   addrs.RootModule,
   132  	}
   133  	singleInstance := states.NewState()
   134  	singleInstance.EnsureModule(addr.Module).SetResourceInstanceCurrent(
   135  		addr.Resource,
   136  		is.Current,
   137  		absPc,
   138  	)
   139  
   140  	output := format.State(&format.StateOpts{
   141  		State:   singleInstance,
   142  		Color:   c.Colorize(),
   143  		Schemas: schemas,
   144  	})
   145  	c.Ui.Output(output[strings.Index(output, "#"):])
   146  
   147  	return 0
   148  }
   149  
   150  func (c *StateShowCommand) Help() string {
   151  	helpText := `
   152  Usage: terraform [global options] state show [options] ADDRESS
   153  
   154    Shows the attributes of a resource in the Terraform state.
   155  
   156    This command shows the attributes of a single resource in the Terraform
   157    state. The address argument must be used to specify a single resource.
   158    You can view the list of available resources with "terraform state list".
   159  
   160  Options:
   161  
   162    -state=statefile    Path to a Terraform state file to use to look
   163                        up Terraform-managed resources. By default it will
   164                        use the state "terraform.tfstate" if it exists.
   165  
   166  `
   167  	return strings.TrimSpace(helpText)
   168  }
   169  
   170  func (c *StateShowCommand) Synopsis() string {
   171  	return "Show a resource in the state"
   172  }
   173  
   174  const errNoInstanceFound = `No instance found for the given address!
   175  
   176  This command requires that the address references one specific instance.
   177  To view the available instances, use "terraform state list". Please modify 
   178  the address to reference a specific instance.`
   179  
   180  const errParsingAddress = `Error parsing instance address: %s
   181  
   182  This command requires that the address references one specific instance.
   183  To view the available instances, use "terraform state list". Please modify 
   184  the address to reference a specific instance.`