package command import ( "fmt" "sort" "strings" "github.com/hashicorp/packer/template" "github.com/posener/complete" ) type InspectCommand struct { Meta } func (c *InspectCommand) Run(args []string) int { flags := c.Meta.FlagSet("inspect", FlagSetNone) flags.Usage = func() { c.Ui.Say(c.Help()) } if err := flags.Parse(args); err != nil { return 1 } args = flags.Args() if len(args) != 1 { flags.Usage() return 1 } // Parse the template tpl, err := template.ParseFile(args[0]) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // Convenience... ui := c.Ui // Description if tpl.Description != "" { ui.Say("Description:\n") ui.Say(tpl.Description + "\n") } // Variables if len(tpl.Variables) == 0 { ui.Say("Variables:\n") ui.Say(" ") } else { requiredHeader := false for k, v := range tpl.Variables { for _, sensitive := range tpl.SensitiveVariables { if ok := strings.Compare(sensitive.Default, v.Default); ok == 0 { v.Default = "" } } if v.Required { if !requiredHeader { requiredHeader = true ui.Say("Required variables:\n") } ui.Machine("template-variable", k, v.Default, "1") ui.Say(" " + k) } } if requiredHeader { ui.Say("") } ui.Say("Optional variables and their defaults:\n") 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] if v.Required { continue } for _, sensitive := range tpl.SensitiveVariables { if ok := strings.Compare(sensitive.Default, v.Default); ok == 0 { v.Default = "" } } padding := strings.Repeat(" ", max-len(k)) output := fmt.Sprintf(" %s%s = %s", k, padding, v.Default) ui.Machine("template-variable", k, v.Default, "0") 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-builder", 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)) } } ui.Say("\nNote: If your build names contain user variables or template\n" + "functions such as 'timestamp', these are processed at build time,\n" + "and therefore only show in their raw form here.") return 0 } func (*InspectCommand) Help() string { 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). Options: -machine-readable Machine-readable output ` return strings.TrimSpace(helpText) } func (c *InspectCommand) Synopsis() string { return "see components of a template" } func (c *InspectCommand) AutocompleteArgs() complete.Predictor { return complete.PredictNothing } func (c *InspectCommand) AutocompleteFlags() complete.Flags { return complete.Flags{ "-machine-readable": complete.PredictNothing, } }