hcl2: inspect command

This commit is contained in:
Adrien Delorme 2020-06-23 11:58:57 +02:00
parent a96a8f22b9
commit 51d02f8c2d
7 changed files with 232 additions and 146 deletions

View File

@ -121,3 +121,12 @@ type ValidateArgs struct {
MetaArgs
SyntaxOnly bool
}
func (va *InspectArgs) AddFlagSets(flags *flag.FlagSet) {
va.MetaArgs.AddFlagSets(flags)
}
// InspectArgs represents a parsed cli line for a `packer inspect`
type InspectArgs struct {
MetaArgs
}

View File

@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert"
)
func Test_piping(t *testing.T) {
func Test_console(t *testing.T) {
tc := []struct {
piped string

View File

@ -1,12 +1,10 @@
package command
import (
"fmt"
"sort"
"context"
"strings"
"github.com/hashicorp/packer/template"
"github.com/hashicorp/packer/packer"
"github.com/posener/complete"
)
@ -15,142 +13,40 @@ type InspectCommand struct {
}
func (c *InspectCommand) Run(args []string) int {
flags := c.Meta.FlagSet("inspect", FlagSetNone)
ctx := context.Background()
cfg, ret := c.ParseArgs(args)
if ret != 0 {
return ret
}
return c.RunContext(ctx, cfg)
}
func (c *InspectCommand) ParseArgs(args []string) (*InspectArgs, int) {
var cfg InspectArgs
flags := c.Meta.FlagSet("inspect", FlagSetVars)
flags.Usage = func() { c.Ui.Say(c.Help()) }
cfg.AddFlagSets(flags)
if err := flags.Parse(args); err != nil {
return 1
return &cfg, 1
}
args = flags.Args()
if len(args) != 1 {
flags.Usage()
return 1
if len(args) == 1 {
cfg.Path = args[0]
}
return &cfg, 0
}
// 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
func (c *InspectCommand) RunContext(ctx context.Context, cla *InspectArgs) int {
packerStarter, ret := c.GetConfig(&cla.MetaArgs)
if ret != 0 {
return ret
}
// 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(" <No variables>")
} 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 = "<sensitive>"
}
}
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 = "<sensitive>"
}
}
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(" <No builders>")
} 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(" <No provisioners>")
} 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
return packerStarter.InspectConfig(packer.InspectConfigOptions{
Ui: c.Ui,
})
}
func (*InspectCommand) Help() string {

1
command/inspect_test.go Normal file
View File

@ -0,0 +1 @@
package command

View File

@ -439,6 +439,13 @@ func (p *PackerConfig) EvaluateExpression(line string) (out string, exit bool, d
case line == "help":
return PackerConsoleHelp, false, nil
case line == "variables":
return p.printVariables(), false, nil
default:
return p.handleEval(line)
}
}
func (p *PackerConfig) printVariables() string {
out := &strings.Builder{}
out.WriteString("> input-variables:\n\n")
for _, v := range p.InputVariables {
@ -450,11 +457,42 @@ func (p *PackerConfig) EvaluateExpression(line string) (out string, exit bool, d
val, _ := v.Value()
fmt.Fprintf(out, "local.%s: %q\n", v.Name, PrintableCtyValue(val))
}
return out.String(), false, nil
default:
return p.handleEval(line)
return out.String()
}
func (p *PackerConfig) printBuilds() string {
out := &strings.Builder{}
out.WriteString("> builds:\n")
for i, build := range p.Builds {
name := build.Name
if name == "" {
name = fmt.Sprintf("<unnamed build %d>", i)
}
fmt.Fprintf(out, "\n > %s:\n", name)
fmt.Fprintf(out, "\n provisioners:\n\n")
if len(build.ProvisionerBlocks) == 0 {
fmt.Fprintf(out, " <no provisioner>\n")
}
for _, prov := range build.ProvisionerBlocks {
str := prov.PType
if prov.PName != "" {
str = strings.Join([]string{prov.PType, prov.PName}, ".")
}
fmt.Fprintf(out, " %s\n", str)
}
fmt.Fprintf(out, "\n post-processors:\n\n")
if len(build.PostProcessors) == 0 {
fmt.Fprintf(out, " <no post-processor>\n")
}
for _, pp := range build.PostProcessors {
str := strings.Join([]string{pp.PType, pp.PName}, ".")
if pp.PName != "" {
str = strings.Join([]string{pp.PType, pp.PName}, ".")
}
fmt.Fprintf(out, " %s\n", str)
}
}
return out.String()
}
func (p *PackerConfig) handleEval(line string) (out string, exit bool, diags hcl.Diagnostics) {
@ -479,3 +517,12 @@ func (p *PackerConfig) FixConfig(_ packer.FixConfigOptions) (diags hcl.Diagnosti
// No Fixers exist for HCL2 configs so there is nothing to do here for now.
return
}
func (p *PackerConfig) InspectConfig(opts packer.InspectConfigOptions) int {
ui := opts.Ui
ui.Say("Packer Inspect: HCL2 mode\n")
ui.Say(p.printVariables())
ui.Say(p.printBuilds())
return 0
}

View File

@ -424,6 +424,129 @@ func (c *Core) EvaluateExpression(line string) (string, bool, hcl.Diagnostics) {
}
}
func (c *Core) InspectConfig(opts InspectConfigOptions) int {
// Convenience...
ui := opts.Ui
tpl := c.Template
ui.Say("Packer Inspect: JSON mode")
// 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(" <No variables>")
} 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 = "<sensitive>"
}
}
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 = "<sensitive>"
}
}
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(" <No builders>")
} 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(" <No provisioners>")
} 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 (c *Core) FixConfig(opts FixConfigOptions) hcl.Diagnostics {
var diags hcl.Diagnostics

View File

@ -29,6 +29,7 @@ type Handler interface {
Evaluator
BuildGetter
ConfigFixer
ConfigInspector
}
//go:generate enumer -type FixConfigMode
@ -52,3 +53,12 @@ type ConfigFixer interface {
// FixConfig will output the config in a fixed manner.
FixConfig(FixConfigOptions) hcl.Diagnostics
}
type InspectConfigOptions struct {
Ui
}
type ConfigInspector interface {
// Inspect will output self inspection for a configuration
InspectConfig(InspectConfigOptions) (ret int)
}