From 30f9358edce895ba38778c641c4872e116363ecb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 13 Aug 2013 09:36:40 -0700 Subject: [PATCH] command/inspect: Inspect a templates contents This command prints out the components of a template, and most importantly respects the machine-readable flag so that you can programmatically inspect a template's contents without manually parsing the JSON. --- CHANGELOG.md | 6 ++ command/inspect/command.go | 118 ++++++++++++++++++++++++++++ command/inspect/command_test.go | 14 ++++ command/inspect/help.go | 9 +++ config.go | 1 + plugin/command-inspect/main.go | 10 +++ plugin/command-inspect/main_test.go | 1 + 7 files changed, 159 insertions(+) create mode 100644 command/inspect/command.go create mode 100644 command/inspect/command_test.go create mode 100644 command/inspect/help.go create mode 100644 plugin/command-inspect/main.go create mode 100644 plugin/command-inspect/main_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a2521e63..55348ff17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## 0.3.2 (unreleased) +FEATURES: + +* New command: `packer inspect`. This command tells you the components of + a template. It respects the `-machine-readable` flag as well so you can + parse out components of a template. + IMPROVEMENTS: * builder/virtualbox: Do not check for VirtualBox as part of template diff --git a/command/inspect/command.go b/command/inspect/command.go new file mode 100644 index 000000000..c7bae8538 --- /dev/null +++ b/command/inspect/command.go @@ -0,0 +1,118 @@ +package inspect + +import ( + "flag" + "fmt" + "github.com/mitchellh/packer/packer" + "log" + "sort" + "strings" +) + +type Command struct{} + +func (Command) Help() string { + return strings.TrimSpace(helpText) +} + +func (c Command) Synopsis() string { + return "see components of a template" +} + +func (c Command) Run(env packer.Environment, args []string) int { + flags := flag.NewFlagSet("inspect", flag.ContinueOnError) + flags.Usage = func() { env.Ui().Say(c.Help()) } + if err := flags.Parse(args); err != nil { + return 1 + } + + args = flags.Args() + if len(args) != 1 { + flags.Usage() + return 1 + } + + // Read the file into a byte array so that we can parse the template + log.Printf("Reading template: %s", args[0]) + tpl, err := packer.ParseTemplateFile(args[0]) + if err != nil { + env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) + return 1 + } + + // Convenience... + ui := env.Ui() + + // Variables + ui.Say("Variables and their defaults:\n") + if len(tpl.Variables) == 0 { + ui.Say(" ") + } else { + keys := make([]string, 0, len(tpl.Variables)) + max := 0 + for k, _ := range tpl.Variables { + keys = append(keys, k) + if len(k) > max { + max = len(k) + } + } + + sort.Strings(keys) + + for _, k := range keys { + v := tpl.Variables[k] + padding := strings.Repeat(" ", max-len(k)) + output := fmt.Sprintf(" %s%s = %s", k, padding, v) + + ui.Machine("template-variable", k, v) + ui.Say(output) + } + } + + ui.Say("") + + // Builders + ui.Say("Builders:\n") + if len(tpl.Builders) == 0 { + ui.Say(" ") + } else { + keys := make([]string, 0, len(tpl.Builders)) + max := 0 + for k, _ := range tpl.Builders { + keys = append(keys, k) + if len(k) > max { + max = len(k) + } + } + + sort.Strings(keys) + + for _, k := range keys { + v := tpl.Builders[k] + padding := strings.Repeat(" ", max-len(k)) + output := fmt.Sprintf(" %s%s", k, padding) + if v.Name != v.Type { + output = fmt.Sprintf("%s (%s)", output, v.Type) + } + + ui.Machine("template-build", k, v.Type) + ui.Say(output) + + } + } + + ui.Say("") + + // Provisioners + ui.Say("Provisioners:\n") + if len(tpl.Provisioners) == 0 { + ui.Say(" ") + } else { + for _, v := range tpl.Provisioners { + ui.Machine("template-provisioner", v.Type) + ui.Say(fmt.Sprintf(" %s", v.Type)) + } + } + + return 0 +} diff --git a/command/inspect/command_test.go b/command/inspect/command_test.go new file mode 100644 index 000000000..a2766fef4 --- /dev/null +++ b/command/inspect/command_test.go @@ -0,0 +1,14 @@ +package inspect + +import ( + "github.com/mitchellh/packer/packer" + "testing" +) + +func TestCommand_Impl(t *testing.T) { + var raw interface{} + raw = new(Command) + if _, ok := raw.(packer.Command); !ok { + t.Fatalf("must be a Command") + } +} diff --git a/command/inspect/help.go b/command/inspect/help.go new file mode 100644 index 000000000..620bbe8e0 --- /dev/null +++ b/command/inspect/help.go @@ -0,0 +1,9 @@ +package inspect + +const helpText = ` +Usage: packer inspect TEMPLATE + + Inspects a template, parsing and outputting the components a template + defines. This does not validate the contents of a template (other than + basic syntax by necessity). +` diff --git a/config.go b/config.go index d40981b72..6803b70ee 100644 --- a/config.go +++ b/config.go @@ -30,6 +30,7 @@ const defaultConfig = ` "commands": { "build": "packer-command-build", "fix": "packer-command-fix", + "inspect": "packer-command-inspect", "validate": "packer-command-validate" }, diff --git a/plugin/command-inspect/main.go b/plugin/command-inspect/main.go new file mode 100644 index 000000000..6d2d7d3d5 --- /dev/null +++ b/plugin/command-inspect/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/mitchellh/packer/command/inspect" + "github.com/mitchellh/packer/packer/plugin" +) + +func main() { + plugin.ServeCommand(new(inspect.Command)) +} diff --git a/plugin/command-inspect/main_test.go b/plugin/command-inspect/main_test.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/plugin/command-inspect/main_test.go @@ -0,0 +1 @@ +package main