Wilken Rivera 1bb154de5a Add support for reading configs
This is the first step in attempting to read a Packer configuration file
via the packer init command. At the present moment it will try to read
one or more configuration templates from a given directory or path. Once
parsed it will error if parsing fails or exist successfully if it is
able to parse the file.

Looking at how the code is structured there will need to be changes made
to the following places:

- When no configuration file is found Packer will display an error. That
error should be bubbled up a bit so that the caller command can
determine if it should be displayed or not. For packer init no
configuration is not an error. Maybe it should be?

- After a configuration has been parsed there needs to be a single way
to determine a list of plugins associated with the configuration. HCL
and JSON configs have fields for this data but some is exported and some
is unexported. Adapting the packerHandler interface may be an option
here. More investigation needed.
2020-09-16 14:46:16 -04:00

160 lines
4.3 KiB
Go

package command
import (
"flag"
"strings"
"github.com/hashicorp/packer/helper/enumflag"
kvflag "github.com/hashicorp/packer/helper/flag-kv"
sliceflag "github.com/hashicorp/packer/helper/flag-slice"
)
//go:generate enumer -type configType -trimprefix ConfigType -transform snake
type configType int
const (
ConfigTypeJSON configType = iota // default config type
ConfigTypeHCL2
)
func (c *configType) Set(value string) error {
v, err := configTypeString(value)
if err == nil {
*c = v
}
return err
}
// ConfigType tells what type of config we should use, it can return values
// like "hcl" or "json".
// Make sure Args was correctly set before.
func (ma *MetaArgs) GetConfigType() (configType, error) {
if ma.Path == "" {
return ma.ConfigType, nil
}
name := ma.Path
if name == "-" {
// TODO(azr): To allow piping HCL2 confs (when args is "-"), we probably
// will need to add a setting that says "this is an HCL config".
return ma.ConfigType, nil
}
if strings.HasSuffix(name, ".pkr.hcl") ||
strings.HasSuffix(name, ".pkr.json") {
return ConfigTypeHCL2, nil
}
isDir, err := isDir(name)
if isDir {
return ConfigTypeHCL2, err
}
return ma.ConfigType, err
}
// NewMetaArgs parses cli args and put possible values
func (ma *MetaArgs) AddFlagSets(fs *flag.FlagSet) {
fs.Var((*sliceflag.StringFlag)(&ma.Only), "only", "")
fs.Var((*sliceflag.StringFlag)(&ma.Except), "except", "")
fs.Var((*kvflag.Flag)(&ma.Vars), "var", "")
fs.Var((*kvflag.StringSlice)(&ma.VarFiles), "var-file", "")
fs.Var(&ma.ConfigType, "config-type", "set to 'hcl2' to run in hcl2 mode when no file is passed.")
}
// MetaArgs defines commonalities between all comands
type MetaArgs struct {
// TODO(azr): in the future, I want to allow passing multiple path to
// merge HCL confs together; but this will probably need an RFC first.
Path string
Only, Except []string
Vars map[string]string
VarFiles []string
// set to "hcl2" to force hcl2 mode
ConfigType configType
}
func (ba *BuildArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&ba.Color, "color", true, "")
flags.BoolVar(&ba.Debug, "debug", false, "")
flags.BoolVar(&ba.Force, "force", false, "")
flags.BoolVar(&ba.TimestampUi, "timestamp-ui", false, "")
flags.BoolVar(&ba.MachineReadable, "machine-readable", false, "")
flags.Int64Var(&ba.ParallelBuilds, "parallel-builds", 0, "")
flagOnError := enumflag.New(&ba.OnError, "cleanup", "abort", "ask", "run-cleanup-provisioner")
flags.Var(flagOnError, "on-error", "")
ba.MetaArgs.AddFlagSets(flags)
}
// BuildArgs represents a parsed cli line for a `packer build`
type BuildArgs struct {
MetaArgs
Color, Debug, Force, TimestampUi, MachineReadable bool
ParallelBuilds int64
OnError string
}
// ConsoleArgs represents a parsed cli line for a `packer console`
type ConsoleArgs struct {
MetaArgs
}
func (fa *FixArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&fa.Validate, "validate", true, "")
fa.MetaArgs.AddFlagSets(flags)
}
// FixArgs represents a parsed cli line for a `packer fix`
type FixArgs struct {
MetaArgs
Validate bool
}
func (va *ValidateArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&va.SyntaxOnly, "syntax-only", false, "check syntax only")
va.MetaArgs.AddFlagSets(flags)
}
// ValidateArgs represents a parsed cli line for a `packer validate`
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
}
func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) {
flags.StringVar(&va.OutputFile, "output-file", "", "File where to put the hcl2 generated config. Defaults to JSON_TEMPLATE.pkr.hcl")
va.MetaArgs.AddFlagSets(flags)
}
// HCL2UpgradeArgs represents a parsed cli line for a `packer build`
type HCL2UpgradeArgs struct {
MetaArgs
OutputFile string
}
func (ia *InitArgs) AddFlagSets(flags *flag.FlagSet) {
flags.StringVar(&ia.PluginDir, "plugin-dir", "", "")
flags.BoolVar(&ia.GetPlugins, "get-plugins", true, "")
flags.BoolVar(&ia.Upgrade, "upgrade", false, "")
ia.MetaArgs.AddFlagSets(flags)
}
//InitArgs respresents a parsed cli line for packer init
type InitArgs struct {
MetaArgs
GetPlugins bool
PluginDir string
Upgrade bool
}