Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"state list" command, starting plumbing CLI #5792

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions command/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ func (m *Meta) flagSet(n string) *flag.FlagSet {
}()
f.SetOutput(errW)

// Set the default Usage to empty
f.Usage = func() {}

return f
}

Expand Down
40 changes: 40 additions & 0 deletions command/state_command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package command

import (
"strings"

"github.com/mitchellh/cli"
)

// StateCommand is a Command implementation that just shows help for
// the subcommands nested below it.
type StateCommand struct {
Meta
}

func (c *StateCommand) Run(args []string) int {
return cli.RunResultHelp
}

func (c *StateCommand) Help() string {
helpText := `
Usage: terraform state <subcommand> [options] [args]

This command has subcommands for advanced state management.

These subcommands can be used to slice and dice the Terraform state.
This is sometimes necessary in advanced cases. For your safety, all
state management commands that modify the state create a timestamped
backup of the state prior to making modifications.

The structure and output of the commands is specifically tailored to work
well with the common Unix utilities such as grep, awk, etc. We recommend
using those tools to perform more advanced state tasks.

`
return strings.TrimSpace(helpText)
}

func (c *StateCommand) Synopsis() string {
return "Advanced state management"
}
89 changes: 89 additions & 0 deletions command/state_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package command

import (
"fmt"
"strings"

"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
)

// StateListCommand is a Command implementation that lists the resources
// within a state file.
type StateListCommand struct {
Meta
}

func (c *StateListCommand) Run(args []string) int {
args = c.Meta.process(args, true)

cmdFlags := c.Meta.flagSet("state list")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
}
args = cmdFlags.Args()

state, err := c.State()
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return cli.RunResultHelp
}

filter := &terraform.StateFilter{State: state.State()}
results, err := filter.Filter(args...)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateFilter, err))
return cli.RunResultHelp
}

for _, result := range results {
if _, ok := result.Value.(*terraform.InstanceState); ok {
c.Ui.Output(result.Address)
}
}

return 0
}

func (c *StateListCommand) Help() string {
helpText := `
Usage: terraform state list [options] [pattern...]

List resources in the Terraform state.

This command lists resources in the Terraform state. The pattern argument
can be used to filter the resources by resource or module. If no pattern
is given, all resources are listed.

The pattern argument is meant to provide very simple filtering. For
advanced filtering, please use tools such as "grep". The output of this
command is designed to be friendly for this usage.

The pattern argument accepts any resource targeting syntax. Please
refer to the documentation on resource targeting syntax for more
information.

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 *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.`
21 changes: 21 additions & 0 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

// Commands is the mapping of all the available Terraform commands.
var Commands map[string]cli.CommandFactory
var PlumbingCommands map[string]struct{}

// Ui is the cli.Ui used for communicating to the outside world.
var Ui cli.Ui
Expand All @@ -34,6 +35,10 @@ func init() {
Ui: Ui,
}

PlumbingCommands = map[string]struct{}{
"state": struct{}{}, // includes all subcommands
}

Commands = map[string]cli.CommandFactory{
"apply": func() (cli.Command, error) {
return &command.ApplyCommand{
Expand Down Expand Up @@ -131,6 +136,22 @@ func init() {
Meta: meta,
}, nil
},

//-----------------------------------------------------------
// Plumbing
//-----------------------------------------------------------

"state": func() (cli.Command, error) {
return &command.StateCommand{
Meta: meta,
}, nil
},

"state list": func() (cli.Command, error) {
return &command.StateListCommand{
Meta: meta,
}, nil
},
}
}

Expand Down
79 changes: 79 additions & 0 deletions help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package main

import (
"bytes"
"fmt"
"log"
"sort"
"strings"

"github.com/mitchellh/cli"
)

// helpFunc is a cli.HelpFunc that can is used to output the help for Terraform.
func helpFunc(commands map[string]cli.CommandFactory) string {
// Determine the maximum key length, and classify based on type
porcelain := make(map[string]cli.CommandFactory)
plumbing := make(map[string]cli.CommandFactory)
maxKeyLen := 0
for key, f := range commands {
if len(key) > maxKeyLen {
maxKeyLen = len(key)
}

if _, ok := PlumbingCommands[key]; ok {
plumbing[key] = f
} else {
porcelain[key] = f
}
}

var buf bytes.Buffer
buf.WriteString("usage: terraform [--version] [--help] <command> [args]\n\n")
buf.WriteString(
"The available commands for execution are listed below.\n" +
"The most common, useful commands are shown first, followed by\n" +
"less common or more advanced commands. If you're just getting\n" +
"started with Terraform, stick with the common commands. For the\n" +
"other commands, please read the help and docs before usage.\n\n")
buf.WriteString("Common commands:\n")
buf.WriteString(listCommands(porcelain, maxKeyLen))
buf.WriteString("\nAll other commands:\n")
buf.WriteString(listCommands(plumbing, maxKeyLen))
return buf.String()
}

// listCommands just lists the commands in the map with the
// given maximum key length.
func listCommands(commands map[string]cli.CommandFactory, maxKeyLen int) string {
var buf bytes.Buffer

// Get the list of keys so we can sort them, and also get the maximum
// key length so they can be aligned properly.
keys := make([]string, 0, len(commands))
for key, _ := range commands {
keys = append(keys, key)
}
sort.Strings(keys)

for _, key := range keys {
commandFunc, ok := commands[key]
if !ok {
// This should never happen since we JUST built the list of
// keys.
panic("command not found: " + key)
}

command, err := commandFunc()
if err != nil {
log.Printf("[ERR] cli: Command '%s' failed to load: %s",
key, err)
continue
}

key = fmt.Sprintf("%s%s", key, strings.Repeat(" ", maxKeyLen-len(key)))
buf.WriteString(fmt.Sprintf(" %s %s\n", key, command.Synopsis()))
}

return buf.String()
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func wrappedMain() int {
cli := &cli.CLI{
Args: args,
Commands: Commands,
HelpFunc: cli.BasicHelpFunc("terraform"),
HelpFunc: helpFunc,
HelpWriter: os.Stdout,
}

Expand Down
29 changes: 29 additions & 0 deletions terraform/resource_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,35 @@ func (r *ResourceAddress) Copy() *ResourceAddress {
return n
}

// String outputs the address that parses into this address.
func (r *ResourceAddress) String() string {
var result []string
for _, p := range r.Path {
result = append(result, "module", p)
}

if r.Type != "" {
result = append(result, r.Type)
}

if r.Name != "" {
name := r.Name
switch r.InstanceType {
case TypeDeposed:
name += ".deposed"
case TypeTainted:
name += ".tainted"
}

if r.Index >= 0 {
name += fmt.Sprintf("[%d]", r.Index)
}
result = append(result, name)
}

return strings.Join(result, ".")
}

func ParseResourceAddress(s string) (*ResourceAddress, error) {
matches, err := tokenizeResourceAddress(s)
if err != nil {
Expand Down
Loading