github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/command/state_show.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/iaas-resource-provision/iaas-rpc/internal/addrs"
     9  	"github.com/iaas-resource-provision/iaas-rpc/internal/backend"
    10  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/format"
    11  	"github.com/iaas-resource-provision/iaas-rpc/internal/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.ignoreRemoteBackendVersionConflict(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  	ctx, _, ctxDiags := local.Context(opReq)
    86  	if ctxDiags.HasErrors() {
    87  		c.showDiagnostics(ctxDiags)
    88  		return 1
    89  	}
    90  
    91  	// Get the schemas from the context
    92  	schemas := ctx.Schemas()
    93  
    94  	// Get the state
    95  	env, err := c.Workspace()
    96  	if err != nil {
    97  		c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err))
    98  		return 1
    99  	}
   100  	stateMgr, err := b.StateMgr(env)
   101  	if err != nil {
   102  		c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
   103  		return 1
   104  	}
   105  	if err := stateMgr.RefreshState(); err != nil {
   106  		c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
   107  		return 1
   108  	}
   109  
   110  	state := stateMgr.State()
   111  	if state == nil {
   112  		c.Ui.Error(errStateNotFound)
   113  		return 1
   114  	}
   115  
   116  	is := state.ResourceInstance(addr)
   117  	if !is.HasCurrent() {
   118  		c.Ui.Error(errNoInstanceFound)
   119  		return 1
   120  	}
   121  
   122  	// check if the resource has a configured provider, otherwise this will use the default provider
   123  	rs := state.Resource(addr.ContainingResource())
   124  	absPc := addrs.AbsProviderConfig{
   125  		Provider: rs.ProviderConfig.Provider,
   126  		Alias:    rs.ProviderConfig.Alias,
   127  		Module:   addrs.RootModule,
   128  	}
   129  	singleInstance := states.NewState()
   130  	singleInstance.EnsureModule(addr.Module).SetResourceInstanceCurrent(
   131  		addr.Resource,
   132  		is.Current,
   133  		absPc,
   134  	)
   135  
   136  	output := format.State(&format.StateOpts{
   137  		State:   singleInstance,
   138  		Color:   c.Colorize(),
   139  		Schemas: schemas,
   140  	})
   141  	c.Ui.Output(output[strings.Index(output, "#"):])
   142  
   143  	return 0
   144  }
   145  
   146  func (c *StateShowCommand) Help() string {
   147  	helpText := `
   148  Usage: terraform [global options] state show [options] ADDRESS
   149  
   150    Shows the attributes of a resource in the Terraform state.
   151  
   152    This command shows the attributes of a single resource in the Terraform
   153    state. The address argument must be used to specify a single resource.
   154    You can view the list of available resources with "terraform state list".
   155  
   156  Options:
   157  
   158    -state=statefile    Path to a Terraform state file to use to look
   159                        up Terraform-managed resources. By default it will
   160                        use the state "resource_state.json" if it exists.
   161  
   162  `
   163  	return strings.TrimSpace(helpText)
   164  }
   165  
   166  func (c *StateShowCommand) Synopsis() string {
   167  	return "Show a resource in the state"
   168  }
   169  
   170  const errNoInstanceFound = `No instance found for the given address!
   171  
   172  This command requires that the address references one specific instance.
   173  To view the available instances, use "terraform state list". Please modify 
   174  the address to reference a specific instance.`
   175  
   176  const errParsingAddress = `Error parsing instance address: %s
   177  
   178  This command requires that the address references one specific instance.
   179  To view the available instances, use "terraform state list". Please modify 
   180  the address to reference a specific instance.`