command: plan shows module-level, can control depth
This commit is contained in:
parent
bc71d6adca
commit
8c17062638
|
@ -10,49 +10,96 @@ import (
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FormatPlanOpts are the options for formatting a plan.
|
||||||
|
type FormatPlanOpts struct {
|
||||||
|
// Plan is the plan to format. This is required.
|
||||||
|
Plan *terraform.Plan
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
// 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(opts *FormatPlanOpts) string {
|
||||||
|
p := opts.Plan
|
||||||
if p.Diff == nil || p.Diff.Empty() {
|
if p.Diff == nil || p.Diff.Empty() {
|
||||||
return "This plan does nothing."
|
return "This plan does nothing."
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == nil {
|
if opts.Color == nil {
|
||||||
c = &colorstring.Colorize{
|
opts.Color = &colorstring.Colorize{
|
||||||
Colors: colorstring.DefaultColors,
|
Colors: colorstring.DefaultColors,
|
||||||
Reset: false,
|
Reset: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
for _, m := range p.Diff.Modules {
|
||||||
|
if len(m.Path)-1 <= opts.ModuleDepth || opts.ModuleDepth == -1 {
|
||||||
|
formatPlanModuleExpand(buf, m, opts)
|
||||||
|
} else {
|
||||||
|
formatPlanModuleSingle(buf, m, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatPlanModuleExpand will output the given module and all of its
|
||||||
|
// resources.
|
||||||
|
func formatPlanModuleExpand(
|
||||||
|
buf *bytes.Buffer, m *terraform.ModuleDiff, opts *FormatPlanOpts) {
|
||||||
|
// Ignore empty diffs
|
||||||
|
if m.Empty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var moduleName string
|
||||||
|
if !m.IsRoot() {
|
||||||
|
moduleName = fmt.Sprintf("module.%s", strings.Join(m.Path[1:], "."))
|
||||||
|
}
|
||||||
|
|
||||||
// We want to output the resources in sorted order to make things
|
// We want to output the resources in sorted order to make things
|
||||||
// easier to scan through, so get all the resource names and sort them.
|
// easier to scan through, so get all the resource names and sort them.
|
||||||
names := make([]string, 0, len(p.Diff.RootModule().Resources))
|
names := make([]string, 0, len(m.Resources))
|
||||||
for name, _ := range p.Diff.RootModule().Resources {
|
for name, _ := range m.Resources {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
|
|
||||||
// Go through each sorted name and start building the output
|
// Go through each sorted name and start building the output
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
rdiff := p.Diff.RootModule().Resources[name]
|
rdiff := m.Resources[name]
|
||||||
|
if rdiff.Empty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if moduleName != "" {
|
||||||
|
name = moduleName + "." + name
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the color for the text (green for adding, yellow
|
// Determine the color for the text (green for adding, yellow
|
||||||
// for change, red for delete), and symbol, and output the
|
// for change, red for delete), and symbol, and output the
|
||||||
// resource header.
|
// resource header.
|
||||||
color := "yellow"
|
color := "yellow"
|
||||||
symbol := "~"
|
symbol := "~"
|
||||||
if rdiff.RequiresNew() && rdiff.Destroy {
|
switch rdiff.ChangeType() {
|
||||||
|
case terraform.DiffDestroyCreate:
|
||||||
color = "green"
|
color = "green"
|
||||||
symbol = "-/+"
|
symbol = "-/+"
|
||||||
} else if rdiff.RequiresNew() {
|
case terraform.DiffCreate:
|
||||||
color = "green"
|
color = "green"
|
||||||
symbol = "+"
|
symbol = "+"
|
||||||
} else if rdiff.Destroy {
|
case terraform.DiffDestroy:
|
||||||
color = "red"
|
color = "red"
|
||||||
symbol = "-"
|
symbol = "-"
|
||||||
}
|
}
|
||||||
buf.WriteString(c.Color(fmt.Sprintf(
|
|
||||||
|
buf.WriteString(opts.Color.Color(fmt.Sprintf(
|
||||||
"[%s]%s %s\n",
|
"[%s]%s %s\n",
|
||||||
color, symbol, name)))
|
color, symbol, name)))
|
||||||
|
|
||||||
|
@ -97,8 +144,40 @@ func FormatPlan(p *terraform.Plan, c *colorstring.Colorize) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the reset color so we don't overload the user's terminal
|
// Write the reset color so we don't overload the user's terminal
|
||||||
buf.WriteString(c.Color("[reset]\n"))
|
buf.WriteString(opts.Color.Color("[reset]\n"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSpace(buf.String())
|
// formatPlanModuleSingle will output the given module and all of its
|
||||||
|
// resources.
|
||||||
|
func formatPlanModuleSingle(
|
||||||
|
buf *bytes.Buffer, m *terraform.ModuleDiff, opts *FormatPlanOpts) {
|
||||||
|
// Ignore empty diffs
|
||||||
|
if m.Empty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleName := fmt.Sprintf("module.%s", strings.Join(m.Path[1:], "."))
|
||||||
|
|
||||||
|
// Determine the color for the text (green for adding, yellow
|
||||||
|
// for change, red for delete), and symbol, and output the
|
||||||
|
// resource header.
|
||||||
|
color := "yellow"
|
||||||
|
symbol := "~"
|
||||||
|
switch m.ChangeType() {
|
||||||
|
case terraform.DiffCreate:
|
||||||
|
color = "green"
|
||||||
|
symbol = "+"
|
||||||
|
case terraform.DiffDestroy:
|
||||||
|
color = "red"
|
||||||
|
symbol = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(opts.Color.Color(fmt.Sprintf(
|
||||||
|
"[%s]%s %s\n",
|
||||||
|
color, symbol, moduleName)))
|
||||||
|
buf.WriteString(fmt.Sprintf(
|
||||||
|
" %d resource(s)",
|
||||||
|
len(m.Resources)))
|
||||||
|
buf.WriteString(opts.Color.Color("[reset]\n"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,14 @@ type PlanCommand struct {
|
||||||
func (c *PlanCommand) Run(args []string) int {
|
func (c *PlanCommand) Run(args []string) int {
|
||||||
var destroy, refresh bool
|
var destroy, refresh bool
|
||||||
var outPath, statePath, backupPath string
|
var outPath, statePath, backupPath string
|
||||||
|
var moduleDepth int
|
||||||
|
|
||||||
args = c.Meta.process(args, true)
|
args = c.Meta.process(args, true)
|
||||||
|
|
||||||
cmdFlags := c.Meta.flagSet("plan")
|
cmdFlags := c.Meta.flagSet("plan")
|
||||||
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
|
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
|
||||||
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
|
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
|
||||||
|
cmdFlags.IntVar(&moduleDepth, "module-depth", 0, "module-depth")
|
||||||
cmdFlags.StringVar(&outPath, "out", "", "path")
|
cmdFlags.StringVar(&outPath, "out", "", "path")
|
||||||
cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
|
cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
|
||||||
cmdFlags.StringVar(&backupPath, "backup", "", "path")
|
cmdFlags.StringVar(&backupPath, "backup", "", "path")
|
||||||
|
@ -136,7 +138,11 @@ func (c *PlanCommand) Run(args []string) int {
|
||||||
outPath))
|
outPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Ui.Output(FormatPlan(plan, c.Colorize()))
|
c.Ui.Output(FormatPlan(&FormatPlanOpts{
|
||||||
|
Plan: plan,
|
||||||
|
Color: c.Colorize(),
|
||||||
|
ModuleDepth: moduleDepth,
|
||||||
|
}))
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -161,6 +167,10 @@ Options:
|
||||||
-destroy If set, a plan will be generated to destroy all resources
|
-destroy If set, a plan will be generated to destroy all resources
|
||||||
managed by the given configuration and state.
|
managed by the given configuration and state.
|
||||||
|
|
||||||
|
-module-depth=n Specifies the depth of modules to show in the output.
|
||||||
|
This does not affect the plan itself, only the output
|
||||||
|
shown. By default, this is zero. -1 will expand all.
|
||||||
|
|
||||||
-no-color If specified, output won't contain any color.
|
-no-color If specified, output won't contain any color.
|
||||||
|
|
||||||
-out=path Write a plan file to the given path. This can be used as
|
-out=path Write a plan file to the given path. This can be used as
|
||||||
|
|
|
@ -72,7 +72,10 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if plan != nil {
|
if plan != nil {
|
||||||
c.Ui.Output(FormatPlan(plan, c.Colorize()))
|
c.Ui.Output(FormatPlan(&FormatPlanOpts{
|
||||||
|
Plan: plan,
|
||||||
|
Color: c.Colorize(),
|
||||||
|
}))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,11 @@ func (d *ModuleDiff) Empty() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsRoot says whether or not this module diff is for the root module.
|
||||||
|
func (d *ModuleDiff) IsRoot() bool {
|
||||||
|
return reflect.DeepEqual(d.Path, rootModulePath)
|
||||||
|
}
|
||||||
|
|
||||||
// String outputs the diff in a long but command-line friendly output
|
// String outputs the diff in a long but command-line friendly output
|
||||||
// format that users can read to quickly inspect a diff.
|
// format that users can read to quickly inspect a diff.
|
||||||
func (d *ModuleDiff) String() string {
|
func (d *ModuleDiff) String() string {
|
||||||
|
|
Loading…
Reference in New Issue