From e5e51d7b17f558101ea07c2c535cc9c4414e3f61 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 25 Sep 2014 19:25:10 -0700 Subject: [PATCH] command: state output is well formatted for modules --- command/format_state.go | 89 ++++++++++++++++++++++++++++++----------- command/show.go | 19 +++++++-- terraform/state.go | 5 +++ 3 files changed, 86 insertions(+), 27 deletions(-) diff --git a/command/format_state.go b/command/format_state.go index 90405042b..23ccc1651 100644 --- a/command/format_state.go +++ b/command/format_state.go @@ -10,26 +10,70 @@ import ( "github.com/mitchellh/colorstring" ) +// FormatStateOpts are the options for formatting a state. +type FormatStateOpts struct { + // State is the state to format. This is required. + State *terraform.State + + // Color is the colorizer. This is optional. + Color *colorstring.Colorize + + // ModuleDepth is the depth of the modules to expand. By default this + // is zero which will not expand modules at all. + ModuleDepth int +} + // FormatState takes a state and returns a string -func FormatState(s *terraform.State, c *colorstring.Colorize) string { - if c == nil { +func FormatState(opts *FormatStateOpts) string { + if opts.Color == nil { panic("colorize not given") } + s := opts.State if len(s.Modules) == 0 { return "The state file is empty. No resources are represented." } var buf bytes.Buffer + buf.WriteString("[reset]") + + // Format all the modules for _, m := range s.Modules { - formatStateModule(&buf, m, c) + if len(m.Path)-1 <= opts.ModuleDepth || opts.ModuleDepth == -1 { + formatStateModuleExpand(&buf, m, opts) + } else { + formatStateModuleSingle(&buf, m, opts) + } } - return c.Color(strings.TrimSpace(buf.String())) + // Write the outputs for the root module + m := s.RootModule() + if len(m.Outputs) > 0 { + buf.WriteString("\nOutputs:\n\n") + + // Sort the outputs + ks := make([]string, 0, len(m.Outputs)) + for k, _ := range m.Outputs { + ks = append(ks, k) + } + sort.Strings(ks) + + // Output each output k/v pair + for _, k := range ks { + v := m.Outputs[k] + buf.WriteString(fmt.Sprintf("%s = %s\n", k, v)) + } + } + + return opts.Color.Color(strings.TrimSpace(buf.String())) } -func formatStateModule(buf *bytes.Buffer, m *terraform.ModuleState, c *colorstring.Colorize) { - buf.WriteString("[reset]") +func formatStateModuleExpand( + buf *bytes.Buffer, m *terraform.ModuleState, opts *FormatStateOpts) { + var moduleName string + if !m.IsRoot() { + moduleName = fmt.Sprintf("module.%s", strings.Join(m.Path[1:], ".")) + } // First get the names of all the resources so we can show them // in alphabetical order. @@ -41,6 +85,11 @@ func formatStateModule(buf *bytes.Buffer, m *terraform.ModuleState, c *colorstri // Go through each resource and begin building up the output. for _, k := range names { + name := k + if moduleName != "" { + name = moduleName + "." + name + } + rs := m.Resources[k] is := rs.Primary id := is.ID @@ -53,7 +102,7 @@ func formatStateModule(buf *bytes.Buffer, m *terraform.ModuleState, c *colorstri taintStr = " (tainted)" } - buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr)) + buf.WriteString(fmt.Sprintf("%s:%s\n", name, taintStr)) buf.WriteString(fmt.Sprintf(" id = %s\n", id)) // Sort the attributes @@ -75,20 +124,14 @@ func formatStateModule(buf *bytes.Buffer, m *terraform.ModuleState, c *colorstri } } - if len(m.Outputs) > 0 { - buf.WriteString("\nOutputs:\n\n") - - // Sort the outputs - ks := make([]string, 0, len(m.Outputs)) - for k, _ := range m.Outputs { - ks = append(ks, k) - } - sort.Strings(ks) - - // Output each output k/v pair - for _, k := range ks { - v := m.Outputs[k] - buf.WriteString(fmt.Sprintf("%s = %s\n", k, v)) - } - } + buf.WriteString("[reset]\n") +} + +func formatStateModuleSingle( + buf *bytes.Buffer, m *terraform.ModuleState, opts *FormatStateOpts) { + // Header with the module name + buf.WriteString(fmt.Sprintf("module.%s\n", strings.Join(m.Path[1:], "."))) + + // Now just write how many resources are in here. + buf.WriteString(fmt.Sprintf(" %d resource(s)\n", len(m.Resources))) } diff --git a/command/show.go b/command/show.go index f6a5c042b..c7943c8f6 100644 --- a/command/show.go +++ b/command/show.go @@ -16,9 +16,12 @@ type ShowCommand struct { } func (c *ShowCommand) Run(args []string) int { + var moduleDepth int + args = c.Meta.process(args, false) cmdFlags := flag.NewFlagSet("show", flag.ContinueOnError) + cmdFlags.IntVar(&moduleDepth, "module-depth", 0, "module-depth") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -73,13 +76,18 @@ func (c *ShowCommand) Run(args []string) int { if plan != nil { c.Ui.Output(FormatPlan(&FormatPlanOpts{ - Plan: plan, - Color: c.Colorize(), + Plan: plan, + Color: c.Colorize(), + ModuleDepth: moduleDepth, })) return 0 } - c.Ui.Output(FormatState(state, c.Colorize())) + c.Ui.Output(FormatState(&FormatStateOpts{ + State: state, + Color: c.Colorize(), + ModuleDepth: moduleDepth, + })) return 0 } @@ -92,7 +100,10 @@ Usage: terraform show [options] path Options: - -no-color If specified, output won't contain any color. + -module-depth=n Specifies the depth of modules to show in the output. + By default this is zero. -1 will expand all. + + -no-color If specified, output won't contain any color. ` return strings.TrimSpace(helpText) diff --git a/terraform/state.go b/terraform/state.go index edb2680d7..51dec770e 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -182,6 +182,11 @@ type ModuleState struct { Resources map[string]*ResourceState `json:"resources"` } +// IsRoot says whether or not this module diff is for the root module. +func (m *ModuleState) IsRoot() bool { + return reflect.DeepEqual(m.Path, rootModulePath) +} + // Orphans returns a list of keys of resources that are in the State // but aren't present in the configuration itself. Hence, these keys // represent the state of resources that are orphans.