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.`