command/show
This commit is contained in:
parent
3d35158170
commit
dbc1c63d79
|
@ -12,6 +12,10 @@ import (
|
||||||
|
|
||||||
// FormatPlan takes a plan and returns a
|
// FormatPlan takes a plan and returns a
|
||||||
func FormatPlan(p *terraform.Plan, c *colorstring.Colorize) string {
|
func FormatPlan(p *terraform.Plan, c *colorstring.Colorize) string {
|
||||||
|
if p.Diff == nil || p.Diff.Empty() {
|
||||||
|
return "This plan does nothing."
|
||||||
|
}
|
||||||
|
|
||||||
if c == nil {
|
if c == nil {
|
||||||
c = &colorstring.Colorize{
|
c = &colorstring.Colorize{
|
||||||
Colors: colorstring.DefaultColors,
|
Colors: colorstring.DefaultColors,
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/colorstring"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FormatState takes a state and returns a string
|
||||||
|
func FormatState(s *terraform.State, c *colorstring.Colorize) string {
|
||||||
|
if len(s.Resources) == 0 {
|
||||||
|
return "The state file is empty. No resources are represented."
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == nil {
|
||||||
|
c = &colorstring.Colorize{
|
||||||
|
Colors: colorstring.DefaultColors,
|
||||||
|
Reset: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.WriteString("[reset]")
|
||||||
|
|
||||||
|
// First get the names of all the resources so we can show them
|
||||||
|
// in alphabetical order.
|
||||||
|
names := make([]string, 0, len(s.Resources))
|
||||||
|
for name, _ := range s.Resources {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
// Go through each resource and begin building up the output.
|
||||||
|
for _, k := range names {
|
||||||
|
rs := s.Resources[k]
|
||||||
|
id := rs.ID
|
||||||
|
if id == "" {
|
||||||
|
id = "<not created>"
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(fmt.Sprintf("%s:\n", k))
|
||||||
|
buf.WriteString(fmt.Sprintf(" id = %s\n", id))
|
||||||
|
|
||||||
|
// Sort the attributes
|
||||||
|
attrKeys := make([]string, 0, len(rs.Attributes))
|
||||||
|
for ak, _ := range rs.Attributes {
|
||||||
|
// Skip the id attribute since we just show the id directly
|
||||||
|
if ak == "id" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
attrKeys = append(attrKeys, ak)
|
||||||
|
}
|
||||||
|
sort.Strings(attrKeys)
|
||||||
|
|
||||||
|
// Output each attribute
|
||||||
|
for _, ak := range attrKeys {
|
||||||
|
av := rs.Attributes[ak]
|
||||||
|
buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.Outputs) > 0 {
|
||||||
|
buf.WriteString("\nOutputs:\n\n")
|
||||||
|
|
||||||
|
// Sort the outputs
|
||||||
|
ks := make([]string, 0, len(s.Outputs))
|
||||||
|
for k, _ := range s.Outputs {
|
||||||
|
ks = append(ks, k)
|
||||||
|
}
|
||||||
|
sort.Strings(ks)
|
||||||
|
|
||||||
|
// Output each output k/v pair
|
||||||
|
for _, k := range ks {
|
||||||
|
v := s.Outputs[k]
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %s\n", k, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(buf.String())
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ func TestGraph(t *testing.T) {
|
||||||
|
|
||||||
func TestGraph_multipleArgs(t *testing.T) {
|
func TestGraph_multipleArgs(t *testing.T) {
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
c := &ApplyCommand{
|
c := &GraphCommand{
|
||||||
ContextOpts: testCtxConfig(testProvider()),
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShowCommand is a Command implementation that reads and outputs the
|
||||||
|
// contents of a Terraform plan or state file.
|
||||||
|
type ShowCommand struct {
|
||||||
|
ContextOpts *terraform.ContextOpts
|
||||||
|
Ui cli.Ui
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ShowCommand) Run(args []string) int {
|
||||||
|
cmdFlags := flag.NewFlagSet("show", flag.ContinueOnError)
|
||||||
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
args = cmdFlags.Args()
|
||||||
|
if len(args) != 1 {
|
||||||
|
c.Ui.Error(
|
||||||
|
"The show command expects exactly one argument with the path\n" +
|
||||||
|
"to a Terraform state or plan file.\n")
|
||||||
|
cmdFlags.Usage()
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
path := args[0]
|
||||||
|
|
||||||
|
var plan *terraform.Plan
|
||||||
|
var state *terraform.State
|
||||||
|
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error loading file: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var planErr, stateErr error
|
||||||
|
plan, err = terraform.ReadPlan(f)
|
||||||
|
if err != nil {
|
||||||
|
if _, err := f.Seek(0, 0); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error reading file: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
plan = nil
|
||||||
|
planErr = err
|
||||||
|
}
|
||||||
|
if plan == nil {
|
||||||
|
state, err = terraform.ReadState(f)
|
||||||
|
if err != nil {
|
||||||
|
stateErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if plan == nil && state == nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"Terraform couldn't read the given file as a state or plan file.\n"+
|
||||||
|
"The errors while attempting to read the file as each format are\n"+
|
||||||
|
"shown below.\n\n"+
|
||||||
|
"State read error: %s\n\nPlan read error: %s",
|
||||||
|
stateErr,
|
||||||
|
planErr))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if plan != nil {
|
||||||
|
c.Ui.Output(FormatPlan(plan, nil))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Ui.Output(FormatState(state, nil))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ShowCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: terraform show [options] path
|
||||||
|
|
||||||
|
Reads and outputs a Terraform state or plan file in a human-readable
|
||||||
|
form.
|
||||||
|
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ShowCommand) Synopsis() string {
|
||||||
|
return "Inspect Terraform state or plan"
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShow(t *testing.T) {
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ShowCommand{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"bad",
|
||||||
|
"bad",
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("bad: \n%s", ui.OutputWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShow_noArgs(t *testing.T) {
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ShowCommand{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{}
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("bad: \n%s", ui.OutputWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShow_plan(t *testing.T) {
|
||||||
|
planPath := testPlanFile(t, &terraform.Plan{
|
||||||
|
Config: new(config.Config),
|
||||||
|
})
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ShowCommand{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
planPath,
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShow_state(t *testing.T) {
|
||||||
|
originalState := &terraform.State{
|
||||||
|
Resources: map[string]*terraform.ResourceState{
|
||||||
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
|
ID: "bar",
|
||||||
|
Type: "test_instance",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
statePath := testStateFile(t, originalState)
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ShowCommand{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
statePath,
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,6 +56,13 @@ func init() {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"show": func() (cli.Command, error) {
|
||||||
|
return &command.ShowCommand{
|
||||||
|
ContextOpts: &ContextOpts,
|
||||||
|
Ui: Ui,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
|
||||||
"version": func() (cli.Command, error) {
|
"version": func() (cli.Command, error) {
|
||||||
return &command.VersionCommand{
|
return &command.VersionCommand{
|
||||||
Revision: GitCommit,
|
Revision: GitCommit,
|
||||||
|
|
Loading…
Reference in New Issue