github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/command/validate.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/packer/packer"
     9  	"github.com/hashicorp/packer/template"
    10  )
    11  
    12  type ValidateCommand struct {
    13  	Meta
    14  }
    15  
    16  func (c *ValidateCommand) Run(args []string) int {
    17  	var cfgSyntaxOnly bool
    18  	flags := c.Meta.FlagSet("validate", FlagSetBuildFilter|FlagSetVars)
    19  	flags.Usage = func() { c.Ui.Say(c.Help()) }
    20  	flags.BoolVar(&cfgSyntaxOnly, "syntax-only", false, "check syntax only")
    21  	if err := flags.Parse(args); err != nil {
    22  		return 1
    23  	}
    24  
    25  	args = flags.Args()
    26  	if len(args) != 1 {
    27  		flags.Usage()
    28  		return 1
    29  	}
    30  
    31  	// Parse the template
    32  	tpl, err := template.ParseFile(args[0])
    33  	if err != nil {
    34  		c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
    35  		return 1
    36  	}
    37  
    38  	// If we're only checking syntax, then we're done already
    39  	if cfgSyntaxOnly {
    40  		c.Ui.Say("Syntax-only check passed. Everything looks okay.")
    41  		return 0
    42  	}
    43  
    44  	// Get the core
    45  	core, err := c.Meta.Core(tpl)
    46  	if err != nil {
    47  		c.Ui.Error(err.Error())
    48  		return 1
    49  	}
    50  
    51  	errs := make([]error, 0)
    52  	warnings := make(map[string][]string)
    53  
    54  	// Get the builds we care about
    55  	buildNames := c.Meta.BuildNames(core)
    56  	builds := make([]packer.Build, 0, len(buildNames))
    57  	for _, n := range buildNames {
    58  		b, err := core.Build(n)
    59  		if err != nil {
    60  			c.Ui.Error(fmt.Sprintf(
    61  				"Failed to initialize build '%s': %s",
    62  				n, err))
    63  			return 1
    64  		}
    65  
    66  		builds = append(builds, b)
    67  	}
    68  
    69  	// Check the configuration of all builds
    70  	for _, b := range builds {
    71  		log.Printf("Preparing build: %s", b.Name())
    72  		warns, err := b.Prepare()
    73  		if len(warns) > 0 {
    74  			warnings[b.Name()] = warns
    75  		}
    76  		if err != nil {
    77  			errs = append(errs, fmt.Errorf("Errors validating build '%s'. %s", b.Name(), err))
    78  		}
    79  	}
    80  
    81  	if len(errs) > 0 {
    82  		c.Ui.Error("Template validation failed. Errors are shown below.\n")
    83  		for i, err := range errs {
    84  			c.Ui.Error(err.Error())
    85  
    86  			if (i + 1) < len(errs) {
    87  				c.Ui.Error("")
    88  			}
    89  		}
    90  
    91  		return 1
    92  	}
    93  
    94  	if len(warnings) > 0 {
    95  		c.Ui.Say("Template validation succeeded, but there were some warnings.")
    96  		c.Ui.Say("These are ONLY WARNINGS, and Packer will attempt to build the")
    97  		c.Ui.Say("template despite them, but they should be paid attention to.\n")
    98  
    99  		for build, warns := range warnings {
   100  			c.Ui.Say(fmt.Sprintf("Warnings for build '%s':\n", build))
   101  			for _, warning := range warns {
   102  				c.Ui.Say(fmt.Sprintf("* %s", warning))
   103  			}
   104  		}
   105  
   106  		return 0
   107  	}
   108  
   109  	c.Ui.Say("Template validated successfully.")
   110  	return 0
   111  }
   112  
   113  func (*ValidateCommand) Help() string {
   114  	helpText := `
   115  Usage: packer validate [options] TEMPLATE
   116  
   117    Checks the template is valid by parsing the template and also
   118    checking the configuration with the various builders, provisioners, etc.
   119  
   120    If it is not valid, the errors will be shown and the command will exit
   121    with a non-zero exit status. If it is valid, it will exit with a zero
   122    exit status.
   123  
   124  Options:
   125  
   126    -syntax-only           Only check syntax. Do not verify config of the template.
   127    -except=foo,bar,baz    Validate all builds other than these
   128    -only=foo,bar,baz      Validate only these builds
   129    -var 'key=value'       Variable for templates, can be used multiple times.
   130    -var-file=path         JSON file containing user variables.
   131  `
   132  
   133  	return strings.TrimSpace(helpText)
   134  }
   135  
   136  func (*ValidateCommand) Synopsis() string {
   137  	return "check that a template is valid"
   138  }