From 5be5cc146bf17000eab61f263a21062c6bb66760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borgstrom=20=E2=99=95?= Date: Tue, 3 Dec 2019 10:06:01 +0800 Subject: [PATCH 001/150] iam:GetInstanceProfile is now required --- website/source/docs/builders/amazon.html.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index 07e4ce23c..15ac7a98a 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -202,7 +202,10 @@ work, but specifics will depend on your use-case. { "Sid": "PackerIAMPassRole", "Effect": "Allow", - "Action": "iam:PassRole", + "Action": [ + "iam:PassRole", + "iam:GetInstanceProfile" + ], "Resource": [ "*" ] @@ -314,4 +317,4 @@ generally during image copy/encryption. Possible reasons for the error include: - Your KMS key is invalid, possibly because of a typo - Your KMS key is valid but does not have the necessary permissions (see above for the necessary key permissions) - - Your KMS key is valid, but not in the region you've told us to use it in. \ No newline at end of file + - Your KMS key is valid, but not in the region you've told us to use it in. From 9d3c718c2e03ef2e997f89f029670e2059bd070e Mon Sep 17 00:00:00 2001 From: Kaczanowski Mateusz Date: Sat, 7 Dec 2019 10:44:32 +0100 Subject: [PATCH 002/150] update documentation for arm images builder --- website/source/partials/builders/_community_builders.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/partials/builders/_community_builders.html.md b/website/source/partials/builders/_community_builders.html.md index be856af18..a3aefbe4e 100644 --- a/website/source/partials/builders/_community_builders.html.md +++ b/website/source/partials/builders/_community_builders.html.md @@ -1,6 +1,6 @@ ### Community Builders -- [ARM builder](https://github.com/solo-io/packer-builder-arm-image) - A builder +- [ARM builder](https://github.com/mkaczanowski/packer-builder-arm) - A builder for creating ARM images - [vSphere builder](https://github.com/jetbrains-infra/packer-builder-vsphere) - @@ -8,4 +8,4 @@ host directly. - [Vultr builder](https://github.com/vultr/packer-builder-vultr) - A builder - for creating [Vultr](https://www.vultr.com/) snapshots. \ No newline at end of file + for creating [Vultr](https://www.vultr.com/) snapshots. From ffb9272fb208443fe777cdd24bda6439579f011c Mon Sep 17 00:00:00 2001 From: pfuender Date: Mon, 9 Dec 2019 20:17:04 +0100 Subject: [PATCH 003/150] adding version-override for chef-client provisioner --- provisioner/chef-client/provisioner.go | 15 +++++++++------ provisioner/chef-client/provisioner.hcl2spec.go | 2 ++ .../docs/provisioners/chef-client.html.md.erb | 3 +++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 6ca75e402..59e433c3f 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -34,13 +34,13 @@ type guestOSTypeConfig struct { var guestOSTypeConfigs = map[string]guestOSTypeConfig{ provisioner.UnixOSType: { executeCommand: "{{if .Sudo}}sudo {{end}}chef-client --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", - installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash", + installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash -s --{{if .Version}} -v {{.Version}}{{end}}", knifeCommand: "{{if .Sudo}}sudo {{end}}knife {{.Args}} {{.Flags}}", stagingDir: "/tmp/packer-chef-client", }, provisioner.WindowsOSType: { executeCommand: "c:/opscode/chef/bin/chef-client.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", - installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install\"", + installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; Install-Project{{if .Version}} -version {{.Version}}{{end}}\"", knifeCommand: "c:/opscode/chef/bin/knife.bat {{.Args}} {{.Flags}}", stagingDir: "C:/Windows/Temp/packer-chef-client", }, @@ -77,6 +77,7 @@ type Config struct { StagingDir string `mapstructure:"staging_directory"` ValidationClientName string `mapstructure:"validation_client_name"` ValidationKeyPath string `mapstructure:"validation_key_path"` + Version string `mapstructure:"version"` ctx interpolate.Context } @@ -114,7 +115,8 @@ type ExecuteTemplate struct { } type InstallChefTemplate struct { - Sudo bool + Sudo bool + Version string } type KnifeTemplate struct { @@ -256,7 +258,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C serverUrl := p.config.ServerUrl if !p.config.SkipInstall { - if err := p.installChef(ui, comm); err != nil { + if err := p.installChef(ui, comm, p.config.Version); err != nil { return fmt.Errorf("Error installing Chef: %s", err) } } @@ -604,12 +606,13 @@ func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config return nil } -func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator, version string) error { ui.Message("Installing Chef...") ctx := context.TODO() p.config.ctx.Data = &InstallChefTemplate{ - Sudo: !p.config.PreventSudo, + Sudo: !p.config.PreventSudo, + Version: version, } command, err := interpolate.Render(p.config.InstallCommand, &p.config.ctx) if err != nil { diff --git a/provisioner/chef-client/provisioner.hcl2spec.go b/provisioner/chef-client/provisioner.hcl2spec.go index fcc0dac58..59378b356 100644 --- a/provisioner/chef-client/provisioner.hcl2spec.go +++ b/provisioner/chef-client/provisioner.hcl2spec.go @@ -43,6 +43,7 @@ type FlatConfig struct { StagingDir *string `mapstructure:"staging_directory" cty:"staging_directory"` ValidationClientName *string `mapstructure:"validation_client_name" cty:"validation_client_name"` ValidationKeyPath *string `mapstructure:"validation_key_path" cty:"validation_key_path"` + Version *string `mapstructure:"version" cty:"version"` } // FlatMapstructure returns a new FlatConfig. @@ -88,6 +89,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "staging_directory": &hcldec.AttrSpec{Name: "staging_directory", Type: cty.String, Required: false}, "validation_client_name": &hcldec.AttrSpec{Name: "validation_client_name", Type: cty.String, Required: false}, "validation_key_path": &hcldec.AttrSpec{Name: "validation_key_path", Type: cty.String, Required: false}, + "version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false}, } return s } diff --git a/website/source/docs/provisioners/chef-client.html.md.erb b/website/source/docs/provisioners/chef-client.html.md.erb index 341f19da0..264432c8c 100644 --- a/website/source/docs/provisioners/chef-client.html.md.erb +++ b/website/source/docs/provisioners/chef-client.html.md.erb @@ -149,6 +149,9 @@ configuration is actually required. machine. If this is NOT set, then it is your responsibility via other means (shell provisioner, etc.) to get a validation key to where Chef expects it. +- `version` (string) - The version of Chef to be installed. By default this + is empty which will install the latest version of Chef. + <%= partial "partials/provisioners/common-config" %> ## Chef Configuration From 89ad8ae4fae11b17a51adf706acce6bdc30640dc Mon Sep 17 00:00:00 2001 From: nywilken Date: Mon, 2 Dec 2019 11:50:42 -0500 Subject: [PATCH 004/150] prepare_changelog: Update regex to work with GNU grep --- scripts/prepare_changelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_changelog.sh b/scripts/prepare_changelog.sh index 5834c639f..74c8382e6 100755 --- a/scripts/prepare_changelog.sh +++ b/scripts/prepare_changelog.sh @@ -30,7 +30,7 @@ fi get_prs(){ # git log --merges v0.10.2...c3861d167533fb797b0fae0c380806625712e5f7 | git log --merges HEAD...${LAST_RELEASE} | - grep -o "Merge pull request #\(\d\+\)" | awk -F\# '{print $2}' | while read line + grep -o "Merge pull request #\([0-9]\+\)" | awk -F\# '{print $2}' | while read line do grep -q "GH-${line}" CHANGELOG.md if [ $? -ne 0 ]; then From 5d5189a9a20dfce9432f6fea502d0b3f56331560 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 9 Dec 2019 12:10:07 -0800 Subject: [PATCH 005/150] only log when external plugin is used, since this is the much rarer event --- config.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index 1b7946b24..173d4ece0 100644 --- a/config.go +++ b/config.go @@ -208,29 +208,32 @@ func (c *config) discoverInternal() error { for builder := range command.Builders { _, found := (c.Builders)[builder] if !found { - log.Printf("Using internal plugin for %s", builder) (c.Builders)[builder] = fmt.Sprintf("%s%splugin%spacker-builder-%s", packerPath, PACKERSPACE, PACKERSPACE, builder) + } else { + log.Printf("Using external plugin for %s", builder) } } for provisioner := range command.Provisioners { _, found := (c.Provisioners)[provisioner] if !found { - log.Printf("Using internal plugin for %s", provisioner) (c.Provisioners)[provisioner] = fmt.Sprintf( "%s%splugin%spacker-provisioner-%s", packerPath, PACKERSPACE, PACKERSPACE, provisioner) + } else { + log.Printf("Using external plugin for %s", provisioner) } } for postProcessor := range command.PostProcessors { _, found := (c.PostProcessors)[postProcessor] if !found { - log.Printf("Using internal plugin for %s", postProcessor) (c.PostProcessors)[postProcessor] = fmt.Sprintf( "%s%splugin%spacker-post-processor-%s", packerPath, PACKERSPACE, PACKERSPACE, postProcessor) + } else { + log.Printf("Using external plugin for %s", postProcessor) } } From a14498ff001dd0717e550755a6c7d5467ac6e973 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 9 Dec 2019 12:13:18 -0800 Subject: [PATCH 006/150] remove timestamps from logger inside of subprocesses to prevent double logging; also remove some cruft and fix spelling in some logs --- main.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 4078d306f..b24ee229b 100644 --- a/main.go +++ b/main.go @@ -62,8 +62,6 @@ func realMain() int { packer.LogSecretFilter.SetOutput(logWriter) - //packer.LogSecrets. - // Disable logging here log.SetOutput(ioutil.Discard) @@ -137,12 +135,16 @@ func wrappedMain() int { packer.LogSecretFilter.SetOutput(os.Stderr) log.SetOutput(&packer.LogSecretFilter) + inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue + if inPlugin { + // This prevents double-logging timestamps + log.SetFlags(0) + } + log.Printf("[INFO] Packer version: %s", version.FormattedVersion()) log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH) log.Printf("Built with Go Version: %s", runtime.Version()) - inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue - config, err := loadConfig() if err != nil { fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err) @@ -195,7 +197,7 @@ func wrappedMain() int { currentPID := os.Getpid() backgrounded, err := checkProcess(currentPID) if err != nil { - fmt.Fprintf(os.Stderr, "cannot determind if process is in "+ + fmt.Fprintf(os.Stderr, "cannot determine if process is in "+ "background: %s\n", err) } if backgrounded { From a02a95e9924030024291160ee2f7cd4f3abe797b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 9 Dec 2019 12:17:23 -0800 Subject: [PATCH 007/150] Move port logging to inside of appropriate functions; log the actual plugin being called if it is part of the main packer binary, rather than the meaningless information 'packer'. --- packer/plugin/client.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packer/plugin/client.go b/packer/plugin/client.go index 7cfcb8e21..33fcf7f75 100644 --- a/packer/plugin/client.go +++ b/packer/plugin/client.go @@ -327,8 +327,10 @@ func (c *Client) Start() (addr net.Addr, err error) { switch parts[1] { case "tcp": addr, err = net.ResolveTCPAddr("tcp", parts[2]) + log.Printf("Received tcp RPC address for %s: addr is %s", cmd.Path, addr) case "unix": addr, err = net.ResolveUnixAddr("unix", parts[2]) + log.Printf("Received unix RPC address for %s: addr is %s", cmd.Path, addr) default: err = fmt.Errorf("Unknown address type: %s", parts[1]) } @@ -339,6 +341,13 @@ func (c *Client) Start() (addr net.Addr, err error) { } func (c *Client) logStderr(r io.Reader) { + logPrefix := filepath.Base(c.config.Cmd.Path) + if logPrefix == "packer" { + // we just called the normal packer binary with the plugin arg. + // grab the last arg from the list which will match the plugin name. + logPrefix = c.config.Cmd.Args[len(c.config.Cmd.Args)-1] + } + bufR := bufio.NewReader(r) for { line, err := bufR.ReadString('\n') @@ -346,7 +355,8 @@ func (c *Client) logStderr(r io.Reader) { c.config.Stderr.Write([]byte(line)) line = strings.TrimRightFunc(line, unicode.IsSpace) - log.Printf("%s: %s", filepath.Base(c.config.Cmd.Path), line) + + log.Printf("%s plugin: %s", logPrefix, line) } if err == io.EOF { From 8b50f0305621928a5a6950a444b96f92aadd056f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 9 Dec 2019 12:21:45 -0800 Subject: [PATCH 008/150] Only log min and max port when connecting via TCP; move port determination inside of the TCP function --- packer/plugin/server.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packer/plugin/server.go b/packer/plugin/server.go index 0efefb7e5..749875b75 100644 --- a/packer/plugin/server.go +++ b/packer/plugin/server.go @@ -51,20 +51,7 @@ func Server() (*packrpc.Server, error) { runtime.GOMAXPROCS(runtime.NumCPU()) } - minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32) - if err != nil { - return nil, err - } - - maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32) - if err != nil { - return nil, err - } - - log.Printf("Plugin minimum port: %d\n", minPort) - log.Printf("Plugin maximum port: %d\n", maxPort) - - listener, err := serverListener(minPort, maxPort) + listener, err := serverListener() if err != nil { return nil, err } @@ -104,15 +91,28 @@ func Server() (*packrpc.Server, error) { return packrpc.NewServer(conn) } -func serverListener(minPort, maxPort int64) (net.Listener, error) { +func serverListener() (net.Listener, error) { if runtime.GOOS == "windows" { - return serverListener_tcp(minPort, maxPort) + return serverListener_tcp() } return serverListener_unix() } -func serverListener_tcp(minPort, maxPort int64) (net.Listener, error) { +func serverListener_tcp() (net.Listener, error) { + minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32) + if err != nil { + return nil, err + } + + maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32) + if err != nil { + return nil, err + } + + log.Printf("Plugin minimum port: %d/%d\n", minPort) + log.Printf("Plugin maximum port: %d/%d\n", maxPort) + for port := minPort; port <= maxPort; port++ { address := fmt.Sprintf("127.0.0.1:%d", port) listener, err := net.Listen("tcp", address) From 2447c9b2805a9db531a801434bd541704413c754 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 9 Dec 2019 12:30:14 -0800 Subject: [PATCH 009/150] typo --- packer/plugin/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packer/plugin/server.go b/packer/plugin/server.go index 749875b75..e30de6fb0 100644 --- a/packer/plugin/server.go +++ b/packer/plugin/server.go @@ -110,8 +110,8 @@ func serverListener_tcp() (net.Listener, error) { return nil, err } - log.Printf("Plugin minimum port: %d/%d\n", minPort) - log.Printf("Plugin maximum port: %d/%d\n", maxPort) + log.Printf("Plugin minimum port: %d\n", minPort) + log.Printf("Plugin maximum port: %d\n", maxPort) for port := minPort; port <= maxPort; port++ { address := fmt.Sprintf("127.0.0.1:%d", port) From 5e81c6f44e640c6b8cd46700e639c27345fe61d7 Mon Sep 17 00:00:00 2001 From: Oded Priva Date: Tue, 10 Dec 2019 10:17:39 +0200 Subject: [PATCH 010/150] added the option to skip IAM instance profile validation (#8457) --- builder/amazon/common/run_config.go | 2 ++ .../common/step_iam_instance_profile.go | 23 +++++++++++-------- builder/amazon/ebs/builder.go | 1 + builder/amazon/ebs/builder.hcl2spec.go | 2 ++ builder/amazon/ebssurrogate/builder.go | 1 + builder/amazon/ebsvolume/builder.go | 1 + builder/amazon/instance/builder.go | 1 + .../common/_RunConfig-not-required.html.md | 2 ++ 8 files changed, 23 insertions(+), 10 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 07449a444..f8d739609 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -145,6 +145,8 @@ type RunConfig struct { // profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) // to launch the EC2 instance with. IamInstanceProfile string `mapstructure:"iam_instance_profile" required:"false"` + // Whether or not to check if the IAM instance profile exists. Defaults to false + SkipProfileValidation bool `mapstructure:"skip_profile_validation" required:"false"` // Temporary IAM instance profile policy document // If IamInstanceProfile is specified it will be used instead. Example: // diff --git a/builder/amazon/common/step_iam_instance_profile.go b/builder/amazon/common/step_iam_instance_profile.go index 5e097b273..eba357769 100644 --- a/builder/amazon/common/step_iam_instance_profile.go +++ b/builder/amazon/common/step_iam_instance_profile.go @@ -16,6 +16,7 @@ import ( type StepIamInstanceProfile struct { IamInstanceProfile string + SkipProfileValidation bool TemporaryIamInstanceProfilePolicyDocument *PolicyDocument createdInstanceProfileName string createdRoleName string @@ -30,16 +31,18 @@ func (s *StepIamInstanceProfile) Run(ctx context.Context, state multistep.StateB state.Put("iamInstanceProfile", "") if len(s.IamInstanceProfile) > 0 { - _, err := iamsvc.GetInstanceProfile( - &iam.GetInstanceProfileInput{ - InstanceProfileName: aws.String(s.IamInstanceProfile), - }, - ) - if err != nil { - err := fmt.Errorf("Couldn't find specified instance profile: %s", err) - log.Printf("[DEBUG] %s", err.Error()) - state.Put("error", err) - return multistep.ActionHalt + if !s.SkipProfileValidation { + _, err := iamsvc.GetInstanceProfile( + &iam.GetInstanceProfileInput{ + InstanceProfileName: aws.String(s.IamInstanceProfile), + }, + ) + if err != nil { + err := fmt.Errorf("Couldn't find specified instance profile: %s", err) + log.Printf("[DEBUG] %s", err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } } log.Printf("Using specified instance profile: %v", s.IamInstanceProfile) state.Put("iamInstanceProfile", s.IamInstanceProfile) diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 4c7c46ad5..f8648e204 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -234,6 +234,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &awscommon.StepIamInstanceProfile{ IamInstanceProfile: b.config.IamInstanceProfile, + SkipProfileValidation: b.config.SkipProfileValidation, TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument, }, &awscommon.StepCleanupVolumes{ diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index 231368838..991b443db 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -55,6 +55,7 @@ type FlatConfig struct { EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` @@ -177,6 +178,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 24af4534f..abf159fad 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -257,6 +257,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &awscommon.StepIamInstanceProfile{ IamInstanceProfile: b.config.IamInstanceProfile, + SkipProfileValidation: b.config.SkipProfileValidation, TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument, }, &awscommon.StepCleanupVolumes{ diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index 5ed61279e..b3d05299d 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -227,6 +227,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &awscommon.StepIamInstanceProfile{ IamInstanceProfile: b.config.IamInstanceProfile, + SkipProfileValidation: b.config.SkipProfileValidation, TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument, }, instanceStep, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 395fac197..92a761459 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -318,6 +318,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &awscommon.StepIamInstanceProfile{ IamInstanceProfile: b.config.IamInstanceProfile, + SkipProfileValidation: b.config.SkipProfileValidation, TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument, }, instanceStep, diff --git a/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md b/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md index ecb31ce6d..92d7f818b 100644 --- a/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md +++ b/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md @@ -64,6 +64,8 @@ profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) to launch the EC2 instance with. +- `skip_profile_validation` (bool) - Whether or not to check if the IAM instance profile exists. Defaults to false + - `temporary_iam_instance_profile_policy_document` (\*PolicyDocument) - Temporary IAM instance profile policy document If IamInstanceProfile is specified it will be used instead. Example: From 20d375f70521ed9fe1b0867a06c8762f8df3405d Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Tue, 10 Dec 2019 06:59:24 -0800 Subject: [PATCH 011/150] post-processor/digitalocean-import: drop unused test functions --- .../post-processor_test.go | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/post-processor/digitalocean-import/post-processor_test.go b/post-processor/digitalocean-import/post-processor_test.go index 45e7a5b32..8173d0302 100644 --- a/post-processor/digitalocean-import/post-processor_test.go +++ b/post-processor/digitalocean-import/post-processor_test.go @@ -1,32 +1,11 @@ package digitaloceanimport import ( - "bytes" "testing" "github.com/hashicorp/packer/packer" ) -func testConfig() map[string]interface{} { - return map[string]interface{}{} -} - -func testPP(t *testing.T) *PostProcessor { - var p PostProcessor - if err := p.Configure(testConfig()); err != nil { - t.Fatalf("err: %s", err) - } - - return &p -} - -func testUi() *packer.BasicUi { - return &packer.BasicUi{ - Reader: new(bytes.Buffer), - Writer: new(bytes.Buffer), - } -} - func TestPostProcessor_ImplementsPostProcessor(t *testing.T) { var _ packer.PostProcessor = new(PostProcessor) } From 7466c4fdca6e525a6a9e4d6d7d92f2ab86f58697 Mon Sep 17 00:00:00 2001 From: Moss Date: Tue, 10 Dec 2019 18:55:18 +0100 Subject: [PATCH 012/150] Return exit code 1 when builder type is not found --- command/build.go | 10 +-- command/build_test.go | 27 ++++++++ .../test-fixtures/build-only/not-found.json | 66 +++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 command/test-fixtures/build-only/not-found.json diff --git a/command/build.go b/command/build.go index 7f582d38a..8dd245458 100644 --- a/command/build.go +++ b/command/build.go @@ -115,6 +115,10 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { } // Get the builds we care about + var errors = struct { + sync.RWMutex + m map[string]error + }{m: make(map[string]error)} buildNames := c.Meta.BuildNames(core) builds := make([]packer.Build, 0, len(buildNames)) for _, n := range buildNames { @@ -123,6 +127,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { c.Ui.Error(fmt.Sprintf( "Failed to initialize build '%s': %s", n, err)) + errors.m[n] = err continue } @@ -200,11 +205,6 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { sync.RWMutex m map[string][]packer.Artifact }{m: make(map[string][]packer.Artifact)} - var errors = struct { - sync.RWMutex - m map[string]error - }{m: make(map[string]error)} - limitParallel := semaphore.NewWeighted(cfg.ParallelBuilds) for i := range builds { if err := buildCtx.Err(); err != nil { diff --git a/command/build_test.go b/command/build_test.go index d96ce6998..b68ef068c 100644 --- a/command/build_test.go +++ b/command/build_test.go @@ -166,6 +166,30 @@ func TestBuildExceptFileCommaFlags(t *testing.T) { } } +func TestBuildExceptNonExistingBuilder(t *testing.T) { + c := &BuildCommand{ + Meta: testMetaFile(t), + } + + args := []string{ + "-parallel=false", + `-except=`, + filepath.Join(testFixture("build-only"), "not-found.json"), + } + + defer cleanup() + + if code := c.Run(args); code != 1 { + t.Errorf("Expected to find exit code 1, found %d", code) + } + if !fileExists("chocolate.txt") { + t.Errorf("Expected to find chocolate.txt") + } + if fileExists("vanilla.txt") { + t.Errorf("NOT expected to find vanilla.tx") + } +} + // fileExists returns true if the filename is found func fileExists(filename string) bool { if _, err := os.Stat(filename); err == nil { @@ -182,6 +206,9 @@ func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig { if n == "file" { return &file.Builder{}, nil } + if n == "non-existing" { + return nil, fmt.Errorf("builder type not found") + } return &null.Builder{}, nil }, Provisioner: func(n string) (packer.Provisioner, error) { diff --git a/command/test-fixtures/build-only/not-found.json b/command/test-fixtures/build-only/not-found.json new file mode 100644 index 000000000..5f6ec3238 --- /dev/null +++ b/command/test-fixtures/build-only/not-found.json @@ -0,0 +1,66 @@ +{ + "builders": [ + { + "name": "chocolate", + "type": "file", + "content": "chocolate", + "target": "chocolate.txt" + }, + { + "name": "vanilla", + "type": "non-existing", + "content": "vanilla", + "target": "vanilla.txt" + } + ], + "post-processors": [ + [ + { + "name": "apple", + "type": "shell-local", + "inline": [ + "echo apple > apple.txt" + ] + }, + { + "name": "peach", + "type": "shell-local", + "inline": [ + "echo peach > peach.txt" + ] + } + ], + [ + { + "name": "pear", + "type": "shell-local", + "inline": [ + "echo pear > pear.txt" + ] + } + ], + [ + { + "only": [ + "vanilla" + ], + "name": "tomato", + "type": "shell-local", + "inline": [ + "echo tomato > tomato.txt" + ] + } + ], + [ + { + "only": [ + "chocolate" + ], + "type": "shell-local", + "inline": [ + "echo unnamed > unnamed.txt" + ] + } + ] + ] +} \ No newline at end of file From a0329ef1964ee07b819843eaadaa432b2169e4ca Mon Sep 17 00:00:00 2001 From: Moss Date: Tue, 10 Dec 2019 18:59:56 +0100 Subject: [PATCH 013/150] Simplify testing template --- .../test-fixtures/build-only/not-found.json | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/command/test-fixtures/build-only/not-found.json b/command/test-fixtures/build-only/not-found.json index 5f6ec3238..4f327d82f 100644 --- a/command/test-fixtures/build-only/not-found.json +++ b/command/test-fixtures/build-only/not-found.json @@ -14,31 +14,6 @@ } ], "post-processors": [ - [ - { - "name": "apple", - "type": "shell-local", - "inline": [ - "echo apple > apple.txt" - ] - }, - { - "name": "peach", - "type": "shell-local", - "inline": [ - "echo peach > peach.txt" - ] - } - ], - [ - { - "name": "pear", - "type": "shell-local", - "inline": [ - "echo pear > pear.txt" - ] - } - ], [ { "only": [ From a645b1d48ead75e639d6705e89fd7999dd0a0a37 Mon Sep 17 00:00:00 2001 From: Mateusz Kaczanowski Date: Tue, 10 Dec 2019 20:41:00 +0100 Subject: [PATCH 014/150] Update _community_builders.html.md --- website/source/partials/builders/_community_builders.html.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/source/partials/builders/_community_builders.html.md b/website/source/partials/builders/_community_builders.html.md index a3aefbe4e..fcdb0525d 100644 --- a/website/source/partials/builders/_community_builders.html.md +++ b/website/source/partials/builders/_community_builders.html.md @@ -1,7 +1,9 @@ ### Community Builders -- [ARM builder](https://github.com/mkaczanowski/packer-builder-arm) - A builder +- [ARM builder]() - A builder for creating ARM images + * [packer-builder-arm-image](https://github.com/solo-io/packer-builder-arm-image) - simple builder lets you extend on existing system images. + * [packer-builder-arm](https://github.com/mkaczanowski/packer-builder-arm) - flexible builder lets you extend or build images from scratch with variety of options (ie. custom partition table). - [vSphere builder](https://github.com/jetbrains-infra/packer-builder-vsphere) - A builder for interacting directly with the vSphere API rather than the esx From f1765dfd798d44e9f66be0e6cd57e8a1bc2451f2 Mon Sep 17 00:00:00 2001 From: Mateusz Kaczanowski Date: Wed, 11 Dec 2019 15:25:23 +0100 Subject: [PATCH 015/150] Update _community_builders.html.md --- website/source/partials/builders/_community_builders.html.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/source/partials/builders/_community_builders.html.md b/website/source/partials/builders/_community_builders.html.md index fcdb0525d..60c892ba1 100644 --- a/website/source/partials/builders/_community_builders.html.md +++ b/website/source/partials/builders/_community_builders.html.md @@ -1,7 +1,6 @@ ### Community Builders -- [ARM builder]() - A builder - for creating ARM images +- ARM builders * [packer-builder-arm-image](https://github.com/solo-io/packer-builder-arm-image) - simple builder lets you extend on existing system images. * [packer-builder-arm](https://github.com/mkaczanowski/packer-builder-arm) - flexible builder lets you extend or build images from scratch with variety of options (ie. custom partition table). From f4c3501af5663959ac2f7d12ff47296453bb59e9 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 11 Jul 2019 09:37:59 -0700 Subject: [PATCH 016/150] pass struct of generated data into provision() call --- common/step_provision.go | 16 ++++++++- packer/plugin/provisioner.go | 4 +-- packer/provisioner.go | 32 +++++++++++++---- packer/provisioner_mock.go | 2 +- packer/provisioner_timeout.go | 4 +-- packer/rpc/provisioner.go | 6 ++-- provisioner/ansible-local/provisioner.go | 2 +- provisioner/ansible/provisioner.go | 2 +- provisioner/breakpoint/provisioner.go | 2 +- provisioner/chef-client/provisioner.go | 2 +- provisioner/chef-solo/provisioner.go | 2 +- provisioner/converge/provisioner.go | 2 +- provisioner/file/provisioner.go | 2 +- provisioner/inspec/provisioner.go | 2 +- provisioner/powershell/provisioner.go | 38 +++++++------------- provisioner/puppet-masterless/provisioner.go | 2 +- provisioner/puppet-server/provisioner.go | 2 +- provisioner/salt-masterless/provisioner.go | 2 +- provisioner/shell-local/provisioner.go | 2 +- provisioner/shell/provisioner.go | 2 +- provisioner/sleep/provisioner.go | 2 +- provisioner/windows-restart/provisioner.go | 2 +- provisioner/windows-shell/provisioner.go | 2 +- 23 files changed, 78 insertions(+), 56 deletions(-) diff --git a/common/step_provision.go b/common/step_provision.go index 07ba39f81..233bcf2fc 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -23,6 +23,17 @@ type StepProvision struct { Comm packer.Communicator } +func PopulateProvisionHookData(state multistep.StateBag) packer.ProvisionHookData { + hookData := packer.NewProvisionHookData() + + // Add WinRMPassword to runtime data + WinRMPassword, ok := state.GetOk("winrm_password") + if ok { + hookData.WinRMPassword = WinRMPassword.(string) + } + return hookData +} + func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBag, hooktype string) multistep.StepAction { // hooktype will be either packer.HookProvision or packer.HookCleanupProvision comm := s.Comm @@ -32,9 +43,12 @@ func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBa comm = raw.(packer.Communicator) } } + hook := state.Get("hook").(packer.Hook) ui := state.Get("ui").(packer.Ui) + hookData := PopulateProvisionHookData(state) + // Run the provisioner in a goroutine so we can continually check // for cancellations... if hooktype == packer.HookProvision { @@ -44,7 +58,7 @@ func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBa } errCh := make(chan error, 1) go func() { - errCh <- hook.Run(ctx, hooktype, ui, comm, nil) + errCh <- hook.Run(ctx, hooktype, ui, comm, &hookData) }() for { diff --git a/packer/plugin/provisioner.go b/packer/plugin/provisioner.go index 9c3b8fb42..23cb41730 100644 --- a/packer/plugin/provisioner.go +++ b/packer/plugin/provisioner.go @@ -21,13 +21,13 @@ func (c *cmdProvisioner) Prepare(configs ...interface{}) error { return c.p.Prepare(configs...) } -func (c *cmdProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (c *cmdProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { defer func() { r := recover() c.checkExit(r, nil) }() - return c.p.Provision(ctx, ui, comm) + return c.p.Provision(ctx, ui, comm, generatedData) } func (c *cmdProvisioner) checkExit(p interface{}, cb func()) { diff --git a/packer/provisioner.go b/packer/provisioner.go index ff89c0c42..c9e9a9f31 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -20,7 +20,7 @@ type Provisioner interface { // given for cancellation, a UI is given to communicate with the user, and // a communicator is given that is guaranteed to be connected to some // machine so that provisioning can be done. - Provision(context.Context, Ui, Communicator) error + Provision(context.Context, Ui, Communicator, *ProvisionHookData) error } // A HookedProvisioner represents a provisioner and information describing it @@ -37,6 +37,21 @@ type ProvisionHook struct { Provisioners []*HookedProvisioner } +type ProvisionHookData struct { + WinRMPassword string +} + +func NewProvisionHookData() ProvisionHookData { + // this is the function we use to create the provisionhookdata, and as a + // bonus the initialized state applies defaults that act as passthroughs so + // that when prepare is called the first time, we can save interpolating + // these values until provisioner run time. + hookData := ProvisionHookData{ + WinRMPassword: `{{.WinRMPassword}}`, + } + return hookData +} + // Runs the provisioners in order. func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error { // Shortcut @@ -44,6 +59,11 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun return nil } + generatedData := NewProvisionHookData() + if data != nil { + generatedData = data.(ProvisionHookData) + } + if comm == nil { return fmt.Errorf( "No communicator found for provisioners! This is usually because the\n" + @@ -53,7 +73,7 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun for _, p := range h.Provisioners { ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config) - err := p.Provisioner.Provision(ctx, ui, comm) + err := p.Provisioner.Provision(ctx, ui, comm, &generatedData) ts.End(err) if err != nil { @@ -75,7 +95,7 @@ func (p *PausedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } -func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator) error { +func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { // Use a select to determine if we get cancelled during the wait ui.Say(fmt.Sprintf("Pausing %s before the next provisioner...", p.PauseBefore)) @@ -85,7 +105,7 @@ func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communica return ctx.Err() } - return p.Provisioner.Provision(ctx, ui, comm) + return p.Provisioner.Provision(ctx, ui, comm, generatedData) } // DebuggedProvisioner is a Provisioner implementation that waits until a key @@ -102,7 +122,7 @@ func (p *DebuggedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } -func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator) error { +func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { // Use a select to determine if we get cancelled during the wait message := "Pausing before the next provisioner . Press enter to continue." @@ -122,5 +142,5 @@ func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communi return ctx.Err() } - return p.Provisioner.Provision(ctx, ui, comm) + return p.Provisioner.Provision(ctx, ui, comm, generatedData) } diff --git a/packer/provisioner_mock.go b/packer/provisioner_mock.go index 9555c5f57..26be569a2 100644 --- a/packer/provisioner_mock.go +++ b/packer/provisioner_mock.go @@ -22,7 +22,7 @@ func (t *MockProvisioner) Prepare(configs ...interface{}) error { return nil } -func (t *MockProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator) error { +func (t *MockProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { t.ProvCalled = true t.ProvCommunicator = comm t.ProvUi = ui diff --git a/packer/provisioner_timeout.go b/packer/provisioner_timeout.go index 1da02f563..cfe0566b5 100644 --- a/packer/provisioner_timeout.go +++ b/packer/provisioner_timeout.go @@ -13,7 +13,7 @@ type TimeoutProvisioner struct { Timeout time.Duration } -func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator) error { +func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { ctx, cancel := context.WithTimeout(ctx, p.Timeout) defer cancel() @@ -37,7 +37,7 @@ func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communic } }() - err := p.Provisioner.Provision(ctx, ui, comm) + err := p.Provisioner.Provision(ctx, ui, comm, generatedData) close(errC) return err } diff --git a/packer/rpc/provisioner.go b/packer/rpc/provisioner.go index faad6f434..5fcabf1f7 100644 --- a/packer/rpc/provisioner.go +++ b/packer/rpc/provisioner.go @@ -38,7 +38,7 @@ func (p *provisioner) Prepare(configs ...interface{}) (err error) { return } -func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { nextId := p.mux.NextId() server := newServerWithMux(p.mux, nextId) server.RegisterCommunicator(comm) @@ -66,7 +66,7 @@ func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *interfa return p.p.Prepare(args.Configs...) } -func (p *ProvisionerServer) Provision(streamId uint32, reply *interface{}) error { +func (p *ProvisionerServer) Provision(streamId uint32, reply *interface{}, generatedData *packer.ProvisionHookData) error { client, err := newClientWithMux(p.mux, streamId) if err != nil { return NewBasicError(err) @@ -77,7 +77,7 @@ func (p *ProvisionerServer) Provision(streamId uint32, reply *interface{}) error p.context, p.contextCancel = context.WithCancel(context.Background()) } - if err := p.p.Provision(p.context, client.Ui(), client.Communicator()); err != nil { + if err := p.p.Provision(p.context, client.Ui(), client.Communicator(), generatedData); err != nil { return NewBasicError(err) } diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 4575115ac..be3449b3b 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -188,7 +188,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with Ansible...") if len(p.config.PlaybookDir) > 0 { diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 6de5bba5f..e717edb50 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -214,7 +214,7 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with Ansible...") // Interpolate env vars to check for .WinRMPassword p.config.ctx.Data = &PassthroughTemplate{ diff --git a/provisioner/breakpoint/provisioner.go b/provisioner/breakpoint/provisioner.go index 8ff752065..668da955b 100644 --- a/provisioner/breakpoint/provisioner.go +++ b/provisioner/breakpoint/provisioner.go @@ -42,7 +42,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { if p.config.Disable { if p.config.Note != "" { ui.Say(fmt.Sprintf( diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 6ca75e402..a5091b748 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -244,7 +244,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { p.communicator = comm diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index 39d59b36b..7d8119eb6 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -238,7 +238,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with chef-solo") if !p.config.SkipInstall { diff --git a/provisioner/converge/provisioner.go b/provisioner/converge/provisioner.go index c0c5cbeb4..b226edaac 100644 --- a/provisioner/converge/provisioner.go +++ b/provisioner/converge/provisioner.go @@ -108,7 +108,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } // Provision node somehow. TODO: actual docs -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with Converge") // bootstrapping diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index 9fc73b950..eb8d3db6a 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -92,7 +92,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { if p.config.Direction == "download" { return p.ProvisionDownload(ui, comm) } else { diff --git a/provisioner/inspec/provisioner.go b/provisioner/inspec/provisioner.go index 0b7d047aa..e12d419ed 100644 --- a/provisioner/inspec/provisioner.go +++ b/provisioner/inspec/provisioner.go @@ -186,7 +186,7 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with Inspec...") for i, envVar := range p.config.InspecEnvVars { diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 5ff6cbe93..9c6fd76aa 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" "github.com/hashicorp/packer/common/uuid" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer/tmp" @@ -73,8 +72,9 @@ type Config struct { } type Provisioner struct { - config Config - communicator packer.Communicator + config Config + communicator packer.Communicator + generatedData *packer.ProvisionHookData } type ExecuteCommandTemplate struct { @@ -83,10 +83,6 @@ type ExecuteCommandTemplate struct { WinRMPassword string } -type EnvVarsTemplate struct { - WinRMPassword string -} - func (p *Provisioner) defaultExecuteCommand() string { baseCmd := `& { if (Test-Path variable:global:ProgressPreference)` + `{set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};` + @@ -101,9 +97,7 @@ func (p *Provisioner) defaultExecuteCommand() string { func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: `{{.WinRMPassword}}`, - } + p.config.ctx.Data = packer.NewProvisionHookData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, @@ -232,9 +226,12 @@ func extractScript(p *Provisioner) (string, error) { return temp.Name(), nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { ui.Say(fmt.Sprintf("Provisioning with Powershell...")) p.communicator = comm + p.generatedData = generatedData + + packer.LogSecretFilter.Set(p.generatedData.WinRMPassword) scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) @@ -344,9 +341,8 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) { } // interpolate environment variables - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), - } + p.config.ctx.Data = p.generatedData + // Split vars into key/value components for _, envVar := range p.config.Vars { envVar, err := interpolate.Render(envVar, &p.config.ctx) @@ -421,7 +417,7 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro p.config.ctx.Data = &ExecuteCommandTemplate{ Path: p.config.RemotePath, Vars: p.config.RemoteEnvVarPath, - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), + WinRMPassword: p.generatedData.WinRMPassword, } command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) @@ -433,12 +429,6 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro return command, nil } -func getWinRMPassword(buildName string) string { - winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) - packer.LogSecretFilter.Set(winRMPass) - return winRMPass -} - func (p *Provisioner) createCommandTextPrivileged() (command string, err error) { // Prepare everything needed to enable the required env vars within the // remote environment @@ -450,7 +440,7 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error) p.config.ctx.Data = &ExecuteCommandTemplate{ Path: p.config.RemotePath, Vars: p.config.RemoteEnvVarPath, - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), + WinRMPassword: p.generatedData.WinRMPassword, } command, err = interpolate.Render(p.config.ElevatedExecuteCommand, &p.config.ctx) if err != nil { @@ -475,9 +465,7 @@ func (p *Provisioner) ElevatedUser() string { func (p *Provisioner) ElevatedPassword() string { // Replace ElevatedPassword for winrm users who used this feature - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), - } + p.config.ctx.Data = p.generatedData elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index d66e54baa..fe6a899d8 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -259,7 +259,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with Puppet...") p.communicator = comm ui.Message("Creating Puppet staging directory...") diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index e4fc2adff..256050ef9 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -229,7 +229,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say("Provisioning with Puppet...") p.communicator = comm ui.Message("Creating Puppet staging directory...") diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index 5531d76b9..485f58389 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -222,7 +222,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { var err error var src, dst string diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index f92f93c70..d5de2b51c 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -25,7 +25,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator, _ *packer.ProvisionHookData) error { _, retErr := sl.Run(ctx, ui, &p.config) return retErr diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index 824225aee..dd2560fc8 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -179,7 +179,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) diff --git a/provisioner/sleep/provisioner.go b/provisioner/sleep/provisioner.go index 646f1c4dd..84de8ae16 100644 --- a/provisioner/sleep/provisioner.go +++ b/provisioner/sleep/provisioner.go @@ -20,7 +20,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return config.Decode(&p, &config.DecodeOpts{}, raws...) } -func (p *Provisioner) Provision(ctx context.Context, _ packer.Ui, _ packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, _ packer.Ui, _ packer.Communicator, _ *packer.ProvisionHookData) error { select { case <-ctx.Done(): return ctx.Err() diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index f2bc01d30..20ff840f2 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -96,7 +96,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { p.cancelLock.Lock() p.cancel = make(chan struct{}) p.cancelLock.Unlock() diff --git a/provisioner/windows-shell/provisioner.go b/provisioner/windows-shell/provisioner.go index 2f42b145d..dbb441f83 100644 --- a/provisioner/windows-shell/provisioner.go +++ b/provisioner/windows-shell/provisioner.go @@ -156,7 +156,7 @@ func extractScript(p *Provisioner) (string, error) { return temp.Name(), nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { ui.Say(fmt.Sprintf("Provisioning with windows-shell...")) scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) From ee336e6d124bfc56a1cbdd6470cd23a2190d4c4b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 11 Jul 2019 10:27:19 -0700 Subject: [PATCH 017/150] decode data interface into generatedData --- packer/provisioner.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packer/provisioner.go b/packer/provisioner.go index c9e9a9f31..32a4d18c2 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -6,6 +6,8 @@ import ( "log" "sync" "time" + + "github.com/hashicorp/packer/helper/config" ) // A provisioner is responsible for installing and configuring software @@ -60,10 +62,13 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun } generatedData := NewProvisionHookData() - if data != nil { - generatedData = data.(ProvisionHookData) + err := config.Decode(&generatedData, &config.DecodeOpts{Interpolate: false}, data) + if err != nil { + log.Printf("Failed to decode provisioner generated data: %s", err) } + log.Printf("Megan decoded data: %#v", generatedData) + if comm == nil { return fmt.Errorf( "No communicator found for provisioners! This is usually because the\n" + From 601007e3e2e3ac1317ee1b4b9f0d6d8de9e0392e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 11 Jul 2019 12:10:57 -0700 Subject: [PATCH 018/150] pas data into provisioners well --- builder/amazon/common/step_get_password.go | 7 ++----- builder/azure/arm/config.go | 1 - builder/azure/arm/step_save_winrm_password.go | 6 ++---- .../googlecompute/step_create_windows_password.go | 1 - builder/oracle/oci/step_get_default_credentials.go | 2 +- packer/rpc/provisioner.go | 12 ++++++++++-- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/builder/amazon/common/step_get_password.go b/builder/amazon/common/step_get_password.go index 3b751cc98..49413faec 100644 --- a/builder/amazon/common/step_get_password.go +++ b/builder/amazon/common/step_get_password.go @@ -91,16 +91,13 @@ WaitLoop: "Password (since debug is enabled): %s", s.Comm.WinRMPassword)) } // store so that we can access this later during provisioning - - commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName) + state.Put("winrm_password", s.Comm.WinRMPassword) packer.LogSecretFilter.Set(s.Comm.WinRMPassword) return multistep.ActionContinue } -func (s *StepGetPassword) Cleanup(multistep.StateBag) { - commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName) -} +func (s *StepGetPassword) Cleanup(multistep.StateBag) {} func (s *StepGetPassword) waitForPassword(ctx context.Context, state multistep.StateBag) (string, error) { ec2conn := state.Get("ec2").(*ec2.EC2) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 552a2389d..28638c24e 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -601,7 +601,6 @@ func setRuntimeValues(c *Config) { c.tmpAdminPassword = tempName.AdminPassword // store so that we can access this later during provisioning - commonhelper.SetSharedState("winrm_password", c.tmpAdminPassword, c.PackerConfig.PackerBuildName) packer.LogSecretFilter.Set(c.tmpAdminPassword) c.tmpCertificatePassword = tempName.CertificatePassword diff --git a/builder/azure/arm/step_save_winrm_password.go b/builder/azure/arm/step_save_winrm_password.go index c23c6b26b..2a37c0960 100644 --- a/builder/azure/arm/step_save_winrm_password.go +++ b/builder/azure/arm/step_save_winrm_password.go @@ -15,11 +15,9 @@ type StepSaveWinRMPassword struct { func (s *StepSaveWinRMPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { // store so that we can access this later during provisioning - commonhelper.SetSharedState("winrm_password", s.Password, s.BuildName) + state.Put("winrm_password", s.Password) packer.LogSecretFilter.Set(s.Password) return multistep.ActionContinue } -func (s *StepSaveWinRMPassword) Cleanup(multistep.StateBag) { - commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName) -} +func (s *StepSaveWinRMPassword) Cleanup(multistep.StateBag) {} diff --git a/builder/googlecompute/step_create_windows_password.go b/builder/googlecompute/step_create_windows_password.go index 2112b843c..198d57640 100644 --- a/builder/googlecompute/step_create_windows_password.go +++ b/builder/googlecompute/step_create_windows_password.go @@ -119,7 +119,6 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta } state.Put("winrm_password", data.password) - commonhelper.SetSharedState("winrm_password", data.password, c.PackerConfig.PackerBuildName) packer.LogSecretFilter.Set(data.password) return multistep.ActionContinue diff --git a/builder/oracle/oci/step_get_default_credentials.go b/builder/oracle/oci/step_get_default_credentials.go index 1f4ea20b3..90a6b64f9 100644 --- a/builder/oracle/oci/step_get_default_credentials.go +++ b/builder/oracle/oci/step_get_default_credentials.go @@ -52,7 +52,7 @@ func (s *stepGetDefaultCredentials) Run(ctx context.Context, state multistep.Sta } // store so that we can access this later during provisioning - commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName) + state.Put("winrm_password", s.Comm.WinRMPassword) packer.LogSecretFilter.Set(s.Comm.WinRMPassword) return multistep.ActionContinue } diff --git a/packer/rpc/provisioner.go b/packer/rpc/provisioner.go index 5fcabf1f7..d03c945c4 100644 --- a/packer/rpc/provisioner.go +++ b/packer/rpc/provisioner.go @@ -38,6 +38,11 @@ func (p *provisioner) Prepare(configs ...interface{}) (err error) { return } +type ProvisionerProvisionArgs struct { + GeneratedData *packer.ProvisionHookData + StreamID uint32 +} + func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { nextId := p.mux.NextId() server := newServerWithMux(p.mux, nextId) @@ -59,14 +64,17 @@ func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C } }() - return p.client.Call("Provisioner.Provision", nextId, new(interface{})) + args := &ProvisionerProvisionArgs{generatedData, nextId} + return p.client.Call("Provisioner.Provision", args, new(interface{})) } func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *interface{}) error { return p.p.Prepare(args.Configs...) } -func (p *ProvisionerServer) Provision(streamId uint32, reply *interface{}, generatedData *packer.ProvisionHookData) error { +func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *interface{}) error { + streamId := args.StreamID + generatedData := args.GeneratedData client, err := newClientWithMux(p.mux, streamId) if err != nil { return NewBasicError(err) From 6f418d0e544d05c58381931e9916373981776c37 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 4 Oct 2019 11:36:57 -0700 Subject: [PATCH 019/150] get data sharing to a working state with the powershell provisioner --- builder/amazon/common/step_get_password.go | 1 - builder/azure/arm/config.go | 1 - builder/azure/arm/step_save_winrm_password.go | 1 - .../step_create_windows_password.go | 1 - .../oci/step_get_default_credentials.go | 1 - common/shell-local/config.go | 12 ++-- common/step_provision.go | 63 ++++++++++++++++--- helper/communicator/step_connect.go | 5 +- helper/communicator/step_connect_ssh.go | 5 +- helper/communicator/step_connect_winrm.go | 4 ++ packer/hook.go | 1 - packer/plugin/provisioner.go | 2 +- packer/provisioner.go | 33 ++-------- packer/provisioner_mock.go | 2 +- packer/provisioner_timeout.go | 2 +- packer/rpc/provisioner.go | 4 +- provisioner/ansible-local/provisioner.go | 2 +- provisioner/ansible/provisioner.go | 2 +- provisioner/breakpoint/provisioner.go | 2 +- provisioner/chef-client/provisioner.go | 2 +- provisioner/chef-solo/provisioner.go | 2 +- provisioner/converge/provisioner.go | 2 +- provisioner/file/provisioner.go | 2 +- provisioner/inspec/provisioner.go | 2 +- provisioner/powershell/provisioner.go | 47 +++++++------- provisioner/puppet-masterless/provisioner.go | 2 +- provisioner/puppet-server/provisioner.go | 2 +- provisioner/salt-masterless/provisioner.go | 2 +- provisioner/shell-local/provisioner.go | 2 +- provisioner/shell/provisioner.go | 2 +- provisioner/sleep/provisioner.go | 2 +- provisioner/windows-restart/provisioner.go | 2 +- provisioner/windows-shell/provisioner.go | 2 +- 33 files changed, 124 insertions(+), 93 deletions(-) diff --git a/builder/amazon/common/step_get_password.go b/builder/amazon/common/step_get_password.go index 49413faec..a2d741af6 100644 --- a/builder/amazon/common/step_get_password.go +++ b/builder/amazon/common/step_get_password.go @@ -13,7 +13,6 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/packer/common/retry" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 28638c24e..e65646dba 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -27,7 +27,6 @@ import ( "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/pkcs12" "github.com/hashicorp/packer/common" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" diff --git a/builder/azure/arm/step_save_winrm_password.go b/builder/azure/arm/step_save_winrm_password.go index 2a37c0960..648a867c6 100644 --- a/builder/azure/arm/step_save_winrm_password.go +++ b/builder/azure/arm/step_save_winrm_password.go @@ -3,7 +3,6 @@ package arm import ( "context" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) diff --git a/builder/googlecompute/step_create_windows_password.go b/builder/googlecompute/step_create_windows_password.go index 198d57640..8ac19f068 100644 --- a/builder/googlecompute/step_create_windows_password.go +++ b/builder/googlecompute/step_create_windows_password.go @@ -13,7 +13,6 @@ import ( "os" "time" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) diff --git a/builder/oracle/oci/step_get_default_credentials.go b/builder/oracle/oci/step_get_default_credentials.go index 90a6b64f9..0ed44b7f6 100644 --- a/builder/oracle/oci/step_get_default_credentials.go +++ b/builder/oracle/oci/step_get_default_credentials.go @@ -5,7 +5,6 @@ import ( "fmt" "log" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/common/shell-local/config.go b/common/shell-local/config.go index e4c808de9..2f3a06e6c 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -41,14 +41,16 @@ type Config struct { // End dedupe with postprocessor UseLinuxPathing bool `mapstructure:"use_linux_pathing"` - ctx interpolate.Context + // used to track the data sent to shell-local from the builder + // GeneratedData + + Ctx interpolate.Context } func Decode(config *Config, raws ...interface{}) error { - //Create passthrough for winrm password so we can fill it in once we know it - config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: `{{.WinRMPassword}}`, - } + // Create passthrough for build-generated data so we can fill it in once we know + // it + config.Ctx.Data = common.PlaceholderData() err := configHelper.Decode(&config, &configHelper.DecodeOpts{ Interpolate: true, diff --git a/common/step_provision.go b/common/step_provision.go index 233bcf2fc..7f51a20c5 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "log" + "reflect" "time" + "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -19,18 +21,62 @@ import ( // // Produces: // + +// Provisioners interpolate most of their fields in the prepare stage; this +// placeholder map helps keep fields that are only generated at build time from +// accidentally being interpolated into empty strings at prepare time. +func PlaceholderData() map[string]string { + placeholderData := map[string]string{} + + // use reflection to grab the communicator config field off the config + var sshExample communicator.SSH + var winrmExample communicator.WinRM + + t := reflect.TypeOf(sshExample) + n := t.NumField() + for i := 0; i < n; i++ { + fVal := t.Field(i) + name := fVal.Name + placeholderData[name] = fmt.Sprintf("{{.%s}}", name) + } + + t = reflect.TypeOf(winrmExample) + n = t.NumField() + for i := 0; i < n; i++ { + fVal := t.Field(i) + name := fVal.Name + placeholderData[name] = fmt.Sprintf("{{.%s}}", name) + } + + return placeholderData +} + type StepProvision struct { Comm packer.Communicator } -func PopulateProvisionHookData(state multistep.StateBag) packer.ProvisionHookData { - hookData := packer.NewProvisionHookData() - - // Add WinRMPassword to runtime data - WinRMPassword, ok := state.GetOk("winrm_password") - if ok { - hookData.WinRMPassword = WinRMPassword.(string) +func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { + hookData := map[string]interface{}{} + // Read communicator data into hook data + commConf, ok := state.GetOk("communicator_config") + if !ok { + log.Printf("Unable to load config from state to populate provisionHookData") + return hookData } + cast := commConf.(*communicator.Config) + + pd := PlaceholderData() + + v := reflect.ValueOf(cast) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + // Loop over all field values and retrieve them from the ssh config + for fieldName, _ := range pd { + fVal := v.FieldByName(fieldName) + hookData[fieldName] = fVal.Interface() + } + return hookData } @@ -48,6 +94,7 @@ func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBa ui := state.Get("ui").(packer.Ui) hookData := PopulateProvisionHookData(state) + log.Printf("Megan hookdata is %T", hookData) // Run the provisioner in a goroutine so we can continually check // for cancellations... @@ -58,7 +105,7 @@ func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBa } errCh := make(chan error, 1) go func() { - errCh <- hook.Run(ctx, hooktype, ui, comm, &hookData) + errCh <- hook.Run(ctx, hooktype, ui, comm, hookData) }() for { diff --git a/helper/communicator/step_connect.go b/helper/communicator/step_connect.go index 6bb062f80..7694e0b1a 100644 --- a/helper/communicator/step_connect.go +++ b/helper/communicator/step_connect.go @@ -101,7 +101,6 @@ func (s *StepConnect) Run(ctx context.Context, state multistep.StateBag) multist if host, err := s.Host(state); err == nil { ui.Say(fmt.Sprintf("Using %s communicator to connect: %s", s.Config.Type, host)) - } else { log.Printf("[DEBUG] Unable to get address during connection step: %s", err) } @@ -119,6 +118,10 @@ func (s *StepConnect) Run(ctx context.Context, state multistep.StateBag) multist } } + // Put communicator config into state so we can pass it to provisioners + // for specialized interpolation later + state.Put("communicator_config", s.Config) + return multistep.ActionContinue } diff --git a/helper/communicator/step_connect_ssh.go b/helper/communicator/step_connect_ssh.go index 962615c3a..1584c3e61 100644 --- a/helper/communicator/step_connect_ssh.go +++ b/helper/communicator/step_connect_ssh.go @@ -134,6 +134,8 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, ctx context.Contex log.Printf("[DEBUG] Error getting SSH address: %s", err) continue } + // store host and port in config so we can access them from provisioners + s.Config.SSHHost = host port := s.Config.SSHPort if s.SSHPort != nil { port, err = s.SSHPort(state) @@ -141,7 +143,9 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, ctx context.Contex log.Printf("[DEBUG] Error getting SSH port: %s", err) continue } + s.Config.SSHPort = port } + state.Put("communicator_config", s.Config) // Retrieve the SSH configuration sshConfig, err := s.SSHConfig(state) @@ -204,7 +208,6 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, ctx context.Contex } log.Printf("[INFO] Attempting SSH connection to %s...", address) - log.Printf("[DEBUG] Config to %#v...", config) comm, err = ssh.New(address, config) if err != nil { log.Printf("[DEBUG] SSH handshake err: %s", err) diff --git a/helper/communicator/step_connect_winrm.go b/helper/communicator/step_connect_winrm.go index 275468bc1..8825f98b0 100644 --- a/helper/communicator/step_connect_winrm.go +++ b/helper/communicator/step_connect_winrm.go @@ -103,6 +103,7 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, ctx context.Co log.Printf("[DEBUG] Error getting WinRM host: %s", err) continue } + s.Config.WinRMHost = host port := s.Config.WinRMPort if s.WinRMPort != nil { @@ -111,8 +112,11 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, ctx context.Co log.Printf("[DEBUG] Error getting WinRM port: %s", err) continue } + s.Config.WinRMPort = port } + state.Put("communicator_config", s.Config) + user := s.Config.WinRMUser password := s.Config.WinRMPassword if s.WinRMConfig != nil { diff --git a/packer/hook.go b/packer/hook.go index 25df5c855..ee8104ac9 100644 --- a/packer/hook.go +++ b/packer/hook.go @@ -34,7 +34,6 @@ type DispatchHook struct { // hooks if a mapping exists. If a mapping doesn't exist, then nothing // happens. func (h *DispatchHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error { - hooks, ok := h.Mapping[name] if !ok { // No hooks for that name. No problem. diff --git a/packer/plugin/provisioner.go b/packer/plugin/provisioner.go index 23cb41730..14bc2a65e 100644 --- a/packer/plugin/provisioner.go +++ b/packer/plugin/provisioner.go @@ -21,7 +21,7 @@ func (c *cmdProvisioner) Prepare(configs ...interface{}) error { return c.p.Prepare(configs...) } -func (c *cmdProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { +func (c *cmdProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { defer func() { r := recover() c.checkExit(r, nil) diff --git a/packer/provisioner.go b/packer/provisioner.go index 32a4d18c2..a317b157f 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -6,8 +6,6 @@ import ( "log" "sync" "time" - - "github.com/hashicorp/packer/helper/config" ) // A provisioner is responsible for installing and configuring software @@ -22,7 +20,7 @@ type Provisioner interface { // given for cancellation, a UI is given to communicate with the user, and // a communicator is given that is guaranteed to be connected to some // machine so that provisioning can be done. - Provision(context.Context, Ui, Communicator, *ProvisionHookData) error + Provision(context.Context, Ui, Communicator, interface{}) error } // A HookedProvisioner represents a provisioner and information describing it @@ -39,21 +37,6 @@ type ProvisionHook struct { Provisioners []*HookedProvisioner } -type ProvisionHookData struct { - WinRMPassword string -} - -func NewProvisionHookData() ProvisionHookData { - // this is the function we use to create the provisionhookdata, and as a - // bonus the initialized state applies defaults that act as passthroughs so - // that when prepare is called the first time, we can save interpolating - // these values until provisioner run time. - hookData := ProvisionHookData{ - WinRMPassword: `{{.WinRMPassword}}`, - } - return hookData -} - // Runs the provisioners in order. func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error { // Shortcut @@ -61,14 +44,6 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun return nil } - generatedData := NewProvisionHookData() - err := config.Decode(&generatedData, &config.DecodeOpts{Interpolate: false}, data) - if err != nil { - log.Printf("Failed to decode provisioner generated data: %s", err) - } - - log.Printf("Megan decoded data: %#v", generatedData) - if comm == nil { return fmt.Errorf( "No communicator found for provisioners! This is usually because the\n" + @@ -78,7 +53,7 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun for _, p := range h.Provisioners { ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config) - err := p.Provisioner.Provision(ctx, ui, comm, &generatedData) + err := p.Provisioner.Provision(ctx, ui, comm, data) ts.End(err) if err != nil { @@ -100,7 +75,7 @@ func (p *PausedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } -func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { +func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { // Use a select to determine if we get cancelled during the wait ui.Say(fmt.Sprintf("Pausing %s before the next provisioner...", p.PauseBefore)) @@ -127,7 +102,7 @@ func (p *DebuggedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } -func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { +func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { // Use a select to determine if we get cancelled during the wait message := "Pausing before the next provisioner . Press enter to continue." diff --git a/packer/provisioner_mock.go b/packer/provisioner_mock.go index 26be569a2..2ae7d68b6 100644 --- a/packer/provisioner_mock.go +++ b/packer/provisioner_mock.go @@ -22,7 +22,7 @@ func (t *MockProvisioner) Prepare(configs ...interface{}) error { return nil } -func (t *MockProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { +func (t *MockProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { t.ProvCalled = true t.ProvCommunicator = comm t.ProvUi = ui diff --git a/packer/provisioner_timeout.go b/packer/provisioner_timeout.go index cfe0566b5..f7bd01894 100644 --- a/packer/provisioner_timeout.go +++ b/packer/provisioner_timeout.go @@ -13,7 +13,7 @@ type TimeoutProvisioner struct { Timeout time.Duration } -func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error { +func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { ctx, cancel := context.WithTimeout(ctx, p.Timeout) defer cancel() diff --git a/packer/rpc/provisioner.go b/packer/rpc/provisioner.go index d03c945c4..dfcea7da1 100644 --- a/packer/rpc/provisioner.go +++ b/packer/rpc/provisioner.go @@ -39,11 +39,11 @@ func (p *provisioner) Prepare(configs ...interface{}) (err error) { } type ProvisionerProvisionArgs struct { - GeneratedData *packer.ProvisionHookData + GeneratedData interface{} StreamID uint32 } -func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { +func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { nextId := p.mux.NextId() server := newServerWithMux(p.mux, nextId) server.RegisterCommunicator(comm) diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index be3449b3b..70bb1e5cc 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -188,7 +188,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with Ansible...") if len(p.config.PlaybookDir) > 0 { diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index e717edb50..79238d655 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -214,7 +214,7 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with Ansible...") // Interpolate env vars to check for .WinRMPassword p.config.ctx.Data = &PassthroughTemplate{ diff --git a/provisioner/breakpoint/provisioner.go b/provisioner/breakpoint/provisioner.go index 668da955b..0eed2f7a7 100644 --- a/provisioner/breakpoint/provisioner.go +++ b/provisioner/breakpoint/provisioner.go @@ -42,7 +42,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { if p.config.Disable { if p.config.Note != "" { ui.Say(fmt.Sprintf( diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index a5091b748..b2d6e21d2 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -244,7 +244,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { p.communicator = comm diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index 7d8119eb6..1e3e20801 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -238,7 +238,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with chef-solo") if !p.config.SkipInstall { diff --git a/provisioner/converge/provisioner.go b/provisioner/converge/provisioner.go index b226edaac..b5953b3ad 100644 --- a/provisioner/converge/provisioner.go +++ b/provisioner/converge/provisioner.go @@ -108,7 +108,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } // Provision node somehow. TODO: actual docs -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with Converge") // bootstrapping diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index eb8d3db6a..db1780c7b 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -92,7 +92,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { if p.config.Direction == "download" { return p.ProvisionDownload(ui, comm) } else { diff --git a/provisioner/inspec/provisioner.go b/provisioner/inspec/provisioner.go index e12d419ed..549a5c774 100644 --- a/provisioner/inspec/provisioner.go +++ b/provisioner/inspec/provisioner.go @@ -186,7 +186,7 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with Inspec...") for i, envVar := range p.config.InspecEnvVars { diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 9c6fd76aa..40b29c07a 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -74,13 +74,7 @@ type Config struct { type Provisioner struct { config Config communicator packer.Communicator - generatedData *packer.ProvisionHookData -} - -type ExecuteCommandTemplate struct { - Vars string - Path string - WinRMPassword string + generatedData map[string]interface{} } func (p *Provisioner) defaultExecuteCommand() string { @@ -95,9 +89,9 @@ func (p *Provisioner) defaultExecuteCommand() string { } func (p *Provisioner) Prepare(raws ...interface{}) error { - // Create passthrough for winrm password so we can fill it in once we know + // Create passthrough for build-generated data so we can fill it in once we know // it - p.config.ctx.Data = packer.NewProvisionHookData() + p.config.ctx.Data = common.PlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, @@ -226,12 +220,21 @@ func extractScript(p *Provisioner) (string, error) { return temp.Name(), nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { ui.Say(fmt.Sprintf("Provisioning with Powershell...")) p.communicator = comm - p.generatedData = generatedData + p.generatedData = map[string]interface{}{} + for key, val := range generatedData.(map[interface{}]interface{}) { + keyString, ok := key.(string) + if ok { + p.generatedData[keyString] = val + } else { + log.Printf("Error casting generated data key to a string.") + } + } - packer.LogSecretFilter.Set(p.generatedData.WinRMPassword) + winRMPass := p.generatedData["WinRMPassword"] + packer.LogSecretFilter.Set(winRMPass.(string)) scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) @@ -414,11 +417,11 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro return "", err } - p.config.ctx.Data = &ExecuteCommandTemplate{ - Path: p.config.RemotePath, - Vars: p.config.RemoteEnvVarPath, - WinRMPassword: p.generatedData.WinRMPassword, - } + ctxData := p.generatedData + ctxData["Path"] = p.config.RemotePath + ctxData["Vars"] = p.config.RemoteEnvVarPath + p.config.ctx.Data = ctxData + command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) if err != nil { @@ -437,11 +440,11 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error) return "", err } - p.config.ctx.Data = &ExecuteCommandTemplate{ - Path: p.config.RemotePath, - Vars: p.config.RemoteEnvVarPath, - WinRMPassword: p.generatedData.WinRMPassword, - } + ctxData := p.generatedData + ctxData["Path"] = p.config.RemotePath + ctxData["Vars"] = p.config.RemoteEnvVarPath + p.config.ctx.Data = ctxData + command, err = interpolate.Render(p.config.ElevatedExecuteCommand, &p.config.ctx) if err != nil { return "", fmt.Errorf("Error processing command: %s", err) diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index fe6a899d8..b8413c41e 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -259,7 +259,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with Puppet...") p.communicator = comm ui.Message("Creating Puppet staging directory...") diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index 256050ef9..d8f52ab50 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -229,7 +229,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say("Provisioning with Puppet...") p.communicator = comm ui.Message("Creating Puppet staging directory...") diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index 485f58389..6963424f9 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -222,7 +222,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { var err error var src, dst string diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index d5de2b51c..aac65f57b 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -25,7 +25,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator, _ interface{}) error { _, retErr := sl.Run(ctx, ui, &p.config) return retErr diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index dd2560fc8..a03c185fa 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -179,7 +179,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) diff --git a/provisioner/sleep/provisioner.go b/provisioner/sleep/provisioner.go index 84de8ae16..8d4bfe0a0 100644 --- a/provisioner/sleep/provisioner.go +++ b/provisioner/sleep/provisioner.go @@ -20,7 +20,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return config.Decode(&p, &config.DecodeOpts{}, raws...) } -func (p *Provisioner) Provision(ctx context.Context, _ packer.Ui, _ packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, _ packer.Ui, _ packer.Communicator, _ interface{}) error { select { case <-ctx.Done(): return ctx.Err() diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 20ff840f2..a02cf1c9b 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -96,7 +96,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { p.cancelLock.Lock() p.cancel = make(chan struct{}) p.cancelLock.Unlock() diff --git a/provisioner/windows-shell/provisioner.go b/provisioner/windows-shell/provisioner.go index dbb441f83..f4121f274 100644 --- a/provisioner/windows-shell/provisioner.go +++ b/provisioner/windows-shell/provisioner.go @@ -156,7 +156,7 @@ func extractScript(p *Provisioner) (string, error) { return temp.Name(), nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ *packer.ProvisionHookData) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { ui.Say(fmt.Sprintf("Provisioning with windows-shell...")) scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) From 7616fbcf3f023327c4162a2e370d5e817621693b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 11 Dec 2019 13:10:07 -0800 Subject: [PATCH 020/150] skip shell local for now --- common/shell-local/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 2f3a06e6c..99070f295 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -44,13 +44,13 @@ type Config struct { // used to track the data sent to shell-local from the builder // GeneratedData - Ctx interpolate.Context + ctx interpolate.Context } func Decode(config *Config, raws ...interface{}) error { // Create passthrough for build-generated data so we can fill it in once we know // it - config.Ctx.Data = common.PlaceholderData() + // config.ctx.Data = common.PlaceholderData() err := configHelper.Decode(&config, &configHelper.DecodeOpts{ Interpolate: true, From 528b853178a187a4c1dd7c907454144e01cbdb0b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 11 Dec 2019 14:26:54 -0800 Subject: [PATCH 021/150] stub out generated func for validation --- common/step_provision.go | 4 +++- template/interpolate/funcs.go | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/common/step_provision.go b/common/step_provision.go index 7f51a20c5..c6c039eea 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -48,6 +48,8 @@ func PlaceholderData() map[string]string { placeholderData[name] = fmt.Sprintf("{{.%s}}", name) } + placeholderData["ID"] = "{{.ID}}" + return placeholderData } @@ -58,6 +60,7 @@ type StepProvision struct { func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { hookData := map[string]interface{}{} // Read communicator data into hook data + // state.GetOK("id") commConf, ok := state.GetOk("communicator_config") if !ok { log.Printf("Unable to load config from state to populate provisionHookData") @@ -94,7 +97,6 @@ func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBa ui := state.Get("ui").(packer.Ui) hookData := PopulateProvisionHookData(state) - log.Printf("Megan hookdata is %T", hookData) // Run the provisioner in a goroutine so we can continually check // for cancellations... diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 609d35f4a..107875023 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -43,6 +43,7 @@ var FuncGens = map[string]interface{}{ "consul_key": funcGenConsul, "vault": funcGenVault, "sed": funcGenSed, + "generated": funcGenGenerated, "replace": replace, "replace_all": replace_all, @@ -162,6 +163,13 @@ func funcGenTemplateDir(ctx *Context) interface{} { } } +func funcGenGenerated(ctx *Context) interface{} { + return func(k string) (string, error) { + // Return the key inside braces _without_ the generated func attached + return fmt.Sprintf("{{.%s}}", k), nil + } +} + func funcGenTimestamp(ctx *Context) interface{} { return func() string { return strconv.FormatInt(InitTime.Unix(), 10) From 5d266b20d0a1ddfb74efb7e0b2f977356b220605 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 11 Dec 2019 15:43:11 -0800 Subject: [PATCH 022/150] generated func now validates based on data provided in context, which needs to include the future values if it is to be interpolated properly. --- common/step_provision.go | 17 ++++++++++++----- template/interpolate/funcs.go | 19 ++++++++++++++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/common/step_provision.go b/common/step_provision.go index c6c039eea..60f7bbffd 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -53,10 +53,6 @@ func PlaceholderData() map[string]string { return placeholderData } -type StepProvision struct { - Comm packer.Communicator -} - func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { hookData := map[string]interface{}{} // Read communicator data into hook data @@ -76,13 +72,24 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} } // Loop over all field values and retrieve them from the ssh config for fieldName, _ := range pd { + if fieldName == "ID" { + continue + } fVal := v.FieldByName(fieldName) - hookData[fieldName] = fVal.Interface() + if fVal.IsZero() { + log.Printf("Megan NONINTERFACABLE fVal is %#v", fVal) + } else { + hookData[fieldName] = fVal.Interface() + } } return hookData } +type StepProvision struct { + Comm packer.Communicator +} + func (s *StepProvision) runWithHook(ctx context.Context, state multistep.StateBag, hooktype string) multistep.StepAction { // hooktype will be either packer.HookProvision or packer.HookCleanupProvision comm := s.Comm diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 107875023..341c4630b 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -3,6 +3,7 @@ package interpolate import ( "errors" "fmt" + "log" "os" "path/filepath" "strconv" @@ -164,9 +165,21 @@ func funcGenTemplateDir(ctx *Context) interface{} { } func funcGenGenerated(ctx *Context) interface{} { - return func(k string) (string, error) { - // Return the key inside braces _without_ the generated func attached - return fmt.Sprintf("{{.%s}}", k), nil + return func(s string) (string, error) { + log.Printf("Megan context data is %#v", ctx.Data) + if data, ok := ctx.Data.(map[string]string); ok { + // PlaceholderData has been passed into generator, and we can check + // the value against the placeholder data to make sure that it is + // valid + if heldPlace, ok := data[s]; ok { + return heldPlace, nil + } else { + return "", fmt.Errorf("loaded data, but couldnt find winrm in it", s) + } + } + + return "", fmt.Errorf("Error validating computed variable: the given "+ + "variable %s will not be passed into your plugin.", s) } } From 0ca7c9f3976972627b606c25ed95e52ac3765288 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 11 Dec 2019 16:29:35 -0800 Subject: [PATCH 023/150] fix tests --- packer/provisioner_test.go | 10 +++---- packer/rpc/provisioner_test.go | 2 +- provisioner/ansible-local/provisioner_test.go | 4 +-- provisioner/ansible/provisioner_test.go | 2 +- provisioner/file/provisioner_test.go | 2 +- provisioner/powershell/provisioner.go | 26 ++++++++++++------- provisioner/powershell/provisioner_test.go | 15 ++++++----- .../puppet-masterless/provisioner_test.go | 4 +-- provisioner/sleep/provisioner_test.go | 2 +- .../windows-restart/provisioner_test.go | 12 ++++----- provisioner/windows-shell/provisioner_test.go | 8 +++--- template/interpolate/funcs.go | 2 +- .../extending/custom-provisioners.html.md | 2 +- 13 files changed, 49 insertions(+), 42 deletions(-) diff --git a/packer/provisioner_test.go b/packer/provisioner_test.go index 641702428..10e546704 100644 --- a/packer/provisioner_test.go +++ b/packer/provisioner_test.go @@ -114,7 +114,7 @@ func TestPausedProvisionerProvision(t *testing.T) { ui := testUi() comm := new(MockCommunicator) - prov.Provision(context.Background(), ui, comm) + prov.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if !mock.ProvCalled { t.Fatal("prov should be called") } @@ -143,7 +143,7 @@ func TestPausedProvisionerProvision_waits(t *testing.T) { }, } - err := prov.Provision(context.Background(), testUi(), new(MockCommunicator)) + err := prov.Provision(context.Background(), testUi(), new(MockCommunicator), make(map[interface{}]interface{})) if err != nil { t.Fatalf("prov failed: %v", err) @@ -164,7 +164,7 @@ func TestPausedProvisionerCancel(t *testing.T) { return ctx.Err() } - err := prov.Provision(topCtx, testUi(), new(MockCommunicator)) + err := prov.Provision(topCtx, testUi(), new(MockCommunicator), make(map[interface{}]interface{})) if err == nil { t.Fatal("should have err") } @@ -198,7 +198,7 @@ func TestDebuggedProvisionerProvision(t *testing.T) { ui := testUi() comm := new(MockCommunicator) writeReader(ui, "\n") - prov.Provision(context.Background(), ui, comm) + prov.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if !mock.ProvCalled { t.Fatal("prov should be called") } @@ -224,7 +224,7 @@ func TestDebuggedProvisionerCancel(t *testing.T) { return ctx.Err() } - err := prov.Provision(topCtx, testUi(), new(MockCommunicator)) + err := prov.Provision(topCtx, testUi(), new(MockCommunicator), make(map[interface{}]interface{})) if err == nil { t.Fatal("should have error") } diff --git a/packer/rpc/provisioner_test.go b/packer/rpc/provisioner_test.go index bbcbc88d8..61e6ade18 100644 --- a/packer/rpc/provisioner_test.go +++ b/packer/rpc/provisioner_test.go @@ -39,7 +39,7 @@ func TestProvisionerRPC(t *testing.T) { // Test Provision ui := &testUi{} comm := &packer.MockCommunicator{} - if err := pClient.Provision(topCtx, ui, comm); err == nil { + if err := pClient.Provision(topCtx, ui, comm, make(map[interface{}]interface{})); err == nil { t.Fatalf("Provison should have err") } if !p.ProvCalled { diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index d2af8abfb..412541a29 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -134,7 +134,7 @@ func TestProvisionerProvision_PlaybookFiles(t *testing.T) { } comm := &communicatorMock{} - if err := p.Provision(context.Background(), new(packer.NoopUi), comm); err != nil { + if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[interface{}]interface{})); err != nil { t.Fatalf("err: %s", err) } @@ -168,7 +168,7 @@ func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) { } comm := &communicatorMock{} - if err := p.Provision(context.Background(), new(packer.NoopUi), comm); err != nil { + if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[interface{}]interface{})); err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/ansible/provisioner_test.go b/provisioner/ansible/provisioner_test.go index 27fa27dc7..6310fbcba 100644 --- a/provisioner/ansible/provisioner_test.go +++ b/provisioner/ansible/provisioner_test.go @@ -349,7 +349,7 @@ func TestAnsibleLongMessages(t *testing.T) { Writer: new(bytes.Buffer), } - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/file/provisioner_test.go b/provisioner/file/provisioner_test.go index 46bccca6d..6f229d512 100644 --- a/provisioner/file/provisioner_test.go +++ b/provisioner/file/provisioner_test.go @@ -127,7 +127,7 @@ func TestProvisionerProvision_SendsFile(t *testing.T) { Writer: b, } comm := &packer.MockCommunicator{} - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatalf("should successfully provision: %s", err) } diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 40b29c07a..88c07702f 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -223,19 +223,25 @@ func extractScript(p *Provisioner) (string, error) { func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { ui.Say(fmt.Sprintf("Provisioning with Powershell...")) p.communicator = comm - p.generatedData = map[string]interface{}{} - for key, val := range generatedData.(map[interface{}]interface{}) { - keyString, ok := key.(string) - if ok { - p.generatedData[keyString] = val - } else { - log.Printf("Error casting generated data key to a string.") + p.generatedData = make(map[string]interface{}) + // test that generatedData is a map, not an empty interface. + dataMap, ok := generatedData.(map[interface{}]interface{}) + if ok { + for key, val := range dataMap { + keyString, ok := key.(string) + if ok { + p.generatedData[keyString] = val + } else { + log.Printf("Error casting generated data key to a string.") + } } + if winRMPass, ok := p.generatedData["WinRMPassword"]; ok { + packer.LogSecretFilter.Set(winRMPass.(string)) + } + } else { + log.Printf("error reading generated data from builder") } - winRMPass := p.generatedData["WinRMPassword"] - packer.LogSecretFilter.Set(winRMPass.(string)) - scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index cef4611d1..07bbd8815 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -355,7 +355,7 @@ func TestProvisionerProvision_ValidExitCodes(t *testing.T) { comm := new(packer.MockCommunicator) comm.StartExitStatus = 200 p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -378,7 +378,7 @@ func TestProvisionerProvision_InvalidExitCodes(t *testing.T) { comm := new(packer.MockCommunicator) comm.StartExitStatus = 201 // Invalid! p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err == nil { t.Fatal("should have error") } @@ -399,7 +399,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -419,7 +419,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { config["remote_path"] = "c:/Windows/Temp/inlineScript.ps1" p.Prepare(config) - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -448,7 +448,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -484,7 +484,7 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -511,7 +511,7 @@ func TestProvisionerProvision_UploadFails(t *testing.T) { comm := new(packer.ScriptUploadErrorMockCommunicator) p.Prepare(config) p.config.StartRetryTimeout = 1 * time.Second - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if !strings.Contains(err.Error(), packer.ScriptUploadErrorMockCommunicatorError.Error()) { t.Fatalf("expected Provision() error %q to contain %q", err.Error(), @@ -621,6 +621,7 @@ func TestProvision_createCommandText(t *testing.T) { p.config.PackerBuilderType = "iso" // Non-elevated + p.generatedData = make(map[string]interface{}) cmd, _ := p.createCommandText() re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1'; exit \$LastExitCode }"`) diff --git a/provisioner/puppet-masterless/provisioner_test.go b/provisioner/puppet-masterless/provisioner_test.go index f8a0ae782..cd01cec32 100644 --- a/provisioner/puppet-masterless/provisioner_test.go +++ b/provisioner/puppet-masterless/provisioner_test.go @@ -494,7 +494,7 @@ func TestProvisionerProvision_extraArguments(t *testing.T) { t.Fatalf("err: %s", err) } - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatalf("err: %s", err) } @@ -514,7 +514,7 @@ func TestProvisionerProvision_extraArguments(t *testing.T) { t.Fatalf("err: %s", err) } - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/sleep/provisioner_test.go b/provisioner/sleep/provisioner_test.go index 6ec26c59e..3a0d1bd28 100644 --- a/provisioner/sleep/provisioner_test.go +++ b/provisioner/sleep/provisioner_test.go @@ -48,7 +48,7 @@ func TestProvisioner_Provision(t *testing.T) { p := &Provisioner{ Duration: tt.fields.Duration, } - if err := p.Provision(tt.args.ctx, nil, nil); (err != nil) != tt.wantErr { + if err := p.Provision(tt.args.ctx, nil, nil, make(map[interface{}]interface{})); (err != nil) != tt.wantErr { t.Errorf("Provisioner.Provision() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/provisioner/windows-restart/provisioner_test.go b/provisioner/windows-restart/provisioner_test.go index ffe9c48a2..0a2eda530 100644 --- a/provisioner/windows-restart/provisioner_test.go +++ b/provisioner/windows-restart/provisioner_test.go @@ -104,7 +104,7 @@ func TestProvisionerProvision_Success(t *testing.T) { waitForRestart = func(context.Context, *Provisioner, packer.Communicator) error { return nil } - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -140,7 +140,7 @@ func TestProvisionerProvision_CustomCommand(t *testing.T) { waitForRestart = func(context.Context, *Provisioner, packer.Communicator) error { return nil } - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -163,7 +163,7 @@ func TestProvisionerProvision_RestartCommandFail(t *testing.T) { comm.StartExitStatus = 1 p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err == nil { t.Fatal("should have error") } @@ -182,7 +182,7 @@ func TestProvisionerProvision_WaitForRestartFail(t *testing.T) { waitForCommunicator = func(context.Context, *Provisioner) error { return fmt.Errorf("Machine did not restart properly") } - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err == nil { t.Fatal("should have error") } @@ -216,7 +216,7 @@ func TestProvision_waitForRestartTimeout(t *testing.T) { } go func() { - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) waitDone <- true }() <-waitContinue @@ -327,7 +327,7 @@ func TestProvision_Cancel(t *testing.T) { // Create two go routines to provision and cancel in parallel // Provision will block until cancel happens go func() { - done <- p.Provision(topCtx, ui, comm) + done <- p.Provision(topCtx, ui, comm, make(map[interface{}]interface{})) }() // Expect interrupt error diff --git a/provisioner/windows-shell/provisioner_test.go b/provisioner/windows-shell/provisioner_test.go index 32ec528e1..b9ce1cf05 100644 --- a/provisioner/windows-shell/provisioner_test.go +++ b/provisioner/windows-shell/provisioner_test.go @@ -292,7 +292,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm) + err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -311,7 +311,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { config["remote_path"] = "c:/Windows/Temp/inlineScript.bat" p.Prepare(config) - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -342,7 +342,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } @@ -381,7 +381,7 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err = p.Provision(context.Background(), ui, comm) + err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) if err != nil { t.Fatal("should not have error") } diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 341c4630b..a3a8e36ee 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -174,7 +174,7 @@ func funcGenGenerated(ctx *Context) interface{} { if heldPlace, ok := data[s]; ok { return heldPlace, nil } else { - return "", fmt.Errorf("loaded data, but couldnt find winrm in it", s) + return "", fmt.Errorf("loaded data, but couldnt find %s in it", s) } } diff --git a/website/source/docs/extending/custom-provisioners.html.md b/website/source/docs/extending/custom-provisioners.html.md index 3d3a1721d..7cfd55939 100644 --- a/website/source/docs/extending/custom-provisioners.html.md +++ b/website/source/docs/extending/custom-provisioners.html.md @@ -36,7 +36,7 @@ explaining what each method should do. ``` go type Provisioner interface { Prepare(...interface{}) error - Provision(Ui, Communicator) error + Provision(Ctx, Ui, Communicator, new(interface{})) error } ``` From c6b9efb522fc3f97ded8c1426820bcb5241248b2 Mon Sep 17 00:00:00 2001 From: Sylvia Moss Date: Thu, 12 Dec 2019 10:43:51 +0100 Subject: [PATCH 024/150] Fix virtualbox-iso TestBuilderAcc_basic (#8481) make sure the test works for later usage --- builder/virtualbox/iso/builder_acc_test.go | 26 ++++------- .../virtualbox/iso/testdata/http/preseed.cfg | 42 +++++++++++++++++ builder/virtualbox/iso/testdata/minimal.json | 46 +++++++++++++++++++ 3 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 builder/virtualbox/iso/testdata/http/preseed.cfg create mode 100644 builder/virtualbox/iso/testdata/minimal.json diff --git a/builder/virtualbox/iso/builder_acc_test.go b/builder/virtualbox/iso/builder_acc_test.go index 630d4094f..dd8fccaeb 100644 --- a/builder/virtualbox/iso/builder_acc_test.go +++ b/builder/virtualbox/iso/builder_acc_test.go @@ -1,30 +1,22 @@ package iso import ( + "io/ioutil" + "path/filepath" "testing" builderT "github.com/hashicorp/packer/helper/builder/testing" ) func TestBuilderAcc_basic(t *testing.T) { + templatePath := filepath.Join("testdata", "minimal.json") + bytes, err := ioutil.ReadFile(templatePath) + if err != nil { + t.Fatalf("failed to load template file %s", templatePath) + } + builderT.Test(t, builderT.TestCase{ Builder: &Builder{}, - Template: testBuilderAccBasic, + Template: string(bytes), }) } - -const testBuilderAccBasic = ` -{ - "builders": [{ - "type": "test", - "guest_os_type": "Ubuntu_64", - "iso_url": "http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso", - "iso_checksum": "769474248a3897f4865817446f9a4a53", - "iso_checksum_type": "md5", - "ssh_username": "packer", - "ssh_password": "packer", - "ssh_wait_timeout": "30s", - "shutdown_command": "echo 'packer' | sudo -S shutdown -P now" - }] -} -` diff --git a/builder/virtualbox/iso/testdata/http/preseed.cfg b/builder/virtualbox/iso/testdata/http/preseed.cfg new file mode 100644 index 000000000..57885df31 --- /dev/null +++ b/builder/virtualbox/iso/testdata/http/preseed.cfg @@ -0,0 +1,42 @@ +# Preseeding only locale sets language, country and locale. +d-i debian-installer/locale string en_US + +# Keyboard selection. +d-i console-setup/ask_detect boolean false +d-i keyboard-configuration/xkb-keymap select us + +choose-mirror-bin mirror/http/proxy string +d-i base-installer/kernel/override-image string linux-server +d-i clock-setup/utc boolean true +d-i clock-setup/utc-auto boolean true +d-i finish-install/reboot_in_progress note +d-i grub-installer/only_debian boolean true +d-i grub-installer/with_other_os boolean true +d-i mirror/country string manual +d-i mirror/http/directory string /ubuntu/ +d-i mirror/http/hostname string archive.ubuntu.com +d-i mirror/http/proxy string +d-i partman-auto-lvm/guided_size string max +d-i partman-auto/choose_recipe select atomic +d-i partman-auto/method string lvm +d-i partman-lvm/confirm boolean true +d-i partman-lvm/confirm boolean true +d-i partman-lvm/confirm_nooverwrite boolean true +d-i partman-lvm/device_remove_lvm boolean true +d-i partman/choose_partition select finish +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true +d-i partman/confirm_write_new_label boolean true +d-i passwd/user-fullname string vagrant +d-i passwd/user-uid string 1000 +d-i passwd/user-password password vagrant +d-i passwd/user-password-again password vagrant +d-i passwd/username string vagrant +d-i pkgsel/include string openssh-server cryptsetup build-essential libssl-dev libreadline-dev zlib1g-dev linux-source dkms nfs-kernel-server nfs-common linux-headers-$(uname -r) perl +d-i pkgsel/install-language-support boolean false +d-i pkgsel/update-policy select none +d-i pkgsel/upgrade select full-upgrade +d-i time/zone string UTC +d-i user-setup/allow-password-weak boolean true +d-i user-setup/encrypt-home boolean false +tasksel tasksel/first multiselect standard, server diff --git a/builder/virtualbox/iso/testdata/minimal.json b/builder/virtualbox/iso/testdata/minimal.json new file mode 100644 index 000000000..9c8ef7730 --- /dev/null +++ b/builder/virtualbox/iso/testdata/minimal.json @@ -0,0 +1,46 @@ +{ + "builders": [ + { + "type": "test", + "iso_checksum": "946a6077af6f5f95a51f82fdc44051c7aa19f9cfc5f737954845a6050543d7c2", + "iso_checksum_type": "sha256", + "iso_url": "http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04.1-server-amd64.iso", + "disk_size": "40960", + "guest_os_type": "Ubuntu_64", + + "ssh_password": "vagrant", + "ssh_username": "vagrant", + "ssh_wait_timeout": "10000s", + + "http_directory": "./testdata/http", + "boot_wait": "10s", + "boot_command": [ + "", + "", + "", + "/install/vmlinuz", + " auto", + " console-setup/ask_detect=false", + " console-setup/layoutcode=us", + " console-setup/modelcode=pc105", + " debconf/frontend=noninteractive", + " debian-installer=en_US.UTF-8", + " fb=false", + " initrd=/install/initrd.gz", + " kbd-chooser/method=us", + " keyboard-configuration/layout=USA", + " keyboard-configuration/variant=USA", + " locale=en_US.UTF-8", + " netcfg/get_domain=vm", + " netcfg/get_hostname=vagrant", + " noapic", + " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg", + " -- ", + "" + ], + + "shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now", + "post_shutdown_delay": "60s" + } + ] +} From 1933e7acbe4ffb0d07b4a2d4ed2a6bf47c13cb18 Mon Sep 17 00:00:00 2001 From: nywilken Date: Tue, 10 Dec 2019 15:41:31 -0500 Subject: [PATCH 025/150] Update CHANGELOG.md --- CHANGELOG.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18a19b271..118bdf148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,47 +3,54 @@ ### IMPROVEMENTS: * builder/amazon: Add no_ephemeral template option to remove ephemeral drives from launch mappings. [GH-8393] -* builder/amazon: Add validation for `subnet_id` when specifying `vpc_id` +* builder/amazon: Add validation for "subnet_id" when specifying "vpc_id" [GH-8360] [GH-8387] [GH-8391] * builder/amazon: allow enabling ena/sr-iov on ebssurrogate spot instances [GH-8397] * builder/amazon: Retry runinstances aws api call to mitigate throttling [GH-8342] +* builder/hyperone: Update builder schema and tags [GH-8444] * builder/qemu: Add display template option for qemu. [GH-7676] +* builder/qemu: Add fixer to convert disk size from int to string [GH-8390] * builder/qemu: When a user adds a new drive in qemuargs, process it to make sure that necessary settings are applied to that drive. [GH-8380] * builder/vmware: Fix error message when ovftool is missing [GH-8371] +* core: Cleanup logging for external plugins [GH-8471] * core: Various fixes to error handling. [GH-8343] [GH-8333] [GH-8316] [GH-8354] [GH-8361] [GH-8363] [GH-8370] * post-processor/shell-local: Add "valid_exit_codes" option to shell-local. [GH-8401] * provisioner/shell-local: Add "valid_exit_codes" option to shell-local. [GH-8401] -* provisioner/shell: Add support for the `env_var_format` parameter [GH-8319] +* provisioner/shell: Add support for the "env_var_format" parameter [GH-8319] ### BUG FIXES: +* builder/amazon: Fix request retry mechanism to launch aws instance [GH-8430] * builder/hyperv: Fix bug in checking VM name that could cause flakiness if many VMs are defined. [GH-8357] * builder/vagrant: Use absolute path for Vagrantfile [GH-8321] * builder/virtualbox: Fix panic in snapshot builder. [GH-8336] [GH-8329] * communicator/winrm: Resolve ntlm nil pointer bug by bumping go-ntlmssp dependency [GH-8369] -* communicator: Fix proxy connection settings to use `SSHProxyUsername` and - `SSHProxyPassword` where relevant instead of bastion username and password. +* communicator: Fix proxy connection settings to use "SSHProxyUsername" and + "SSHProxyPassword" where relevant instead of bastion username and password. [GH-8375] * core: Fix bug where Packer froze if asked to log an extremely long line [GH-8356] * core: Fix iso_target_path option; don't cache when target path is non-nil [GH-8394] +* core: Return exit code 1 when builder type is not found [GH-8474] * core: Update to newest version of go-tty to re-enable CTRL-S and CTRL-Q usage [GH-8364] - ### BACKWARDS INCOMPATIBILITIES: * builder/amazon: Complete deprecation of clean_ami_name template func [GH-8320] [GH-8193] * builder/qemu: Disk Size is now read as a string to support units. [GH-8320] [GH-7546] +* provisioner/ansible-local: The "galaxycommand" option has been renamed to + "galaxy_command". A fixer has been written for this, which can be invoked + with `packer fix`. [GH-8411] ## 1.4.5 (November 4, 2019) From 39fd462b561f2058d83acfc1d52d6894dbb28fee Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 12 Dec 2019 10:59:44 -0800 Subject: [PATCH 026/150] change all provision func signatures to use map[string]interface{} --- common/shell-local/config.go | 6 ++-- common/shell-local/run.go | 30 +++++++++---------- common/step_provision.go | 5 ++-- packer/plugin/provisioner.go | 2 +- packer/provisioner.go | 27 ++++++++++++++--- packer/provisioner_mock.go | 2 +- packer/provisioner_test.go | 10 +++---- packer/provisioner_timeout.go | 2 +- packer/rpc/provisioner.go | 8 ++--- packer/rpc/provisioner_test.go | 2 +- post-processor/shell-local/post-processor.go | 2 +- provisioner/ansible-local/provisioner.go | 2 +- provisioner/ansible-local/provisioner_test.go | 4 +-- provisioner/ansible/provisioner.go | 2 +- provisioner/ansible/provisioner_test.go | 2 +- provisioner/breakpoint/provisioner.go | 2 +- provisioner/chef-client/provisioner.go | 2 +- provisioner/chef-solo/provisioner.go | 2 +- provisioner/converge/provisioner.go | 2 +- provisioner/file/provisioner.go | 2 +- provisioner/file/provisioner_test.go | 2 +- provisioner/inspec/provisioner.go | 2 +- provisioner/powershell/provisioner.go | 22 +++++--------- provisioner/powershell/provisioner_test.go | 14 ++++----- provisioner/puppet-masterless/provisioner.go | 2 +- .../puppet-masterless/provisioner_test.go | 4 +-- provisioner/puppet-server/provisioner.go | 2 +- provisioner/salt-masterless/provisioner.go | 2 +- provisioner/shell-local/provisioner.go | 4 +-- provisioner/shell/provisioner.go | 2 +- provisioner/sleep/provisioner.go | 2 +- provisioner/windows-restart/provisioner.go | 2 +- .../windows-restart/provisioner_test.go | 12 ++++---- provisioner/windows-shell/provisioner.go | 2 +- provisioner/windows-shell/provisioner_test.go | 8 ++--- template/interpolate/funcs.go | 2 -- 36 files changed, 103 insertions(+), 97 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 99070f295..bdf3dfbd7 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -10,6 +10,7 @@ import ( "runtime" "strings" + "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/shell" configHelper "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -44,13 +45,14 @@ type Config struct { // used to track the data sent to shell-local from the builder // GeneratedData - ctx interpolate.Context + ctx interpolate.Context + generatedData map[string]interface{} } func Decode(config *Config, raws ...interface{}) error { // Create passthrough for build-generated data so we can fill it in once we know // it - // config.ctx.Data = common.PlaceholderData() + config.ctx.Data = common.PlaceholderData() err := configHelper.Decode(&config, &configHelper.DecodeOpts{ Interpolate: true, diff --git a/common/shell-local/run.go b/common/shell-local/run.go index 06b8c78d3..8fb58adf2 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -28,7 +28,15 @@ type EnvVarsTemplate struct { WinRMPassword string } -func Run(ctx context.Context, ui packer.Ui, config *Config) (bool, error) { +func Run(ctx context.Context, ui packer.Ui, config *Config, generatedData map[string]interface{}) (bool, error) { + if generatedData != nil { + config.generatedData = generatedData + } else { + // No fear; probably just in the post-processor, not provisioner. + // Make sure it's not a nil map so we can assign to it later. + config.generatedData = make(map[string]interface{}) + } + config.ctx.Data = generatedData // Check if shell-local can even execute against this runtime OS if len(config.OnlyOn) > 0 { runCommand := false @@ -120,11 +128,6 @@ func createInlineScriptFile(config *Config) (string, error) { writer.WriteString(shebang) } - // generate context so you can interpolate the command - config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(config.PackerBuildName), - } - for _, command := range config.Inline { // interpolate command to check for template variables. command, err := interpolate.Render(command, &config.ctx) @@ -152,12 +155,11 @@ func createInlineScriptFile(config *Config) (string, error) { // user-provided ExecuteCommand or defaulting to something that makes sense for // the host OS func createInterpolatedCommands(config *Config, script string, flattenedEnvVars string) ([]string, error) { - config.ctx.Data = &ExecuteCommandTemplate{ - Vars: flattenedEnvVars, - Script: script, - Command: script, - WinRMPassword: getWinRMPassword(config.PackerBuildName), - } + config.generatedData["Vars"] = flattenedEnvVars + config.generatedData["Script"] = script + config.generatedData["Command"] = script + + config.ctx.Data = config.generatedData interpolatedCmds := make([]string, len(config.ExecuteCommand)) for i, cmd := range config.ExecuteCommand { @@ -192,10 +194,6 @@ func createFlattenedEnvVars(config *Config) (string, error) { envVars["PACKER_HTTP_PORT"] = httpPort } - // interpolate environment variables - config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(config.PackerBuildName), - } // Split vars into key/value components for _, envVar := range config.Vars { envVar, err := interpolate.Render(envVar, &config.ctx) diff --git a/common/step_provision.go b/common/step_provision.go index 60f7bbffd..f7a6d19a1 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -75,10 +75,9 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} if fieldName == "ID" { continue } + fVal := v.FieldByName(fieldName) - if fVal.IsZero() { - log.Printf("Megan NONINTERFACABLE fVal is %#v", fVal) - } else { + if !fVal.IsZero() { hookData[fieldName] = fVal.Interface() } } diff --git a/packer/plugin/provisioner.go b/packer/plugin/provisioner.go index 14bc2a65e..ac9f1f89d 100644 --- a/packer/plugin/provisioner.go +++ b/packer/plugin/provisioner.go @@ -21,7 +21,7 @@ func (c *cmdProvisioner) Prepare(configs ...interface{}) error { return c.p.Prepare(configs...) } -func (c *cmdProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { +func (c *cmdProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { defer func() { r := recover() c.checkExit(r, nil) diff --git a/packer/provisioner.go b/packer/provisioner.go index a317b157f..6e29e2e61 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -20,7 +20,7 @@ type Provisioner interface { // given for cancellation, a UI is given to communicate with the user, and // a communicator is given that is guaranteed to be connected to some // machine so that provisioning can be done. - Provision(context.Context, Ui, Communicator, interface{}) error + Provision(context.Context, Ui, Communicator, map[string]interface{}) error } // A HookedProvisioner represents a provisioner and information describing it @@ -53,7 +53,26 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun for _, p := range h.Provisioners { ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config) - err := p.Provisioner.Provision(ctx, ui, comm, data) + // Provisioners expect a map[string]interface{} in their data field, but + // it gets converted into a map[interface]interface on the way over the + // RPC. Check that data can be cast into such a form, and cast it. + cast := make(map[string]interface{}) + interMap, ok := data.(map[interface{}]interface{}) + if !ok { + log.Printf("Unable to read map[string]interface out of data." + + "Using empty interface.") + } else { + for key, val := range interMap { + keyString, ok := key.(string) + if ok { + cast[keyString] = val + } else { + log.Printf("Error casting generated data key to a string.") + } + } + } + + err := p.Provisioner.Provision(ctx, ui, comm, cast) ts.End(err) if err != nil { @@ -75,7 +94,7 @@ func (p *PausedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } -func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { +func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData map[string]interface{}) error { // Use a select to determine if we get cancelled during the wait ui.Say(fmt.Sprintf("Pausing %s before the next provisioner...", p.PauseBefore)) @@ -102,7 +121,7 @@ func (p *DebuggedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } -func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { +func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData map[string]interface{}) error { // Use a select to determine if we get cancelled during the wait message := "Pausing before the next provisioner . Press enter to continue." diff --git a/packer/provisioner_mock.go b/packer/provisioner_mock.go index 2ae7d68b6..f94b62047 100644 --- a/packer/provisioner_mock.go +++ b/packer/provisioner_mock.go @@ -22,7 +22,7 @@ func (t *MockProvisioner) Prepare(configs ...interface{}) error { return nil } -func (t *MockProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { +func (t *MockProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData map[string]interface{}) error { t.ProvCalled = true t.ProvCommunicator = comm t.ProvUi = ui diff --git a/packer/provisioner_test.go b/packer/provisioner_test.go index 10e546704..108dac90f 100644 --- a/packer/provisioner_test.go +++ b/packer/provisioner_test.go @@ -114,7 +114,7 @@ func TestPausedProvisionerProvision(t *testing.T) { ui := testUi() comm := new(MockCommunicator) - prov.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + prov.Provision(context.Background(), ui, comm, make(map[string]interface{})) if !mock.ProvCalled { t.Fatal("prov should be called") } @@ -143,7 +143,7 @@ func TestPausedProvisionerProvision_waits(t *testing.T) { }, } - err := prov.Provision(context.Background(), testUi(), new(MockCommunicator), make(map[interface{}]interface{})) + err := prov.Provision(context.Background(), testUi(), new(MockCommunicator), make(map[string]interface{})) if err != nil { t.Fatalf("prov failed: %v", err) @@ -164,7 +164,7 @@ func TestPausedProvisionerCancel(t *testing.T) { return ctx.Err() } - err := prov.Provision(topCtx, testUi(), new(MockCommunicator), make(map[interface{}]interface{})) + err := prov.Provision(topCtx, testUi(), new(MockCommunicator), make(map[string]interface{})) if err == nil { t.Fatal("should have err") } @@ -198,7 +198,7 @@ func TestDebuggedProvisionerProvision(t *testing.T) { ui := testUi() comm := new(MockCommunicator) writeReader(ui, "\n") - prov.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + prov.Provision(context.Background(), ui, comm, make(map[string]interface{})) if !mock.ProvCalled { t.Fatal("prov should be called") } @@ -224,7 +224,7 @@ func TestDebuggedProvisionerCancel(t *testing.T) { return ctx.Err() } - err := prov.Provision(topCtx, testUi(), new(MockCommunicator), make(map[interface{}]interface{})) + err := prov.Provision(topCtx, testUi(), new(MockCommunicator), make(map[string]interface{})) if err == nil { t.Fatal("should have error") } diff --git a/packer/provisioner_timeout.go b/packer/provisioner_timeout.go index f7bd01894..a3104815c 100644 --- a/packer/provisioner_timeout.go +++ b/packer/provisioner_timeout.go @@ -13,7 +13,7 @@ type TimeoutProvisioner struct { Timeout time.Duration } -func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData interface{}) error { +func (p *TimeoutProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData map[string]interface{}) error { ctx, cancel := context.WithTimeout(ctx, p.Timeout) defer cancel() diff --git a/packer/rpc/provisioner.go b/packer/rpc/provisioner.go index dfcea7da1..5acca71ad 100644 --- a/packer/rpc/provisioner.go +++ b/packer/rpc/provisioner.go @@ -39,11 +39,11 @@ func (p *provisioner) Prepare(configs ...interface{}) (err error) { } type ProvisionerProvisionArgs struct { - GeneratedData interface{} + GeneratedData map[string]interface{} StreamID uint32 } -func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { +func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { nextId := p.mux.NextId() server := newServerWithMux(p.mux, nextId) server.RegisterCommunicator(comm) @@ -74,7 +74,6 @@ func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *interfa func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *interface{}) error { streamId := args.StreamID - generatedData := args.GeneratedData client, err := newClientWithMux(p.mux, streamId) if err != nil { return NewBasicError(err) @@ -84,8 +83,7 @@ func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *int if p.context == nil { p.context, p.contextCancel = context.WithCancel(context.Background()) } - - if err := p.p.Provision(p.context, client.Ui(), client.Communicator(), generatedData); err != nil { + if err := p.p.Provision(p.context, client.Ui(), client.Communicator(), args.GeneratedData); err != nil { return NewBasicError(err) } diff --git a/packer/rpc/provisioner_test.go b/packer/rpc/provisioner_test.go index 61e6ade18..f70e09dad 100644 --- a/packer/rpc/provisioner_test.go +++ b/packer/rpc/provisioner_test.go @@ -39,7 +39,7 @@ func TestProvisionerRPC(t *testing.T) { // Test Provision ui := &testUi{} comm := &packer.MockCommunicator{} - if err := pClient.Provision(topCtx, ui, comm, make(map[interface{}]interface{})); err == nil { + if err := pClient.Provision(topCtx, ui, comm, make(map[string]interface{})); err == nil { t.Fatalf("Provison should have err") } if !p.ProvCalled { diff --git a/post-processor/shell-local/post-processor.go b/post-processor/shell-local/post-processor.go index e23d31648..ef5cf0849 100644 --- a/post-processor/shell-local/post-processor.go +++ b/post-processor/shell-local/post-processor.go @@ -41,7 +41,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact // this particular post-processor doesn't do anything with the artifact // except to return it. - success, retErr := sl.Run(ctx, ui, &p.config) + success, retErr := sl.Run(ctx, ui, &p.config, map[string]interface{}{}) if !success { return nil, false, false, retErr } diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 70bb1e5cc..eab8d2115 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -188,7 +188,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with Ansible...") if len(p.config.PlaybookDir) > 0 { diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 412541a29..180bd0dc9 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -134,7 +134,7 @@ func TestProvisionerProvision_PlaybookFiles(t *testing.T) { } comm := &communicatorMock{} - if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[interface{}]interface{})); err != nil { + if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[string]interface{})); err != nil { t.Fatalf("err: %s", err) } @@ -168,7 +168,7 @@ func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) { } comm := &communicatorMock{} - if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[interface{}]interface{})); err != nil { + if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[string]interface{})); err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 79238d655..8adf04175 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -214,7 +214,7 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with Ansible...") // Interpolate env vars to check for .WinRMPassword p.config.ctx.Data = &PassthroughTemplate{ diff --git a/provisioner/ansible/provisioner_test.go b/provisioner/ansible/provisioner_test.go index 6310fbcba..39035d4ed 100644 --- a/provisioner/ansible/provisioner_test.go +++ b/provisioner/ansible/provisioner_test.go @@ -349,7 +349,7 @@ func TestAnsibleLongMessages(t *testing.T) { Writer: new(bytes.Buffer), } - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/breakpoint/provisioner.go b/provisioner/breakpoint/provisioner.go index 0eed2f7a7..49f122aa4 100644 --- a/provisioner/breakpoint/provisioner.go +++ b/provisioner/breakpoint/provisioner.go @@ -42,7 +42,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { if p.config.Disable { if p.config.Note != "" { ui.Say(fmt.Sprintf( diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index b2d6e21d2..208c1744e 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -244,7 +244,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { p.communicator = comm diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index 1e3e20801..3c9be9778 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -238,7 +238,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with chef-solo") if !p.config.SkipInstall { diff --git a/provisioner/converge/provisioner.go b/provisioner/converge/provisioner.go index b5953b3ad..563823336 100644 --- a/provisioner/converge/provisioner.go +++ b/provisioner/converge/provisioner.go @@ -108,7 +108,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } // Provision node somehow. TODO: actual docs -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with Converge") // bootstrapping diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index db1780c7b..f95d069f0 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -92,7 +92,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { if p.config.Direction == "download" { return p.ProvisionDownload(ui, comm) } else { diff --git a/provisioner/file/provisioner_test.go b/provisioner/file/provisioner_test.go index 6f229d512..d27dfaaa7 100644 --- a/provisioner/file/provisioner_test.go +++ b/provisioner/file/provisioner_test.go @@ -127,7 +127,7 @@ func TestProvisionerProvision_SendsFile(t *testing.T) { Writer: b, } comm := &packer.MockCommunicator{} - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatalf("should successfully provision: %s", err) } diff --git a/provisioner/inspec/provisioner.go b/provisioner/inspec/provisioner.go index 549a5c774..c6d92ef6d 100644 --- a/provisioner/inspec/provisioner.go +++ b/provisioner/inspec/provisioner.go @@ -186,7 +186,7 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with Inspec...") for i, envVar := range p.config.InspecEnvVars { diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 88c07702f..2d86d5a8b 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -220,26 +220,20 @@ func extractScript(p *Provisioner) (string, error) { return temp.Name(), nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { ui.Say(fmt.Sprintf("Provisioning with Powershell...")) p.communicator = comm p.generatedData = make(map[string]interface{}) - // test that generatedData is a map, not an empty interface. - dataMap, ok := generatedData.(map[interface{}]interface{}) - if ok { - for key, val := range dataMap { - keyString, ok := key.(string) - if ok { - p.generatedData[keyString] = val - } else { - log.Printf("Error casting generated data key to a string.") - } - } + + if generatedData != nil { + log.Printf("Gen data isn't nil.") + p.generatedData = generatedData if winRMPass, ok := p.generatedData["WinRMPassword"]; ok { + log.Printf("Found winrm pass") packer.LogSecretFilter.Set(winRMPass.(string)) } } else { - log.Printf("error reading generated data from builder") + log.Printf("generatedData passed to Provision method is nil!") } scripts := make([]string, len(p.config.Scripts)) @@ -445,7 +439,6 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error) if err != nil { return "", err } - ctxData := p.generatedData ctxData["Path"] = p.config.RemotePath ctxData["Vars"] = p.config.RemoteEnvVarPath @@ -475,7 +468,6 @@ func (p *Provisioner) ElevatedUser() string { func (p *Provisioner) ElevatedPassword() string { // Replace ElevatedPassword for winrm users who used this feature p.config.ctx.Data = p.generatedData - elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) return elevatedPassword diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index 07bbd8815..152905bb8 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -355,7 +355,7 @@ func TestProvisionerProvision_ValidExitCodes(t *testing.T) { comm := new(packer.MockCommunicator) comm.StartExitStatus = 200 p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -378,7 +378,7 @@ func TestProvisionerProvision_InvalidExitCodes(t *testing.T) { comm := new(packer.MockCommunicator) comm.StartExitStatus = 201 // Invalid! p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err == nil { t.Fatal("should have error") } @@ -399,7 +399,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -419,7 +419,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { config["remote_path"] = "c:/Windows/Temp/inlineScript.ps1" p.Prepare(config) - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -448,7 +448,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -484,7 +484,7 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -511,7 +511,7 @@ func TestProvisionerProvision_UploadFails(t *testing.T) { comm := new(packer.ScriptUploadErrorMockCommunicator) p.Prepare(config) p.config.StartRetryTimeout = 1 * time.Second - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if !strings.Contains(err.Error(), packer.ScriptUploadErrorMockCommunicatorError.Error()) { t.Fatalf("expected Provision() error %q to contain %q", err.Error(), diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index b8413c41e..a570c69b9 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -259,7 +259,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with Puppet...") p.communicator = comm ui.Message("Creating Puppet staging directory...") diff --git a/provisioner/puppet-masterless/provisioner_test.go b/provisioner/puppet-masterless/provisioner_test.go index cd01cec32..582b8d311 100644 --- a/provisioner/puppet-masterless/provisioner_test.go +++ b/provisioner/puppet-masterless/provisioner_test.go @@ -494,7 +494,7 @@ func TestProvisionerProvision_extraArguments(t *testing.T) { t.Fatalf("err: %s", err) } - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatalf("err: %s", err) } @@ -514,7 +514,7 @@ func TestProvisionerProvision_extraArguments(t *testing.T) { t.Fatalf("err: %s", err) } - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index d8f52ab50..a82f6098e 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -229,7 +229,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say("Provisioning with Puppet...") p.communicator = comm ui.Message("Creating Puppet staging directory...") diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index 6963424f9..a25fae4f1 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -222,7 +222,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { var err error var src, dst string diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index aac65f57b..b51b8278c 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -25,8 +25,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator, _ interface{}) error { - _, retErr := sl.Run(ctx, ui, &p.config) +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator, generatedData map[string]interface{}) error { + _, retErr := sl.Run(ctx, ui, &p.config, generatedData) return retErr } diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index a03c185fa..71e209e44 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -179,7 +179,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) diff --git a/provisioner/sleep/provisioner.go b/provisioner/sleep/provisioner.go index 8d4bfe0a0..46acc1806 100644 --- a/provisioner/sleep/provisioner.go +++ b/provisioner/sleep/provisioner.go @@ -20,7 +20,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return config.Decode(&p, &config.DecodeOpts{}, raws...) } -func (p *Provisioner) Provision(ctx context.Context, _ packer.Ui, _ packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, _ packer.Ui, _ packer.Communicator, _ map[string]interface{}) error { select { case <-ctx.Done(): return ctx.Err() diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index a02cf1c9b..8e1d9713e 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -96,7 +96,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { p.cancelLock.Lock() p.cancel = make(chan struct{}) p.cancelLock.Unlock() diff --git a/provisioner/windows-restart/provisioner_test.go b/provisioner/windows-restart/provisioner_test.go index 0a2eda530..391068457 100644 --- a/provisioner/windows-restart/provisioner_test.go +++ b/provisioner/windows-restart/provisioner_test.go @@ -104,7 +104,7 @@ func TestProvisionerProvision_Success(t *testing.T) { waitForRestart = func(context.Context, *Provisioner, packer.Communicator) error { return nil } - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -140,7 +140,7 @@ func TestProvisionerProvision_CustomCommand(t *testing.T) { waitForRestart = func(context.Context, *Provisioner, packer.Communicator) error { return nil } - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -163,7 +163,7 @@ func TestProvisionerProvision_RestartCommandFail(t *testing.T) { comm.StartExitStatus = 1 p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err == nil { t.Fatal("should have error") } @@ -182,7 +182,7 @@ func TestProvisionerProvision_WaitForRestartFail(t *testing.T) { waitForCommunicator = func(context.Context, *Provisioner) error { return fmt.Errorf("Machine did not restart properly") } - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err == nil { t.Fatal("should have error") } @@ -216,7 +216,7 @@ func TestProvision_waitForRestartTimeout(t *testing.T) { } go func() { - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) waitDone <- true }() <-waitContinue @@ -327,7 +327,7 @@ func TestProvision_Cancel(t *testing.T) { // Create two go routines to provision and cancel in parallel // Provision will block until cancel happens go func() { - done <- p.Provision(topCtx, ui, comm, make(map[interface{}]interface{})) + done <- p.Provision(topCtx, ui, comm, make(map[string]interface{})) }() // Expect interrupt error diff --git a/provisioner/windows-shell/provisioner.go b/provisioner/windows-shell/provisioner.go index f4121f274..366fd2f80 100644 --- a/provisioner/windows-shell/provisioner.go +++ b/provisioner/windows-shell/provisioner.go @@ -156,7 +156,7 @@ func extractScript(p *Provisioner) (string, error) { return temp.Name(), nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { ui.Say(fmt.Sprintf("Provisioning with windows-shell...")) scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) diff --git a/provisioner/windows-shell/provisioner_test.go b/provisioner/windows-shell/provisioner_test.go index b9ce1cf05..4a5663bdb 100644 --- a/provisioner/windows-shell/provisioner_test.go +++ b/provisioner/windows-shell/provisioner_test.go @@ -292,7 +292,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) p.Prepare(config) - err := p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -311,7 +311,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { config["remote_path"] = "c:/Windows/Temp/inlineScript.bat" p.Prepare(config) - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -342,7 +342,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } @@ -381,7 +381,7 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { p := new(Provisioner) comm := new(packer.MockCommunicator) p.Prepare(config) - err = p.Provision(context.Background(), ui, comm, make(map[interface{}]interface{})) + err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") } diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index a3a8e36ee..2373ded1b 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -3,7 +3,6 @@ package interpolate import ( "errors" "fmt" - "log" "os" "path/filepath" "strconv" @@ -166,7 +165,6 @@ func funcGenTemplateDir(ctx *Context) interface{} { func funcGenGenerated(ctx *Context) interface{} { return func(s string) (string, error) { - log.Printf("Megan context data is %#v", ctx.Data) if data, ok := ctx.Data.(map[string]string); ok { // PlaceholderData has been passed into generator, and we can check // the value against the placeholder data to make sure that it is From b2012c7ff3ca7a7f036090dbd200f595f6ae6ec1 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 12 Dec 2019 15:47:17 -0800 Subject: [PATCH 027/150] fix tests --- provisioner/sleep/provisioner_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/sleep/provisioner_test.go b/provisioner/sleep/provisioner_test.go index 3a0d1bd28..2ed66ff50 100644 --- a/provisioner/sleep/provisioner_test.go +++ b/provisioner/sleep/provisioner_test.go @@ -48,7 +48,7 @@ func TestProvisioner_Provision(t *testing.T) { p := &Provisioner{ Duration: tt.fields.Duration, } - if err := p.Provision(tt.args.ctx, nil, nil, make(map[interface{}]interface{})); (err != nil) != tt.wantErr { + if err := p.Provision(tt.args.ctx, nil, nil, make(map[string]interface{})); (err != nil) != tt.wantErr { t.Errorf("Provisioner.Provision() error = %v, wantErr %v", err, tt.wantErr) } }) From 3389d843f09701d706da0064c1e60296b3ea97c9 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 12 Dec 2019 16:42:53 -0800 Subject: [PATCH 028/150] update all provisioners that used the winrmpassword tooling to use the new generateddata option --- builder/azure/arm/builder.go | 4 --- builder/azure/arm/step_save_winrm_password.go | 22 ------------ builder/openstack/step_get_password.go | 6 +--- builder/osc/common/step_get_password.go | 10 +----- common/shell-local/run.go | 18 ---------- provisioner/ansible/provisioner.go | 35 ++++++------------- provisioner/chef-client/provisioner.go | 28 ++++----------- provisioner/powershell/provisioner.go | 16 ++------- provisioner/puppet-masterless/provisioner.go | 27 ++++---------- provisioner/puppet-server/provisioner.go | 26 ++++---------- 10 files changed, 33 insertions(+), 159 deletions(-) delete mode 100644 builder/azure/arm/step_save_winrm_password.go diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 2a06f1b84..0a087c6fd 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -231,10 +231,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), - &StepSaveWinRMPassword{ - Password: b.config.tmpAdminPassword, - BuildName: b.config.PackerBuildName, - }, &communicator.StepConnectWinRM{ Config: &b.config.Comm, Host: func(stateBag multistep.StateBag) (string, error) { diff --git a/builder/azure/arm/step_save_winrm_password.go b/builder/azure/arm/step_save_winrm_password.go deleted file mode 100644 index 648a867c6..000000000 --- a/builder/azure/arm/step_save_winrm_password.go +++ /dev/null @@ -1,22 +0,0 @@ -package arm - -import ( - "context" - - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -type StepSaveWinRMPassword struct { - Password string - BuildName string -} - -func (s *StepSaveWinRMPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - // store so that we can access this later during provisioning - state.Put("winrm_password", s.Password) - packer.LogSecretFilter.Set(s.Password) - return multistep.ActionContinue -} - -func (s *StepSaveWinRMPassword) Cleanup(multistep.StateBag) {} diff --git a/builder/openstack/step_get_password.go b/builder/openstack/step_get_password.go index c97bc4944..fd9bfa156 100644 --- a/builder/openstack/step_get_password.go +++ b/builder/openstack/step_get_password.go @@ -8,7 +8,6 @@ import ( "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -78,12 +77,9 @@ func (s *StepGetPassword) Run(ctx context.Context, state multistep.StateBag) mul "Password (since debug is enabled) \"%s\"", s.Comm.WinRMPassword)) } - commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName) packer.LogSecretFilter.Set(s.Comm.WinRMPassword) return multistep.ActionContinue } -func (s *StepGetPassword) Cleanup(multistep.StateBag) { - commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName) -} +func (s *StepGetPassword) Cleanup(multistep.StateBag) {} diff --git a/builder/osc/common/step_get_password.go b/builder/osc/common/step_get_password.go index 63ebbb288..9971379d5 100644 --- a/builder/osc/common/step_get_password.go +++ b/builder/osc/common/step_get_password.go @@ -11,7 +11,6 @@ import ( "log" "time" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -95,19 +94,12 @@ WaitLoop: "Password (since debug is enabled): %s", s.Comm.WinRMPassword)) } // store so that we can access this later during provisioning - - err = commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName) - if err != nil { - log.Printf("[WARN] commonhelper.SetSharedState returned error: %s", err) - } packer.LogSecretFilter.Set(s.Comm.WinRMPassword) return multistep.ActionContinue } -func (s *StepGetPassword) Cleanup(multistep.StateBag) { - commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName) -} +func (s *StepGetPassword) Cleanup(multistep.StateBag) {} func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) { oapiconn := state.Get("oapi").(*oapi.Client) diff --git a/common/shell-local/run.go b/common/shell-local/run.go index 8fb58adf2..9f7170394 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -11,23 +11,11 @@ import ( "strings" "github.com/hashicorp/packer/common" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer/tmp" "github.com/hashicorp/packer/template/interpolate" ) -type ExecuteCommandTemplate struct { - Vars string - Script string - Command string - WinRMPassword string -} - -type EnvVarsTemplate struct { - WinRMPassword string -} - func Run(ctx context.Context, ui packer.Ui, config *Config, generatedData map[string]interface{}) (bool, error) { if generatedData != nil { config.generatedData = generatedData @@ -219,9 +207,3 @@ func createFlattenedEnvVars(config *Config) (string, error) { } return flattened, nil } - -func getWinRMPassword(buildName string) string { - winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) - packer.LogSecretFilter.Set(winRMPass) - return winRMPass -} diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 8adf04175..bdcf578c3 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -30,7 +30,6 @@ import ( "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/adapter" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer/tmp" @@ -75,20 +74,14 @@ type Provisioner struct { done chan struct{} ansibleVersion string ansibleMajVersion uint -} - -type PassthroughTemplate struct { - WinRMPassword string + generatedData map[string]interface{} } func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) - // Create passthrough for winrm password so we can fill it in once we know - // it - p.config.ctx.Data = &PassthroughTemplate{ - WinRMPassword: `{{.WinRMPassword}}`, - } + // Create passthrough for build-generated data + p.config.ctx.Data = common.PlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, @@ -214,12 +207,11 @@ func (p *Provisioner) getVersion() error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { ui.Say("Provisioning with Ansible...") - // Interpolate env vars to check for .WinRMPassword - p.config.ctx.Data = &PassthroughTemplate{ - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), - } + // Interpolate env vars to check for generated values like password and port + p.generatedData = generatedData + p.config.ctx.Data = generatedData for i, envVar := range p.config.AnsibleEnvVars { envVar, err := interpolate.Render(envVar, &p.config.ctx) if err != nil { @@ -227,7 +219,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C } p.config.AnsibleEnvVars[i] = envVar } - // Interpolate extra vars to check for .WinRMPassword + // Interpolate extra vars to check for generated values like password and port for i, arg := range p.config.ExtraArguments { arg, err := interpolate.Render(arg, &p.config.ctx) if err != nil { @@ -502,9 +494,10 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator, pri // remove winrm password from command, if it's been added flattenedCmd := strings.Join(cmd.Args, " ") sanitized := flattenedCmd - if len(getWinRMPassword(p.config.PackerBuildName)) > 0 { + winRMPass, ok := p.generatedData["WinRMPassword"] + if ok { sanitized = strings.Replace(sanitized, - getWinRMPassword(p.config.PackerBuildName), "*****", -1) + winRMPass.(string), "*****", -1) } ui.Say(fmt.Sprintf("Executing Ansible: %s", sanitized)) @@ -633,9 +626,3 @@ func newSigner(privKeyFile string) (*signer, error) { return signer, nil } - -func getWinRMPassword(buildName string) string { - winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) - packer.LogSecretFilter.Set(winRMPass) - return winRMPass -} diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 208c1744e..399a3c847 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/uuid" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/provisioner" @@ -86,6 +85,7 @@ type Provisioner struct { communicator packer.Communicator guestOSTypeConfig guestOSTypeConfig guestCommands *provisioner.GuestCommands + generatedData map[string]interface{} } type ConfigTemplate struct { @@ -103,10 +103,6 @@ type ConfigTemplate struct { ValidationKeyPath string } -type EnvVarsTemplate struct { - WinRMPassword string -} - type ExecuteTemplate struct { ConfigPath string JsonPath string @@ -124,12 +120,8 @@ type KnifeTemplate struct { } func (p *Provisioner) Prepare(raws ...interface{}) error { - // Create passthrough for winrm password so we can fill it in once we know - // it - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: `{{.WinRMPassword}}`, - } - + // Create passthrough for build-generated data + p.config.ctx.Data = common.PlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &p.config.ctx, @@ -244,8 +236,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { - +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { + p.generatedData = generatedData p.communicator = comm nodeName := p.config.NodeName @@ -713,12 +705,6 @@ func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) { return result, nil } -func getWinRMPassword(buildName string) string { - winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) - packer.LogSecretFilter.Set(winRMPass) - return winRMPass -} - func (p *Provisioner) Communicator() packer.Communicator { return p.communicator } @@ -729,9 +715,7 @@ func (p *Provisioner) ElevatedUser() string { func (p *Provisioner) ElevatedPassword() string { // Replace ElevatedPassword for winrm users who used this feature - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), - } + p.config.ctx.Data = p.generatedData elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 2d86d5a8b..c0d567627 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -89,8 +89,7 @@ func (p *Provisioner) defaultExecuteCommand() string { } func (p *Provisioner) Prepare(raws ...interface{}) error { - // Create passthrough for build-generated data so we can fill it in once we know - // it + // Create passthrough for build-generated data p.config.ctx.Data = common.PlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ @@ -223,18 +222,7 @@ func extractScript(p *Provisioner) (string, error) { func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { ui.Say(fmt.Sprintf("Provisioning with Powershell...")) p.communicator = comm - p.generatedData = make(map[string]interface{}) - - if generatedData != nil { - log.Printf("Gen data isn't nil.") - p.generatedData = generatedData - if winRMPass, ok := p.generatedData["WinRMPassword"]; ok { - log.Printf("Found winrm pass") - packer.LogSecretFilter.Set(winRMPass.(string)) - } - } else { - log.Printf("generatedData passed to Provision method is nil!") - } + p.generatedData = generatedData scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index a570c69b9..7a6a968ed 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -13,7 +13,6 @@ import ( "strings" "github.com/hashicorp/packer/common" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/provisioner" @@ -130,6 +129,7 @@ type Provisioner struct { communicator packer.Communicator guestOSTypeConfig guestOSTypeConfig guestCommands *provisioner.GuestCommands + generatedData map[string]interface{} } type ExecuteTemplate struct { @@ -146,17 +146,9 @@ type ExecuteTemplate struct { WorkingDir string } -type EnvVarsTemplate struct { - WinRMPassword string -} - func (p *Provisioner) Prepare(raws ...interface{}) error { - // Create passthrough for winrm password so we can fill it in once we know - // it - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: `{{.WinRMPassword}}`, - } - + // Create passthrough for build-generated data + p.config.ctx.Data = common.PlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &p.config.ctx, @@ -259,9 +251,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { ui.Say("Provisioning with Puppet...") p.communicator = comm + p.generatedData = generatedData ui.Message("Creating Puppet staging directory...") if err := p.createDir(ui, comm, p.config.StagingDir); err != nil { return fmt.Errorf("Error creating staging directory: %s", err) @@ -484,12 +477,6 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds return comm.UploadDir(dst, src, nil) } -func getWinRMPassword(buildName string) string { - winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) - packer.LogSecretFilter.Set(winRMPass) - return winRMPass -} - func (p *Provisioner) Communicator() packer.Communicator { return p.communicator } @@ -500,9 +487,7 @@ func (p *Provisioner) ElevatedUser() string { func (p *Provisioner) ElevatedPassword() string { // Replace ElevatedPassword for winrm users who used this feature - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), - } + p.config.ctx.Data = p.generatedData elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index a82f6098e..fd30d46d4 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/hashicorp/packer/common" - commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/provisioner" @@ -125,6 +124,7 @@ type Provisioner struct { communicator packer.Communicator guestOSTypeConfig guestOSTypeConfig guestCommands *provisioner.GuestCommands + generatedData map[string]interface{} } type ExecuteTemplate struct { @@ -140,16 +140,9 @@ type ExecuteTemplate struct { WorkingDir string } -type EnvVarsTemplate struct { - WinRMPassword string -} - func (p *Provisioner) Prepare(raws ...interface{}) error { - // Create passthrough for winrm password so we can fill it in once we know - // it - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: `{{.WinRMPassword}}`, - } + // Create passthrough for build-generated data + p.config.ctx.Data = common.PlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, @@ -229,9 +222,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error { +func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error { ui.Say("Provisioning with Puppet...") p.communicator = comm + p.generatedData = generatedData ui.Message("Creating Puppet staging directory...") if err := p.createDir(ui, comm, p.config.StagingDir); err != nil { return fmt.Errorf("Error creating staging directory: %s", err) @@ -373,12 +367,6 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds return comm.UploadDir(dst, src, nil) } -func getWinRMPassword(buildName string) string { - winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName) - packer.LogSecretFilter.Set(winRMPass) - return winRMPass -} - func (p *Provisioner) Communicator() packer.Communicator { return p.communicator } @@ -389,9 +377,7 @@ func (p *Provisioner) ElevatedUser() string { func (p *Provisioner) ElevatedPassword() string { // Replace ElevatedPassword for winrm users who used this feature - p.config.ctx.Data = &EnvVarsTemplate{ - WinRMPassword: getWinRMPassword(p.config.PackerBuildName), - } + p.config.ctx.Data = p.generatedData elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) From 2ee33110823eb13304080f341c8b2bd13b2b1c93 Mon Sep 17 00:00:00 2001 From: Sylvia Moss Date: Fri, 13 Dec 2019 10:47:19 +0100 Subject: [PATCH 029/150] Retry command execution when VM session is locked (#8483) --- builder/virtualbox/common/driver_4_2.go | 16 ++++++++++++---- builder/virtualbox/common/shutdown_config.go | 4 ++++ .../virtualbox/common/shutdown_config_test.go | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/builder/virtualbox/common/driver_4_2.go b/builder/virtualbox/common/driver_4_2.go index 86edd9174..e1a1a0119 100644 --- a/builder/virtualbox/common/driver_4_2.go +++ b/builder/virtualbox/common/driver_4_2.go @@ -165,9 +165,6 @@ func (d *VBox42Driver) Stop(name string) error { return err } - // We sleep here for a little bit to let the session "unlock" - time.Sleep(2 * time.Second) - return nil } @@ -189,7 +186,18 @@ func (d *VBox42Driver) SuppressMessages() error { } func (d *VBox42Driver) VBoxManage(args ...string) error { - _, err := d.VBoxManageWithOutput(args...) + ctx := context.TODO() + err := retry.Config{ + Tries: 5, + ShouldRetry: func(err error) bool { + return strings.Contains(err.Error(), "VBOX_E_INVALID_OBJECT_STATE") + }, + RetryDelay: func() time.Duration { return 2 * time.Minute }, + }.Run(ctx, func(ctx context.Context) error { + _, err := d.VBoxManageWithOutput(args...) + return err + }) + return err } diff --git a/builder/virtualbox/common/shutdown_config.go b/builder/virtualbox/common/shutdown_config.go index 0a626c9c7..48ad9c61d 100644 --- a/builder/virtualbox/common/shutdown_config.go +++ b/builder/virtualbox/common/shutdown_config.go @@ -41,5 +41,9 @@ func (c *ShutdownConfig) Prepare(ctx *interpolate.Context) []error { c.ShutdownTimeout = 5 * time.Minute } + if c.PostShutdownDelay == 0 { + c.PostShutdownDelay = 2 * time.Second + } + return nil } diff --git a/builder/virtualbox/common/shutdown_config_test.go b/builder/virtualbox/common/shutdown_config_test.go index e5afd6f0b..3e177fd5f 100644 --- a/builder/virtualbox/common/shutdown_config_test.go +++ b/builder/virtualbox/common/shutdown_config_test.go @@ -49,8 +49,8 @@ func TestShutdownConfigPrepare_PostShutdownDelay(t *testing.T) { if len(errs) > 0 { t.Fatalf("err: %#v", errs) } - if c.PostShutdownDelay.Nanoseconds() != 0 { - t.Fatalf("bad: %s", c.PostShutdownDelay) + if c.PostShutdownDelay != 2*time.Second { + t.Fatalf("bad: PostShutdownDelay should be 2 seconds but was %s", c.PostShutdownDelay) } // Test with a good one From 40357e2534b0f993f44057039dedce7ddf349c34 Mon Sep 17 00:00:00 2001 From: Kris Hicks Date: Fri, 13 Dec 2019 07:35:24 -0800 Subject: [PATCH 030/150] docs/builder/azure-arm: Remove errant html comment (#8484) * Remove errant html comment from source * make generate --- builder/azure/arm/config.go | 2 -- website/source/docs/builders/azure-arm.html.md.erb | 2 -- .../partials/builder/azure/arm/_Config-not-required.html.md | 2 -- 3 files changed, 6 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 552a2389d..86ffab134 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -120,8 +120,6 @@ type Config struct { // // Following is an example. // - // - // // "shared_image_gallery_destination": { // "resource_group": "ResourceGroup", // "gallery_name": "GalleryName", diff --git a/website/source/docs/builders/azure-arm.html.md.erb b/website/source/docs/builders/azure-arm.html.md.erb index b08e8506e..77d01a294 100644 --- a/website/source/docs/builders/azure-arm.html.md.erb +++ b/website/source/docs/builders/azure-arm.html.md.erb @@ -114,8 +114,6 @@ a Shared Image Gallery**. When publishing to a Shared Image Gallery the followin Following is an example. - - "shared_image_gallery_destination": { "resource_group": "ResourceGroup", "gallery_name": "GalleryName", diff --git a/website/source/partials/builder/azure/arm/_Config-not-required.html.md b/website/source/partials/builder/azure/arm/_Config-not-required.html.md index b0cc4b5c8..02a0f9a91 100644 --- a/website/source/partials/builder/azure/arm/_Config-not-required.html.md +++ b/website/source/partials/builder/azure/arm/_Config-not-required.html.md @@ -22,8 +22,6 @@ Following is an example. - - "shared_image_gallery_destination": { "resource_group": "ResourceGroup", "gallery_name": "GalleryName", From e1652696a6a0bcf103c7b310265b8113b09cbe09 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 13 Dec 2019 10:02:13 -0800 Subject: [PATCH 031/150] Rather than using reflection to grab the whole SSH config, for now just use the config convenience functions to get the majority of needed values --- common/step_provision.go | 63 +++++++++++++++------------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/common/step_provision.go b/common/step_provision.go index f7a6d19a1..548150b9e 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "reflect" "time" "github.com/hashicorp/packer/helper/communicator" @@ -28,27 +27,20 @@ import ( func PlaceholderData() map[string]string { placeholderData := map[string]string{} - // use reflection to grab the communicator config field off the config - var sshExample communicator.SSH - var winrmExample communicator.WinRM + // The following correspond to communicator-agnostic functions that are + // part of the SSH and WinRM communicator implementations. These functions + // are not part of the communicator interface, but are stored on the + // Communicator Config and return the appropriate values rather than + // depending on the actual communicator config values. E.g "Password" + // reprosents either WinRMPassword or SSHPassword, which makes this more + // useful if a template contains multiple builds. + placeholderData["Host"] = "{{.Host}}" + placeholderData["Port"] = "{{.Port}}" + placeholderData["User"] = "{{.User}}" + placeholderData["Password"] = "{{.Password}}" - t := reflect.TypeOf(sshExample) - n := t.NumField() - for i := 0; i < n; i++ { - fVal := t.Field(i) - name := fVal.Name - placeholderData[name] = fmt.Sprintf("{{.%s}}", name) - } - - t = reflect.TypeOf(winrmExample) - n = t.NumField() - for i := 0; i < n; i++ { - fVal := t.Field(i) - name := fVal.Name - placeholderData[name] = fmt.Sprintf("{{.%s}}", name) - } - - placeholderData["ID"] = "{{.ID}}" + // Backwards-compatability: + placeholderData["WinRMPassword"] = "{{.WinRMPassword}}" return placeholderData } @@ -56,31 +48,24 @@ func PlaceholderData() map[string]string { func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { hookData := map[string]interface{}{} // Read communicator data into hook data - // state.GetOK("id") - commConf, ok := state.GetOk("communicator_config") + comm, ok := state.GetOk("communicator_config") if !ok { log.Printf("Unable to load config from state to populate provisionHookData") return hookData } - cast := commConf.(*communicator.Config) + commConf := comm.(*communicator.Config) - pd := PlaceholderData() - - v := reflect.ValueOf(cast) - if v.Kind() == reflect.Ptr { - v = v.Elem() - } // Loop over all field values and retrieve them from the ssh config - for fieldName, _ := range pd { - if fieldName == "ID" { - continue - } + hookData["Host"] = commConf.Host() + hookData["Port"] = commConf.Port() + hookData["User"] = commConf.User() + hookData["Password"] = commConf.Password() + // Backwards compatibility; in practice, WinRMPassword is fulfilled by + // Password. + hookData["WinRMPassword"] = commConf.WinRMPassword - fVal := v.FieldByName(fieldName) - if !fVal.IsZero() { - hookData[fieldName] = fVal.Interface() - } - } + // TODO + // state.GetOK("id") return hookData } From 84063d2132b5c87caae767d7584255da26906d78 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 13 Dec 2019 11:57:01 -0800 Subject: [PATCH 032/150] implement ID and Type values in the provisioner template info sharing --- builder/alicloud/ecs/step_create_instance.go | 3 +++ .../amazon/common/step_run_source_instance.go | 3 +++ .../amazon/common/step_run_spot_instance.go | 3 +++ builder/digitalocean/step_create_droplet.go | 3 +++ builder/docker/step_run.go | 3 +++ builder/googlecompute/step_create_instance.go | 3 +++ builder/hcloud/step_create_server.go | 3 +++ builder/hyperone/step_create_vm.go | 3 +++ builder/hyperv/common/step_clone_vm.go | 3 +++ builder/hyperv/common/step_create_vm.go | 3 +++ builder/jdcloud/step_create_instance.go | 3 +++ builder/linode/step_create_linode.go | 3 +++ .../ncloud/step_create_public_ip_instance.go | 3 +++ builder/ncloud/step_create_server_instance.go | 3 +++ builder/oneandone/step_create_server.go | 3 +++ builder/openstack/step_run_source_server.go | 3 +++ builder/osc/common/step_run_source_vm.go | 3 +++ builder/parallels/common/step_run.go | 3 +++ builder/profitbricks/step_create_server.go | 3 +++ builder/proxmox/step_start_vm.go | 3 +++ builder/scaleway/step_create_server.go | 3 +++ builder/tencentcloud/cvm/step_run_instance.go | 3 +++ builder/triton/step_create_source_machine.go | 3 +++ builder/ucloud/uhost/step_create_instance.go | 3 +++ builder/vagrant/step_up.go | 6 +++++- builder/virtualbox/common/step_run.go | 3 +++ builder/vmware/common/step_run.go | 4 ++++ builder/yandex/step_create_instance.go | 3 +++ common/step_provision.go | 19 ++++++++++++++++--- 29 files changed, 103 insertions(+), 4 deletions(-) diff --git a/builder/alicloud/ecs/step_create_instance.go b/builder/alicloud/ecs/step_create_instance.go index 909720e2d..24d3acf27 100644 --- a/builder/alicloud/ecs/step_create_instance.go +++ b/builder/alicloud/ecs/step_create_instance.go @@ -77,6 +77,9 @@ func (s *stepCreateAlicloudInstance) Run(ctx context.Context, state multistep.St ui.Message(fmt.Sprintf("Created instance: %s", instanceId)) s.instance = &instances.Instances.Instance[0] state.Put("instance", s.instance) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", instanceId) return multistep.ActionContinue } diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 0f1941b85..b83db0383 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -280,6 +280,9 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa } state.Put("instance", instance) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", instance.InstanceId) // If we're in a region that doesn't support tagging on instance creation, // do that now. diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index 06cb28912..d6db5be62 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -441,6 +441,9 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } state.Put("instance", instance) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", instance.InstanceId) return multistep.ActionContinue } diff --git a/builder/digitalocean/step_create_droplet.go b/builder/digitalocean/step_create_droplet.go index a64325d35..d3a54c955 100644 --- a/builder/digitalocean/step_create_droplet.go +++ b/builder/digitalocean/step_create_droplet.go @@ -63,6 +63,9 @@ func (s *stepCreateDroplet) Run(ctx context.Context, state multistep.StateBag) m // Store the droplet id for later state.Put("droplet_id", droplet.ID) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", droplet.ID) return multistep.ActionContinue } diff --git a/builder/docker/step_run.go b/builder/docker/step_run.go index 281505104..bdd8e7dcd 100644 --- a/builder/docker/step_run.go +++ b/builder/docker/step_run.go @@ -42,6 +42,9 @@ func (s *StepRun) Run(ctx context.Context, state multistep.StateBag) multistep.S // Save the container ID s.containerId = containerId state.Put("container_id", s.containerId) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.containerId) ui.Message(fmt.Sprintf("Container ID: %s", s.containerId)) return multistep.ActionContinue } diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index ca15eeccb..088c88925 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -176,6 +176,9 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) // Things succeeded, store the name so we can remove it later state.Put("instance_name", name) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", name) return multistep.ActionContinue } diff --git a/builder/hcloud/step_create_server.go b/builder/hcloud/step_create_server.go index c5c38b9ad..f0d2f863a 100644 --- a/builder/hcloud/step_create_server.go +++ b/builder/hcloud/step_create_server.go @@ -85,6 +85,9 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu // Store the server id for later state.Put("server_id", serverCreateResult.Server.ID) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", serverCreateResult.Server.ID) if err := waitForAction(ctx, client, serverCreateResult.Action); err != nil { err := fmt.Errorf("Error creating server: %s", err) diff --git a/builder/hyperone/step_create_vm.go b/builder/hyperone/step_create_vm.go index 155f383a0..58c1dfbc0 100644 --- a/builder/hyperone/step_create_vm.go +++ b/builder/hyperone/step_create_vm.go @@ -67,6 +67,9 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis s.vmID = vm.Id state.Put("vm_id", vm.Id) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", vm.Id) hdds, _, err := client.VmApi.VmListHdd(ctx, vm.Id) if err != nil { diff --git a/builder/hyperv/common/step_clone_vm.go b/builder/hyperv/common/step_clone_vm.go index 20d40abfe..5213574e1 100644 --- a/builder/hyperv/common/step_clone_vm.go +++ b/builder/hyperv/common/step_clone_vm.go @@ -154,6 +154,9 @@ func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multist // Set the final name in the state bag so others can use it state.Put("vmName", s.VMName) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.VMName) return multistep.ActionContinue } diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index f9a05f797..920edfa45 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -166,6 +166,9 @@ func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis // Set the final name in the state bag so others can use it state.Put("vmName", s.VMName) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.VMName) return multistep.ActionContinue } diff --git a/builder/jdcloud/step_create_instance.go b/builder/jdcloud/step_create_instance.go index fe66f464d..930910544 100644 --- a/builder/jdcloud/step_create_instance.go +++ b/builder/jdcloud/step_create_instance.go @@ -96,6 +96,9 @@ func (s *stepCreateJDCloudInstance) Run(_ context.Context, state multistep.State "Hi, we have created the instance, its name=%v , "+ "its id=%v, "+ "and its eip=%v :) ", instance.InstanceName, s.InstanceSpecConfig.InstanceId, eip.Result.ElasticIp.ElasticIpAddress)) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.InstanceSpecConfig.InstanceId) return multistep.ActionContinue } diff --git a/builder/linode/step_create_linode.go b/builder/linode/step_create_linode.go index ca15facfd..90dc6342a 100644 --- a/builder/linode/step_create_linode.go +++ b/builder/linode/step_create_linode.go @@ -49,6 +49,9 @@ func (s *stepCreateLinode) Run(ctx context.Context, state multistep.StateBag) mu return multistep.ActionHalt } state.Put("instance", instance) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", instance.ID) } disk, err := s.findDisk(ctx, instance.ID) diff --git a/builder/ncloud/step_create_public_ip_instance.go b/builder/ncloud/step_create_public_ip_instance.go index 39c31966c..e4550e6ef 100644 --- a/builder/ncloud/step_create_public_ip_instance.go +++ b/builder/ncloud/step_create_public_ip_instance.go @@ -97,6 +97,9 @@ func (s *StepCreatePublicIPInstance) Run(ctx context.Context, state multistep.St if err == nil { state.Put("PublicIP", publicIPInstance.PublicIP) state.Put("PublicIPInstance", publicIPInstance) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", publicIPInstance) } return processStepResult(err, s.Error, state) diff --git a/builder/ncloud/step_create_server_instance.go b/builder/ncloud/step_create_server_instance.go index 2a72789f6..49648cbb0 100644 --- a/builder/ncloud/step_create_server_instance.go +++ b/builder/ncloud/step_create_server_instance.go @@ -96,6 +96,9 @@ func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.Stat serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, zoneNo, feeSystemTypeCode) if err == nil { state.Put("InstanceNo", serverInstanceNo) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", serverInstanceNo) } return processStepResult(err, s.Error, state) diff --git a/builder/oneandone/step_create_server.go b/builder/oneandone/step_create_server.go index 1fc9863aa..6d70db862 100644 --- a/builder/oneandone/step_create_server.go +++ b/builder/oneandone/step_create_server.go @@ -94,6 +94,9 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu } state.Put("server_id", server_id) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", server_id) state.Put("server_ip", server.Ips[0].Ip) diff --git a/builder/openstack/step_run_source_server.go b/builder/openstack/step_run_source_server.go index 3c3bdff1b..59cf63afe 100644 --- a/builder/openstack/step_run_source_server.go +++ b/builder/openstack/step_run_source_server.go @@ -128,6 +128,9 @@ func (s *StepRunSourceServer) Run(ctx context.Context, state multistep.StateBag) s.server = latestServer.(*servers.Server) state.Put("server", s.server) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.server.ID) return multistep.ActionContinue } diff --git a/builder/osc/common/step_run_source_vm.go b/builder/osc/common/step_run_source_vm.go index 04fe1d5b3..a0f92a6a5 100644 --- a/builder/osc/common/step_run_source_vm.go +++ b/builder/osc/common/step_run_source_vm.go @@ -253,6 +253,9 @@ func (s *StepRunSourceVm) Run(ctx context.Context, state multistep.StateBag) mul } state.Put("vm", vm) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", vmId) // If we're in a region that doesn't support tagging on vm creation, // do that now. diff --git a/builder/parallels/common/step_run.go b/builder/parallels/common/step_run.go index d3d60fd1a..062312069 100644 --- a/builder/parallels/common/step_run.go +++ b/builder/parallels/common/step_run.go @@ -36,6 +36,9 @@ func (s *StepRun) Run(ctx context.Context, state multistep.StateBag) multistep.S } s.vmName = vmName + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", vmName) return multistep.ActionContinue } diff --git a/builder/profitbricks/step_create_server.go b/builder/profitbricks/step_create_server.go index 3d2e44d70..b7320d6d0 100644 --- a/builder/profitbricks/step_create_server.go +++ b/builder/profitbricks/step_create_server.go @@ -147,6 +147,9 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu state.Put("volume_id", server.Entities.Volumes.Items[0].Id) server = profitbricks.GetServer(datacenter.Id, server.Id) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", server.Id) state.Put("server_ip", server.Entities.Nics.Items[0].Properties.Ips[0]) diff --git a/builder/proxmox/step_start_vm.go b/builder/proxmox/step_start_vm.go index 8020cf9db..378b689ea 100644 --- a/builder/proxmox/step_start_vm.go +++ b/builder/proxmox/step_start_vm.go @@ -76,6 +76,9 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist // Store the vm id for later state.Put("vmRef", vmRef) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", vmRef) ui.Say("Starting VM") _, err = client.StartVm(vmRef) diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index 9cfee4207..06a72d95a 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -60,6 +60,9 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu s.serverID = server state.Put("server_id", server) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.serverID) return multistep.ActionContinue } diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index b43e1fbf6..72f3f7b90 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -175,6 +175,9 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul } state.Put("instance", describeResp.Response.InstanceSet[0]) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.instanceId) Message(state, s.instanceId, "Instance created") return multistep.ActionContinue diff --git a/builder/triton/step_create_source_machine.go b/builder/triton/step_create_source_machine.go index 1aad77508..24080ba23 100644 --- a/builder/triton/step_create_source_machine.go +++ b/builder/triton/step_create_source_machine.go @@ -43,6 +43,9 @@ func (s *StepCreateSourceMachine) Run(ctx context.Context, state multistep.State } state.Put("machine", machineId) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", machineId) return multistep.ActionContinue } diff --git a/builder/ucloud/uhost/step_create_instance.go b/builder/ucloud/uhost/step_create_instance.go index 3c60daef8..8ce0836a2 100644 --- a/builder/ucloud/uhost/step_create_instance.go +++ b/builder/ucloud/uhost/step_create_instance.go @@ -77,6 +77,9 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) s.instanceId = instanceId state.Put("instance", instance) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", instance) if instance.BootDiskState != ucloudcommon.BootDiskStateNormal { ui.Say("Waiting for boot disk of instance initialized") diff --git a/builder/vagrant/step_up.go b/builder/vagrant/step_up.go index 6ca931f3a..e78834f36 100644 --- a/builder/vagrant/step_up.go +++ b/builder/vagrant/step_up.go @@ -35,7 +35,11 @@ func (s *StepUp) Run(ctx context.Context, state multistep.StateBag) multistep.St ui.Say("Calling Vagrant Up (this can take some time)...") - _, _, err := driver.Up(s.generateArgs()) + args := s.generateArgs() + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", args[0]) + _, _, err := driver.Up(args) if err != nil { state.Put("error", err) diff --git a/builder/virtualbox/common/step_run.go b/builder/virtualbox/common/step_run.go index 46a45d001..8144087b2 100644 --- a/builder/virtualbox/common/step_run.go +++ b/builder/virtualbox/common/step_run.go @@ -57,6 +57,9 @@ func (s *StepRun) Run(ctx context.Context, state multistep.StateBag) multistep.S } s.vmName = vmName + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", s.vmName) return multistep.ActionContinue } diff --git a/builder/vmware/common/step_run.go b/builder/vmware/common/step_run.go index 5f03ebca5..e9ac27331 100644 --- a/builder/vmware/common/step_run.go +++ b/builder/vmware/common/step_run.go @@ -64,6 +64,10 @@ func (s *StepRun) Run(ctx context.Context, state multistep.StateBag) multistep.S return multistep.ActionHalt } + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", vmxPath) + return multistep.ActionContinue } diff --git a/builder/yandex/step_create_instance.go b/builder/yandex/step_create_instance.go index 5d7b28f10..b13cbca1e 100644 --- a/builder/yandex/step_create_instance.go +++ b/builder/yandex/step_create_instance.go @@ -249,6 +249,9 @@ runcmd: } state.Put("disk_id", instance.BootDisk.DiskId) + // instance_id is the generic term used so that users can have access to the + // instance id inside of the provisioners, used in step_provision. + state.Put("instance_id", instance.Id) if s.Debug { ui.Message(fmt.Sprintf("Instance ID %s started. Current instance status %s", instance.Id, instance.Status)) diff --git a/common/step_provision.go b/common/step_provision.go index 548150b9e..4c1df2659 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -26,7 +26,7 @@ import ( // accidentally being interpolated into empty strings at prepare time. func PlaceholderData() map[string]string { placeholderData := map[string]string{} - + placeholderData["ID"] = "{{.ID}}" // The following correspond to communicator-agnostic functions that are // part of the SSH and WinRM communicator implementations. These functions // are not part of the communicator interface, but are stored on the @@ -38,6 +38,7 @@ func PlaceholderData() map[string]string { placeholderData["Port"] = "{{.Port}}" placeholderData["User"] = "{{.User}}" placeholderData["Password"] = "{{.Password}}" + placeholderData["ConnType"] = "{{.Type}}" // Backwards-compatability: placeholderData["WinRMPassword"] = "{{.WinRMPassword}}" @@ -60,12 +61,24 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} hookData["Port"] = commConf.Port() hookData["User"] = commConf.User() hookData["Password"] = commConf.Password() + hookData["ConnType"] = commConf.Type + // Backwards compatibility; in practice, WinRMPassword is fulfilled by // Password. hookData["WinRMPassword"] = commConf.WinRMPassword - // TODO - // state.GetOK("id") + // instance_id is placed in state by the builders. + // Not yet implemented in Chroot, lxc/lxd, Azure, Qemu. + // Implemented in most others including digitalOcean (droplet id), + // docker (container_id), and clouds which use "server" internally instead + // of instance. + id, ok := state.GetOk("instance_id") + if ok { + hookData["ID"] = id + } else { + // Warn user that the id isn't implemented + hookData["ID"] = "ERR_ID_NOT_IMPLEMENTED_BY_BUILDER" + } return hookData } From 88d4ce37c93c3b77251264efc62400e57d4ff8d2 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 13 Dec 2019 13:03:09 -0800 Subject: [PATCH 033/150] template functioin to let Provisioners access PACKER_RUN_UUID --- common/step_provision.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/common/step_provision.go b/common/step_provision.go index 4c1df2659..3f2c59ecd 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "os" "time" "github.com/hashicorp/packer/helper/communicator" @@ -39,6 +40,7 @@ func PlaceholderData() map[string]string { placeholderData["User"] = "{{.User}}" placeholderData["Password"] = "{{.Password}}" placeholderData["ConnType"] = "{{.Type}}" + placeholderData["PACKER_RUN_UUID"] = "{{.PACKER_RUN_UUID}}" // Backwards-compatability: placeholderData["WinRMPassword"] = "{{.WinRMPassword}}" @@ -48,6 +50,20 @@ func PlaceholderData() map[string]string { func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { hookData := map[string]interface{}{} + // instance_id is placed in state by the builders. + // Not yet implemented in Chroot, lxc/lxd, Azure, Qemu. + // Implemented in most others including digitalOcean (droplet id), + // docker (container_id), and clouds which use "server" internally instead + // of instance. + id, ok := state.GetOk("instance_id") + if ok { + hookData["ID"] = id + } else { + // Warn user that the id isn't implemented + hookData["ID"] = "ERR_ID_NOT_IMPLEMENTED_BY_BUILDER" + } + hookData["PACKER_RUN_UUID"] = os.Getenv("PACKER_RUN_UUID") + // Read communicator data into hook data comm, ok := state.GetOk("communicator_config") if !ok { @@ -67,19 +83,6 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} // Password. hookData["WinRMPassword"] = commConf.WinRMPassword - // instance_id is placed in state by the builders. - // Not yet implemented in Chroot, lxc/lxd, Azure, Qemu. - // Implemented in most others including digitalOcean (droplet id), - // docker (container_id), and clouds which use "server" internally instead - // of instance. - id, ok := state.GetOk("instance_id") - if ok { - hookData["ID"] = id - } else { - // Warn user that the id isn't implemented - hookData["ID"] = "ERR_ID_NOT_IMPLEMENTED_BY_BUILDER" - } - return hookData } From ac570e0cc08b091719e03221c6f85fbd22971199 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 13 Dec 2019 13:15:03 -0800 Subject: [PATCH 034/150] store ssh public and private keys on shared info --- common/step_provision.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/step_provision.go b/common/step_provision.go index 3f2c59ecd..2745a18b2 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -41,6 +41,8 @@ func PlaceholderData() map[string]string { placeholderData["Password"] = "{{.Password}}" placeholderData["ConnType"] = "{{.Type}}" placeholderData["PACKER_RUN_UUID"] = "{{.PACKER_RUN_UUID}}" + placeholderData["SSHPublicKey"] = "{{.SSHPublicKey}}" + placeholderData["SSHPrivateKey"] = "{{.SSHPrivateKey}}" // Backwards-compatability: placeholderData["WinRMPassword"] = "{{.WinRMPassword}}" @@ -78,6 +80,8 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} hookData["User"] = commConf.User() hookData["Password"] = commConf.Password() hookData["ConnType"] = commConf.Type + hookData["SSHPublicKey"] = commConf.SSHPublicKey + hookData["SSHPrivateKey"] = commConf.SSHPrivateKey // Backwards compatibility; in practice, WinRMPassword is fulfilled by // Password. From 82367a88f88553035a99b581d526ff76a5ac017d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Sat, 14 Dec 2019 03:32:38 -0800 Subject: [PATCH 035/150] reorganize placeholder data call to live with provisioner implementation; force users to use the generated function, therefore forcing validation, for all variables except winrmpassword, by doing a simple string check against the placeholder data. --- common/shell-local/config.go | 3 +- common/step_provision.go | 28 --------------- helper/common/shared_state.go | 5 +++ packer/provisioner.go | 38 ++++++++++++++++++++ provisioner/ansible/provisioner.go | 2 +- provisioner/chef-client/provisioner.go | 2 +- provisioner/powershell/provisioner.go | 2 +- provisioner/puppet-masterless/provisioner.go | 2 +- provisioner/puppet-server/provisioner.go | 2 +- template/interpolate/funcs.go | 15 +++++--- 10 files changed, 60 insertions(+), 39 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index bdf3dfbd7..46f2f8088 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -10,7 +10,6 @@ import ( "runtime" "strings" - "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/shell" configHelper "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -52,7 +51,7 @@ type Config struct { func Decode(config *Config, raws ...interface{}) error { // Create passthrough for build-generated data so we can fill it in once we know // it - config.ctx.Data = common.PlaceholderData() + config.ctx.Data = packer.BasicPlaceholderData() err := configHelper.Decode(&config, &configHelper.DecodeOpts{ Interpolate: true, diff --git a/common/step_provision.go b/common/step_provision.go index 2745a18b2..7b2233c77 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -22,34 +22,6 @@ import ( // Produces: // -// Provisioners interpolate most of their fields in the prepare stage; this -// placeholder map helps keep fields that are only generated at build time from -// accidentally being interpolated into empty strings at prepare time. -func PlaceholderData() map[string]string { - placeholderData := map[string]string{} - placeholderData["ID"] = "{{.ID}}" - // The following correspond to communicator-agnostic functions that are - // part of the SSH and WinRM communicator implementations. These functions - // are not part of the communicator interface, but are stored on the - // Communicator Config and return the appropriate values rather than - // depending on the actual communicator config values. E.g "Password" - // reprosents either WinRMPassword or SSHPassword, which makes this more - // useful if a template contains multiple builds. - placeholderData["Host"] = "{{.Host}}" - placeholderData["Port"] = "{{.Port}}" - placeholderData["User"] = "{{.User}}" - placeholderData["Password"] = "{{.Password}}" - placeholderData["ConnType"] = "{{.Type}}" - placeholderData["PACKER_RUN_UUID"] = "{{.PACKER_RUN_UUID}}" - placeholderData["SSHPublicKey"] = "{{.SSHPublicKey}}" - placeholderData["SSHPrivateKey"] = "{{.SSHPrivateKey}}" - - // Backwards-compatability: - placeholderData["WinRMPassword"] = "{{.WinRMPassword}}" - - return placeholderData -} - func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { hookData := map[string]interface{}{} // instance_id is placed in state by the builders. diff --git a/helper/common/shared_state.go b/helper/common/shared_state.go index 87e111cfb..dce05beec 100644 --- a/helper/common/shared_state.go +++ b/helper/common/shared_state.go @@ -7,6 +7,11 @@ import ( "path/filepath" ) +// This is used in the BasicPlaceholderData() func in the packer/provisioner.go +// To force users to access generated data via the "generated" func. +const PlaceholderMsg = "To set this dynamically in the Packer template, " + + "you must use the `generated` function" + // Used to set variables which we need to access later in the build, where // state bag and config information won't work func sharedStateFilename(suffix string, buildName string) string { diff --git a/packer/provisioner.go b/packer/provisioner.go index 6e29e2e61..d20e4d360 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -6,6 +6,8 @@ import ( "log" "sync" "time" + + "github.com/hashicorp/packer/helper/common" ) // A provisioner is responsible for installing and configuring software @@ -37,6 +39,42 @@ type ProvisionHook struct { Provisioners []*HookedProvisioner } +// Provisioners interpolate most of their fields in the prepare stage; this +// placeholder map helps keep fields that are only generated at build time from +// accidentally being interpolated into empty strings at prepare time. +// This helper function generates the most basic placeholder data which should +// be accessible to the provisioners. It is used to initialize provisioners, to +// force validation using the `generated` template function. In the future, +// custom generated data could be passed into provisioners from builders to +// enable specialized builder-specific (but still validated!!) access to builder +// data. +func BasicPlaceholderData() map[string]string { + placeholderData := map[string]string{} + msg := "Generated_%s. " + common.PlaceholderMsg + placeholderData["ID"] = fmt.Sprintf(msg, "ID") + // The following correspond to communicator-agnostic functions that are + // part of the SSH and WinRM communicator implementations. These functions + // are not part of the communicator interface, but are stored on the + // Communicator Config and return the appropriate values rather than + // depending on the actual communicator config values. E.g "Password" + // reprosents either WinRMPassword or SSHPassword, which makes this more + // useful if a template contains multiple builds. + placeholderData["Host"] = fmt.Sprintf(msg, "Host") + placeholderData["Port"] = fmt.Sprintf(msg, "Port") + placeholderData["User"] = fmt.Sprintf(msg, "User") + placeholderData["Password"] = fmt.Sprintf(msg, "Password") + placeholderData["ConnType"] = fmt.Sprintf(msg, "Type") + placeholderData["PACKER_RUN_UUID"] = fmt.Sprintf(msg, "PACKER_RUN_UUID") + placeholderData["SSHPublicKey"] = fmt.Sprintf(msg, "SSHPublicKey") + placeholderData["SSHPrivateKey"] = fmt.Sprintf(msg, "SSHPrivateKey") + + // Backwards-compatability: WinRM Password can get through without forcing + // the generated func validation. + placeholderData["WinRMPassword"] = "{{.WinRMPassword}}" + + return placeholderData +} + // Runs the provisioners in order. func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error { // Shortcut diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index bdcf578c3..201bfd086 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -81,7 +81,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) // Create passthrough for build-generated data - p.config.ctx.Data = common.PlaceholderData() + p.config.ctx.Data = packer.BasicPlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 399a3c847..a0d33e4f3 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -121,7 +121,7 @@ type KnifeTemplate struct { func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for build-generated data - p.config.ctx.Data = common.PlaceholderData() + p.config.ctx.Data = packer.BasicPlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &p.config.ctx, diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index c0d567627..71ef42765 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -90,7 +90,7 @@ func (p *Provisioner) defaultExecuteCommand() string { func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for build-generated data - p.config.ctx.Data = common.PlaceholderData() + p.config.ctx.Data = packer.BasicPlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index 7a6a968ed..289b0e6c9 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -148,7 +148,7 @@ type ExecuteTemplate struct { func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for build-generated data - p.config.ctx.Data = common.PlaceholderData() + p.config.ctx.Data = packer.BasicPlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &p.config.ctx, diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index fd30d46d4..ba7cf4584 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -142,7 +142,7 @@ type ExecuteTemplate struct { func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for build-generated data - p.config.ctx.Data = common.PlaceholderData() + p.config.ctx.Data = packer.BasicPlaceholderData() err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 2373ded1b..96b04ff67 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -12,6 +12,7 @@ import ( consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/version" vaultapi "github.com/hashicorp/vault/api" strftime "github.com/jehiah/go-strftime" @@ -166,11 +167,17 @@ func funcGenTemplateDir(ctx *Context) interface{} { func funcGenGenerated(ctx *Context) interface{} { return func(s string) (string, error) { if data, ok := ctx.Data.(map[string]string); ok { - // PlaceholderData has been passed into generator, and we can check - // the value against the placeholder data to make sure that it is - // valid + // PlaceholderData has been passed into generator, so if the given + // key already exists in data, then we know it's an "allowed" key if heldPlace, ok := data[s]; ok { - return heldPlace, nil + // If we're in the first interpolation pass, the goal is to + // make sure that we pass the value through. + // TODO match against an actual string constant + if strings.Contains(heldPlace, common.PlaceholderMsg) { + return fmt.Sprintf("{{.%s}}", s), nil + } else { + return heldPlace, nil + } } else { return "", fmt.Errorf("loaded data, but couldnt find %s in it", s) } From d47f151bf922f66319a6093398f9126614a8f9d7 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Sat, 14 Dec 2019 03:38:27 -0800 Subject: [PATCH 036/150] punctuation --- template/interpolate/funcs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 96b04ff67..b47ceb2dd 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -179,7 +179,7 @@ func funcGenGenerated(ctx *Context) interface{} { return heldPlace, nil } } else { - return "", fmt.Errorf("loaded data, but couldnt find %s in it", s) + return "", fmt.Errorf("loaded data, but couldnt find %s in it.", s) } } From a125f81fbac0ab5fc49e337f7d6107bf9027de8c Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Sat, 14 Dec 2019 03:44:09 -0800 Subject: [PATCH 037/150] add some more comments --- common/step_provision.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/step_provision.go b/common/step_provision.go index 7b2233c77..b490aaaea 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -23,12 +23,17 @@ import ( // func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { - hookData := map[string]interface{}{} + // Load Builder hook data from state, if it has been set. + // hookData := state.GetOk("generated_data").(map[string]interface{}) + // instance_id is placed in state by the builders. // Not yet implemented in Chroot, lxc/lxd, Azure, Qemu. // Implemented in most others including digitalOcean (droplet id), // docker (container_id), and clouds which use "server" internally instead // of instance. + + // Also note that Chroot and lxc/lxd builders tend to have their own custom + // step_provision, so they won't use this code path. id, ok := state.GetOk("instance_id") if ok { hookData["ID"] = id From 1679d9fb9dbbd6b125e8c94e37491e80f081272f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Sat, 14 Dec 2019 03:53:22 -0800 Subject: [PATCH 038/150] docs --- website/source/docs/templates/engine.html.md | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/website/source/docs/templates/engine.html.md b/website/source/docs/templates/engine.html.md index 4c7883c88..3391e4b77 100644 --- a/website/source/docs/templates/engine.html.md +++ b/website/source/docs/templates/engine.html.md @@ -62,6 +62,29 @@ Here is a full list of the available functions for reference. each function will behave. - `env` - Returns environment variables. See example in [using home variable](/docs/templates/user-variables.html#using-home-variable) +- `generated` - This engine will allow you to access special variables that + provide connection information and basic instance state information. + Usage example: + ```json + { + "type": "shell-local", + "environment_vars": ["TESTVAR={{ generated `PACKER_RUN_UUID`}}"], + "inline": ["echo $TESTVAR"] + }, + ``` + Valid variables to request are: "ID", "Host", "Port", "User", "Password", + "ConnType", "PACKER_RUN_UUID", "SSHPublicKey", "SSHPrivateKey", and + "WinRMPassword", though obviously depending on which communicator you are + using, some of these values may be empty. ID represents the vm being + provisioned. For example, in Amazon it is the instance id; in digitalocean, + it is the droplet id; in Vmware, it is the vm name. + + This function is only for use within _provisioners_, and does not yet work + if the provisioners are being used in conjunction with our chroot builders + or with lxc/lxd builders. + + This engine is in beta; please report any issues or requests on the Packer + issue tracker on GitHub. - `isotime [FORMAT]` - UTC time, which can be [formatted](https://golang.org/pkg/time/#example_Time_Format). See more examples below in [the `isotime` format From 0a8b172bf92d94566e21fe86d1d529038120780d Mon Sep 17 00:00:00 2001 From: Sylvia Moss Date: Mon, 16 Dec 2019 14:31:15 +0100 Subject: [PATCH 039/150] Remove duplicated retry mechanism when deleting VM ffrom virtual box (#8485) This retry was also added to fix a locked by session error reported in #5501 and fixed in #5512. --- builder/virtualbox/common/driver_4_2.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/builder/virtualbox/common/driver_4_2.go b/builder/virtualbox/common/driver_4_2.go index e1a1a0119..0599251ff 100644 --- a/builder/virtualbox/common/driver_4_2.go +++ b/builder/virtualbox/common/driver_4_2.go @@ -76,14 +76,7 @@ func (d *VBox42Driver) CreateSCSIController(vmName string, name string) error { } func (d *VBox42Driver) Delete(name string) error { - ctx := context.TODO() - return retry.Config{ - Tries: 5, - RetryDelay: (&retry.Backoff{InitialBackoff: 1 * time.Second, MaxBackoff: 1 * time.Second, Multiplier: 2}).Linear, - }.Run(ctx, func(ctx context.Context) error { - err := d.VBoxManage("unregistervm", name, "--delete") - return err - }) + return d.VBoxManage("unregistervm", name, "--delete") } func (d *VBox42Driver) Iso() (string, error) { From c57f093efe1b45365ae24761828c413463679dc4 Mon Sep 17 00:00:00 2001 From: Sylvia Moss Date: Mon, 16 Dec 2019 16:50:21 +0100 Subject: [PATCH 040/150] Remove unnecessary source_path validation for virtualbox-ovf (#8489) --- builder/virtualbox/ovf/config.go | 10 ++------- builder/virtualbox/ovf/config_test.go | 22 ------------------- .../virtualbox/ovf/_Config-required.html.md | 4 ++-- 3 files changed, 4 insertions(+), 32 deletions(-) diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 10999f463..3a6ae041a 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -5,7 +5,6 @@ package ovf import ( "fmt" - "os" "strings" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" @@ -83,8 +82,8 @@ type Config struct { // VBoxManage import. This can be useful for passing keepallmacs or // keepnatmacs options for existing ovf images. ImportOpts string `mapstructure:"import_opts" required:"false"` - // The path to an OVF or OVA file that acts as the - // source of this build. This currently must be a local file. + // The filepath or URL to an OVF or OVA file that acts as the + // source of this build. SourcePath string `mapstructure:"source_path" required:"true"` // The path where the OVA should be saved // after download. By default, it will go in the packer cache, with a hash of @@ -163,11 +162,6 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required")) } - if _, err := os.Stat(c.SourcePath); err != nil { - packer.MultiErrorAppend(errs, - fmt.Errorf("Source file '%s' needs to exist at time of config validation! %v", c.SourcePath, err)) - } - validMode := false validModes := []string{ vboxcommon.GuestAdditionsModeDisable, diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index e36c44fcf..517eda04b 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -65,28 +65,6 @@ func TestNewConfig_sourcePath(t *testing.T) { t.Fatalf("should error with empty `source_path`") } - // Want this to fail on validation - c = testConfig(t) - c["source_path"] = "/i/dont/exist" - _, warns, err = NewConfig(c) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatalf("Nonexistent file should throw a validation error!") - } - - // Bad - c = testConfig(t) - c["source_path"] = "ftp://i/dont/exist" - _, warns, err = NewConfig(c) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatalf("should error") - } - // Good tf := getTempFile(t) defer os.Remove(tf.Name()) diff --git a/website/source/partials/builder/virtualbox/ovf/_Config-required.html.md b/website/source/partials/builder/virtualbox/ovf/_Config-required.html.md index 90f3e8359..09143e5e4 100644 --- a/website/source/partials/builder/virtualbox/ovf/_Config-required.html.md +++ b/website/source/partials/builder/virtualbox/ovf/_Config-required.html.md @@ -7,6 +7,6 @@ file or an URL, in which case checksum_type must be set to file; the go-getter will download it and use the first hash found. -- `source_path` (string) - The path to an OVF or OVA file that acts as the - source of this build. This currently must be a local file. +- `source_path` (string) - The filepath or URL to an OVF or OVA file that acts as the + source of this build. \ No newline at end of file From 21a849354b4e19308c7745a96eec8d865bc2c94e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 16 Dec 2019 11:22:21 -0800 Subject: [PATCH 041/150] fix hook data loading --- common/step_provision.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/common/step_provision.go b/common/step_provision.go index b490aaaea..c9c399e16 100644 --- a/common/step_provision.go +++ b/common/step_provision.go @@ -23,8 +23,13 @@ import ( // func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} { + hookData := make(map[string]interface{}) + // Load Builder hook data from state, if it has been set. - // hookData := state.GetOk("generated_data").(map[string]interface{}) + hd, ok := state.GetOk("generated_data") + if ok { + hookData = hd.(map[string]interface{}) + } // instance_id is placed in state by the builders. // Not yet implemented in Chroot, lxc/lxd, Azure, Qemu. @@ -41,7 +46,7 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} // Warn user that the id isn't implemented hookData["ID"] = "ERR_ID_NOT_IMPLEMENTED_BY_BUILDER" } - hookData["PACKER_RUN_UUID"] = os.Getenv("PACKER_RUN_UUID") + hookData["PackerRunUUID"] = os.Getenv("PACKER_RUN_UUID") // Read communicator data into hook data comm, ok := state.GetOk("communicator_config") From 18bb4ffb44f98107f4a643e8c3023a71bc76f154 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 16 Dec 2019 11:46:46 -0800 Subject: [PATCH 042/150] bump API version since this change makes things backwards incompatible. --- packer/plugin/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/plugin/server.go b/packer/plugin/server.go index 0efefb7e5..b230a1413 100644 --- a/packer/plugin/server.go +++ b/packer/plugin/server.go @@ -36,7 +36,7 @@ const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d69 // The APIVersion is outputted along with the RPC address. The plugin // client validates this API version and will show an error if it doesn't // know how to speak it. -const APIVersion = "4" +const APIVersion = "5" // Server waits for a connection to this plugin and returns a Packer // RPC server that you can use to register components and serve them. From 0f6d1beccf6cffee75acef741329bb80831b69f6 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 16 Dec 2019 21:23:05 -0800 Subject: [PATCH 043/150] add an extra string array to the Prepare() return values in the builder interfaces; this sets up the ability for builders to give the provisioners custom user-accessible build-time variables. --- builder/alicloud/ecs/builder.go | 8 +-- builder/alicloud/ecs/builder_test.go | 22 +++--- builder/amazon/chroot/builder.go | 8 +-- builder/amazon/chroot/builder_test.go | 26 +++---- builder/amazon/ebs/builder.go | 8 +-- builder/amazon/ebs/builder_test.go | 16 ++--- builder/amazon/ebssurrogate/builder.go | 8 +-- builder/amazon/ebssurrogate/builder_test.go | 4 +- builder/amazon/ebsvolume/builder.go | 8 +-- builder/amazon/ebsvolume/builder_test.go | 10 +-- builder/amazon/instance/builder.go | 8 +-- builder/amazon/instance/builder_test.go | 36 +++++----- builder/azure/arm/builder.go | 6 +- builder/azure/arm/builder_test.go | 2 +- builder/azure/chroot/builder.go | 10 +-- builder/azure/chroot/builder_test.go | 2 +- builder/cloudstack/builder.go | 6 +- builder/cloudstack/builder_test.go | 2 +- builder/digitalocean/builder.go | 6 +- builder/digitalocean/builder_test.go | 46 ++++++------- builder/docker/builder.go | 6 +- builder/docker/communicator_test.go | 6 +- builder/file/builder.go | 6 +- builder/googlecompute/builder.go | 6 +- builder/hcloud/builder.go | 6 +- builder/hyperone/builder.go | 6 +- builder/hyperv/iso/builder.go | 8 +-- builder/hyperv/iso/builder_test.go | 64 ++++++++--------- builder/hyperv/vmcx/builder.go | 8 +-- builder/hyperv/vmcx/builder_test.go | 42 ++++++------ builder/jdcloud/builder.go | 8 +-- builder/linode/builder.go | 6 +- builder/linode/builder_test.go | 30 ++++---- builder/lxc/builder.go | 6 +- builder/lxc/builder_test.go | 4 +- builder/lxd/builder.go | 6 +- builder/lxd/builder_test.go | 8 +-- builder/ncloud/builder.go | 6 +- builder/null/builder.go | 6 +- builder/oneandone/builder.go | 6 +- builder/oneandone/builder_test.go | 4 +- builder/openstack/builder.go | 10 +-- builder/openstack/builder_test.go | 2 +- builder/oracle/classic/builder.go | 8 +-- builder/oracle/oci/builder.go | 6 +- builder/osc/bsu/builder.go | 8 +-- builder/osc/bsu/builder_test.go | 16 ++--- builder/osc/bsusurrogate/builder.go | 8 +-- builder/osc/bsuvolume/builder.go | 8 +-- builder/osc/bsuvolume/builder_test.go | 10 +-- builder/osc/chroot/builder.go | 8 +-- builder/parallels/iso/builder.go | 8 +-- builder/parallels/iso/builder_test.go | 28 ++++---- builder/parallels/pvm/builder.go | 6 +- builder/profitbricks/builder.go | 6 +- builder/profitbricks/builder_test.go | 4 +- builder/proxmox/builder.go | 6 +- builder/proxmox/config_test.go | 4 +- builder/qemu/builder.go | 8 +-- builder/qemu/builder_test.go | 68 +++++++++---------- builder/scaleway/builder.go | 6 +- builder/scaleway/builder_test.go | 30 ++++---- builder/tencentcloud/cvm/builder.go | 8 +-- builder/triton/builder.go | 4 +- builder/ucloud/uhost/builder.go | 8 +-- builder/ucloud/uhost/builder_test.go | 12 ++-- builder/vagrant/builder.go | 8 +-- builder/vagrant/builder_test.go | 2 +- builder/virtualbox/iso/builder.go | 8 +-- builder/virtualbox/iso/builder_test.go | 44 ++++++------ builder/virtualbox/ovf/builder.go | 6 +- builder/virtualbox/vm/builder.go | 6 +- builder/vmware/iso/builder.go | 6 +- builder/vmware/iso/builder_test.go | 60 ++++++++-------- builder/vmware/vmx/builder.go | 6 +- builder/vmware/vmx/builder_test.go | 6 +- builder/yandex/builder.go | 6 +- command/build_parallel_test.go | 6 +- common/shell-local/config.go | 8 +-- helper/config/decode.go | 42 +++++++++++- packer/build.go | 18 ++++- packer/build_test.go | 4 +- packer/builder.go | 7 +- packer/builder_mock.go | 4 +- packer/plugin/builder.go | 2 +- packer/provisioner.go | 44 ++++++------ packer/rpc/builder.go | 18 ++--- packer/rpc/builder_test.go | 4 +- .../checksum/post-processor_test.go | 2 +- .../compress/post-processor_test.go | 2 +- provisioner/ansible-local/provisioner_test.go | 2 +- provisioner/ansible/provisioner.go | 3 - template/interpolate/funcs.go | 21 +++--- 93 files changed, 592 insertions(+), 533 deletions(-) diff --git a/builder/alicloud/ecs/builder.go b/builder/alicloud/ecs/builder.go index 5b593cee9..44eab7fbd 100644 --- a/builder/alicloud/ecs/builder.go +++ b/builder/alicloud/ecs/builder.go @@ -41,7 +41,7 @@ const ( ALICLOUD_DEFAULT_LONG_TIMEOUT = 3600 ) -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -53,7 +53,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, raws...) b.config.ctx.EnableEnv = true if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -68,11 +68,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.AlicloudAccessKey, b.config.AlicloudSecretKey) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/alicloud/ecs/builder_test.go b/builder/alicloud/ecs/builder_test.go index e67861f5d..9a691de77 100644 --- a/builder/alicloud/ecs/builder_test.go +++ b/builder/alicloud/ecs/builder_test.go @@ -35,7 +35,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "access_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -50,7 +50,7 @@ func TestBuilderPrepare_ECSImageName(t *testing.T) { // Test good config["image_name"] = "ecs.n1.tiny" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -61,7 +61,7 @@ func TestBuilderPrepare_ECSImageName(t *testing.T) { // Test bad config["ecs_image_name"] = "foo {{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -72,7 +72,7 @@ func TestBuilderPrepare_ECSImageName(t *testing.T) { // Test bad delete(config, "image_name") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -87,7 +87,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -120,7 +120,7 @@ func TestBuilderPrepare_Devices(t *testing.T) { "disk_device": "/dev/xvdc", }, } - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -160,7 +160,7 @@ func TestBuilderPrepare_IgnoreDataDisks(t *testing.T) { var b Builder config := testBuilderConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -173,7 +173,7 @@ func TestBuilderPrepare_IgnoreDataDisks(t *testing.T) { } config["image_ignore_data_disks"] = "false" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -186,7 +186,7 @@ func TestBuilderPrepare_IgnoreDataDisks(t *testing.T) { } config["image_ignore_data_disks"] = "true" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -203,7 +203,7 @@ func TestBuilderPrepare_WaitSnapshotReadyTimeout(t *testing.T) { var b Builder config := testBuilderConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -219,7 +219,7 @@ func TestBuilderPrepare_WaitSnapshotReadyTimeout(t *testing.T) { } config["wait_snapshot_ready_timeout"] = ALICLOUD_DEFAULT_TIMEOUT - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index f1cf7432a..2a0331265 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -182,7 +182,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -201,7 +201,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } if b.config.Architecture == "" { @@ -319,11 +319,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return warns, nil + return nil, warns, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/amazon/chroot/builder_test.go b/builder/amazon/chroot/builder_test.go index e6999bff6..b181b878f 100644 --- a/builder/amazon/chroot/builder_test.go +++ b/builder/amazon/chroot/builder_test.go @@ -31,7 +31,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test good config["ami_name"] = "foo" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -42,7 +42,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test bad config["ami_name"] = "foo {{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -53,7 +53,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test bad delete(config, "ami_name") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -67,7 +67,7 @@ func TestBuilderPrepare_ChrootMounts(t *testing.T) { config := testConfig() config["chroot_mounts"] = nil - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -83,7 +83,7 @@ func TestBuilderPrepare_ChrootMountsBadDefaults(t *testing.T) { config["chroot_mounts"] = [][]string{ {"bad"}, } - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -96,7 +96,7 @@ func TestBuilderPrepare_SourceAmi(t *testing.T) { config := testConfig() config["source_ami"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -105,7 +105,7 @@ func TestBuilderPrepare_SourceAmi(t *testing.T) { } config["source_ami"] = "foo" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -119,7 +119,7 @@ func TestBuilderPrepare_CommandWrapper(t *testing.T) { config := testConfig() config["command_wrapper"] = "echo hi; {{.Command}}" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -132,7 +132,7 @@ func TestBuilderPrepare_CopyFiles(t *testing.T) { b := &Builder{} config := testConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -150,7 +150,7 @@ func TestBuilderPrepare_CopyFilesNoDefault(t *testing.T) { config := testConfig() config["copy_files"] = []string{} - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -171,7 +171,7 @@ func TestBuilderPrepare_RootDeviceNameAndAMIMappings(t *testing.T) { config["root_device_name"] = "/dev/sda" config["ami_block_device_mappings"] = []interface{}{map[string]string{}} config["root_volume_size"] = 15 - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) == 0 { t.Fatal("Missing warning, stating block device mappings will be overwritten") } else if len(warnings) > 1 { @@ -187,7 +187,7 @@ func TestBuilderPrepare_AMIMappingsNoRootDeviceName(t *testing.T) { config := testConfig() config["ami_block_device_mappings"] = []interface{}{map[string]string{}} - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -201,7 +201,7 @@ func TestBuilderPrepare_RootDeviceNameNoAMIMappings(t *testing.T) { config := testConfig() config["root_device_name"] = "/dev/sda" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index f8648e204..08d93119c 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -72,7 +72,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -89,7 +89,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -123,11 +123,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return warns, nil + return nil, warns, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/amazon/ebs/builder_test.go b/builder/amazon/ebs/builder_test.go index a8200b5a8..4ea816130 100644 --- a/builder/amazon/ebs/builder_test.go +++ b/builder/amazon/ebs/builder_test.go @@ -32,7 +32,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "access_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -48,7 +48,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test good config["ami_name"] = "foo" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -59,7 +59,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test bad config["ami_name"] = "foo {{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -70,7 +70,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test bad delete(config, "ami_name") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -85,7 +85,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -101,7 +101,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "terminate" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -111,7 +111,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "stop" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -121,7 +121,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test bad config["shutdown_behavior"] = "foobar" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index abf159fad..835d86cdc 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -70,7 +70,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -87,7 +87,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -145,12 +145,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, errors.New(`The only valid ami_architecture values are "x86_64" and "arm64"`)) } if errs != nil && len(errs.Errors) > 0 { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return warns, nil + return nil, warns, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/amazon/ebssurrogate/builder_test.go b/builder/amazon/ebssurrogate/builder_test.go index 94fdacd6b..37490bc61 100644 --- a/builder/amazon/ebssurrogate/builder_test.go +++ b/builder/amazon/ebssurrogate/builder_test.go @@ -31,7 +31,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "access_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index b3d05299d..fc9305983 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -79,7 +79,7 @@ type EngineVarsTemplate struct { SourceAMI string } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs // Create passthrough for {{ .BuildRegion }} and {{ .SourceAMI }} variables // so we can fill them in later @@ -92,7 +92,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { InterpolateContext: &b.config.ctx, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors @@ -129,11 +129,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return warns, nil + return nil, warns, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/amazon/ebsvolume/builder_test.go b/builder/amazon/ebsvolume/builder_test.go index 85c8ae833..0d8b27ac1 100644 --- a/builder/amazon/ebsvolume/builder_test.go +++ b/builder/amazon/ebsvolume/builder_test.go @@ -31,7 +31,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "access_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -62,7 +62,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "terminate" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -72,7 +72,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "stop" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -82,7 +82,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test bad config["shutdown_behavior"] = "foobar" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 92a761459..79a688ec4 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -93,7 +93,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { configs := make([]interface{}, len(raws)+1) configs[0] = map[string]interface{}{ "bundle_prefix": "image-{{timestamp}}", @@ -118,7 +118,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, configs...) if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -218,10 +218,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return warns, nil + return nil, warns, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/amazon/instance/builder_test.go b/builder/amazon/instance/builder_test.go index 0c2a05145..21d4d4030 100644 --- a/builder/amazon/instance/builder_test.go +++ b/builder/amazon/instance/builder_test.go @@ -47,7 +47,7 @@ func TestBuilderPrepare_AccountId(t *testing.T) { defer tempfile.Close() config["account_id"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -56,7 +56,7 @@ func TestBuilderPrepare_AccountId(t *testing.T) { } config["account_id"] = "foo" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -65,7 +65,7 @@ func TestBuilderPrepare_AccountId(t *testing.T) { } config["account_id"] = "0123-0456-7890" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -87,7 +87,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test good config["ami_name"] = "foo" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -98,7 +98,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test bad config["ami_name"] = "foo {{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -109,7 +109,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) { // Test bad delete(config, "ami_name") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -126,7 +126,7 @@ func TestBuilderPrepare_BundleDestination(t *testing.T) { defer tempfile.Close() config["bundle_destination"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -146,7 +146,7 @@ func TestBuilderPrepare_BundlePrefix(t *testing.T) { defer os.Remove(tempfile.Name()) defer tempfile.Close() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -167,7 +167,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -184,7 +184,7 @@ func TestBuilderPrepare_S3Bucket(t *testing.T) { defer tempfile.Close() config["s3_bucket"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -193,7 +193,7 @@ func TestBuilderPrepare_S3Bucket(t *testing.T) { } config["s3_bucket"] = "foo" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -210,7 +210,7 @@ func TestBuilderPrepare_X509CertPath(t *testing.T) { defer tempfile.Close() config["x509_cert_path"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -219,7 +219,7 @@ func TestBuilderPrepare_X509CertPath(t *testing.T) { } config["x509_cert_path"] = "i/am/a/file/that/doesnt/exist" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -235,7 +235,7 @@ func TestBuilderPrepare_X509CertPath(t *testing.T) { defer tf.Close() config["x509_cert_path"] = tf.Name() - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -252,7 +252,7 @@ func TestBuilderPrepare_X509KeyPath(t *testing.T) { defer tempfile.Close() config["x509_key_path"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -261,7 +261,7 @@ func TestBuilderPrepare_X509KeyPath(t *testing.T) { } config["x509_key_path"] = "i/am/a/file/that/doesnt/exist" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -277,7 +277,7 @@ func TestBuilderPrepare_X509KeyPath(t *testing.T) { defer tf.Close() config["x509_key_path"] = tf.Name() - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -294,7 +294,7 @@ func TestBuilderPrepare_X509UploadPath(t *testing.T) { defer tempfile.Close() config["x509_upload_path"] = "" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 0a087c6fd..f1e6259c9 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -34,10 +34,10 @@ const ( DefaultSecretName = "packerKeyVaultSecret" ) -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := newConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c @@ -47,7 +47,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.setTemplateParameters(b.stateBag) b.setImageParameters(b.stateBag) - return warnings, errs + return nil, warnings, errs } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go index a71f92874..e9b781259 100644 --- a/builder/azure/arm/builder_test.go +++ b/builder/azure/arm/builder_test.go @@ -8,7 +8,7 @@ import ( func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { var testSubject = &Builder{} - _, err := testSubject.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + _, _, err := testSubject.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if err != nil { t.Fatalf("failed to prepare: %s", err) } diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 91ceca991..c5fbefc9d 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -116,7 +116,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = azcommon.TemplateFuncs b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc() err := config.Decode(&b.config, &config.DecodeOpts{ @@ -134,7 +134,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } var errs *packer.MultiError @@ -143,7 +143,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Defaults err = b.config.ClientConfig.SetDefaultValues() if err != nil { - return nil, err + return nil, nil, err } if b.config.ChrootMounts == nil { @@ -254,11 +254,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.ClientConfig.ClientSecret, b.config.ClientConfig.ClientJWT) - return warns, nil + return nil, warns, nil } func checkDiskCacheType(s string) interface{} { diff --git a/builder/azure/chroot/builder_test.go b/builder/azure/chroot/builder_test.go index dd40f7737..f84dd9ccc 100644 --- a/builder/azure/chroot/builder_test.go +++ b/builder/azure/chroot/builder_test.go @@ -55,7 +55,7 @@ func TestBuilder_Prepare(t *testing.T) { t.Run(tt.name, func(t *testing.T) { b := &Builder{} - _, err := b.Prepare(tt.config) + _, _, err := b.Prepare(tt.config) if (err != nil) != tt.wantErr { t.Errorf("Builder.Prepare() error = %v, wantErr %v", err, tt.wantErr) diff --git a/builder/cloudstack/builder.go b/builder/cloudstack/builder.go index dc1dace69..138943956 100644 --- a/builder/cloudstack/builder.go +++ b/builder/cloudstack/builder.go @@ -21,14 +21,14 @@ type Builder struct { } // Prepare implements the packer.Builder interface. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { config, errs := NewConfig(raws...) if errs != nil { - return nil, errs + return nil, nil, errs } b.config = config - return nil, nil + return nil, nil, nil } // Run implements the packer.Builder interface. diff --git a/builder/cloudstack/builder_test.go b/builder/cloudstack/builder_test.go index 6d7ab1444..0b61dfd20 100644 --- a/builder/cloudstack/builder_test.go +++ b/builder/cloudstack/builder_test.go @@ -43,7 +43,7 @@ func TestBuilder_Prepare(t *testing.T) { b := &Builder{} for desc, tc := range cases { - _, errs := b.Prepare(tc.Config) + _, _, errs := b.Prepare(tc.Config) if tc.Err { if errs == nil { diff --git a/builder/digitalocean/builder.go b/builder/digitalocean/builder.go index 661e2c2a4..baaeffce1 100644 --- a/builder/digitalocean/builder.go +++ b/builder/digitalocean/builder.go @@ -25,14 +25,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = *c - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/digitalocean/builder_test.go b/builder/digitalocean/builder_test.go index 70ec6fea9..5113495cf 100644 --- a/builder/digitalocean/builder_test.go +++ b/builder/digitalocean/builder_test.go @@ -32,7 +32,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "api_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -47,7 +47,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -62,7 +62,7 @@ func TestBuilderPrepare_Region(t *testing.T) { // Test default delete(config, "region") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -75,7 +75,7 @@ func TestBuilderPrepare_Region(t *testing.T) { // Test set config["region"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -94,7 +94,7 @@ func TestBuilderPrepare_Size(t *testing.T) { // Test default delete(config, "size") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -107,7 +107,7 @@ func TestBuilderPrepare_Size(t *testing.T) { // Test set config["size"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -126,7 +126,7 @@ func TestBuilderPrepare_Image(t *testing.T) { // Test default delete(config, "image") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -139,7 +139,7 @@ func TestBuilderPrepare_Image(t *testing.T) { // Test set config["image"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -157,7 +157,7 @@ func TestBuilderPrepare_StateTimeout(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -172,7 +172,7 @@ func TestBuilderPrepare_StateTimeout(t *testing.T) { // Test set config["state_timeout"] = "5m" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -183,7 +183,7 @@ func TestBuilderPrepare_StateTimeout(t *testing.T) { // Test bad config["state_timeout"] = "tubes" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -197,7 +197,7 @@ func TestBuilderPrepare_SnapshotTimeout(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -212,7 +212,7 @@ func TestBuilderPrepare_SnapshotTimeout(t *testing.T) { // Test set config["snapshot_timeout"] = "15m" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -223,7 +223,7 @@ func TestBuilderPrepare_SnapshotTimeout(t *testing.T) { // Test bad config["snapshot_timeout"] = "badstring" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -237,7 +237,7 @@ func TestBuilderPrepare_PrivateNetworking(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -252,7 +252,7 @@ func TestBuilderPrepare_PrivateNetworking(t *testing.T) { // Test set config["private_networking"] = true b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -270,7 +270,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -285,7 +285,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) { // Test set config["snapshot_name"] = "foobarbaz" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -296,7 +296,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) { // Test set with template config["snapshot_name"] = "{{timestamp}}" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -316,7 +316,7 @@ func TestBuilderPrepare_DropletName(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -331,7 +331,7 @@ func TestBuilderPrepare_DropletName(t *testing.T) { // Test normal set config["droplet_name"] = "foobar" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -342,7 +342,7 @@ func TestBuilderPrepare_DropletName(t *testing.T) { // Test with template config["droplet_name"] = "foobar-{{timestamp}}" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -353,7 +353,7 @@ func TestBuilderPrepare_DropletName(t *testing.T) { // Test with bad template config["droplet_name"] = "foobar-{{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/docker/builder.go b/builder/docker/builder.go index 63cb8f55d..f8b2bbf96 100644 --- a/builder/docker/builder.go +++ b/builder/docker/builder.go @@ -20,14 +20,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/docker/communicator_test.go b/builder/docker/communicator_test.go index b028d367a..afaa5e60f 100644 --- a/builder/docker/communicator_test.go +++ b/builder/docker/communicator_test.go @@ -35,7 +35,7 @@ func TestUploadDownload(t *testing.T) { // Setup the builder builder := &Builder{} - warnings, err := builder.Prepare(tpl.Builders["docker"].Config) + _, warnings, err := builder.Prepare(tpl.Builders["docker"].Config) if err != nil { t.Fatalf("Error preparing configuration %s", err) } @@ -118,7 +118,7 @@ func TestLargeDownload(t *testing.T) { // Setup the builder builder := &Builder{} - warnings, err := builder.Prepare(tpl.Builders["docker"].Config) + _, warnings, err := builder.Prepare(tpl.Builders["docker"].Config) if err != nil { t.Fatalf("Error preparing configuration %s", err) } @@ -222,7 +222,7 @@ func TestFixUploadOwner(t *testing.T) { // Setup the builder builder := &Builder{} - warnings, err := builder.Prepare(tpl.Builders["docker"].Config) + _, warnings, err := builder.Prepare(tpl.Builders["docker"].Config) if err != nil { t.Fatalf("Error preparing configuration %s", err) } diff --git a/builder/file/builder.go b/builder/file/builder.go index 0defa6049..a318e87eb 100644 --- a/builder/file/builder.go +++ b/builder/file/builder.go @@ -23,14 +23,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run is where the actual build should take place. It takes a Build and a Ui. diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index 2a30d39c5..803970972 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -23,13 +23,13 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run executes a googlecompute Packer build and returns a packer.Artifact diff --git a/builder/hcloud/builder.go b/builder/hcloud/builder.go index 92f9f474b..3514d74ba 100644 --- a/builder/hcloud/builder.go +++ b/builder/hcloud/builder.go @@ -22,13 +22,13 @@ type Builder struct { var pluginVersion = "1.0.0" -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { config, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = *config - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/hyperone/builder.go b/builder/hyperone/builder.go index d9e85e5f0..fcaf50490 100644 --- a/builder/hyperone/builder.go +++ b/builder/hyperone/builder.go @@ -20,10 +20,10 @@ type Builder struct { client *openapi.APIClient } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { config, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = *config @@ -43,7 +43,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.client = openapi.NewAPIClient(cfg) - return nil, nil + return nil, nil, nil } type wrappedCommandTemplate struct { diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 1b40fac84..bde46d478 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -85,7 +85,7 @@ type Config struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -96,7 +96,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors and warnings @@ -164,10 +164,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warnings, errs + return nil, warnings, errs } - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/hyperv/iso/builder_test.go b/builder/hyperv/iso/builder_test.go index 640221de2..b3c4736f3 100644 --- a/builder/hyperv/iso/builder_test.go +++ b/builder/hyperv/iso/builder_test.go @@ -42,7 +42,7 @@ func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder config := testConfig() - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -60,7 +60,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config := testConfig() delete(config, "disk_size") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -74,7 +74,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config["disk_size"] = 256 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -96,7 +96,7 @@ func TestBuilderPrepare_DiskBlockSize(t *testing.T) { // Test default with empty disk_block_size delete(config, "disk_block_size") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -112,7 +112,7 @@ func TestBuilderPrepare_DiskBlockSize(t *testing.T) { for _, test_size := range test_sizes { config["disk_block_size"] = test_size b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if test_size > expected_max_block_size || test_size < expected_min_block_size { if len(warns) > 0 { t.Fatalf("bad, should have no warns: %#v", warns) @@ -153,7 +153,7 @@ func TestBuilderPrepare_FixedVHDFormat(t *testing.T) { // use_fixed_vhd_format should work with generation = 1, skip_compaction // = true, and differencing_disk = false - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -164,7 +164,7 @@ func TestBuilderPrepare_FixedVHDFormat(t *testing.T) { //use_fixed_vhd_format should not work with differencing_disk = true config["differencing_disk"] = true b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -176,7 +176,7 @@ func TestBuilderPrepare_FixedVHDFormat(t *testing.T) { //use_fixed_vhd_format should not work with skip_compaction = false config["skip_compaction"] = false b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -188,7 +188,7 @@ func TestBuilderPrepare_FixedVHDFormat(t *testing.T) { //use_fixed_vhd_format should not work with generation = 2 config["generation"] = 2 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -202,7 +202,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config := testConfig() delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -217,7 +217,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppiesPath := "../../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppiesPath), fmt.Sprintf("%s/foo.ps1", floppiesPath)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -236,7 +236,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig() config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -252,7 +252,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -267,7 +267,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) { // Test bad config["iso_checksum"] = "" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -278,7 +278,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) { // Test good config["iso_checksum"] = "FOo" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -294,7 +294,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test bad config["iso_checksum_type"] = "" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -305,7 +305,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test good config["iso_checksum_type"] = "mD5" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -320,7 +320,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test unknown config["iso_checksum_type"] = "fake" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -331,7 +331,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test none config["iso_checksum_type"] = "none" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) == 0 { t.Fatalf("bad: %#v", warns) } @@ -353,7 +353,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { // Test both empty config["iso_url"] = "" b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -364,7 +364,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { // Test iso_url set config["iso_url"] = "http://www.packer.io" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -381,7 +381,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { config["iso_url"] = "http://www.packer.io" config["iso_urls"] = []string{"http://www.packer.io"} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -397,7 +397,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { } b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -431,7 +431,7 @@ func TestBuilderPrepare_SizeNotRequiredWhenUsingExistingHarddrive(t *testing.T) } b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -455,7 +455,7 @@ func TestBuilderPrepare_SizeNotRequiredWhenUsingExistingHarddrive(t *testing.T) } b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -489,7 +489,7 @@ func TestBuilderPrepare_SizeIsRequiredWhenNotUsingExistingHarddrive(t *testing.T } b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -517,7 +517,7 @@ func TestBuilderPrepare_MaximumOfSixtyFourAdditionalDisks(t *testing.T) { config["disk_additional_size"] = disks b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -537,7 +537,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config["winrm_host"] = "1.2.3.4" var b Builder - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -565,7 +565,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config["ssh_host"] = "1.2.3.4" var b Builder - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -593,7 +593,7 @@ func TestUserVariablesInBootCommand(t *testing.T) { config[packer.UserVariablesConfigKey] = map[string]string{"test-variable": "test"} config["boot_command"] = []string{"blah {{user `test-variable`}} blah"} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -634,7 +634,7 @@ func TestBuilderPrepare_UseLegacyNetworkAdapter(t *testing.T) { config["use_legacy_network_adapter"] = true b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -646,7 +646,7 @@ func TestBuilderPrepare_UseLegacyNetworkAdapter(t *testing.T) { config["generation"] = 2 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index af93007e5..17baf0b75 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -78,7 +78,7 @@ type Config struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -89,7 +89,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors and warnings @@ -204,10 +204,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warnings, errs + return nil, warnings, errs } - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/hyperv/vmcx/builder_test.go b/builder/hyperv/vmcx/builder_test.go index 9f268aab4..2fa7ba3cf 100644 --- a/builder/hyperv/vmcx/builder_test.go +++ b/builder/hyperv/vmcx/builder_test.go @@ -49,7 +49,7 @@ func TestBuilderPrepare_Defaults(t *testing.T) { defer os.RemoveAll(td) config["clone_from_vmcx_path"] = td - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -76,7 +76,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -90,7 +90,7 @@ func TestBuilderPrepare_CloneFromExistingMachineOrImportFromExportedMachineSetti config := testConfig() delete(config, "clone_from_vmcx_path") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -114,7 +114,7 @@ func TestBuilderPrepare_ExportedMachinePathDoesNotExist(t *testing.T) { config["clone_from_vmcx_path"] = td - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -138,7 +138,7 @@ func TestBuilderPrepare_ExportedMachinePathExists(t *testing.T) { config["clone_from_vmcx_path"] = td - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -154,7 +154,7 @@ func disabled_TestBuilderPrepare_CloneFromVmSettingUsedSoNoCloneFromVmcxPathRequ config["clone_from_vm_name"] = "test_machine_name_that_does_not_exist" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -184,7 +184,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) { // Test bad config["iso_checksum"] = "" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -195,7 +195,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) { // Test good config["iso_checksum"] = "FOo" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -219,7 +219,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test bad config["iso_checksum_type"] = "" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -230,7 +230,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test good config["iso_checksum_type"] = "mD5" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -245,7 +245,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test none config["iso_checksum_type"] = "none" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) == 0 { t.Fatalf("bad: %#v", warns) } @@ -276,7 +276,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { // Test both empty (should be allowed, as we cloning a vm so we probably don't need an ISO file) config["iso_url"] = "" b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -287,7 +287,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { // Test iso_url set config["iso_url"] = "http://www.packer.io" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -304,7 +304,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { config["iso_url"] = "http://www.packer.io" config["iso_urls"] = []string{"http://www.packer.io"} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -320,7 +320,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { } b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -350,7 +350,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config["clone_from_vmcx_path"] = td delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -365,7 +365,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppies_path := "../../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -393,7 +393,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -422,7 +422,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config["winrm_host"] = "1.2.3.4" var b Builder - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -459,7 +459,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config["ssh_host"] = "1.2.3.4" var b Builder - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -494,7 +494,7 @@ func TestUserVariablesInBootCommand(t *testing.T) { config[packer.UserVariablesConfigKey] = map[string]string{"test-variable": "test"} config["boot_command"] = []string{"blah {{user `test-variable`}} blah"} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/jdcloud/builder.go b/builder/jdcloud/builder.go index 4b3700f11..4f6a4c0ba 100644 --- a/builder/jdcloud/builder.go +++ b/builder/jdcloud/builder.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/packer/template/interpolate" ) -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -22,19 +22,19 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, fmt.Errorf("[ERROR] Failed in decoding JSON->mapstructure") + return nil, nil, fmt.Errorf("[ERROR] Failed in decoding JSON->mapstructure") } errs := &packer.MultiError{} errs = packer.MultiErrorAppend(errs, b.config.JDCloudCredentialConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.JDCloudInstanceSpecConfig.Prepare(&b.config.ctx)...) if errs != nil && len(errs.Errors) != 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/linode/builder.go b/builder/linode/builder.go index 3bc3dfb3f..ce6850f72 100644 --- a/builder/linode/builder.go +++ b/builder/linode/builder.go @@ -25,13 +25,13 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (ret packer.Artifact, err error) { diff --git a/builder/linode/builder_test.go b/builder/linode/builder_test.go index c80d1c76c..8176114a4 100644 --- a/builder/linode/builder_test.go +++ b/builder/linode/builder_test.go @@ -31,7 +31,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "linode_token": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -61,7 +61,7 @@ func TestBuilderPrepare_Region(t *testing.T) { // Test default delete(config, "region") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -74,7 +74,7 @@ func TestBuilderPrepare_Region(t *testing.T) { // Test set config["region"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -93,7 +93,7 @@ func TestBuilderPrepare_Size(t *testing.T) { // Test default delete(config, "instance_type") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -106,7 +106,7 @@ func TestBuilderPrepare_Size(t *testing.T) { // Test set config["instance_type"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -125,7 +125,7 @@ func TestBuilderPrepare_Image(t *testing.T) { // Test default delete(config, "image") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -138,7 +138,7 @@ func TestBuilderPrepare_Image(t *testing.T) { // Test set config["image"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -156,7 +156,7 @@ func TestBuilderPrepare_ImageLabel(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -171,7 +171,7 @@ func TestBuilderPrepare_ImageLabel(t *testing.T) { // Test set config["image_label"] = "foobarbaz" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -182,7 +182,7 @@ func TestBuilderPrepare_ImageLabel(t *testing.T) { // Test set with template config["image_label"] = "{{timestamp}}" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -202,7 +202,7 @@ func TestBuilderPrepare_Label(t *testing.T) { config := testConfig() // Test default - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -217,7 +217,7 @@ func TestBuilderPrepare_Label(t *testing.T) { // Test normal set config["instance_label"] = "foobar" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -228,7 +228,7 @@ func TestBuilderPrepare_Label(t *testing.T) { // Test with template config["instance_label"] = "foobar-{{timestamp}}" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -239,7 +239,7 @@ func TestBuilderPrepare_Label(t *testing.T) { // Test with bad template config["instance_label"] = "foobar-{{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/lxc/builder.go b/builder/lxc/builder.go index 5c8747c7e..f5dfd4cbb 100644 --- a/builder/lxc/builder.go +++ b/builder/lxc/builder.go @@ -23,14 +23,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, errs := NewConfig(raws...) if errs != nil { - return nil, errs + return nil, nil, errs } b.config = c - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/lxc/builder_test.go b/builder/lxc/builder_test.go index 4eeb27594..20108202d 100644 --- a/builder/lxc/builder_test.go +++ b/builder/lxc/builder_test.go @@ -25,7 +25,7 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { var b Builder // Good config := testConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -37,7 +37,7 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { config = testConfig() delete(config, "config_file") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index ce1656217..a4a1d4863 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -21,14 +21,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, errs := NewConfig(raws...) if errs != nil { - return nil, errs + return nil, nil, errs } b.config = c - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/lxd/builder_test.go b/builder/lxd/builder_test.go index b2ea70e55..a2277932c 100644 --- a/builder/lxd/builder_test.go +++ b/builder/lxd/builder_test.go @@ -24,7 +24,7 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { var b Builder // Good config := testConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -35,7 +35,7 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { // Good, remote image config = testConfig() config["image"] = "remote:bar" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { // Good, remote output image config = testConfig() config["output_image"] = "remote:foo" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -58,7 +58,7 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { config = testConfig() delete(config, "image") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/ncloud/builder.go b/builder/ncloud/builder.go index 36702c933..1205573fd 100644 --- a/builder/ncloud/builder.go +++ b/builder/ncloud/builder.go @@ -17,16 +17,16 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c b.stateBag = new(multistep.BasicStateBag) - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/null/builder.go b/builder/null/builder.go index 5939cd024..5bba58bc2 100644 --- a/builder/null/builder.go +++ b/builder/null/builder.go @@ -16,14 +16,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/oneandone/builder.go b/builder/oneandone/builder.go index 5ea7674bc..785d9fdbd 100644 --- a/builder/oneandone/builder.go +++ b/builder/oneandone/builder.go @@ -18,14 +18,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/oneandone/builder_test.go b/builder/oneandone/builder_test.go index daa8536e3..46045bf9d 100644 --- a/builder/oneandone/builder_test.go +++ b/builder/oneandone/builder_test.go @@ -30,7 +30,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "api_key": []string{}, } - warns, err := b.Prepare(c) + _, warns, err := b.Prepare(c) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { config := testConfig() config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index 8411f803d..d8a3ae66f 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -35,13 +35,13 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors @@ -51,11 +51,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } if b.config.ImageConfig.ImageDiskFormat != "" && !b.config.RunConfig.UseBlockStorageVolume { - return nil, fmt.Errorf("use_blockstorage_volume must be true if image_disk_format is specified.") + return nil, nil, fmt.Errorf("use_blockstorage_volume must be true if image_disk_format is specified.") } // By default, instance name is same as image name @@ -64,7 +64,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } packer.LogSecretFilter.Set(b.config.Password) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/openstack/builder_test.go b/builder/openstack/builder_test.go index 5fa296d30..0a9ac3288 100644 --- a/builder/openstack/builder_test.go +++ b/builder/openstack/builder_test.go @@ -20,7 +20,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "password": []string{}, } - warns, err := b.Prepare(c) + _, warns, err := b.Prepare(c) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index d97b3be10..0905e4e84 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -24,10 +24,10 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { +func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, []string, error) { config, err := NewConfig(rawConfig...) if err != nil { - return nil, err + return nil, nil, err } b.config = config @@ -36,9 +36,9 @@ func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.PVConfig.Prepare(&b.config.ctx)) if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/oracle/oci/builder.go b/builder/oracle/oci/builder.go index a4b31b8e6..e54c4000b 100644 --- a/builder/oracle/oci/builder.go +++ b/builder/oracle/oci/builder.go @@ -26,14 +26,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { +func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, []string, error) { config, err := NewConfig(rawConfig...) if err != nil { - return nil, err + return nil, nil, err } b.config = config - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/osc/bsu/builder.go b/builder/osc/bsu/builder.go index ab4280417..e96011190 100644 --- a/builder/osc/bsu/builder.go +++ b/builder/osc/bsu/builder.go @@ -42,7 +42,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -59,7 +59,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -75,11 +75,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/osc/bsu/builder_test.go b/builder/osc/bsu/builder_test.go index 1736044df..c700ccc06 100644 --- a/builder/osc/bsu/builder_test.go +++ b/builder/osc/bsu/builder_test.go @@ -32,7 +32,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "access_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -48,7 +48,7 @@ func TestBuilderPrepare_OMIName(t *testing.T) { // Test good config["omi_name"] = "foo" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -59,7 +59,7 @@ func TestBuilderPrepare_OMIName(t *testing.T) { // Test bad config["omi_name"] = "foo {{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -70,7 +70,7 @@ func TestBuilderPrepare_OMIName(t *testing.T) { // Test bad delete(config, "omi_name") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -85,7 +85,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -101,7 +101,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "terminate" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -111,7 +111,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "stop" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -121,7 +121,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test bad config["shutdown_behavior"] = "foobar" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index 1186a5bdc..ed8b7cdf2 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -39,7 +39,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs @@ -58,7 +58,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -90,11 +90,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return nil, nil + return nil, nil, nil } diff --git a/builder/osc/bsuvolume/builder.go b/builder/osc/bsuvolume/builder.go index e0b5a8fa7..7ab76aa88 100644 --- a/builder/osc/bsuvolume/builder.go +++ b/builder/osc/bsuvolume/builder.go @@ -44,7 +44,7 @@ type EngineVarsTemplate struct { SourceOMI string } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs // Create passthrough for {{ .BuildRegion }} and {{ .SourceOMI }} variables // so we can fill them in later @@ -57,7 +57,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { InterpolateContext: &b.config.ctx, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors @@ -78,11 +78,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/osc/bsuvolume/builder_test.go b/builder/osc/bsuvolume/builder_test.go index 956fba60f..27c4a80c0 100644 --- a/builder/osc/bsuvolume/builder_test.go +++ b/builder/osc/bsuvolume/builder_test.go @@ -31,7 +31,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "access_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -62,7 +62,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "terminate" config["skip_region_validation"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -72,7 +72,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test good config["shutdown_behavior"] = "stop" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -82,7 +82,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) { // Test bad config["shutdown_behavior"] = "foobar" - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/osc/chroot/builder.go b/builder/osc/chroot/builder.go index e6813d263..ac506aa24 100644 --- a/builder/osc/chroot/builder.go +++ b/builder/osc/chroot/builder.go @@ -63,7 +63,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -82,7 +82,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } if b.config.PackerConfig.PackerForce { @@ -178,11 +178,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warns, errs + return nil, warns, errs } packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) - return warns, nil + return nil, warns, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/parallels/iso/builder.go b/builder/parallels/iso/builder.go index b7ea4788f..8764ad36a 100644 --- a/builder/parallels/iso/builder.go +++ b/builder/parallels/iso/builder.go @@ -82,7 +82,7 @@ type Config struct { ctx interpolate.Context } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -96,7 +96,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors and warnings @@ -169,10 +169,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warnings, errs + return nil, warnings, errs } - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/parallels/iso/builder_test.go b/builder/parallels/iso/builder_test.go index 88a28144e..e131bc137 100644 --- a/builder/parallels/iso/builder_test.go +++ b/builder/parallels/iso/builder_test.go @@ -32,7 +32,7 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder config := testConfig() - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -54,7 +54,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config := testConfig() delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -69,7 +69,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppies_path := "../../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -88,7 +88,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig() config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -103,7 +103,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config := testConfig() delete(config, "disk_size") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -117,7 +117,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config["disk_size"] = 60000 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -136,7 +136,7 @@ func TestBuilderPrepare_DiskType(t *testing.T) { // Test a default disk_type delete(config, "disk_type") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -151,7 +151,7 @@ func TestBuilderPrepare_DiskType(t *testing.T) { // Test with a bad config["disk_type"] = "fake" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -163,7 +163,7 @@ func TestBuilderPrepare_DiskType(t *testing.T) { config["disk_type"] = "plain" config["skip_compaction"] = false b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) == 0 { t.Fatalf("should have warning") } @@ -175,7 +175,7 @@ func TestBuilderPrepare_DiskType(t *testing.T) { config["disk_type"] = "plain" config["skip_compaction"] = true b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -191,7 +191,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) { // Test a default boot_wait delete(config, "hard_drive_interface") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -206,7 +206,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) { // Test with a bad config["hard_drive_interface"] = "fake" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -217,7 +217,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) { // Test with a good config["hard_drive_interface"] = "scsi" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -232,7 +232,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/parallels/pvm/builder.go b/builder/parallels/pvm/builder.go index a199650d1..10296f4cc 100644 --- a/builder/parallels/pvm/builder.go +++ b/builder/parallels/pvm/builder.go @@ -20,14 +20,14 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/profitbricks/builder.go b/builder/profitbricks/builder.go index 3ab9d18fe..01bfeb1f5 100644 --- a/builder/profitbricks/builder.go +++ b/builder/profitbricks/builder.go @@ -17,14 +17,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/profitbricks/builder_test.go b/builder/profitbricks/builder_test.go index 98252b410..f16953a33 100644 --- a/builder/profitbricks/builder_test.go +++ b/builder/profitbricks/builder_test.go @@ -31,7 +31,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "api_key": []string{}, } - warns, err := b.Prepare(c) + _, warns, err := b.Prepare(c) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -47,7 +47,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { config := testConfig() config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/proxmox/builder.go b/builder/proxmox/builder.go index 083cfeb3d..59038f6c2 100644 --- a/builder/proxmox/builder.go +++ b/builder/proxmox/builder.go @@ -27,13 +27,13 @@ var _ packer.Builder = &Builder{} var pluginVersion = "1.0.0" -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { config, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = *config - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/proxmox/config_test.go b/builder/proxmox/config_test.go index 84dd13c6d..6733eda17 100644 --- a/builder/proxmox/config_test.go +++ b/builder/proxmox/config_test.go @@ -81,7 +81,7 @@ func TestBasicExampleFromDocsIsValid(t *testing.T) { } b := &Builder{} - warn, err := b.Prepare(tpl.Builders["proxmox"].Config) + _, warn, err := b.Prepare(tpl.Builders["proxmox"].Config) if err != nil { t.Fatal(err, warn) } @@ -149,7 +149,7 @@ func TestAgentSetToFalse(t *testing.T) { } b := &Builder{} - warn, err := b.Prepare(tpl.Builders["proxmox"].Config) + _, warn, err := b.Prepare(tpl.Builders["proxmox"].Config) if err != nil { t.Fatal(err, warn) } diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 6d85d2654..a8093f2fe 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -336,7 +336,7 @@ type Config struct { ctx interpolate.Context } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -348,7 +348,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } var errs *packer.MultiError @@ -569,10 +569,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warnings, errs + return nil, warnings, errs } - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/qemu/builder_test.go b/builder/qemu/builder_test.go index 4616ed3e2..4eea7174e 100644 --- a/builder/qemu/builder_test.go +++ b/builder/qemu/builder_test.go @@ -62,7 +62,7 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder config := testConfig() - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -101,7 +101,7 @@ func TestBuilderPrepare_VNCBindAddress(t *testing.T) { // Test a default boot_wait delete(config, "vnc_bind_address") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -122,7 +122,7 @@ func TestBuilderPrepare_DiskCompaction(t *testing.T) { config["skip_compaction"] = false config["disk_compression"] = true config["format"] = "img" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -141,7 +141,7 @@ func TestBuilderPrepare_DiskCompaction(t *testing.T) { config["disk_compression"] = true config["format"] = "qcow2" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -178,7 +178,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { delete(config, "disk_size") config["disk_size"] = tc.InputSize - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -198,7 +198,7 @@ func TestBuilderPrepare_AdditionalDiskSize(t *testing.T) { config["disk_additional_size"] = []string{"1M"} config["disk_image"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -209,7 +209,7 @@ func TestBuilderPrepare_AdditionalDiskSize(t *testing.T) { delete(config, "disk_image") config["disk_additional_size"] = []string{"1M"} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -228,7 +228,7 @@ func TestBuilderPrepare_Format(t *testing.T) { // Bad config["format"] = "illegal value" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -239,7 +239,7 @@ func TestBuilderPrepare_Format(t *testing.T) { // Good config["format"] = "qcow2" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -250,7 +250,7 @@ func TestBuilderPrepare_Format(t *testing.T) { // Good config["format"] = "raw" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -269,7 +269,7 @@ func TestBuilderPrepare_UseBackingFile(t *testing.T) { config["disk_image"] = false config["format"] = "qcow2" b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -281,7 +281,7 @@ func TestBuilderPrepare_UseBackingFile(t *testing.T) { config["disk_image"] = true config["format"] = "raw" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -293,7 +293,7 @@ func TestBuilderPrepare_UseBackingFile(t *testing.T) { config["disk_image"] = true config["format"] = "qcow2" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -307,7 +307,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config := testConfig() delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -322,7 +322,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppies_path := "../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -341,7 +341,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig() config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -357,7 +357,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -379,7 +379,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) { config["output_directory"] = dir b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -390,7 +390,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) { // Test with a good one config["output_directory"] = "i-hope-i-dont-exist" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -405,7 +405,7 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { // Test with a bad value config["shutdown_timeout"] = "this is not good" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -416,7 +416,7 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { // Test with a good one config["shutdown_timeout"] = "5s" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -433,7 +433,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) { config["ssh_host_port_min"] = 1000 config["ssh_host_port_max"] = 500 b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -444,7 +444,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) { // Bad config["ssh_host_port_min"] = -500 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -456,7 +456,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) { config["ssh_host_port_min"] = 500 config["ssh_host_port_max"] = 1000 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -471,7 +471,7 @@ func TestBuilderPrepare_SSHPrivateKey(t *testing.T) { config["ssh_private_key_file"] = "" b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -481,7 +481,7 @@ func TestBuilderPrepare_SSHPrivateKey(t *testing.T) { config["ssh_private_key_file"] = "/i/dont/exist" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -503,7 +503,7 @@ func TestBuilderPrepare_SSHPrivateKey(t *testing.T) { config["ssh_private_key_file"] = tf.Name() b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -517,7 +517,7 @@ func TestBuilderPrepare_SSHPrivateKey(t *testing.T) { tf.Write([]byte(testPem)) config["ssh_private_key_file"] = tf.Name() b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -532,7 +532,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) { // Test a default boot_wait delete(config, "ssh_wait_timeout") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -543,7 +543,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) { // Test with a bad value config["ssh_wait_timeout"] = "this is not good" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -554,7 +554,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) { // Test with a good one config["ssh_wait_timeout"] = "5s" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -569,7 +569,7 @@ func TestBuilderPrepare_QemuArgs(t *testing.T) { // Test with empty delete(config, "qemuargs") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -587,7 +587,7 @@ func TestBuilderPrepare_QemuArgs(t *testing.T) { } b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -611,7 +611,7 @@ func TestBuilderPrepare_VNCPassword(t *testing.T) { config["vnc_use_password"] = true config["output_directory"] = "not-a-real-directory" b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 47ddc8376..b1086955a 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -23,14 +23,14 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/scaleway/builder_test.go b/builder/scaleway/builder_test.go index a1a0fccc3..cfb192614 100644 --- a/builder/scaleway/builder_test.go +++ b/builder/scaleway/builder_test.go @@ -32,7 +32,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "api_token": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -46,7 +46,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { config := testConfig() config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -60,7 +60,7 @@ func TestBuilderPrepare_Region(t *testing.T) { config := testConfig() delete(config, "region") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -72,7 +72,7 @@ func TestBuilderPrepare_Region(t *testing.T) { config["region"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -90,7 +90,7 @@ func TestBuilderPrepare_CommercialType(t *testing.T) { config := testConfig() delete(config, "commercial_type") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -102,7 +102,7 @@ func TestBuilderPrepare_CommercialType(t *testing.T) { config["commercial_type"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -120,7 +120,7 @@ func TestBuilderPrepare_Image(t *testing.T) { config := testConfig() delete(config, "image") - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -132,7 +132,7 @@ func TestBuilderPrepare_Image(t *testing.T) { config["image"] = expected b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -149,7 +149,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) { var b Builder config := testConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -163,7 +163,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) { config["snapshot_name"] = "foobarbaz" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -173,7 +173,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) { config["snapshot_name"] = "{{timestamp}}" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -192,7 +192,7 @@ func TestBuilderPrepare_ServerName(t *testing.T) { var b Builder config := testConfig() - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -206,7 +206,7 @@ func TestBuilderPrepare_ServerName(t *testing.T) { config["server_name"] = "foobar" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -216,7 +216,7 @@ func TestBuilderPrepare_ServerName(t *testing.T) { config["server_name"] = "foobar-{{timestamp}}" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -226,7 +226,7 @@ func TestBuilderPrepare_ServerName(t *testing.T) { config["server_name"] = "foobar-{{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 8934d0649..596f52534 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -31,7 +31,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -43,7 +43,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, raws...) b.config.ctx.EnableEnv = true if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors @@ -52,13 +52,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.TencentCloudImageConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.TencentCloudRunConfig.Prepare(&b.config.ctx)...) if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.SecretId, b.config.SecretKey) log.Printf("[DEBUG]packer config: %v", b.config) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/triton/builder.go b/builder/triton/builder.go index ba6e2baa8..7a4cd54f1 100644 --- a/builder/triton/builder.go +++ b/builder/triton/builder.go @@ -20,7 +20,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { errs := &multierror.Error{} err := config.Decode(&b.config, &config.DecodeOpts{ @@ -42,7 +42,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.Comm.SSHAgentAuth = true } - return nil, errs.ErrorOrNil() + return nil, nil, errs.ErrorOrNil() } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/ucloud/uhost/builder.go b/builder/ucloud/uhost/builder.go index a9903bf4a..6b2e25d4e 100644 --- a/builder/ucloud/uhost/builder.go +++ b/builder/ucloud/uhost/builder.go @@ -33,7 +33,7 @@ type Builder struct { runner multistep.Runner } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -45,7 +45,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, raws...) b.config.ctx.EnableEnv = true if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors @@ -55,11 +55,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return nil, nil, errs } packer.LogSecretFilter.Set(b.config.PublicKey, b.config.PrivateKey) - return nil, nil + return nil, nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/ucloud/uhost/builder_test.go b/builder/ucloud/uhost/builder_test.go index dca944bc5..70da65fa5 100644 --- a/builder/ucloud/uhost/builder_test.go +++ b/builder/ucloud/uhost/builder_test.go @@ -35,7 +35,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "public_key": []string{}, } - warnings, err := b.Prepare(c) + _, warnings, err := b.Prepare(c) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -50,7 +50,7 @@ func TestBuilderPrepare_ImageName(t *testing.T) { // Test good config["image_name"] = "foo" - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -61,7 +61,7 @@ func TestBuilderPrepare_ImageName(t *testing.T) { // Test bad config["image_name"] = "foo {{" b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -72,7 +72,7 @@ func TestBuilderPrepare_ImageName(t *testing.T) { // Test bad delete(config, "image_name") b = Builder{} - warnings, err = b.Prepare(config) + _, warnings, err = b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -87,7 +87,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -113,7 +113,7 @@ func TestBuilderPrepare_ImageDestinations(t *testing.T) { "description": "bar", }, } - warnings, err := b.Prepare(config) + _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index 5fb189e13..d6cd83d0a 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -132,7 +132,7 @@ type Config struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { b.config = new(Config) err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -144,7 +144,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors and warnings @@ -215,10 +215,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warnings, errs + return nil, warnings, errs } - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/vagrant/builder_test.go b/builder/vagrant/builder_test.go index 65c2e5e9f..ee15b86e3 100644 --- a/builder/vagrant/builder_test.go +++ b/builder/vagrant/builder_test.go @@ -83,7 +83,7 @@ func TestBuilder_Prepare_ValidateSource(t *testing.T) { } for _, tc := range cases { - _, err := b.Prepare(tc.config) + _, _, err := b.Prepare(tc.config) if (err != nil) != tc.errExpected { t.Fatalf("Unexpected behavior from test case %#v; %s.", tc.config, tc.reason) } diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 02c7aeeac..ecdf1f470 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -130,7 +130,7 @@ type Config struct { ctx interpolate.Context } -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -145,7 +145,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) if err != nil { - return nil, err + return nil, nil, err } // Accumulate any errors and warnings @@ -267,10 +267,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return warnings, errs + return nil, warnings, errs } - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index 370ebe675..7f996efdf 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -32,7 +32,7 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder config := testConfig() - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -62,7 +62,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config := testConfig() delete(config, "disk_size") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -76,7 +76,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config["disk_size"] = 60000 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -94,7 +94,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config := testConfig() delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -109,7 +109,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppies_path := "../../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -128,7 +128,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig() config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -144,7 +144,7 @@ func TestBuilderPrepare_GuestAdditionsMode(t *testing.T) { // test default mode delete(config, "guest_additions_mode") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -155,7 +155,7 @@ func TestBuilderPrepare_GuestAdditionsMode(t *testing.T) { // Test another mode config["guest_additions_mode"] = "attach" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -170,7 +170,7 @@ func TestBuilderPrepare_GuestAdditionsMode(t *testing.T) { // Test bad mode config["guest_additions_mode"] = "teleport" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -184,7 +184,7 @@ func TestBuilderPrepare_GuestAdditionsPath(t *testing.T) { config := testConfig() delete(config, "guest_additions_path") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -198,7 +198,7 @@ func TestBuilderPrepare_GuestAdditionsPath(t *testing.T) { config["guest_additions_path"] = "foo" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -216,7 +216,7 @@ func TestBuilderPrepare_GuestAdditionsSHA256(t *testing.T) { config := testConfig() delete(config, "guest_additions_sha256") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -230,7 +230,7 @@ func TestBuilderPrepare_GuestAdditionsSHA256(t *testing.T) { config["guest_additions_sha256"] = "FOO" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -248,7 +248,7 @@ func TestBuilderPrepare_GuestAdditionsURL(t *testing.T) { config := testConfig() config["guest_additions_url"] = "" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -262,7 +262,7 @@ func TestBuilderPrepare_GuestAdditionsURL(t *testing.T) { config["guest_additions_url"] = "http://www.packer.io" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -277,7 +277,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) { // Test a default boot_wait delete(config, "hard_drive_interface") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -292,7 +292,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) { // Test with a bad config["hard_drive_interface"] = "fake" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -303,7 +303,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) { // Test with a good config["hard_drive_interface"] = "sata" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -318,7 +318,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -333,7 +333,7 @@ func TestBuilderPrepare_ISOInterface(t *testing.T) { // Test a default boot_wait delete(config, "iso_interface") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -348,7 +348,7 @@ func TestBuilderPrepare_ISOInterface(t *testing.T) { // Test with a bad config["iso_interface"] = "fake" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -359,7 +359,7 @@ func TestBuilderPrepare_ISOInterface(t *testing.T) { // Test with a good config["iso_interface"] = "sata" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index b20b5b663..da63b6951 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -20,14 +20,14 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/virtualbox/vm/builder.go b/builder/virtualbox/vm/builder.go index 2336e58d5..ba76bacf1 100644 --- a/builder/virtualbox/vm/builder.go +++ b/builder/virtualbox/vm/builder.go @@ -20,14 +20,14 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 7e94d9663..9de2d9284 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -19,15 +19,15 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = *c - return warnings, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { diff --git a/builder/vmware/iso/builder_test.go b/builder/vmware/iso/builder_test.go index 195f8e381..38441d633 100644 --- a/builder/vmware/iso/builder_test.go +++ b/builder/vmware/iso/builder_test.go @@ -33,7 +33,7 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder config := testConfig() - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -63,7 +63,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config := testConfig() delete(config, "disk_size") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -77,7 +77,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { config["disk_size"] = 60000 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -95,7 +95,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config := testConfig() delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -110,7 +110,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppies_path := "../../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -129,7 +129,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig() config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -149,7 +149,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { config["skip_validate_credentials"] = true // Bad config["remote_type"] = "foobar" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -161,7 +161,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { // Bad config["remote_host"] = "" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -176,7 +176,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { config["remote_password"] = "" config["remote_private_key_file"] = "" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -189,7 +189,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { config["remote_host"] = "foobar.example.com" config["remote_password"] = "supersecret" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -207,7 +207,7 @@ func TestBuilderPrepare_RemoteExport(t *testing.T) { config["skip_validate_credentials"] = true // Bad config["remote_password"] = "" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) != 0 { t.Fatalf("bad: %#v", warns) } @@ -218,7 +218,7 @@ func TestBuilderPrepare_RemoteExport(t *testing.T) { // Good config["remote_password"] = "supersecret" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) != 0 { t.Fatalf("err: %s", err) } @@ -233,7 +233,7 @@ func TestBuilderPrepare_Format(t *testing.T) { // Bad config["format"] = "foobar" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -252,7 +252,7 @@ func TestBuilderPrepare_Format(t *testing.T) { config["skip_validate_credentials"] = true b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -268,7 +268,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { // Add a random key config["i_should_not_be_valid"] = true - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -290,7 +290,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) { config["output_directory"] = dir b = Builder{} - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -301,7 +301,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) { // Test with a good one config["output_directory"] = "i-hope-i-dont-exist" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -316,7 +316,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) { // Test a default delete(config, "tools_upload_path") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -331,7 +331,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) { // Test with a bad value config["tools_upload_path"] = "{{{nope}" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -342,7 +342,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) { // Test with a good one config["tools_upload_path"] = "hey" b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -357,7 +357,7 @@ func TestBuilderPrepare_VMXTemplatePath(t *testing.T) { // Test bad config["vmx_template_path"] = "/i/dont/exist/forreal" - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -379,7 +379,7 @@ func TestBuilderPrepare_VMXTemplatePath(t *testing.T) { config["vmx_template_path"] = tf.Name() b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -401,7 +401,7 @@ func TestBuilderPrepare_VMXTemplatePath(t *testing.T) { config["vmx_template_path"] = tf2.Name() b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -417,7 +417,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) { // Bad config["vnc_port_min"] = 1000 config["vnc_port_max"] = 500 - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -428,7 +428,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) { // Bad config["vnc_port_min"] = -500 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -440,7 +440,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) { config["vnc_port_min"] = 500 config["vnc_port_max"] = 1000 b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -458,7 +458,7 @@ func TestBuilderCheckCollisions(t *testing.T) { } { var b Builder - warns, _ := b.Prepare(config) + _, warns, _ := b.Prepare(config) if len(warns) != 1 { t.Fatalf("Should have warning about two collisions.") } @@ -466,7 +466,7 @@ func TestBuilderCheckCollisions(t *testing.T) { { config["vmx_template_path"] = "some/path.vmx" var b Builder - warns, _ := b.Prepare(config) + _, warns, _ := b.Prepare(config) if len(warns) != 0 { t.Fatalf("Should not check for collisions with custom template.") } @@ -484,7 +484,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config["winrm_host"] = "1.2.3.4" var b Builder - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -512,7 +512,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config["ssh_host"] = "1.2.3.4" var b Builder - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index e1e33dd7c..f33f14ffa 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -22,14 +22,14 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run executes a Packer build and returns a packer.Artifact representing diff --git a/builder/vmware/vmx/builder_test.go b/builder/vmware/vmx/builder_test.go index 841d4f718..4e2a165a7 100644 --- a/builder/vmware/vmx/builder_test.go +++ b/builder/vmware/vmx/builder_test.go @@ -24,7 +24,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config["source_path"] = tf.Name() delete(config, "floppy_files") - warns, err := b.Prepare(config) + _, warns, err := b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -39,7 +39,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { floppies_path := "../../../common/test-fixtures/floppies" config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} b = Builder{} - warns, err = b.Prepare(config) + _, warns, err = b.Prepare(config) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -58,7 +58,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig(t) config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} - _, errs := b.Prepare(config) + _, _, errs := b.Prepare(config) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } diff --git a/builder/yandex/builder.go b/builder/yandex/builder.go index 69bb9469f..e59ae9b25 100644 --- a/builder/yandex/builder.go +++ b/builder/yandex/builder.go @@ -24,13 +24,13 @@ type Builder struct { } // Prepare processes the build configuration parameters. -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { +func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { c, warnings, errs := NewConfig(raws...) if errs != nil { - return warnings, errs + return nil, warnings, errs } b.config = c - return warnings, nil + return nil, warnings, nil } // Run executes a yandex Packer build and returns a packer.Artifact diff --git a/command/build_parallel_test.go b/command/build_parallel_test.go index a2dc240ae..a6228be32 100644 --- a/command/build_parallel_test.go +++ b/command/build_parallel_test.go @@ -28,7 +28,9 @@ type ParallelTestBuilder struct { wg sync.WaitGroup } -func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, error) { return nil, nil } +func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, []string, error) { + return nil, nil, nil +} func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { ui.Say("building") @@ -39,7 +41,7 @@ func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer // LockedBuilder wont run until unlock is called type LockedBuilder struct{ unlock chan interface{} } -func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, error) { return nil, nil } +func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, []string, error) { return nil, nil, nil } func (b *LockedBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { ui.Say("locking build") diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 46f2f8088..500cf0132 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -5,6 +5,7 @@ package shell_local import ( "errors" "fmt" + // "log" "os" "path/filepath" "runtime" @@ -49,10 +50,6 @@ type Config struct { } func Decode(config *Config, raws ...interface{}) error { - // Create passthrough for build-generated data so we can fill it in once we know - // it - config.ctx.Data = packer.BasicPlaceholderData() - err := configHelper.Decode(&config, &configHelper.DecodeOpts{ Interpolate: true, InterpolateContext: &config.ctx, @@ -63,7 +60,8 @@ func Decode(config *Config, raws ...interface{}) error { }, }, raws...) if err != nil { - return fmt.Errorf("Error decoding config: %s, config is %#v, and raws is %#v", err, config, raws) + return fmt.Errorf("Error decoding config: %s", err) + // return fmt.Errorf("Error decoding config: %s, config is %#v, and raws is %#v", err, config, raws) } return nil diff --git a/helper/config/decode.go b/helper/config/decode.go index 43673e1dc..5aefadcb4 100644 --- a/helper/config/decode.go +++ b/helper/config/decode.go @@ -41,9 +41,11 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { config = &DecodeOpts{Interpolate: true} } + // Detect user variables from the raws and merge them into our context + ctxData, raws := DetectContextData(raws...) + // Interpolate first if config.Interpolate { - // Detect user variables from the raws and merge them into our context ctx, err := DetectContext(raws...) if err != nil { return err @@ -55,6 +57,9 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { config.InterpolateContext.BuildType = ctx.BuildType config.InterpolateContext.TemplatePath = ctx.TemplatePath config.InterpolateContext.UserVariables = ctx.UserVariables + if config.InterpolateContext.Data == nil { + config.InterpolateContext.Data = ctxData + } } ctx = config.InterpolateContext @@ -103,7 +108,7 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { for _, unused := range md.Unused { if unused != "type" && !strings.HasPrefix(unused, "packer_") { err = multierror.Append(err, fmt.Errorf( - "unknown configuration key: %q", unused)) + "unknown configuration key: %q; raws is %#v \n\n and ctx data is %#v", unused, raws, ctxData)) } } if err != nil { @@ -114,6 +119,39 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { return nil } +func DetectContextData(raws ...interface{}) (map[interface{}]interface{}, []interface{}) { + // In provisioners, the last value pulled from raws is the placeholder data + // for build-specific variables. Pull these out to add to interpolation + // context. + + // Internally, our tests may cause this to be read as a map[string]string + placeholderData := raws[len(raws)-1] + if pd, ok := placeholderData.(map[string]string); ok { + if uuid, ok := pd["PackerRunUUID"]; ok { + if strings.Contains(uuid, "Generated_PackerRunUUID.") { + cast := make(map[interface{}]interface{}) + for k, v := range pd { + cast[k] = v + } + raws = raws[:len(raws)-1] + return cast, raws + } + } + } + + // but with normal interface conversion across the rpc, it'll look like a + // map[interface]interface, not a map[string]string + if pd, ok := placeholderData.(map[interface{}]interface{}); ok { + if uuid, ok := pd["PackerRunUUID"]; ok { + if strings.Contains(uuid.(string), "Generated_PackerRunUUID.") { + raws = raws[:len(raws)-1] + return pd, raws + } + } + } + return nil, raws +} + // DetectContext builds a base interpolate.Context, automatically // detecting things like user variables from the raw configuration params. func DetectContext(raws ...interface{}) (*interpolate.Context, error) { diff --git a/packer/build.go b/packer/build.go index badd67c1f..9c83c8992 100644 --- a/packer/build.go +++ b/packer/build.go @@ -5,6 +5,8 @@ import ( "fmt" "log" "sync" + + "github.com/hashicorp/packer/helper/common" ) const ( @@ -148,17 +150,30 @@ func (b *coreBuild) Prepare() (warn []string, err error) { } // Prepare the builder - warn, err = b.builder.Prepare(b.builderConfig, packerConfig) + generatedVars, warn, err := b.builder.Prepare(b.builderConfig, packerConfig) if err != nil { log.Printf("Build '%s' prepare failure: %s\n", b.name, err) return } + // If the builder has provided a list of to-be-generated variables that + // should be made accessible to provisioners, pass that list into + // the provisioner prepare() so that the provisioner can appropriately + // validate user input against what will become available. + generatedPlaceholderMap := BasicPlaceholderData() + if generatedVars != nil { + for _, k := range generatedVars { + generatedPlaceholderMap[k] = fmt.Sprintf("Generated_%s. "+ + common.PlaceholderMsg, k) + } + } + // Prepare the provisioners for _, coreProv := range b.provisioners { configs := make([]interface{}, len(coreProv.config), len(coreProv.config)+1) copy(configs, coreProv.config) configs = append(configs, packerConfig) + configs = append(configs, generatedPlaceholderMap) if err = coreProv.provisioner.Prepare(configs...); err != nil { return @@ -170,6 +185,7 @@ func (b *coreBuild) Prepare() (warn []string, err error) { configs := make([]interface{}, len(b.cleanupProvisioner.config), len(b.cleanupProvisioner.config)+1) copy(configs, b.cleanupProvisioner.config) configs = append(configs, packerConfig) + configs = append(configs, generatedPlaceholderMap) err = b.cleanupProvisioner.provisioner.Prepare(configs...) if err != nil { return diff --git a/packer/build_test.go b/packer/build_test.go index 0c58b405d..8102c4e67 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -69,7 +69,7 @@ func TestBuild_Prepare(t *testing.T) { if !prov.PrepCalled { t.Fatal("prep should be called") } - if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig}) { + if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, BasicPlaceholderData()}) { t.Fatalf("bad: %#v", prov.PrepConfigs) } @@ -144,7 +144,7 @@ func TestBuild_Prepare_Debug(t *testing.T) { if !prov.PrepCalled { t.Fatal("prepare should be called") } - if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig}) { + if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, BasicPlaceholderData()}) { t.Fatalf("bad: %#v", prov.PrepConfigs) } } diff --git a/packer/builder.go b/packer/builder.go index e24073128..349f94ba8 100644 --- a/packer/builder.go +++ b/packer/builder.go @@ -25,9 +25,10 @@ type Builder interface { // Each of the configuration values should merge into the final // configuration. // - // Prepare should return a list of warnings along with any errors - // that occurred while preparing. - Prepare(...interface{}) ([]string, error) + // Prepare should return a list of variables that will be made accessible to + // users during the provison methods, a list of warnings along with any + // errors that occurred while preparing. + Prepare(...interface{}) ([]string, []string, error) // Run is where the actual build should take place. It takes a Build and a Ui. Run(context.Context, Ui, Hook) (Artifact, error) diff --git a/packer/builder_mock.go b/packer/builder_mock.go index 5ebd2d993..47dc5aec0 100644 --- a/packer/builder_mock.go +++ b/packer/builder_mock.go @@ -23,10 +23,10 @@ type MockBuilder struct { RunFn func(ctx context.Context) } -func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, error) { +func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, []string, error) { tb.PrepareCalled = true tb.PrepareConfig = config - return tb.PrepareWarnings, nil + return nil, tb.PrepareWarnings, nil } func (tb *MockBuilder) Run(ctx context.Context, ui Ui, h Hook) (Artifact, error) { diff --git a/packer/plugin/builder.go b/packer/plugin/builder.go index 44af7dd67..35665dc7b 100644 --- a/packer/plugin/builder.go +++ b/packer/plugin/builder.go @@ -12,7 +12,7 @@ type cmdBuilder struct { client *Client } -func (b *cmdBuilder) Prepare(config ...interface{}) ([]string, error) { +func (b *cmdBuilder) Prepare(config ...interface{}) ([]string, []string, error) { defer func() { r := recover() b.checkExit(r, nil) diff --git a/packer/provisioner.go b/packer/provisioner.go index d20e4d360..b7f729bc3 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -64,7 +64,7 @@ func BasicPlaceholderData() map[string]string { placeholderData["User"] = fmt.Sprintf(msg, "User") placeholderData["Password"] = fmt.Sprintf(msg, "Password") placeholderData["ConnType"] = fmt.Sprintf(msg, "Type") - placeholderData["PACKER_RUN_UUID"] = fmt.Sprintf(msg, "PACKER_RUN_UUID") + placeholderData["PackerRunUUID"] = fmt.Sprintf(msg, "PackerRunUUID") placeholderData["SSHPublicKey"] = fmt.Sprintf(msg, "SSHPublicKey") placeholderData["SSHPrivateKey"] = fmt.Sprintf(msg, "SSHPrivateKey") @@ -75,6 +75,28 @@ func BasicPlaceholderData() map[string]string { return placeholderData } +func CastDataToMap(data interface{}) map[string]interface{} { + // Provisioners expect a map[string]interface{} in their data field, but + // it gets converted into a map[interface]interface on the way over the + // RPC. Check that data can be cast into such a form, and cast it. + cast := make(map[string]interface{}) + interMap, ok := data.(map[interface{}]interface{}) + if !ok { + log.Printf("Unable to read map[string]interface out of data."+ + "Using empty interface: %#v", data) + } else { + for key, val := range interMap { + keyString, ok := key.(string) + if ok { + cast[keyString] = val + } else { + log.Printf("Error casting generated data key to a string.") + } + } + } + return cast +} + // Runs the provisioners in order. func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error { // Shortcut @@ -91,25 +113,7 @@ func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Commun for _, p := range h.Provisioners { ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config) - // Provisioners expect a map[string]interface{} in their data field, but - // it gets converted into a map[interface]interface on the way over the - // RPC. Check that data can be cast into such a form, and cast it. - cast := make(map[string]interface{}) - interMap, ok := data.(map[interface{}]interface{}) - if !ok { - log.Printf("Unable to read map[string]interface out of data." + - "Using empty interface.") - } else { - for key, val := range interMap { - keyString, ok := key.(string) - if ok { - cast[keyString] = val - } else { - log.Printf("Error casting generated data key to a string.") - } - } - } - + cast := CastDataToMap(data) err := p.Provisioner.Provision(ctx, ui, comm, cast) ts.End(err) diff --git a/packer/rpc/builder.go b/packer/rpc/builder.go index 4b95bea3e..5be4cc2bb 100644 --- a/packer/rpc/builder.go +++ b/packer/rpc/builder.go @@ -30,22 +30,23 @@ type BuilderPrepareArgs struct { } type BuilderPrepareResponse struct { - Warnings []string - Error *BasicError + GeneratedVars []string + Warnings []string + Error *BasicError } -func (b *builder) Prepare(config ...interface{}) ([]string, error) { +func (b *builder) Prepare(config ...interface{}) ([]string, []string, error) { var resp BuilderPrepareResponse cerr := b.client.Call("Builder.Prepare", &BuilderPrepareArgs{config}, &resp) if cerr != nil { - return nil, cerr + return nil, nil, cerr } var err error = nil if resp.Error != nil { err = resp.Error } - return resp.Warnings, err + return resp.GeneratedVars, resp.Warnings, err } func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { @@ -87,10 +88,11 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *BuilderPrepareResponse) error { - warnings, err := b.builder.Prepare(args.Configs...) + generated, warnings, err := b.builder.Prepare(args.Configs...) *reply = BuilderPrepareResponse{ - Warnings: warnings, - Error: NewBasicError(err), + GeneratedVars: generated, + Warnings: warnings, + Error: NewBasicError(err), } return nil } diff --git a/packer/rpc/builder_test.go b/packer/rpc/builder_test.go index 9d89239b0..75d442865 100644 --- a/packer/rpc/builder_test.go +++ b/packer/rpc/builder_test.go @@ -20,7 +20,7 @@ func TestBuilderPrepare(t *testing.T) { // Test Prepare config := 42 - warnings, err := bClient.Prepare(config) + _, warnings, err := bClient.Prepare(config) if err != nil { t.Fatalf("bad: %s", err) } @@ -50,7 +50,7 @@ func TestBuilderPrepare_Warnings(t *testing.T) { b.PrepareWarnings = expected // Test Prepare - warnings, err := bClient.Prepare(nil) + _, warnings, err := bClient.Prepare(nil) if err != nil { t.Fatalf("bad: %s", err) } diff --git a/post-processor/checksum/post-processor_test.go b/post-processor/checksum/post-processor_test.go index fce5fd1cf..e52974ec4 100644 --- a/post-processor/checksum/post-processor_test.go +++ b/post-processor/checksum/post-processor_test.go @@ -57,7 +57,7 @@ func setup(t *testing.T) (packer.Ui, packer.Artifact, error) { // Prepare the file builder builder := file.Builder{} - warnings, err := builder.Prepare(tpl.Builders["file"].Config) + _, warnings, err := builder.Prepare(tpl.Builders["file"].Config) if len(warnings) > 0 { for _, warn := range warnings { return nil, nil, fmt.Errorf("Configuration warning: %s", warn) diff --git a/post-processor/compress/post-processor_test.go b/post-processor/compress/post-processor_test.go index 0f5e1a19e..5b4cfcd9f 100644 --- a/post-processor/compress/post-processor_test.go +++ b/post-processor/compress/post-processor_test.go @@ -197,7 +197,7 @@ func setup(t *testing.T) (packer.Ui, packer.Artifact, error) { // Prepare the file builder builder := file.Builder{} - warnings, err := builder.Prepare(tpl.Builders["file"].Config) + _, warnings, err := builder.Prepare(tpl.Builders["file"].Config) if len(warnings) > 0 { for _, warn := range warnings { return nil, nil, fmt.Errorf("Configuration warning: %s", warn) diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 180bd0dc9..1b8e5e34a 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -343,7 +343,7 @@ func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateStrin // Setup the builder builder := &docker.Builder{} - warnings, err := builder.Prepare(tpl.Builders["docker"].Config) + _, warnings, err := builder.Prepare(tpl.Builders["docker"].Config) if err != nil { t.Fatalf("Error preparing configuration %s", err) } diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 201bfd086..4ae101f6a 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -80,9 +80,6 @@ type Provisioner struct { func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) - // Create passthrough for build-generated data - p.config.ctx.Data = packer.BasicPlaceholderData() - err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &p.config.ctx, diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index b47ceb2dd..dfbaa9b50 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -166,21 +166,22 @@ func funcGenTemplateDir(ctx *Context) interface{} { func funcGenGenerated(ctx *Context) interface{} { return func(s string) (string, error) { - if data, ok := ctx.Data.(map[string]string); ok { + if data, ok := ctx.Data.(map[interface{}]interface{}); ok { // PlaceholderData has been passed into generator, so if the given // key already exists in data, then we know it's an "allowed" key if heldPlace, ok := data[s]; ok { - // If we're in the first interpolation pass, the goal is to - // make sure that we pass the value through. - // TODO match against an actual string constant - if strings.Contains(heldPlace, common.PlaceholderMsg) { - return fmt.Sprintf("{{.%s}}", s), nil - } else { - return heldPlace, nil + if hp, ok := heldPlace.(string); ok { + // If we're in the first interpolation pass, the goal is to + // make sure that we pass the value through. + // TODO match against an actual string constant + if strings.Contains(hp, common.PlaceholderMsg) { + return fmt.Sprintf("{{.%s}}", s), nil + } else { + return hp, nil + } } - } else { - return "", fmt.Errorf("loaded data, but couldnt find %s in it.", s) } + return "", fmt.Errorf("loaded data, but couldnt find %s in it.", s) } return "", fmt.Errorf("Error validating computed variable: the given "+ From 6bc5a95c3a92231b663258a5545d232db4d5eed0 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 16 Dec 2019 21:35:42 -0800 Subject: [PATCH 044/150] tweak docs; change to --- template/interpolate/funcs.go | 6 +++--- website/source/docs/templates/engine.html.md | 21 ++++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index dfbaa9b50..4306b200e 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -44,7 +44,7 @@ var FuncGens = map[string]interface{}{ "consul_key": funcGenConsul, "vault": funcGenVault, "sed": funcGenSed, - "generated": funcGenGenerated, + "build": funcGenBuild, "replace": replace, "replace_all": replace_all, @@ -164,7 +164,7 @@ func funcGenTemplateDir(ctx *Context) interface{} { } } -func funcGenGenerated(ctx *Context) interface{} { +func funcGenBuild(ctx *Context) interface{} { return func(s string) (string, error) { if data, ok := ctx.Data.(map[interface{}]interface{}); ok { // PlaceholderData has been passed into generator, so if the given @@ -184,7 +184,7 @@ func funcGenGenerated(ctx *Context) interface{} { return "", fmt.Errorf("loaded data, but couldnt find %s in it.", s) } - return "", fmt.Errorf("Error validating computed variable: the given "+ + return "", fmt.Errorf("Error validating build variable: the given "+ "variable %s will not be passed into your plugin.", s) } } diff --git a/website/source/docs/templates/engine.html.md b/website/source/docs/templates/engine.html.md index 3391e4b77..f868669ef 100644 --- a/website/source/docs/templates/engine.html.md +++ b/website/source/docs/templates/engine.html.md @@ -62,22 +62,27 @@ Here is a full list of the available functions for reference. each function will behave. - `env` - Returns environment variables. See example in [using home variable](/docs/templates/user-variables.html#using-home-variable) -- `generated` - This engine will allow you to access special variables that +- `build` - This engine will allow you to access special variables that provide connection information and basic instance state information. Usage example: ```json { "type": "shell-local", - "environment_vars": ["TESTVAR={{ generated `PACKER_RUN_UUID`}}"], + "environment_vars": ["TESTVAR={{ build `PackerRunUUID`}}"], "inline": ["echo $TESTVAR"] }, ``` - Valid variables to request are: "ID", "Host", "Port", "User", "Password", - "ConnType", "PACKER_RUN_UUID", "SSHPublicKey", "SSHPrivateKey", and - "WinRMPassword", though obviously depending on which communicator you are - using, some of these values may be empty. ID represents the vm being - provisioned. For example, in Amazon it is the instance id; in digitalocean, - it is the droplet id; in Vmware, it is the vm name. + Valid variables to request are: "InstanceID", "CommunicatorHost", + "CommunicatorPort", "CommunicatorUser", "CommunicatorPassword", "ConnType", + "PackerRunUUID", "CommunicatorPublicKey", and "CommunicatorPrivateKey". + Depending on which communicator you are using, some of these values may be + empty -- for example, the public and private keys are unique to the SSH + communicator. InstanceID represents the vm being provisioned. For example, + in Amazon it is the instance id; in digitalocean, it is the droplet id; in + Vmware, it is the vm name. + + For backwards compatability, `WinRMPassword` is also available through this + engine, though it is no different than using the more general `Password`. This function is only for use within _provisioners_, and does not yet work if the provisioners are being used in conjunction with our chroot builders From 0785c2f6fca9c22bf25528e0176042799dd79df9 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Tue, 17 Dec 2019 11:25:56 +0100 Subject: [PATCH 045/150] build using HCL2 (#8423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ``` --- builder/alicloud/ecs/builder.go | 3 + builder/alicloud/ecs/builder.hcl2spec.go | 24 +- builder/alicloud/ecs/step_create_snapshot.go | 2 +- builder/amazon/chroot/builder.go | 3 + builder/amazon/chroot/builder.hcl2spec.go | 11 +- .../amazon/common/access_config.hcl2spec.go | 9 +- .../amazon/common/block_device.hcl2spec.go | 9 +- builder/amazon/common/run_config.hcl2spec.go | 54 ++- .../common/step_iam_instance_profile.go | 3 +- builder/amazon/ebs/builder.go | 3 + builder/amazon/ebs/builder.hcl2spec.go | 17 +- builder/amazon/ebssurrogate/builder.go | 4 + .../amazon/ebssurrogate/builder.hcl2spec.go | 331 ++++++++++++++++ builder/amazon/ebsvolume/builder.go | 4 + builder/amazon/ebsvolume/builder.hcl2spec.go | 258 +++++++++++++ builder/amazon/instance/builder.go | 4 + builder/amazon/instance/builder.hcl2spec.go | 267 +++++++++++++ builder/azure/arm/builder.go | 39 +- builder/azure/arm/builder_test.go | 2 +- builder/azure/arm/config.go | 35 +- builder/azure/arm/config.hcl2spec.go | 38 +- builder/azure/arm/config_test.go | 233 ++++++++---- builder/azure/arm/resource_resolver_test.go | 15 +- .../step_publish_to_shared_image_gallery.go | 1 + ...ep_publish_to_shared_image_gallery_test.go | 3 +- builder/azure/arm/template_factory_test.go | 100 +++-- builder/azure/chroot/builder.go | 4 + builder/azure/chroot/builder.hcl2spec.go | 92 +++++ builder/azure/chroot/diskattacher_test.go | 3 +- builder/azure/chroot/step_create_image.go | 3 +- builder/azure/common/client/platform_image.go | 5 +- builder/cloudstack/builder.go | 11 +- builder/cloudstack/builder_test.go | 4 +- builder/cloudstack/config.go | 9 +- builder/cloudstack/config.hcl2spec.go | 13 +- builder/cloudstack/config_test.go | 3 +- builder/digitalocean/builder.go | 6 +- builder/digitalocean/config.go | 9 +- builder/digitalocean/config.hcl2spec.go | 13 +- builder/docker/builder.go | 8 +- builder/docker/config.go | 9 +- builder/docker/config.hcl2spec.go | 13 +- builder/docker/config_test.go | 34 +- builder/file/builder.go | 8 +- builder/file/config.go | 9 +- builder/file/config.hcl2spec.go | 9 +- builder/file/config_test.go | 9 +- builder/googlecompute/builder.go | 13 +- builder/googlecompute/config.go | 9 +- builder/googlecompute/config.hcl2spec.go | 22 +- builder/googlecompute/config_test.go | 28 +- builder/hcloud/builder.go | 6 +- builder/hcloud/config.go | 10 +- builder/hcloud/config.hcl2spec.go | 22 +- builder/hyperone/builder.go | 7 +- builder/hyperone/config.go | 17 +- builder/hyperone/config.hcl2spec.go | 13 +- .../hyperv/common/output_config.hcl2spec.go | 9 +- builder/hyperv/iso/builder.go | 4 +- builder/hyperv/iso/builder.hcl2spec.go | 13 +- builder/hyperv/vmcx/builder.go | 4 +- builder/hyperv/vmcx/builder.hcl2spec.go | 13 +- builder/jdcloud/builder.go | 4 + builder/jdcloud/common.hcl2spec.go | 13 +- builder/jdcloud/step_config_credentials.go | 3 +- builder/jdcloud/step_create_image.go | 3 +- builder/jdcloud/step_create_instance.go | 5 +- builder/jdcloud/step_stop_instance.go | 1 + builder/jdcloud/step_validate_parameters.go | 1 + builder/linode/builder.go | 8 +- builder/linode/config.go | 9 +- builder/linode/config.hcl2spec.go | 13 +- builder/lxc/builder.go | 8 +- builder/lxc/config.go | 12 +- builder/lxc/config.hcl2spec.go | 66 ++++ builder/lxd/builder.go | 8 +- builder/lxd/config.go | 11 +- builder/lxd/config.hcl2spec.go | 9 +- builder/ncloud/builder.go | 36 +- builder/ncloud/config.go | 11 +- builder/ncloud/config.hcl2spec.go | 13 +- builder/ncloud/config_test.go | 12 +- builder/null/builder.go | 8 +- builder/null/config.go | 9 +- builder/null/config.hcl2spec.go | 13 +- builder/null/config_test.go | 21 +- builder/oneandone/builder.go | 8 +- builder/oneandone/config.go | 11 +- builder/oneandone/config.hcl2spec.go | 13 +- builder/openstack/builder.go | 3 + builder/openstack/builder.hcl2spec.go | 31 +- builder/oracle/classic/builder.go | 12 +- ...config.hcl2spec.go => builder.hcl2spec.go} | 13 +- builder/oracle/classic/config.go | 13 +- builder/oracle/classic/config_test.go | 9 +- builder/oracle/oci/builder.go | 12 +- builder/oracle/oci/config.go | 15 +- builder/oracle/oci/config.hcl2spec.go | 13 +- builder/oracle/oci/config_test.go | 33 +- builder/oracle/oci/step_test.go | 5 +- builder/osc/bsu/builder.go | 3 + builder/osc/bsu/builder.hcl2spec.go | 17 +- builder/osc/bsusurrogate/builder.go | 5 + builder/osc/bsusurrogate/builder.hcl2spec.go | 264 +++++++++++++ builder/osc/bsuvolume/builder.go | 3 + builder/osc/bsuvolume/builder.hcl2spec.go | 24 +- builder/osc/chroot/builder.go | 3 + builder/osc/chroot/builder.hcl2spec.go | 11 +- builder/osc/common/run_config.hcl2spec.go | 43 ++- builder/parallels/iso/builder.go | 3 + builder/parallels/iso/builder.hcl2spec.go | 13 +- builder/parallels/pvm/builder.go | 9 +- builder/parallels/pvm/config.go | 9 +- builder/parallels/pvm/config.hcl2spec.go | 13 +- builder/parallels/pvm/config_test.go | 22 +- builder/profitbricks/builder.go | 8 +- builder/profitbricks/config.go | 9 +- builder/profitbricks/config.hcl2spec.go | 13 +- builder/proxmox/builder.go | 7 +- builder/proxmox/config.go | 9 +- builder/proxmox/config.hcl2spec.go | 35 +- builder/proxmox/config_test.go | 3 +- builder/qemu/builder.go | 3 + builder/qemu/builder.hcl2spec.go | 13 +- builder/scaleway/builder.go | 8 +- builder/scaleway/config.go | 9 +- builder/scaleway/config.hcl2spec.go | 13 +- builder/tencentcloud/cvm/builder.go | 5 +- builder/tencentcloud/cvm/builder.hcl2spec.go | 15 +- .../tencentcloud/cvm/run_config.hcl2spec.go | 9 +- builder/triton/builder.go | 3 + builder/triton/config.hcl2spec.go | 13 +- .../triton/source_machine_config.hcl2spec.go | 9 +- builder/ucloud/common/artifact.go | 5 +- builder/ucloud/common/artifact_test.go | 3 +- builder/ucloud/common/client.go | 2 +- .../ucloud/common/image_config.hcl2spec.go | 9 +- builder/ucloud/common/utils.go | 3 +- builder/ucloud/uhost/builder.go | 3 + builder/ucloud/uhost/builder.hcl2spec.go | 15 +- builder/ucloud/uhost/builder_acc_test.go | 5 +- builder/ucloud/uhost/builder_test.go | 5 +- .../ucloud/uhost/step_check_source_image.go | 1 + .../uhost/step_config_security_group.go | 1 + builder/ucloud/uhost/step_config_subnet.go | 1 + builder/ucloud/uhost/step_config_vpc.go | 1 + builder/ucloud/uhost/step_copy_image.go | 5 +- builder/ucloud/uhost/step_create_image.go | 3 +- builder/ucloud/uhost/step_create_instance.go | 7 +- builder/ucloud/uhost/step_pre_validate.go | 1 + builder/ucloud/uhost/step_stop_instance.go | 3 +- builder/vagrant/builder.go | 7 +- builder/vagrant/builder.hcl2spec.go | 13 +- builder/vagrant/builder_test.go | 3 +- builder/virtualbox/iso/builder.go | 3 + builder/virtualbox/iso/builder.hcl2spec.go | 13 +- builder/virtualbox/ovf/builder.go | 9 +- builder/virtualbox/ovf/config.go | 9 +- builder/virtualbox/ovf/config.hcl2spec.go | 13 +- builder/virtualbox/ovf/config_test.go | 40 +- builder/virtualbox/ovf/step_import_test.go | 11 +- builder/virtualbox/vm/builder.go | 7 +- builder/virtualbox/vm/config.go | 9 +- builder/virtualbox/vm/config.hcl2spec.go | 13 +- builder/vmware/iso/builder.go | 8 +- builder/vmware/iso/config.go | 9 +- builder/vmware/iso/config.hcl2spec.go | 13 +- builder/vmware/iso/step_create_vmx_test.go | 12 +- builder/vmware/vmx/builder.go | 9 +- builder/vmware/vmx/config.go | 9 +- builder/vmware/vmx/config.hcl2spec.go | 13 +- builder/vmware/vmx/config_test.go | 18 +- builder/yandex/builder.go | 15 +- builder/yandex/config.go | 9 +- builder/yandex/config.hcl2spec.go | 13 +- builder/yandex/config_test.go | 28 +- cmd/hcl2-schema/hcl2-schema.go | 313 ---------------- .../mapstructure-to-hcl2.go | 41 +- command/build.go | 105 ++++-- command/build_parallel_test.go | 27 +- command/build_test.go | 31 +- command/build_timeout_test.go | 21 +- command/utils.go | 22 ++ common/shell-local/config.go | 2 +- common/shell-local/config.hcl2spec.go | 9 +- config.go | 148 +++++--- go.mod | 5 +- go.sum | 14 + hcl2template/common_test.go | 236 ++++++++++++ hcl2template/config_load.go | 101 ----- hcl2template/decode.go | 15 + hcl2template/load_test.go | 352 ------------------ hcl2template/mock.go | 120 ++++++ hcl2template/mock.hcl2spec.go | 85 +++++ hcl2template/parser.go | 74 ++-- hcl2template/parser_test.go | 213 ----------- hcl2template/testdata/build/basic.pkr.hcl | 163 +++++--- .../build/post-processor_inexistent.pkr.hcl | 6 + .../build/post-processor_untyped.pkr.hcl | 6 + .../build/provisioner_inexistent.pkr.hcl | 6 + .../build/provisioner_untyped.pkr.hcl | 6 + .../testdata/communicator/basic.pkr.hcl | 60 +-- hcl2template/testdata/complete/build.pkr.hcl | 162 +++++--- .../testdata/complete/communicator.pkr.hcl | 27 -- .../testdata/complete/sources.pkr.hcl | 94 +++-- .../complete/subfolder/shouldnotload.pkr.hcl | 61 --- .../testdata/complete/variables.pkr.hcl | 6 - hcl2template/testdata/sources/basic.pkr.hcl | 93 +++-- .../testdata/sources/duplicate.pkr.hcl | 3 + .../testdata/sources/inexistent.pkr.hcl | 4 + hcl2template/testdata/sources/unnamed.pkr.hcl | 4 + hcl2template/testdata/sources/untyped.pkr.hcl | 4 + hcl2template/types.build.from.go | 50 +-- hcl2template/types.build.go | 92 +++-- hcl2template/types.build.post-processor.go | 61 +++ hcl2template/types.build.provisioners.go | 98 +++-- hcl2template/types.build_test.go | 88 +++++ hcl2template/types.communicator.go | 88 ----- hcl2template/types.decodable.go | 56 --- hcl2template/types.hcl_ref.go | 6 - hcl2template/types.packer_config.go | 203 ++++------ hcl2template/types.packer_config_test.go | 55 +++ hcl2template/types.source.go | 57 ++- hcl2template/types.source_test.go | 75 ++++ .../{types.variable.go => types.variables.go} | 0 hcl2template/types.variables_test.go | 29 ++ hcl2template/utils.go | 23 ++ helper/builder/testing/testing.go | 19 +- helper/communicator/config.go | 24 +- helper/communicator/config.hcl2spec.go | 31 +- helper/config/decode.go | 35 ++ main.go | 20 +- packer/build.go | 138 +++---- packer/build_test.go | 62 +-- packer/builder.go | 6 +- packer/builder_mock.go | 8 + packer/builder_mock.hcl2spec.go | 207 ++++++++++ packer/communicator.go | 19 +- packer/config_file.go | 2 - packer/core.go | 80 ++-- packer/hcl2spec.go | 12 + packer/maps.go | 74 ++++ packer/plugin/builder.go | 10 + packer/plugin/post_processor.go | 10 + packer/plugin/post_processor_test.go | 3 + packer/plugin/provisioner.go | 10 + packer/plugin/server.go | 3 +- packer/post_processor.go | 2 + packer/post_processor_mock.go | 8 +- packer/provisioner.go | 8 + packer/provisioner_mock.go | 6 + packer/rpc/artifact.go | 5 +- packer/rpc/build.go | 4 +- packer/rpc/builder.go | 24 +- packer/rpc/client.go | 53 ++- packer/rpc/client_test.go | 5 +- packer/rpc/common.go | 71 ++++ packer/rpc/communicator.go | 24 +- packer/rpc/cty_encode.go | 35 ++ packer/rpc/hook.go | 8 +- packer/rpc/post_processor.go | 32 +- packer/rpc/post_processor_test.go | 5 +- packer/rpc/provisioner.go | 30 +- packer/rpc/server.go | 25 +- packer/rpc/ui.go | 3 +- packer/testing.go | 32 +- .../alicloud-import/post-processor.go | 4 +- .../post-processor.hcl2spec.go | 15 +- .../amazon-import/post-processor.go | 4 +- .../amazon-import/post-processor.hcl2spec.go | 9 +- post-processor/artifice/post-processor.go | 3 + .../artifice/post-processor.hcl2spec.go | 9 +- post-processor/checksum/post-processor.go | 3 + .../checksum/post-processor.hcl2spec.go | 9 +- post-processor/compress/post-processor.go | 3 + .../compress/post-processor.hcl2spec.go | 9 +- .../digitalocean-import/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- .../docker-import/post-processor.go | 3 + .../docker-import/post-processor.hcl2spec.go | 9 +- post-processor/docker-push/post-processor.go | 3 + .../docker-push/post-processor.hcl2spec.go | 9 +- post-processor/docker-save/post-processor.go | 3 + .../docker-save/post-processor.hcl2spec.go | 9 +- post-processor/docker-tag/post-processor.go | 3 + .../docker-tag/post-processor.hcl2spec.go | 9 +- .../exoscale-import/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- .../googlecompute-export/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- .../googlecompute-import/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- post-processor/manifest/post-processor.go | 3 + .../manifest/post-processor.hcl2spec.go | 9 +- post-processor/shell-local/post-processor.go | 3 + .../ucloud-import/post-processor.go | 15 +- .../ucloud-import/post-processor.hcl2spec.go | 70 ++++ .../vagrant-cloud/post-processor.go | 3 + .../vagrant-cloud/post-processor.hcl2spec.go | 9 +- post-processor/vagrant/post-processor.go | 6 + .../vagrant/post-processor.hcl2spec.go | 9 +- .../vsphere-template/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- post-processor/vsphere/post-processor.go | 3 + .../vsphere/post-processor.hcl2spec.go | 9 +- provisioner/ansible-local/provisioner.go | 3 + .../ansible-local/provisioner.hcl2spec.go | 9 +- provisioner/ansible/provisioner.go | 3 + provisioner/ansible/provisioner.hcl2spec.go | 9 +- provisioner/breakpoint/provisioner.go | 3 + .../breakpoint/provisioner.hcl2spec.go | 9 +- provisioner/chef-client/provisioner.go | 3 + .../chef-client/provisioner.hcl2spec.go | 9 +- provisioner/chef-solo/provisioner.go | 3 + provisioner/chef-solo/provisioner.hcl2spec.go | 9 +- provisioner/converge/provisioner.go | 4 +- provisioner/converge/provisioner.hcl2spec.go | 20 +- provisioner/file/provisioner.go | 3 + provisioner/file/provisioner.hcl2spec.go | 9 +- provisioner/inspec/provisioner.go | 3 + provisioner/inspec/provisioner.hcl2spec.go | 9 +- provisioner/powershell/provisioner.go | 2 + .../powershell/provisioner.hcl2spec.go | 9 +- provisioner/puppet-masterless/provisioner.go | 3 + .../puppet-masterless/provisioner.hcl2spec.go | 9 +- provisioner/puppet-server/provisioner.go | 3 + .../puppet-server/provisioner.hcl2spec.go | 9 +- provisioner/salt-masterless/provisioner.go | 3 + .../salt-masterless/provisioner.hcl2spec.go | 9 +- provisioner/shell-local/provisioner.go | 3 + provisioner/shell/provisioner.go | 3 + provisioner/shell/provisioner.hcl2spec.go | 9 +- provisioner/sleep/provisioner.go | 5 + provisioner/sleep/provisioner.hcl2spec.go | 9 +- provisioner/windows-restart/provisioner.go | 3 + .../windows-restart/provisioner.hcl2spec.go | 9 +- provisioner/windows-shell/provisioner.go | 3 + .../windows-shell/provisioner.hcl2spec.go | 9 +- template/template.hcl2spec.go | 9 +- .../cty/convert/conversion_primitive.go | 11 +- .../go-cty/cty/function/stdlib/format.go | 18 +- vendor/github.com/zclconf/go-cty/cty/gob.go | 84 ++++- .../zclconf/go-cty/cty/set_internals.go | 11 + vendor/modules.txt | 2 +- .../guides/hcl/from-json-v1/index.html.md.erb | 179 +++++++++ website/source/guides/hcl/index.html.md.erb | 52 +++ website/source/layouts/guides.erb | 8 + .../communicator/_SSH-not-required.html.md | 5 +- 348 files changed, 5944 insertions(+), 3160 deletions(-) create mode 100644 builder/amazon/ebssurrogate/builder.hcl2spec.go create mode 100644 builder/amazon/ebsvolume/builder.hcl2spec.go create mode 100644 builder/amazon/instance/builder.hcl2spec.go create mode 100644 builder/azure/chroot/builder.hcl2spec.go create mode 100644 builder/lxc/config.hcl2spec.go rename builder/oracle/classic/{config.hcl2spec.go => builder.hcl2spec.go} (96%) create mode 100644 builder/osc/bsusurrogate/builder.hcl2spec.go delete mode 100644 cmd/hcl2-schema/hcl2-schema.go create mode 100644 command/utils.go create mode 100644 hcl2template/common_test.go delete mode 100644 hcl2template/config_load.go create mode 100644 hcl2template/decode.go delete mode 100644 hcl2template/load_test.go create mode 100644 hcl2template/mock.go create mode 100644 hcl2template/mock.hcl2spec.go delete mode 100644 hcl2template/parser_test.go create mode 100644 hcl2template/testdata/build/post-processor_inexistent.pkr.hcl create mode 100644 hcl2template/testdata/build/post-processor_untyped.pkr.hcl create mode 100644 hcl2template/testdata/build/provisioner_inexistent.pkr.hcl create mode 100644 hcl2template/testdata/build/provisioner_untyped.pkr.hcl delete mode 100644 hcl2template/testdata/complete/communicator.pkr.hcl delete mode 100644 hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl delete mode 100644 hcl2template/testdata/complete/variables.pkr.hcl create mode 100644 hcl2template/testdata/sources/duplicate.pkr.hcl create mode 100644 hcl2template/testdata/sources/inexistent.pkr.hcl create mode 100644 hcl2template/testdata/sources/unnamed.pkr.hcl create mode 100644 hcl2template/testdata/sources/untyped.pkr.hcl create mode 100644 hcl2template/types.build.post-processor.go create mode 100644 hcl2template/types.build_test.go delete mode 100644 hcl2template/types.communicator.go delete mode 100644 hcl2template/types.decodable.go create mode 100644 hcl2template/types.packer_config_test.go create mode 100644 hcl2template/types.source_test.go rename hcl2template/{types.variable.go => types.variables.go} (100%) create mode 100644 hcl2template/types.variables_test.go create mode 100644 hcl2template/utils.go create mode 100644 packer/builder_mock.hcl2spec.go create mode 100644 packer/hcl2spec.go create mode 100644 packer/maps.go create mode 100644 packer/rpc/common.go create mode 100644 packer/rpc/cty_encode.go create mode 100644 post-processor/ucloud-import/post-processor.hcl2spec.go create mode 100644 website/source/guides/hcl/from-json-v1/index.html.md.erb create mode 100644 website/source/guides/hcl/index.html.md.erb diff --git a/builder/alicloud/ecs/builder.go b/builder/alicloud/ecs/builder.go index 5b593cee9..20c070f6a 100644 --- a/builder/alicloud/ecs/builder.go +++ b/builder/alicloud/ecs/builder.go @@ -8,6 +8,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -41,6 +42,8 @@ const ( ALICLOUD_DEFAULT_LONG_TIMEOUT = 3600 ) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/alicloud/ecs/builder.hcl2spec.go b/builder/alicloud/ecs/builder.hcl2spec.go index 595ada017..9b5a36e73 100644 --- a/builder/alicloud/ecs/builder.hcl2spec.go +++ b/builder/alicloud/ecs/builder.hcl2spec.go @@ -22,10 +22,13 @@ type FlatAlicloudDiskDevice struct { // FlatMapstructure returns a new FlatAlicloudDiskDevice. // FlatAlicloudDiskDevice is an auto-generated flat version of AlicloudDiskDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*AlicloudDiskDevice) FlatMapstructure() interface{} { return new(FlatAlicloudDiskDevice) } +func (*AlicloudDiskDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatAlicloudDiskDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatAlicloudDiskDevice. -// This spec is used by HCL to read the fields of FlatAlicloudDiskDevice. +// HCL2Spec returns the hcl spec of a AlicloudDiskDevice. +// This spec is used by HCL to read the fields of AlicloudDiskDevice. +// The decoded values from this spec will then be applied to a FlatAlicloudDiskDevice. func (*FlatAlicloudDiskDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false}, @@ -121,8 +124,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -137,10 +140,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -169,7 +175,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_ignore_data_disks": &hcldec.AttrSpec{Name: "image_ignore_data_disks", Type: cty.Bool, Required: false}, "tags": &hcldec.BlockAttrsSpec{TypeName: "tags", ElementType: cty.String, Required: false}, "system_disk_mapping": &hcldec.BlockSpec{TypeName: "system_disk_mapping", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}, - "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}}, + "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "zone_id": &hcldec.AttrSpec{Name: "zone_id", Type: cty.String, Required: false}, "io_optimized": &hcldec.AttrSpec{Name: "io_optimized", Type: cty.Bool, Required: false}, diff --git a/builder/alicloud/ecs/step_create_snapshot.go b/builder/alicloud/ecs/step_create_snapshot.go index c9e934d60..32f155e08 100644 --- a/builder/alicloud/ecs/step_create_snapshot.go +++ b/builder/alicloud/ecs/step_create_snapshot.go @@ -3,9 +3,9 @@ package ecs import ( "context" "fmt" - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "time" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index f1cf7432a..91d00f749 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -13,6 +13,7 @@ import ( "runtime" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/chroot" @@ -182,6 +183,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/amazon/chroot/builder.hcl2spec.go b/builder/amazon/chroot/builder.hcl2spec.go index f1f0a2c50..e040855ee 100644 --- a/builder/amazon/chroot/builder.hcl2spec.go +++ b/builder/amazon/chroot/builder.hcl2spec.go @@ -72,10 +72,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -116,7 +119,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, - "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "chroot_mounts": &hcldec.BlockListSpec{TypeName: "chroot_mounts", Nested: &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.String), Required: false}}, "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, "copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false}, diff --git a/builder/amazon/common/access_config.hcl2spec.go b/builder/amazon/common/access_config.hcl2spec.go index b3bdad7e0..f61fa5e40 100644 --- a/builder/amazon/common/access_config.hcl2spec.go +++ b/builder/amazon/common/access_config.hcl2spec.go @@ -18,10 +18,13 @@ type FlatVaultAWSEngineOptions struct { // FlatMapstructure returns a new FlatVaultAWSEngineOptions. // FlatVaultAWSEngineOptions is an auto-generated flat version of VaultAWSEngineOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*VaultAWSEngineOptions) FlatMapstructure() interface{} { return new(FlatVaultAWSEngineOptions) } +func (*VaultAWSEngineOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVaultAWSEngineOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatVaultAWSEngineOptions. -// This spec is used by HCL to read the fields of FlatVaultAWSEngineOptions. +// HCL2Spec returns the hcl spec of a VaultAWSEngineOptions. +// This spec is used by HCL to read the fields of VaultAWSEngineOptions. +// The decoded values from this spec will then be applied to a FlatVaultAWSEngineOptions. func (*FlatVaultAWSEngineOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, diff --git a/builder/amazon/common/block_device.hcl2spec.go b/builder/amazon/common/block_device.hcl2spec.go index 1865bda3a..e0f3013f3 100644 --- a/builder/amazon/common/block_device.hcl2spec.go +++ b/builder/amazon/common/block_device.hcl2spec.go @@ -24,10 +24,13 @@ type FlatBlockDevice struct { // FlatMapstructure returns a new FlatBlockDevice. // FlatBlockDevice is an auto-generated flat version of BlockDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) } +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice. -// This spec is used by HCL to read the fields of FlatBlockDevice. +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, diff --git a/builder/amazon/common/run_config.hcl2spec.go b/builder/amazon/common/run_config.hcl2spec.go index 2ef6ce62d..d27b01148 100644 --- a/builder/amazon/common/run_config.hcl2spec.go +++ b/builder/amazon/common/run_config.hcl2spec.go @@ -17,10 +17,13 @@ type FlatAmiFilterOptions struct { // FlatMapstructure returns a new FlatAmiFilterOptions. // FlatAmiFilterOptions is an auto-generated flat version of AmiFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*AmiFilterOptions) FlatMapstructure() interface{} { return new(FlatAmiFilterOptions) } +func (*AmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatAmiFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatAmiFilterOptions. -// This spec is used by HCL to read the fields of FlatAmiFilterOptions. +// HCL2Spec returns the hcl spec of a AmiFilterOptions. +// This spec is used by HCL to read the fields of AmiFilterOptions. +// The decoded values from this spec will then be applied to a FlatAmiFilterOptions. func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -40,14 +43,17 @@ type FlatPolicyDocument struct { // FlatMapstructure returns a new FlatPolicyDocument. // FlatPolicyDocument is an auto-generated flat version of PolicyDocument. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*PolicyDocument) FlatMapstructure() interface{} { return new(FlatPolicyDocument) } +func (*PolicyDocument) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPolicyDocument) +} -// HCL2Spec returns the hcldec.Spec of a FlatPolicyDocument. -// This spec is used by HCL to read the fields of FlatPolicyDocument. +// HCL2Spec returns the hcl spec of a PolicyDocument. +// This spec is used by HCL to read the fields of PolicyDocument. +// The decoded values from this spec will then be applied to a FlatPolicyDocument. func (*FlatPolicyDocument) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false}, - "statement": &hcldec.BlockListSpec{TypeName: "statement", Nested: &hcldec.BlockSpec{TypeName: "statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())}}, + "statement": &hcldec.BlockListSpec{TypeName: "statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())}, } return s } @@ -61,12 +67,13 @@ type FlatSecurityGroupFilterOptions struct { // FlatMapstructure returns a new FlatSecurityGroupFilterOptions. // FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SecurityGroupFilterOptions) FlatMapstructure() interface{} { +func (*SecurityGroupFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSecurityGroupFilterOptions) } -// HCL2Spec returns the hcldec.Spec of a FlatSecurityGroupFilterOptions. -// This spec is used by HCL to read the fields of FlatSecurityGroupFilterOptions. +// HCL2Spec returns the hcl spec of a SecurityGroupFilterOptions. +// This spec is used by HCL to read the fields of SecurityGroupFilterOptions. +// The decoded values from this spec will then be applied to a FlatSecurityGroupFilterOptions. func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -85,10 +92,13 @@ type FlatStatement struct { // FlatMapstructure returns a new FlatStatement. // FlatStatement is an auto-generated flat version of Statement. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Statement) FlatMapstructure() interface{} { return new(FlatStatement) } +func (*Statement) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatStatement) +} -// HCL2Spec returns the hcldec.Spec of a FlatStatement. -// This spec is used by HCL to read the fields of FlatStatement. +// HCL2Spec returns the hcl spec of a Statement. +// This spec is used by HCL to read the fields of Statement. +// The decoded values from this spec will then be applied to a FlatStatement. func (*FlatStatement) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "effect": &hcldec.AttrSpec{Name: "effect", Type: cty.String, Required: false}, @@ -109,10 +119,13 @@ type FlatSubnetFilterOptions struct { // FlatMapstructure returns a new FlatSubnetFilterOptions. // FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SubnetFilterOptions) FlatMapstructure() interface{} { return new(FlatSubnetFilterOptions) } +func (*SubnetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSubnetFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatSubnetFilterOptions. -// This spec is used by HCL to read the fields of FlatSubnetFilterOptions. +// HCL2Spec returns the hcl spec of a SubnetFilterOptions. +// This spec is used by HCL to read the fields of SubnetFilterOptions. +// The decoded values from this spec will then be applied to a FlatSubnetFilterOptions. func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -131,10 +144,13 @@ type FlatVpcFilterOptions struct { // FlatMapstructure returns a new FlatVpcFilterOptions. // FlatVpcFilterOptions is an auto-generated flat version of VpcFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*VpcFilterOptions) FlatMapstructure() interface{} { return new(FlatVpcFilterOptions) } +func (*VpcFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVpcFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatVpcFilterOptions. -// This spec is used by HCL to read the fields of FlatVpcFilterOptions. +// HCL2Spec returns the hcl spec of a VpcFilterOptions. +// This spec is used by HCL to read the fields of VpcFilterOptions. +// The decoded values from this spec will then be applied to a FlatVpcFilterOptions. func (*FlatVpcFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, diff --git a/builder/amazon/common/step_iam_instance_profile.go b/builder/amazon/common/step_iam_instance_profile.go index eba357769..3ef72e9ee 100644 --- a/builder/amazon/common/step_iam_instance_profile.go +++ b/builder/amazon/common/step_iam_instance_profile.go @@ -2,11 +2,10 @@ package common import ( "context" + "encoding/json" "fmt" "log" - "encoding/json" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/packer/common/uuid" diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index f8648e204..6e9b25de4 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -72,6 +73,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index 991b443db..cb738d843 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -107,8 +107,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -127,10 +127,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -241,8 +244,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, - "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, - "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, "no_ephemeral": &hcldec.AttrSpec{Name: "no_ephemeral", Type: cty.Bool, Required: false}, } diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index abf159fad..4a52c1de9 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config,RootBlockDevice,BlockDevice // The ebssurrogate package contains a packer.Builder implementation that // builds a new EBS-backed AMI using an ephemeral instance. @@ -11,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -70,6 +72,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/amazon/ebssurrogate/builder.hcl2spec.go b/builder/amazon/ebssurrogate/builder.hcl2spec.go new file mode 100644 index 000000000..5b9f1c1dd --- /dev/null +++ b/builder/amazon/ebssurrogate/builder.hcl2spec.go @@ -0,0 +1,331 @@ +// Code generated by "mapstructure-to-hcl2 -type Config,RootBlockDevice,BlockDevice"; DO NOT EDIT. +package ebssurrogate + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/amazon/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatBlockDevice struct { + DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"` + DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"` + Encrypted *bool `mapstructure:"encrypted" required:"false" cty:"encrypted"` + IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"` + NoDevice *bool `mapstructure:"no_device" required:"false" cty:"no_device"` + SnapshotId *string `mapstructure:"snapshot_id" required:"false" cty:"snapshot_id"` + VirtualName *string `mapstructure:"virtual_name" required:"false" cty:"virtual_name"` + VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"` + KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + OmitFromArtifact *bool `mapstructure:"omit_from_artifact" cty:"omit_from_artifact"` +} + +// FlatMapstructure returns a new FlatBlockDevice. +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} + +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. +func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "encrypted": &hcldec.AttrSpec{Name: "encrypted", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "no_device": &hcldec.AttrSpec{Name: "no_device", Type: cty.Bool, Required: false}, + "snapshot_id": &hcldec.AttrSpec{Name: "snapshot_id", Type: cty.String, Required: false}, + "virtual_name": &hcldec.AttrSpec{Name: "virtual_name", Type: cty.String, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "omit_from_artifact": &hcldec.AttrSpec{Name: "omit_from_artifact", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" required:"false" cty:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` + IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` + TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` + InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"` + SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` + SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` + SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"` + SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"` + TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name"` + AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description"` + AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type"` + AMIUsers []string `mapstructure:"ami_users" required:"false" cty:"ami_users"` + AMIGroups []string `mapstructure:"ami_groups" required:"false" cty:"ami_groups"` + AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"` + AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"` + AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` + AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"` + AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"` + AMIForceDeregister *bool `mapstructure:"force_deregister" required:"false" cty:"force_deregister"` + AMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" required:"false" cty:"force_delete_snapshot"` + AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot" required:"false" cty:"encrypt_boot"` + AMIKmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids" required:"false" cty:"region_kms_key_ids"` + AMISkipBuildRegion *bool `mapstructure:"skip_save_build_region" cty:"skip_save_build_region"` + SnapshotTags common.TagMap `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"` + SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"` + AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings"` + LaunchMappings []FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings"` + RootDevice *FlatRootBlockDevice `mapstructure:"ami_root_device" required:"true" cty:"ami_root_device"` + VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"` + Architecture *string `mapstructure:"ami_architecture" required:"false" cty:"ami_architecture"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, + "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false}, + "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, + "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false}, + "source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())}, + "spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())}, + "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, + "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, + "ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false}, + "ami_users": &hcldec.AttrSpec{Name: "ami_users", Type: cty.List(cty.String), Required: false}, + "ami_groups": &hcldec.AttrSpec{Name: "ami_groups", Type: cty.List(cty.String), Required: false}, + "ami_product_codes": &hcldec.AttrSpec{Name: "ami_product_codes", Type: cty.List(cty.String), Required: false}, + "ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, + "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, + "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, + "force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false}, + "encrypt_boot": &hcldec.AttrSpec{Name: "encrypt_boot", Type: cty.Bool, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "region_kms_key_ids": &hcldec.BlockAttrsSpec{TypeName: "region_kms_key_ids", ElementType: cty.String, Required: false}, + "skip_save_build_region": &hcldec.AttrSpec{Name: "skip_save_build_region", Type: cty.Bool, Required: false}, + "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false}, + "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}, + "ami_root_device": &hcldec.BlockSpec{TypeName: "ami_root_device", Nested: hcldec.ObjectSpec((*FlatRootBlockDevice)(nil).HCL2Spec())}, + "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "ami_architecture": &hcldec.AttrSpec{Name: "ami_architecture", Type: cty.String, Required: false}, + } + return s +} + +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRootBlockDevice struct { + SourceDeviceName *string `mapstructure:"source_device_name" cty:"source_device_name"` + DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"` + DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"` + IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"` + VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"` +} + +// FlatMapstructure returns a new FlatRootBlockDevice. +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RootBlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRootBlockDevice) +} + +// HCL2Spec returns the hcl spec of a RootBlockDevice. +// This spec is used by HCL to read the fields of RootBlockDevice. +// The decoded values from this spec will then be applied to a FlatRootBlockDevice. +func (*FlatRootBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "source_device_name": &hcldec.AttrSpec{Name: "source_device_name", Type: cty.String, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index b3d05299d..886123cb4 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config,BlockDevice // The ebsvolume package contains a packer.Builder implementation that builds // EBS volumes for Amazon EC2 using an ephemeral instance, @@ -10,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -79,6 +81,8 @@ type EngineVarsTemplate struct { SourceAMI string } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs // Create passthrough for {{ .BuildRegion }} and {{ .SourceAMI }} variables diff --git a/builder/amazon/ebsvolume/builder.hcl2spec.go b/builder/amazon/ebsvolume/builder.hcl2spec.go new file mode 100644 index 000000000..20aab5aef --- /dev/null +++ b/builder/amazon/ebsvolume/builder.hcl2spec.go @@ -0,0 +1,258 @@ +// Code generated by "mapstructure-to-hcl2 -type Config,BlockDevice"; DO NOT EDIT. +package ebsvolume + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/amazon/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatBlockDevice struct { + DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"` + DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"` + Encrypted *bool `mapstructure:"encrypted" required:"false" cty:"encrypted"` + IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"` + NoDevice *bool `mapstructure:"no_device" required:"false" cty:"no_device"` + SnapshotId *string `mapstructure:"snapshot_id" required:"false" cty:"snapshot_id"` + VirtualName *string `mapstructure:"virtual_name" required:"false" cty:"virtual_name"` + VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"` + KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + Tags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` +} + +// FlatMapstructure returns a new FlatBlockDevice. +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} + +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. +func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "encrypted": &hcldec.AttrSpec{Name: "encrypted", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "no_device": &hcldec.AttrSpec{Name: "no_device", Type: cty.Bool, Required: false}, + "snapshot_id": &hcldec.AttrSpec{Name: "snapshot_id", Type: cty.String, Required: false}, + "virtual_name": &hcldec.AttrSpec{Name: "virtual_name", Type: cty.String, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + } + return s +} + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" required:"false" cty:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` + IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` + TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` + InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"` + SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` + SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` + SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"` + SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"` + TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"` + AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"` + VolumeMappings []FlatBlockDevice `mapstructure:"ebs_volumes" required:"false" cty:"ebs_volumes"` + VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, + "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false}, + "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, + "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false}, + "source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())}, + "spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())}, + "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, + "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, + "ebs_volumes": &hcldec.BlockListSpec{TypeName: "ebs_volumes", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}, + "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + } + return s +} diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 92a761459..ba87b61d8 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config // The instance package contains a packer.Builder implementation that builds // AMIs for Amazon EC2 backed by instance storage, as opposed to EBS storage. @@ -13,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -93,6 +95,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { configs := make([]interface{}, len(raws)+1) configs[0] = map[string]interface{}{ diff --git a/builder/amazon/instance/builder.hcl2spec.go b/builder/amazon/instance/builder.hcl2spec.go new file mode 100644 index 000000000..a78c4fa2d --- /dev/null +++ b/builder/amazon/instance/builder.hcl2spec.go @@ -0,0 +1,267 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package instance + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/amazon/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" required:"false" cty:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"` + AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name"` + AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description"` + AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type"` + AMIUsers []string `mapstructure:"ami_users" required:"false" cty:"ami_users"` + AMIGroups []string `mapstructure:"ami_groups" required:"false" cty:"ami_groups"` + AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"` + AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"` + AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` + AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"` + AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"` + AMIForceDeregister *bool `mapstructure:"force_deregister" required:"false" cty:"force_deregister"` + AMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" required:"false" cty:"force_delete_snapshot"` + AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot" required:"false" cty:"encrypt_boot"` + AMIKmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids" required:"false" cty:"region_kms_key_ids"` + AMISkipBuildRegion *bool `mapstructure:"skip_save_build_region" cty:"skip_save_build_region"` + SnapshotTags common.TagMap `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"` + SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` + IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` + TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` + InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"` + SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` + SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` + SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"` + SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"` + TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings"` + LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings"` + AccountId *string `mapstructure:"account_id" required:"true" cty:"account_id"` + BundleDestination *string `mapstructure:"bundle_destination" required:"false" cty:"bundle_destination"` + BundlePrefix *string `mapstructure:"bundle_prefix" required:"false" cty:"bundle_prefix"` + BundleUploadCommand *string `mapstructure:"bundle_upload_command" required:"false" cty:"bundle_upload_command"` + BundleVolCommand *string `mapstructure:"bundle_vol_command" required:"false" cty:"bundle_vol_command"` + S3Bucket *string `mapstructure:"s3_bucket" required:"true" cty:"s3_bucket"` + X509CertPath *string `mapstructure:"x509_cert_path" required:"true" cty:"x509_cert_path"` + X509KeyPath *string `mapstructure:"x509_key_path" required:"true" cty:"x509_key_path"` + X509UploadPath *string `mapstructure:"x509_upload_path" required:"false" cty:"x509_upload_path"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, + "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, + "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, + "ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false}, + "ami_users": &hcldec.AttrSpec{Name: "ami_users", Type: cty.List(cty.String), Required: false}, + "ami_groups": &hcldec.AttrSpec{Name: "ami_groups", Type: cty.List(cty.String), Required: false}, + "ami_product_codes": &hcldec.AttrSpec{Name: "ami_product_codes", Type: cty.List(cty.String), Required: false}, + "ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, + "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, + "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, + "force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false}, + "encrypt_boot": &hcldec.AttrSpec{Name: "encrypt_boot", Type: cty.Bool, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "region_kms_key_ids": &hcldec.BlockAttrsSpec{TypeName: "region_kms_key_ids", ElementType: cty.String, Required: false}, + "skip_save_build_region": &hcldec.AttrSpec{Name: "skip_save_build_region", Type: cty.Bool, Required: false}, + "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false}, + "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false}, + "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, + "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false}, + "source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())}, + "spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())}, + "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "account_id": &hcldec.AttrSpec{Name: "account_id", Type: cty.String, Required: false}, + "bundle_destination": &hcldec.AttrSpec{Name: "bundle_destination", Type: cty.String, Required: false}, + "bundle_prefix": &hcldec.AttrSpec{Name: "bundle_prefix", Type: cty.String, Required: false}, + "bundle_upload_command": &hcldec.AttrSpec{Name: "bundle_upload_command", Type: cty.String, Required: false}, + "bundle_vol_command": &hcldec.AttrSpec{Name: "bundle_vol_command", Type: cty.String, Required: false}, + "s3_bucket": &hcldec.AttrSpec{Name: "s3_bucket", Type: cty.String, Required: false}, + "x509_cert_path": &hcldec.AttrSpec{Name: "x509_cert_path", Type: cty.String, Required: false}, + "x509_key_path": &hcldec.AttrSpec{Name: "x509_key_path", Type: cty.String, Required: false}, + "x509_upload_path": &hcldec.AttrSpec{Name: "x509_upload_path", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 2a06f1b84..39917f0a2 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -14,6 +14,7 @@ import ( "github.com/Azure/azure-sdk-for-go/storage" "github.com/Azure/go-autorest/autorest/adal" "github.com/dgrijalva/jwt-go" + "github.com/hashicorp/hcl/v2/hcldec" packerAzureCommon "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/common/lin" @@ -24,7 +25,7 @@ import ( ) type Builder struct { - config *Config + config Config stateBag multistep.StateBag runner multistep.Runner } @@ -34,14 +35,14 @@ const ( DefaultSecretName = "packerKeyVaultSecret" ) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := newConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c - b.stateBag = new(multistep.BasicStateBag) b.configureStateBag(b.stateBag) b.setTemplateParameters(b.stateBag) @@ -64,7 +65,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } log.Print(":: Configuration") - packerAzureCommon.DumpConfig(b.config, func(s string) { log.Print(s) }) + packerAzureCommon.DumpConfig(&b.config, func(s string) { log.Print(s) }) b.stateBag.Put("hook", hook) b.stateBag.Put(constants.Ui, ui) @@ -90,7 +91,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } resolver := newResourceResolver(azureClient) - if err := resolver.Resolve(b.config); err != nil { + if err := resolver.Resolve(&b.config); err != nil { return nil, err } if b.config.ClientConfig.ObjectID == "" { @@ -197,8 +198,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.OSType == constants.Target_Linux { steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), - NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), + NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), &communicator.StepConnectSSH{ Config: &b.config.Comm, @@ -212,10 +213,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack NewStepGetOSDisk(azureClient, ui), NewStepGetAdditionalDisks(azureClient, ui), NewStepPowerOffCompute(azureClient, ui), - NewStepSnapshotOSDisk(azureClient, ui, b.config), - NewStepSnapshotDataDisks(azureClient, ui, b.config), + NewStepSnapshotOSDisk(azureClient, ui, &b.config), + NewStepSnapshotDataDisks(azureClient, ui, &b.config), NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, b.config), + NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), NewStepDeleteAdditionalDisks(azureClient, ui), @@ -224,12 +225,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string) steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), - NewStepValidateTemplate(azureClient, ui, b.config, GetKeyVaultDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, keyVaultDeploymentName, GetKeyVaultDeployment), + NewStepValidateTemplate(azureClient, ui, &b.config, GetKeyVaultDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetKeyVaultDeployment), NewStepGetCertificate(azureClient, ui), - NewStepSetCertificate(b.config, ui), - NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), + NewStepSetCertificate(&b.config, ui), + NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), &StepSaveWinRMPassword{ Password: b.config.tmpAdminPassword, @@ -251,10 +252,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack NewStepGetOSDisk(azureClient, ui), NewStepGetAdditionalDisks(azureClient, ui), NewStepPowerOffCompute(azureClient, ui), - NewStepSnapshotOSDisk(azureClient, ui, b.config), - NewStepSnapshotDataDisks(azureClient, ui, b.config), + NewStepSnapshotOSDisk(azureClient, ui, &b.config), + NewStepSnapshotDataDisks(azureClient, ui, &b.config), NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, b.config), + NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), NewStepDeleteAdditionalDisks(azureClient, ui), diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go index a71f92874..d22a49314 100644 --- a/builder/azure/arm/builder_test.go +++ b/builder/azure/arm/builder_test.go @@ -7,7 +7,7 @@ import ( ) func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { - var testSubject = &Builder{} + var testSubject Builder _, err := testSubject.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if err != nil { t.Fatalf("failed to prepare: %s", err) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 86ffab134..271ff967c 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -493,58 +493,57 @@ func (c *Config) createCertificate() (string, error) { return base64.StdEncoding.EncodeToString(bytes), nil } -func newConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ctx.Funcs = azcommon.TemplateFuncs - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, nil, err + return nil, err } - provideDefaultValues(&c) - setRuntimeValues(&c) - setUserNamePassword(&c) + provideDefaultValues(c) + setRuntimeValues(c) + setUserNamePassword(c) err = c.ClientConfig.SetDefaultValues() if err != nil { - return nil, nil, err + return nil, err } - err = setCustomData(&c) + err = setCustomData(c) if err != nil { - return nil, nil, err + return nil, err } // NOTE: if the user did not specify a communicator, then default to both // SSH and WinRM. This is for backwards compatibility because the code did // not specifically force the user to set a communicator. if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "ssh") { - err = setSshValues(&c) + err = setSshValues(c) if err != nil { - return nil, nil, err + return nil, err } } if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "winrm") { - err = setWinRMCertificate(&c) + err = setWinRMCertificate(c) if err != nil { - return nil, nil, err + return nil, err } } var errs *packer.MultiError errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...) - assertRequiredParametersSet(&c, errs) - assertTagProperties(&c, errs) + assertRequiredParametersSet(c, errs) + assertTagProperties(c, errs) if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return &c, nil, nil + return nil, nil } func setSshValues(c *Config) error { diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index 264120891..317528478 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -94,8 +94,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -110,10 +110,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -228,10 +231,13 @@ type FlatPlanInformation struct { // FlatMapstructure returns a new FlatPlanInformation. // FlatPlanInformation is an auto-generated flat version of PlanInformation. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*PlanInformation) FlatMapstructure() interface{} { return new(FlatPlanInformation) } +func (*PlanInformation) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPlanInformation) +} -// HCL2Spec returns the hcldec.Spec of a FlatPlanInformation. -// This spec is used by HCL to read the fields of FlatPlanInformation. +// HCL2Spec returns the hcl spec of a PlanInformation. +// This spec is used by HCL to read the fields of PlanInformation. +// The decoded values from this spec will then be applied to a FlatPlanInformation. func (*FlatPlanInformation) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "plan_name": &hcldec.AttrSpec{Name: "plan_name", Type: cty.String, Required: false}, @@ -255,10 +261,13 @@ type FlatSharedImageGallery struct { // FlatMapstructure returns a new FlatSharedImageGallery. // FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGallery) FlatMapstructure() interface{} { return new(FlatSharedImageGallery) } +func (*SharedImageGallery) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSharedImageGallery) +} -// HCL2Spec returns the hcldec.Spec of a FlatSharedImageGallery. -// This spec is used by HCL to read the fields of FlatSharedImageGallery. +// HCL2Spec returns the hcl spec of a SharedImageGallery. +// This spec is used by HCL to read the fields of SharedImageGallery. +// The decoded values from this spec will then be applied to a FlatSharedImageGallery. func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false}, @@ -283,12 +292,13 @@ type FlatSharedImageGalleryDestination struct { // FlatMapstructure returns a new FlatSharedImageGalleryDestination. // FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGalleryDestination) FlatMapstructure() interface{} { +func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSharedImageGalleryDestination) } -// HCL2Spec returns the hcldec.Spec of a FlatSharedImageGalleryDestination. -// This spec is used by HCL to read the fields of FlatSharedImageGalleryDestination. +// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination. +// This spec is used by HCL to read the fields of SharedImageGalleryDestination. +// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination. func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 52ad4814e..128df2be3 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -26,7 +26,8 @@ var requiredConfigValues = []string{ } func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) { - c, _, err := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + _, err := c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if err != nil { t.Error("Expected configuration creation to succeed, but it failed!\n") @@ -63,7 +64,8 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { builderValues["managed_image_storage_account_type"] = "Premium_LRS" builderValues["disk_caching_type"] = "None" - c, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Fatalf("newConfig failed: %s", err) @@ -99,7 +101,8 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { } func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.VMSize != "Standard_A1" { t.Errorf("Expected 'VMSize' to default to 'Standard_A1', but got '%s'.", c.VMSize) @@ -107,7 +110,8 @@ func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) { } func TestConfigShouldDefaultImageVersionToLatest(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.ImageVersion != "latest" { t.Errorf("Expected 'ImageVersion' to default to 'latest', but got '%s'.", c.ImageVersion) @@ -127,7 +131,8 @@ func TestConfigShouldNotDefaultImageVersionIfCustomImage(t *testing.T) { "communicator": "none", } - c, _, _ := newConfig(config, getPackerConfiguration()) + var c Config + c.Prepare(config, getPackerConfiguration()) if c.ImageVersion != "" { t.Errorf("Expected 'ImageVersion' to empty, but got '%s'.", c.ImageVersion) } @@ -153,7 +158,8 @@ func TestConfigShouldNormalizeOSTypeCase(t *testing.T) { for k, v := range os_types { for _, os_type := range v { config["os_type"] = os_type - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("Expected config to accept the value %q, but it did not", os_type) } @@ -167,7 +173,8 @@ func TestConfigShouldNormalizeOSTypeCase(t *testing.T) { bad_os_types := []string{"", "does-not-exist"} for _, os_type := range bad_os_types { config["os_type"] = os_type - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatalf("Expected config to not accept the value %q, but it did", os_type) } @@ -191,7 +198,8 @@ func TestConfigShouldRejectCustomImageAndMarketPlace(t *testing.T) { for _, x := range marketPlace { config[x] = "ignore" - _, _, err := newConfig(config, packerConfiguration) + var c Config + _, err := c.Prepare(config, packerConfiguration) if err == nil { t.Errorf("Expected Config to reject image_url and %s, but it did not", x) } @@ -212,7 +220,8 @@ func TestConfigVirtualNetworkNameIsOptional(t *testing.T) { "virtual_network_name": "MyVirtualNetwork", } - c, _, _ := newConfig(config, getPackerConfiguration()) + var c Config + c.Prepare(config, getPackerConfiguration()) if c.VirtualNetworkName != "MyVirtualNetwork" { t.Errorf("Expected Config to set virtual_network_name to MyVirtualNetwork, but got %q", c.VirtualNetworkName) } @@ -241,7 +250,8 @@ func TestConfigVirtualNetworkResourceGroupNameMustBeSetWithVirtualNetworkName(t "virtual_network_resource_group_name": "MyVirtualNetworkRG", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Error("Expected Config to reject virtual_network_resource_group_name, if virtual_network_name is not set.") } @@ -264,7 +274,8 @@ func TestConfigVirtualNetworkSubnetNameMustBeSetWithVirtualNetworkName(t *testin "virtual_network_subnet_name": "MyVirtualNetworkRG", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Error("Expected Config to reject virtual_network_subnet_name, if virtual_network_name is not set.") } @@ -284,7 +295,8 @@ func TestConfigAllowedInboundIpAddressesIsOptional(t *testing.T) { "virtual_network_name": "MyVirtualNetwork", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -311,7 +323,8 @@ func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = ipValue0 - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -321,7 +334,7 @@ func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = cidrValue2 - c, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -331,7 +344,7 @@ func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = []string{ipValue0, cidrValue2, ipValue1, cidrValue3} - c, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -357,13 +370,14 @@ func TestConfigShouldRejectIncorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = []string{"127.0.0.1", "127.0.0.two"} - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) } config["allowed_inbound_ip_addresses"] = []string{"192.168.100.1000/24", "10.10.1.16/32"} - c, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err == nil { // 192.168.100.1000/24 is invalid t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) @@ -384,20 +398,22 @@ func TestConfigShouldRejectInboundIpAddressesWithVirtualNetwork(t *testing.T) { "allowed_inbound_ip_addresses": "127.0.0.1", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } config["virtual_network_name"] = "some_vnet_name" - _, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected configuration creation to fail, but it succeeded with allowed_inbound_ip_addresses and virtual_network_name both specified") } } func TestConfigShouldDefaultToPublicCloud(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.ClientConfig.CloudEnvironmentName != "Public" { t.Errorf("Expected 'CloudEnvironmentName' to default to 'Public', but got '%s'.", c.ClientConfig.CloudEnvironmentName) @@ -448,7 +464,8 @@ func TestConfigInstantiatesCorrectAzureEnvironment(t *testing.T) { for _, x := range table { config["cloud_environment_name"] = x.name - c, _, err := newConfig(config, packerConfiguration) + var c Config + _, err := c.Prepare(config, packerConfiguration) if err != nil { t.Fatal(err) } @@ -463,7 +480,8 @@ func TestUserShouldProvideRequiredValues(t *testing.T) { builderValues := getArmBuilderConfiguration() // Ensure we can successfully create a config. - _, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Error("Expected configuration creation to succeed, but it failed!\n") t.Fatalf(" -> %+v\n", builderValues) @@ -474,7 +492,8 @@ func TestUserShouldProvideRequiredValues(t *testing.T) { originalValue := builderValues[v] delete(builderValues, v) - _, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err == nil { t.Error("Expected configuration creation to fail, but it succeeded!\n") t.Fatalf(" -> %+v\n", builderValues) @@ -485,7 +504,8 @@ func TestUserShouldProvideRequiredValues(t *testing.T) { } func TestSystemShouldDefineRuntimeValues(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.Password == "" { t.Errorf("Expected Password to not be empty, but it was '%s'!", c.Password) @@ -513,7 +533,8 @@ func TestSystemShouldDefineRuntimeValues(t *testing.T) { } func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) parameters := c.toVirtualMachineCaptureParameters() if *parameters.DestinationContainerName != c.CaptureContainerName { @@ -530,7 +551,8 @@ func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { } func TestConfigShouldSupportPackersConfigElements(t *testing.T) { - c, _, err := newConfig( + var c Config + _, err := c.Prepare( getArmBuilderConfiguration(), getPackerConfiguration(), getPackerCommunicatorConfiguration()) @@ -554,7 +576,8 @@ func TestWinRMConfigShouldSetRoundTripDecorator(t *testing.T) { config["winrm_username"] = "username" config["winrm_password"] = "password" - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -579,7 +602,8 @@ func TestUserDeviceLoginIsEnabledForLinux(t *testing.T) { "communicator": "none", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("failed to use device login for Linux: %s", err) } @@ -610,7 +634,8 @@ func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { for _, x := range wellFormedCaptureNamePrefix { config["capture_name_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed capture_name_prefix set to %q.", x) @@ -628,7 +653,8 @@ func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { for _, x := range malformedCaptureNamePrefix { config["capture_name_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed capture_name_prefix set to %q.", x) @@ -660,7 +686,8 @@ func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { for _, x := range wellFormedCaptureContainerName { config["capture_container_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed capture_container_name set to %q.", x) @@ -678,7 +705,8 @@ func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { for _, x := range malformedCaptureContainerName { config["capture_container_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed capture_container_name set to %q.", x) @@ -710,7 +738,8 @@ func TestConfigShouldRejectMalformedManagedImageOSDiskSnapshotName(t *testing.T) for _, x := range wellFormedManagedImageOSDiskSnapshotName { config["managed_image_os_disk_snapshot_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed managed_image_os_disk_snapshot_name set to %q.", x) @@ -727,7 +756,8 @@ func TestConfigShouldRejectMalformedManagedImageOSDiskSnapshotName(t *testing.T) for _, x := range malformedManagedImageOSDiskSnapshotName { config["managed_image_os_disk_snapshot_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed managed_image_os_disk_snapshot_name set to %q.", x) @@ -760,7 +790,8 @@ func TestConfigShouldRejectMalformedManagedImageDataDiskSnapshotPrefix(t *testin for _, x := range wellFormedManagedImageDataDiskSnapshotPrefix { config["managed_image_data_disk_snapshot_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed managed_image_data_disk_snapshot_prefix set to %q.", x) @@ -777,7 +808,8 @@ func TestConfigShouldRejectMalformedManagedImageDataDiskSnapshotPrefix(t *testin for _, x := range malformedManagedImageDataDiskSnapshotPrefix { config["managed_image_data_disk_snapshot_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed managed_image_data_disk_snapshot_prefix set to %q.", x) @@ -805,7 +837,8 @@ func TestConfigShouldAcceptTags(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) @@ -855,7 +888,8 @@ func TestConfigShouldRejectTagsInExcessOf15AcceptTags(t *testing.T) { "azure_tags": tooManyTags, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject based on an excessive amount of tags (> 15)") @@ -887,7 +921,8 @@ func TestConfigShouldRejectExcessiveTagNameLength(t *testing.T) { "azure_tags": tags, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject tag name based on length (> 512)") } @@ -918,7 +953,8 @@ func TestConfigShouldRejectExcessiveTagValueLength(t *testing.T) { "azure_tags": tags, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject tag value based on length (> 256)") } @@ -935,7 +971,8 @@ func TestConfigZoneResilientShouldDefaultToFalse(t *testing.T) { "os_type": "linux", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -958,7 +995,8 @@ func TestConfigZoneResilientSetFromConfig(t *testing.T) { "managed_image_zone_resilient": true, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -986,7 +1024,8 @@ func TestConfigShouldRejectMissingCustomDataFile(t *testing.T) { "custom_data_file": "/this/file/does/not/exist", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject missing custom data file") } @@ -1006,7 +1045,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameWithoutManagedImageName "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with OS disk snapshot name but without managed image name") } @@ -1026,7 +1066,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameWithoutManagedImageReso "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with OS disk snapshot name but without managed image resource group name") } @@ -1046,7 +1087,8 @@ func TestConfigShouldRejectImageDataDiskSnapshotPrefixWithoutManagedImageName(t "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix but without managed image name") } @@ -1066,7 +1108,8 @@ func TestConfigShouldRejectImageDataDiskSnapshotPrefixWithoutManagedImageResourc "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix but without managed image resource group name") } @@ -1088,7 +1131,8 @@ func TestConfigShouldAcceptManagedImageOSDiskSnapshotNameAndManagedImageDataDisk "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal("expected config to accept platform managed image build") } @@ -1109,7 +1153,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameAndManagedImageDataDisk "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix and OS disk snapshot name with capture container name") } @@ -1130,7 +1175,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameAndManagedImageDataDisk "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix and OS disk snapshot name with capture name prefix") } @@ -1151,7 +1197,8 @@ func TestConfigShouldAcceptPlatformManagedImageBuild(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal("expected config to accept platform managed image build") } @@ -1175,7 +1222,8 @@ func TestConfigShouldRejectVhdAndManagedImageOutput(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject VHD and Managed Image build") } @@ -1195,7 +1243,8 @@ func TestConfigShouldRejectManagedImageSourceAndVhdOutput(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject VHD and Managed Image build") } @@ -1218,7 +1267,8 @@ func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1239,7 +1289,8 @@ func TestConfigShouldRejectCustomAndImageUrlForManagedImageBuild(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1260,7 +1311,8 @@ func TestConfigShouldRejectMalformedManageImageStorageAccountTypes(t *testing.T) "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1281,7 +1333,8 @@ func TestConfigShouldRejectMalformedDiskCachingType(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1305,7 +1358,8 @@ func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) { for _, x := range storage_account_types { config["managed_image_storage_account_type"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a managed_image_storage_account_type of %q", x) } @@ -1330,7 +1384,8 @@ func TestConfigShouldAcceptDiskCachingTypes(t *testing.T) { for _, x := range storage_account_types { config["disk_caching_type"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a disk_caching_type of %q", x) } @@ -1355,7 +1410,8 @@ func TestConfigShouldRejectTempAndBuildResourceGroupName(t *testing.T) { "build_resource_group_name": "rgn00", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of both temp_resource_group_name and build_resource_group_name") } @@ -1402,7 +1458,8 @@ func TestConfigShouldRejectInvalidResourceGroupNames(t *testing.T) { for _, y := range tests { config[x] = y.name - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if !y.ok && err == nil { t.Errorf("expected config to reject %q for setting %q", y.name, x) } else if y.ok && err != nil { @@ -1452,7 +1509,8 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { for _, y := range testsResourceGroupNames { config[settingUnderTest] = y.name - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if !y.ok && err == nil { t.Errorf("expected config to reject %q for setting %q", y.name, settingUnderTest) } else if y.ok && err != nil { @@ -1486,7 +1544,8 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { for _, y := range testNames { config[settingUnderTest] = y.name - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if !y.ok && err == nil { t.Logf("expected config to reject %q for setting %q", y.name, settingUnderTest) } else if y.ok && err != nil { @@ -1496,7 +1555,8 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { } func TestConfigAdditionalDiskDefaultIsNil(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.AdditionalDiskSize != nil { t.Errorf("Expected Config to not have a set of additional disks, but got a non nil value") } @@ -1519,7 +1579,8 @@ func TestConfigAdditionalDiskOverrideDefault(t *testing.T) { "disk_additional_size": {32, 64}, } - c, _, _ := newConfig(config, diskconfig, getPackerConfiguration()) + var c Config + c.Prepare(config, diskconfig, getPackerConfiguration()) if c.AdditionalDiskSize == nil { t.Errorf("Expected Config to have a set of additional disks, but got nil") } @@ -1561,19 +1622,20 @@ func TestPlanInfoConfiguration(t *testing.T) { } config["plan_info"] = planInfo - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of plan_name without plan_product and plan_publisher") } planInfo["plan_product"] = "--plan-product--" - _, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of plan_name and plan_product without plan_publisher") } planInfo["plan_publisher"] = "--plan-publisher--" - c, _, err := newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a complete plan configuration: %s", err) } @@ -1610,7 +1672,8 @@ func TestPlanInfoPromotionCode(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept plan_info configuration, but got %s", err) } @@ -1659,7 +1722,8 @@ func TestPlanInfoTooManyTagsErrors(t *testing.T) { }, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject configuration due to excess tags") } @@ -1682,7 +1746,8 @@ func TestConfigShouldAllowTempNameOverrides(t *testing.T) { "temp_compute_name": "myTempComputeName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1716,7 +1781,8 @@ func TestConfigShouldAllowAsyncResourceGroupOverride(t *testing.T) { "async_resourcegroup_delete": "true", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1738,7 +1804,8 @@ func TestConfigShouldAllowAsyncResourceGroupOverrideNoValue(t *testing.T) { "managed_image_resource_group_name": "ignore", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1761,10 +1828,10 @@ func TestConfigShouldAllowAsyncResourceGroupOverrideBadValue(t *testing.T) { "async_resourcegroup_delete": "asdasda", } - c, _, err := newConfig(config, getPackerConfiguration()) - if err != nil && c == nil { - t.Log("newConfig failed which is expected ", err) - + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) + if err != nil { + t.Log("newConfig failed which is expected ", err) } } @@ -1782,7 +1849,8 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { }, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Log("expected config to accept Shared Image Gallery options", err) } @@ -1807,7 +1875,8 @@ func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { "capture_name_prefix": "ignore", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Log("expected an error if Shared Image Gallery source is used with VHD target", err) } @@ -1818,7 +1887,8 @@ func Test_GivenZoneNotSupportingResiliency_ConfigValidate_ShouldWarn(t *testing. builderValues["managed_image_zone_resilient"] = "true" builderValues["location"] = "ukwest" - c, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1836,7 +1906,8 @@ func Test_GivenZoneSupportingResiliency_ConfigValidate_ShouldNotWarn(t *testing. builderValues["managed_image_zone_resilient"] = "true" builderValues["location"] = "westeurope" - c, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } diff --git a/builder/azure/arm/resource_resolver_test.go b/builder/azure/arm/resource_resolver_test.go index f8e339687..68b227ee0 100644 --- a/builder/azure/arm/resource_resolver_test.go +++ b/builder/azure/arm/resource_resolver_test.go @@ -5,14 +5,15 @@ import ( ) func TestResourceResolverIgnoresEmptyVirtualNetworkName(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.VirtualNetworkName != "" { t.Fatalf("Expected VirtualNetworkName to be empty by default") } sut := newTestResourceResolver() sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called - sut.Resolve(c) + sut.Resolve(&c) if c.VirtualNetworkName != "" { t.Fatalf("Expected VirtualNetworkName to be empty") @@ -25,7 +26,8 @@ func TestResourceResolverIgnoresEmptyVirtualNetworkName(t *testing.T) { // If the user fully specified the virtual network name and resource group then // there is no need to do a lookup. func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) c.VirtualNetworkName = "--virtual-network-name--" c.VirtualNetworkResourceGroupName = "--virtual-network-resource-group-name--" c.VirtualNetworkSubnetName = "--virtual-network-subnet-name--" @@ -33,7 +35,7 @@ func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { sut := newTestResourceResolver() sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called sut.findVirtualNetworkSubnet = nil // assert that this is not even called - sut.Resolve(c) + sut.Resolve(&c) if c.VirtualNetworkName != "--virtual-network-name--" { t.Fatalf("Expected VirtualNetworkName to be --virtual-network-name--") @@ -49,11 +51,12 @@ func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { // If the user set virtual network name then the code should resolve virtual network // resource group name. func TestResourceResolverSetVirtualNetworkResourceGroupName(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) c.VirtualNetworkName = "--virtual-network-name--" sut := newTestResourceResolver() - sut.Resolve(c) + sut.Resolve(&c) if c.VirtualNetworkResourceGroupName != "findVirtualNetworkResourceGroup is mocked" { t.Fatalf("Expected VirtualNetworkResourceGroupName to be 'findVirtualNetworkResourceGroup is mocked'") diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery.go b/builder/azure/arm/step_publish_to_shared_image_gallery.go index 347a9002c..f30b6fa96 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery.go @@ -3,6 +3,7 @@ package arm import ( "context" "fmt" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/helper/multistep" diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go index 97c7aa289..ebcc681f8 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go @@ -2,9 +2,10 @@ package arm import ( "context" + "testing" + "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/helper/multistep" - "testing" ) func TestStepPublishToSharedImageGalleryShouldNotPublishForVhd(t *testing.T) { diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index 069415893..7c465a791 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -13,8 +13,9 @@ import ( // Ensure the link values are not set, and the concrete values are set. func TestVirtualMachineDeployment00(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -42,8 +43,9 @@ func TestVirtualMachineDeployment00(t *testing.T) { // Ensure the Virtual Machine template is a valid JSON document. func TestVirtualMachineDeployment01(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -56,8 +58,9 @@ func TestVirtualMachineDeployment01(t *testing.T) { // Ensure the Virtual Machine template parameters are correct. func TestVirtualMachineDeployment02(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -104,8 +107,9 @@ func TestVirtualMachineDeployment03(t *testing.T) { m["image_sku"] = "ImageSku" m["image_version"] = "ImageVersion" - c, _, _ := newConfig(m, getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(m, getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -130,12 +134,13 @@ func TestVirtualMachineDeployment04(t *testing.T) { "communicator": "none", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -162,12 +167,13 @@ func TestVirtualMachineDeployment05(t *testing.T) { "virtual_network_subnet_name": "virtualNetworkSubnetName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -197,12 +203,13 @@ func TestVirtualMachineDeployment06(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -227,7 +234,8 @@ func TestVirtualMachineDeployment07(t *testing.T) { "communicator": "none", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -247,7 +255,7 @@ growpart: base64CustomData := base64.StdEncoding.EncodeToString([]byte(customData)) c.customData = base64CustomData - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -271,12 +279,13 @@ func TestVirtualMachineDeployment08(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -302,12 +311,13 @@ func TestVirtualMachineDeployment09(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -339,12 +349,13 @@ func TestVirtualMachineDeployment10(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -375,12 +386,13 @@ func TestVirtualMachineDeployment11(t *testing.T) { "capture_container_name": "packerimages", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -409,12 +421,13 @@ func TestVirtualMachineDeployment12(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -442,13 +455,14 @@ func TestVirtualMachineDeployment13(t *testing.T) { "allowed_inbound_ip_addresses": []string{"127.0.0.1", "192.168.100.0/24"}, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } c.tmpKeyVaultName = "--keyvault-name--" - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -461,8 +475,9 @@ func TestVirtualMachineDeployment13(t *testing.T) { // Ensure the link values are not set, and the concrete values are set. func TestKeyVaultDeployment00(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -490,8 +505,9 @@ func TestKeyVaultDeployment00(t *testing.T) { // Ensure the KeyVault template is a valid JSON document. func TestKeyVaultDeployment01(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -504,9 +520,10 @@ func TestKeyVaultDeployment01(t *testing.T) { // Ensure the KeyVault template parameters are correct. func TestKeyVaultDeployment02(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -546,8 +563,9 @@ func TestKeyVaultDeployment03(t *testing.T) { }, } - c, _, _ := newConfig(tags, getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + var c Config + c.Prepare(tags, getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -567,8 +585,9 @@ func TestPlanInfo01(t *testing.T) { }, } - c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -592,8 +611,9 @@ func TestPlanInfo02(t *testing.T) { }, } - c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 91ceca991..8cf4ba205 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config // Package chroot is able to create an Azure managed image without requiring the // launch of a new virtual machine for every build. It does this by attaching and @@ -14,6 +15,7 @@ import ( "runtime" "strings" + "github.com/hashicorp/hcl/v2/hcldec" azcommon "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/client" "github.com/hashicorp/packer/common" @@ -116,6 +118,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = azcommon.TemplateFuncs b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc() diff --git a/builder/azure/chroot/builder.hcl2spec.go b/builder/azure/chroot/builder.hcl2spec.go new file mode 100644 index 000000000..50a42e314 --- /dev/null +++ b/builder/azure/chroot/builder.hcl2spec.go @@ -0,0 +1,92 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package chroot + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name"` + ClientID *string `mapstructure:"client_id" cty:"client_id"` + ClientSecret *string `mapstructure:"client_secret" cty:"client_secret"` + ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path"` + ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt"` + ObjectID *string `mapstructure:"object_id" cty:"object_id"` + TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id"` + SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id"` + FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch"` + Source *string `mapstructure:"source" required:"true" cty:"source"` + CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper"` + PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands"` + MountOptions []string `mapstructure:"mount_options" cty:"mount_options"` + MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition"` + MountPath *string `mapstructure:"mount_path" cty:"mount_path"` + PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands"` + ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts"` + CopyFiles []string `mapstructure:"copy_files" cty:"copy_files"` + TemporaryOSDiskName *string `mapstructure:"temporary_os_disk_name" cty:"temporary_os_disk_name"` + OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" cty:"os_disk_size_gb"` + OSDiskStorageAccountType *string `mapstructure:"os_disk_storage_account_type" cty:"os_disk_storage_account_type"` + OSDiskCacheType *string `mapstructure:"os_disk_cache_type" cty:"os_disk_cache_type"` + OSDiskSkipCleanup *bool `mapstructure:"os_disk_skip_cleanup" cty:"os_disk_skip_cleanup"` + ImageResourceID *string `mapstructure:"image_resource_id" required:"true" cty:"image_resource_id"` + ImageHyperVGeneration *string `mapstructure:"image_hyperv_generation" cty:"image_hyperv_generation"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, + "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, + "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, + "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, + "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, + "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, + "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, + "subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false}, + "from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false}, + "source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false}, + "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, + "pre_mount_commands": &hcldec.AttrSpec{Name: "pre_mount_commands", Type: cty.List(cty.String), Required: false}, + "mount_options": &hcldec.AttrSpec{Name: "mount_options", Type: cty.List(cty.String), Required: false}, + "mount_partition": &hcldec.AttrSpec{Name: "mount_partition", Type: cty.String, Required: false}, + "mount_path": &hcldec.AttrSpec{Name: "mount_path", Type: cty.String, Required: false}, + "post_mount_commands": &hcldec.AttrSpec{Name: "post_mount_commands", Type: cty.List(cty.String), Required: false}, + "chroot_mounts": &hcldec.BlockListSpec{TypeName: "chroot_mounts", Nested: &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.String), Required: false}}, + "copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false}, + "temporary_os_disk_name": &hcldec.AttrSpec{Name: "temporary_os_disk_name", Type: cty.String, Required: false}, + "os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false}, + "os_disk_storage_account_type": &hcldec.AttrSpec{Name: "os_disk_storage_account_type", Type: cty.String, Required: false}, + "os_disk_cache_type": &hcldec.AttrSpec{Name: "os_disk_cache_type", Type: cty.String, Required: false}, + "os_disk_skip_cleanup": &hcldec.AttrSpec{Name: "os_disk_skip_cleanup", Type: cty.Bool, Required: false}, + "image_resource_id": &hcldec.AttrSpec{Name: "image_resource_id", Type: cty.String, Required: false}, + "image_hyperv_generation": &hcldec.AttrSpec{Name: "image_hyperv_generation", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/azure/chroot/diskattacher_test.go b/builder/azure/chroot/diskattacher_test.go index 3b8c55e4d..fb66fb9e3 100644 --- a/builder/azure/chroot/diskattacher_test.go +++ b/builder/azure/chroot/diskattacher_test.go @@ -2,9 +2,10 @@ package chroot import ( "context" - "github.com/Azure/go-autorest/autorest/to" "testing" + "github.com/Azure/go-autorest/autorest/to" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" "github.com/hashicorp/packer/builder/azure/common/client" "github.com/stretchr/testify/assert" diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go index 4ae90d2fe..2b0640a50 100644 --- a/builder/azure/chroot/step_create_image.go +++ b/builder/azure/chroot/step_create_image.go @@ -3,13 +3,14 @@ package chroot import ( "context" "fmt" + "log" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/to" "github.com/hashicorp/packer/builder/azure/common/client" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "log" ) var _ multistep.Step = &StepCreateImage{} diff --git a/builder/azure/common/client/platform_image.go b/builder/azure/common/client/platform_image.go index ac11ec935..9b8afb566 100644 --- a/builder/azure/common/client/platform_image.go +++ b/builder/azure/common/client/platform_image.go @@ -3,11 +3,12 @@ package client import ( "context" "fmt" + "regexp" + "strings" + "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute" "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute/computeapi" "github.com/Azure/go-autorest/autorest/to" - "regexp" - "strings" ) var platformImageRegex = regexp.MustCompile(`^[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+$`) diff --git a/builder/cloudstack/builder.go b/builder/cloudstack/builder.go index dc1dace69..ae18e93bc 100644 --- a/builder/cloudstack/builder.go +++ b/builder/cloudstack/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -15,18 +16,18 @@ const BuilderId = "packer.cloudstack" // Builder represents the CloudStack builder. type Builder struct { - config *Config + config Config runner multistep.Runner ui packer.Ui } -// Prepare implements the packer.Builder interface. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, errs := NewConfig(raws...) + errs := b.config.Prepare(raws...) if errs != nil { return nil, errs } - b.config = config return nil, nil } @@ -109,7 +110,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack // Build the artifact and return it artifact := &Artifact{ client: client, - config: b.config, + config: &b.config, template: state.Get("template").(*cloudstack.CreateTemplateResponse), } diff --git a/builder/cloudstack/builder_test.go b/builder/cloudstack/builder_test.go index 6d7ab1444..158ff5d0b 100644 --- a/builder/cloudstack/builder_test.go +++ b/builder/cloudstack/builder_test.go @@ -40,10 +40,8 @@ func TestBuilder_Prepare(t *testing.T) { }, } - b := &Builder{} - for desc, tc := range cases { - _, errs := b.Prepare(tc.Config) + _, errs := (&Builder{}).Prepare(tc.Config) if tc.Err { if errs == nil { diff --git a/builder/cloudstack/config.go b/builder/cloudstack/config.go index cff9e2472..c3487afba 100644 --- a/builder/cloudstack/config.go +++ b/builder/cloudstack/config.go @@ -167,8 +167,7 @@ type Config struct { } // NewConfig parses and validates the given config. -func NewConfig(raws ...interface{}) (*Config, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) error { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -179,7 +178,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { }, }, raws...) if err != nil { - return nil, err + return err } var errs *packer.MultiError @@ -309,8 +308,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Check for errors and return if we have any. if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return c, nil + return nil } diff --git a/builder/cloudstack/config.hcl2spec.go b/builder/cloudstack/config.hcl2spec.go index b92d27d0c..79839316b 100644 --- a/builder/cloudstack/config.hcl2spec.go +++ b/builder/cloudstack/config.hcl2spec.go @@ -49,8 +49,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -104,10 +104,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/cloudstack/config_test.go b/builder/cloudstack/config_test.go index 54fb3248a..bff6c55e0 100644 --- a/builder/cloudstack/config_test.go +++ b/builder/cloudstack/config_test.go @@ -132,7 +132,8 @@ func TestNewConfig(t *testing.T) { raw[tc.Nullify] = nil } - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if tc.Err { if errs == nil { diff --git a/builder/digitalocean/builder.go b/builder/digitalocean/builder.go index 661e2c2a4..0812f3c78 100644 --- a/builder/digitalocean/builder.go +++ b/builder/digitalocean/builder.go @@ -10,6 +10,7 @@ import ( "net/url" "github.com/digitalocean/godo" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -25,12 +26,13 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *c return nil, nil } diff --git a/builder/digitalocean/config.go b/builder/digitalocean/config.go index b087cdb9e..a2f42fdae 100644 --- a/builder/digitalocean/config.go +++ b/builder/digitalocean/config.go @@ -89,8 +89,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ @@ -104,7 +103,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -189,9 +188,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.APIToken) - return c, nil, nil + return nil, nil } diff --git a/builder/digitalocean/config.hcl2spec.go b/builder/digitalocean/config.hcl2spec.go index f39cc404d..a6f895ac7 100644 --- a/builder/digitalocean/config.hcl2spec.go +++ b/builder/digitalocean/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -77,10 +77,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/docker/builder.go b/builder/docker/builder.go index 63cb8f55d..470fa0f6e 100644 --- a/builder/docker/builder.go +++ b/builder/docker/builder.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -16,16 +17,17 @@ const ( ) type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/docker/config.go b/builder/docker/config.go index f67b48974..3785ad079 100644 --- a/builder/docker/config.go +++ b/builder/docker/config.go @@ -107,8 +107,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.FixUploadOwner = true @@ -124,7 +123,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -191,8 +190,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return c, nil, nil + return nil, nil } diff --git a/builder/docker/config.hcl2spec.go b/builder/docker/config.hcl2spec.go index fe278055e..9183f7770 100644 --- a/builder/docker/config.hcl2spec.go +++ b/builder/docker/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -86,10 +86,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/docker/config_test.go b/builder/docker/config_test.go index 442c6ffe2..cf66e1a7d 100644 --- a/builder/docker/config_test.go +++ b/builder/docker/config_test.go @@ -14,7 +14,8 @@ func testConfig() map[string]interface{} { } func testConfigStruct(t *testing.T) *Config { - c, warns, errs := NewConfig(testConfig()) + var c Config + warns, errs := c.Prepare(testConfig()) if len(warns) > 0 { t.Fatalf("bad: %#v", len(warns)) } @@ -22,7 +23,7 @@ func testConfigStruct(t *testing.T) *Config { t.Fatalf("bad: %#v", errs) } - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error) { @@ -55,17 +56,18 @@ func TestConfigPrepare_exportPath(t *testing.T) { // No export path. This is invalid. Previously this would not error during // validation and as a result the failure would happen at build time. delete(raw, "export_path") - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs) // Good export path raw["export_path"] = "good" - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) // Bad export path (directory) raw["export_path"] = td - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigErr(t, warns, errs) } @@ -74,17 +76,17 @@ func TestConfigPrepare_exportPathAndCommit(t *testing.T) { // Export but no commit (explicit default) raw["commit"] = false - _, warns, errs := NewConfig(raw) + warns, errs := (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // Commit AND export specified (invalid) raw["commit"] = true - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) // Commit but no export delete(raw, "export_path") - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) } @@ -93,18 +95,18 @@ func TestConfigPrepare_exportDiscard(t *testing.T) { // Export but no discard (explicit default) raw["discard"] = false - _, warns, errs := NewConfig(raw) + warns, errs := (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // Discard AND export (invalid) raw["discard"] = true - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) // Discard but no export raw["discard"] = true delete(raw, "export_path") - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) } @@ -113,12 +115,13 @@ func TestConfigPrepare_image(t *testing.T) { // No image delete(raw, "image") - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs) // Good image raw["image"] = "path" - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) } @@ -127,7 +130,8 @@ func TestConfigPrepare_pull(t *testing.T) { // No pull set delete(raw, "pull") - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigOk(t, warns, errs) if !c.Pull { t.Fatal("should pull by default") @@ -135,7 +139,7 @@ func TestConfigPrepare_pull(t *testing.T) { // Pull set raw["pull"] = false - c, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) if c.Pull { t.Fatal("should not pull") diff --git a/builder/file/builder.go b/builder/file/builder.go index 0defa6049..4178e6f1c 100644 --- a/builder/file/builder.go +++ b/builder/file/builder.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "os" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -19,16 +20,17 @@ import ( const BuilderId = "packer.file" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/file/config.go b/builder/file/config.go index fea112684..730c497f5 100644 --- a/builder/file/config.go +++ b/builder/file/config.go @@ -22,8 +22,7 @@ type Config struct { Content string `mapstructure:"content"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { warnings := []string{} err := config.Decode(c, &config.DecodeOpts{ @@ -33,7 +32,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, warnings, err + return warnings, err } var errs *packer.MultiError @@ -51,8 +50,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/file/config.hcl2spec.go b/builder/file/config.hcl2spec.go index f47193d7d..627b92840 100644 --- a/builder/file/config.hcl2spec.go +++ b/builder/file/config.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/file/config_test.go b/builder/file/config_test.go index 9d8f346fc..a701b9609 100644 --- a/builder/file/config_test.go +++ b/builder/file/config_test.go @@ -16,7 +16,8 @@ func testConfig() map[string]interface{} { func TestContentSourceConflict(t *testing.T) { raw := testConfig() - _, _, errs := NewConfig(raw) + var c Config + _, errs := c.Prepare(raw) if !strings.Contains(errs.Error(), ErrContentSourceConflict.Error()) { t.Errorf("Expected config error: %s", ErrContentSourceConflict.Error()) } @@ -26,7 +27,8 @@ func TestNoFilename(t *testing.T) { raw := testConfig() delete(raw, "filename") - _, _, errs := NewConfig(raw) + var c Config + _, errs := c.Prepare(raw) if errs == nil { t.Errorf("Expected config error: %s", ErrTargetRequired.Error()) } @@ -37,7 +39,8 @@ func TestNoContent(t *testing.T) { delete(raw, "content") delete(raw, "source") - _, warns, _ := NewConfig(raw) + var c Config + warns, _ := c.Prepare(raw) if len(warns) == 0 { t.Error("Expected config warning without any content") diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index 2a30d39c5..163cc9d2c 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -7,6 +7,7 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -18,17 +19,17 @@ const BuilderId = "packer.googlecompute" // Builder represents a Packer Builder. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } @@ -43,7 +44,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack // Set up the state. state := new(multistep.BasicStateBag) - state.Put("config", b.config) + state.Put("config", &b.config) state.Put("driver", driver) state.Put("hook", hook) state.Put("ui", ui) @@ -97,7 +98,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack artifact := &Artifact{ image: state.Get("image").(*Image), driver: driver, - config: b.config, + config: &b.config, } return artifact, nil } diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 7aeeb4cb5..0b6c3c267 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -187,8 +187,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ctx.Funcs = TemplateFuncs err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, @@ -200,7 +199,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -372,10 +371,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return c, nil, nil + return nil, nil } type CustomerEncryptionKey struct { diff --git a/builder/googlecompute/config.hcl2spec.go b/builder/googlecompute/config.hcl2spec.go index cfa169a02..acc64f1c9 100644 --- a/builder/googlecompute/config.hcl2spec.go +++ b/builder/googlecompute/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -100,10 +100,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -206,10 +209,13 @@ type FlatCustomerEncryptionKey struct { // FlatMapstructure returns a new FlatCustomerEncryptionKey. // FlatCustomerEncryptionKey is an auto-generated flat version of CustomerEncryptionKey. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*CustomerEncryptionKey) FlatMapstructure() interface{} { return new(FlatCustomerEncryptionKey) } +func (*CustomerEncryptionKey) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatCustomerEncryptionKey) +} -// HCL2Spec returns the hcldec.Spec of a FlatCustomerEncryptionKey. -// This spec is used by HCL to read the fields of FlatCustomerEncryptionKey. +// HCL2Spec returns the hcl spec of a CustomerEncryptionKey. +// This spec is used by HCL to read the fields of CustomerEncryptionKey. +// The decoded values from this spec will then be applied to a FlatCustomerEncryptionKey. func (*FlatCustomerEncryptionKey) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "kms_key_name": &hcldec.AttrSpec{Name: "kms_key_name", Type: cty.String, Required: false}, diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index b2f05e139..7753e1d8a 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -242,7 +242,8 @@ func TestConfigPrepare(t *testing.T) { raw[tc.Key] = tc.Value } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, tc.Key) @@ -302,7 +303,8 @@ func TestConfigPrepareAccelerator(t *testing.T) { } } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) @@ -352,7 +354,8 @@ func TestConfigPrepareServiceAccount(t *testing.T) { } } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) @@ -371,7 +374,8 @@ func TestConfigPrepareStartupScriptFile(t *testing.T) { "zone": "us-central1-a", } - _, _, errs := NewConfig(config) + var c Config + _, errs := c.Prepare(config) if errs == nil || !strings.Contains(errs.Error(), "startup_script_file") { t.Fatalf("should error: startup_script_file") @@ -398,10 +402,11 @@ func TestConfigDefaults(t *testing.T) { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigOk(t, warns, errs) - actual := tc.Read(c) + actual := tc.Read(&c) if actual != tc.Value { t.Fatalf("bad: %#v", actual) } @@ -412,7 +417,8 @@ func TestImageName(t *testing.T) { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if !strings.HasPrefix(c.ImageName, "packer-") { t.Fatalf("ImageName should have 'packer-' prefix, found %s", c.ImageName) } @@ -425,7 +431,8 @@ func TestRegion(t *testing.T) { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.Region != "us-east1" { t.Fatalf("Region should be 'us-east1' given Zone of 'us-east1-a', but is %s", c.Region) } @@ -460,7 +467,8 @@ func testConfigStruct(t *testing.T) *Config { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if len(warns) > 0 { t.Fatalf("bad: %#v", len(warns)) } @@ -468,7 +476,7 @@ func testConfigStruct(t *testing.T) *Config { t.Fatalf("bad: %#v", errs) } - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error, extra string) { diff --git a/builder/hcloud/builder.go b/builder/hcloud/builder.go index 92f9f474b..efd29adc9 100644 --- a/builder/hcloud/builder.go +++ b/builder/hcloud/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -22,12 +23,13 @@ type Builder struct { var pluginVersion = "1.0.0" +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *config return nil, nil } diff --git a/builder/hcloud/config.go b/builder/hcloud/config.go index 6e8b56ba6..878c7cc1f 100644 --- a/builder/hcloud/config.go +++ b/builder/hcloud/config.go @@ -50,9 +50,7 @@ type imageFilter struct { MostRecent bool `mapstructure:"most_recent"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) - +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, @@ -65,7 +63,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -142,11 +140,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.HCloudToken) - return c, nil, nil + return nil, nil } func getServerIP(state multistep.StateBag) (string, error) { diff --git a/builder/hcloud/config.hcl2spec.go b/builder/hcloud/config.hcl2spec.go index 36ea266ff..cf1ff3186 100644 --- a/builder/hcloud/config.hcl2spec.go +++ b/builder/hcloud/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -75,10 +75,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -156,10 +159,13 @@ type FlatimageFilter struct { // FlatMapstructure returns a new FlatimageFilter. // FlatimageFilter is an auto-generated flat version of imageFilter. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*imageFilter) FlatMapstructure() interface{} { return new(FlatimageFilter) } +func (*imageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatimageFilter) +} -// HCL2Spec returns the hcldec.Spec of a FlatimageFilter. -// This spec is used by HCL to read the fields of FlatimageFilter. +// HCL2Spec returns the hcl spec of a imageFilter. +// This spec is used by HCL to read the fields of imageFilter. +// The decoded values from this spec will then be applied to a FlatimageFilter. func (*FlatimageFilter) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "with_selector": &hcldec.AttrSpec{Name: "with_selector", Type: cty.List(cty.String), Required: false}, diff --git a/builder/hyperone/builder.go b/builder/hyperone/builder.go index d9e85e5f0..93c0b84c4 100644 --- a/builder/hyperone/builder.go +++ b/builder/hyperone/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -20,14 +21,14 @@ type Builder struct { client *openapi.APIClient } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *config - cfg := openapi.NewConfiguration() cfg.AddDefaultHeader("x-auth-token", b.config.Token) if b.config.Project != "" { diff --git a/builder/hyperone/config.go b/builder/hyperone/config.go index 6c5d5eefb..603651bfc 100644 --- a/builder/hyperone/config.go +++ b/builder/hyperone/config.go @@ -114,8 +114,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ @@ -133,12 +132,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } cliConfig, err := loadCLIConfig() if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -165,7 +164,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.TokenLogin != "" && c.APIURL == "" { c.Token, err = fetchTokenBySSH(c.TokenLogin) if err != nil { - return nil, nil, err + return nil, err } } } @@ -181,7 +180,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.ImageName == "" { name, err := interpolate.Render("packer-{{timestamp}}", nil) if err != nil { - return nil, nil, err + return nil, err } c.ImageName = name } @@ -217,7 +216,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.ChrootMountPath == "" { path, err := interpolate.Render("/mnt/packer-hyperone-volumes/{{timestamp}}", nil) if err != nil { - return nil, nil, err + return nil, err } c.ChrootMountPath = path } @@ -281,12 +280,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Token) - return c, nil, nil + return nil, nil } type cliConfig struct { diff --git a/builder/hyperone/config.hcl2spec.go b/builder/hyperone/config.hcl2spec.go index 4978d71b8..1fa450196 100644 --- a/builder/hyperone/config.hcl2spec.go +++ b/builder/hyperone/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -94,10 +94,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/hyperv/common/output_config.hcl2spec.go b/builder/hyperv/common/output_config.hcl2spec.go index c3794c70d..683b8f314 100644 --- a/builder/hyperv/common/output_config.hcl2spec.go +++ b/builder/hyperv/common/output_config.hcl2spec.go @@ -15,10 +15,13 @@ type FlatOutputConfig struct { // FlatMapstructure returns a new FlatOutputConfig. // FlatOutputConfig is an auto-generated flat version of OutputConfig. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*OutputConfig) FlatMapstructure() interface{} { return new(FlatOutputConfig) } +func (*OutputConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatOutputConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatOutputConfig. -// This spec is used by HCL to read the fields of FlatOutputConfig. +// HCL2Spec returns the hcl spec of a OutputConfig. +// This spec is used by HCL to read the fields of OutputConfig. +// The decoded values from this spec will then be applied to a FlatOutputConfig. func (*FlatOutputConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 1b40fac84..193cce388 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" hypervcommon "github.com/hashicorp/packer/builder/hyperv/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -84,7 +85,8 @@ type Config struct { ctx interpolate.Context } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/hyperv/iso/builder.hcl2spec.go b/builder/hyperv/iso/builder.hcl2spec.go index 09f3fa0d3..d06b4b3af 100644 --- a/builder/hyperv/iso/builder.hcl2spec.go +++ b/builder/hyperv/iso/builder.hcl2spec.go @@ -60,8 +60,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -108,10 +108,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index af93007e5..3ab84a6f2 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -10,6 +10,7 @@ import ( "os" "strings" + "github.com/hashicorp/hcl/v2/hcldec" hypervcommon "github.com/hashicorp/packer/builder/hyperv/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -77,7 +78,8 @@ type Config struct { ctx interpolate.Context } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/hyperv/vmcx/builder.hcl2spec.go b/builder/hyperv/vmcx/builder.hcl2spec.go index 1ba1e83ee..628436bf0 100644 --- a/builder/hyperv/vmcx/builder.hcl2spec.go +++ b/builder/hyperv/vmcx/builder.hcl2spec.go @@ -60,8 +60,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -110,10 +110,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/jdcloud/builder.go b/builder/jdcloud/builder.go index 4b3700f11..2115827b4 100644 --- a/builder/jdcloud/builder.go +++ b/builder/jdcloud/builder.go @@ -3,6 +3,8 @@ package jdcloud import ( "context" "fmt" + + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -11,6 +13,8 @@ import ( "github.com/hashicorp/packer/template/interpolate" ) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/jdcloud/common.hcl2spec.go b/builder/jdcloud/common.hcl2spec.go index 0bc486d27..486aeb51d 100644 --- a/builder/jdcloud/common.hcl2spec.go +++ b/builder/jdcloud/common.hcl2spec.go @@ -48,8 +48,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -74,10 +74,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, diff --git a/builder/jdcloud/step_config_credentials.go b/builder/jdcloud/step_config_credentials.go index 1c6fdfc76..360a27629 100644 --- a/builder/jdcloud/step_config_credentials.go +++ b/builder/jdcloud/step_config_credentials.go @@ -3,10 +3,11 @@ package jdcloud import ( "context" "fmt" + "io/ioutil" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" - "io/ioutil" ) type stepConfigCredentials struct { diff --git a/builder/jdcloud/step_create_image.go b/builder/jdcloud/step_create_image.go index 03e2d3b60..c126d8047 100644 --- a/builder/jdcloud/step_create_image.go +++ b/builder/jdcloud/step_create_image.go @@ -3,10 +3,11 @@ package jdcloud import ( "context" "fmt" + "time" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" - "time" ) type stepCreateJDCloudImage struct { diff --git a/builder/jdcloud/step_create_instance.go b/builder/jdcloud/step_create_instance.go index fe66f464d..950f360d4 100644 --- a/builder/jdcloud/step_create_instance.go +++ b/builder/jdcloud/step_create_instance.go @@ -3,6 +3,9 @@ package jdcloud import ( "context" "fmt" + "regexp" + "time" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/core" @@ -11,8 +14,6 @@ import ( vpcApis "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/apis" vpcClient "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/client" vpc "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/models" - "regexp" - "time" ) type stepCreateJDCloudInstance struct { diff --git a/builder/jdcloud/step_stop_instance.go b/builder/jdcloud/step_stop_instance.go index 1cf249c2c..0468b7469 100644 --- a/builder/jdcloud/step_stop_instance.go +++ b/builder/jdcloud/step_stop_instance.go @@ -3,6 +3,7 @@ package jdcloud import ( "context" "fmt" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" diff --git a/builder/jdcloud/step_validate_parameters.go b/builder/jdcloud/step_validate_parameters.go index 62c04078b..adfab5e14 100644 --- a/builder/jdcloud/step_validate_parameters.go +++ b/builder/jdcloud/step_validate_parameters.go @@ -3,6 +3,7 @@ package jdcloud import ( "context" "fmt" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" vm "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" diff --git a/builder/linode/builder.go b/builder/linode/builder.go index 3bc3dfb3f..65a6c30c5 100644 --- a/builder/linode/builder.go +++ b/builder/linode/builder.go @@ -8,6 +8,7 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/linode/linodego" @@ -21,16 +22,17 @@ const BuilderID = "packer.linode" // Builder represents a Packer Builder. type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return nil, nil } diff --git a/builder/linode/config.go b/builder/linode/config.go index ea8eac073..fd4233827 100644 --- a/builder/linode/config.go +++ b/builder/linode/config.go @@ -48,8 +48,7 @@ func createRandomRootPassword() (string, error) { return rootPass, nil } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { if err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, @@ -60,7 +59,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, }, raws...); err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -136,9 +135,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.PersonalAccessToken) - return c, nil, nil + return nil, nil } diff --git a/builder/linode/config.hcl2spec.go b/builder/linode/config.hcl2spec.go index e22bc7507..5f9e8c8ed 100644 --- a/builder/linode/config.hcl2spec.go +++ b/builder/linode/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -72,10 +72,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/lxc/builder.go b/builder/lxc/builder.go index 5c8747c7e..54044ebc0 100644 --- a/builder/lxc/builder.go +++ b/builder/lxc/builder.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -19,16 +20,17 @@ type wrappedCommandTemplate struct { } type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, errs := NewConfig(raws...) + errs := b.config.Prepare(raws...) if errs != nil { return nil, errs } - b.config = c return nil, nil } diff --git a/builder/lxc/config.go b/builder/lxc/config.go index 587e205a0..fbbd0e117 100644 --- a/builder/lxc/config.go +++ b/builder/lxc/config.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config package lxc @@ -67,16 +68,15 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) error { var md mapstructure.Metadata - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, Interpolate: true, }, raws...) if err != nil { - return nil, err + return err } // Accumulate any errors @@ -107,8 +107,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return &c, nil + return nil } diff --git a/builder/lxc/config.hcl2spec.go b/builder/lxc/config.hcl2spec.go new file mode 100644 index 000000000..22e0ffc8b --- /dev/null +++ b/builder/lxc/config.hcl2spec.go @@ -0,0 +1,66 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package lxc + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + ConfigFile *string `mapstructure:"config_file" required:"true" cty:"config_file"` + OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"` + ContainerName *string `mapstructure:"container_name" required:"false" cty:"container_name"` + CommandWrapper *string `mapstructure:"command_wrapper" required:"false" cty:"command_wrapper"` + InitTimeout *string `mapstructure:"init_timeout" required:"false" cty:"init_timeout"` + CreateOptions []string `mapstructure:"create_options" required:"false" cty:"create_options"` + StartOptions []string `mapstructure:"start_options" required:"false" cty:"start_options"` + AttachOptions []string `mapstructure:"attach_options" required:"false" cty:"attach_options"` + Name *string `mapstructure:"template_name" required:"true" cty:"template_name"` + Parameters []string `mapstructure:"template_parameters" required:"false" cty:"template_parameters"` + EnvVars []string `mapstructure:"template_environment_vars" required:"true" cty:"template_environment_vars"` + TargetRunlevel *int `mapstructure:"target_runlevel" required:"false" cty:"target_runlevel"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "config_file": &hcldec.AttrSpec{Name: "config_file", Type: cty.String, Required: false}, + "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, + "container_name": &hcldec.AttrSpec{Name: "container_name", Type: cty.String, Required: false}, + "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, + "init_timeout": &hcldec.AttrSpec{Name: "init_timeout", Type: cty.String, Required: false}, + "create_options": &hcldec.AttrSpec{Name: "create_options", Type: cty.List(cty.String), Required: false}, + "start_options": &hcldec.AttrSpec{Name: "start_options", Type: cty.List(cty.String), Required: false}, + "attach_options": &hcldec.AttrSpec{Name: "attach_options", Type: cty.List(cty.String), Required: false}, + "template_name": &hcldec.AttrSpec{Name: "template_name", Type: cty.String, Required: false}, + "template_parameters": &hcldec.AttrSpec{Name: "template_parameters", Type: cty.List(cty.String), Required: false}, + "template_environment_vars": &hcldec.AttrSpec{Name: "template_environment_vars", Type: cty.List(cty.String), Required: false}, + "target_runlevel": &hcldec.AttrSpec{Name: "target_runlevel", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index ce1656217..9312dd158 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -3,6 +3,7 @@ package lxd import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -17,16 +18,17 @@ type wrappedCommandTemplate struct { } type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, errs := NewConfig(raws...) + errs := b.config.Prepare(raws...) if errs != nil { return nil, errs } - b.config = c return nil, nil } diff --git a/builder/lxd/config.go b/builder/lxd/config.go index 6914392b4..220c47ca0 100644 --- a/builder/lxd/config.go +++ b/builder/lxd/config.go @@ -43,16 +43,15 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) error { var md mapstructure.Metadata - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, Interpolate: true, }, raws...) if err != nil { - return nil, err + return err } // Accumulate any errors @@ -85,8 +84,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return &c, nil + return nil } diff --git a/builder/lxd/config.hcl2spec.go b/builder/lxd/config.hcl2spec.go index 61cc74e19..e8d2d3b0d 100644 --- a/builder/lxd/config.hcl2spec.go +++ b/builder/lxd/config.hcl2spec.go @@ -29,10 +29,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/ncloud/builder.go b/builder/ncloud/builder.go index 36702c933..45c616e92 100644 --- a/builder/ncloud/builder.go +++ b/builder/ncloud/builder.go @@ -4,6 +4,7 @@ import ( "context" ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -12,17 +13,18 @@ import ( // Builder assume this implements packer.Builder type Builder struct { - config *Config + config Config stateBag multistep.StateBag runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c b.stateBag = new(multistep.BasicStateBag) @@ -42,12 +44,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.Comm.Type == "ssh" { steps = []multistep.Step{ - NewStepValidateTemplate(conn, ui, b.config), + NewStepValidateTemplate(conn, ui, &b.config), NewStepCreateLoginKey(conn, ui), - NewStepCreateServerInstance(conn, ui, b.config), - NewStepCreateBlockStorageInstance(conn, ui, b.config), - NewStepGetRootPassword(conn, ui, b.config), - NewStepCreatePublicIPInstance(conn, ui, b.config), + NewStepCreateServerInstance(conn, ui, &b.config), + NewStepCreateBlockStorageInstance(conn, ui, &b.config), + NewStepGetRootPassword(conn, ui, &b.config), + NewStepCreatePublicIPInstance(conn, ui, &b.config), &communicator.StepConnectSSH{ Config: &b.config.Comm, Host: func(stateBag multistep.StateBag) (string, error) { @@ -60,18 +62,18 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Comm: &b.config.Comm, }, NewStepStopServerInstance(conn, ui), - NewStepCreateServerImage(conn, ui, b.config), - NewStepDeleteBlockStorageInstance(conn, ui, b.config), + NewStepCreateServerImage(conn, ui, &b.config), + NewStepDeleteBlockStorageInstance(conn, ui, &b.config), NewStepTerminateServerInstance(conn, ui), } } else if b.config.Comm.Type == "winrm" { steps = []multistep.Step{ - NewStepValidateTemplate(conn, ui, b.config), + NewStepValidateTemplate(conn, ui, &b.config), NewStepCreateLoginKey(conn, ui), - NewStepCreateServerInstance(conn, ui, b.config), - NewStepCreateBlockStorageInstance(conn, ui, b.config), - NewStepGetRootPassword(conn, ui, b.config), - NewStepCreatePublicIPInstance(conn, ui, b.config), + NewStepCreateServerInstance(conn, ui, &b.config), + NewStepCreateBlockStorageInstance(conn, ui, &b.config), + NewStepGetRootPassword(conn, ui, &b.config), + NewStepCreatePublicIPInstance(conn, ui, &b.config), &communicator.StepConnectWinRM{ Config: &b.config.Comm, Host: func(stateBag multistep.StateBag) (string, error) { @@ -86,8 +88,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &common.StepProvision{}, NewStepStopServerInstance(conn, ui), - NewStepCreateServerImage(conn, ui, b.config), - NewStepDeleteBlockStorageInstance(conn, ui, b.config), + NewStepCreateServerImage(conn, ui, &b.config), + NewStepDeleteBlockStorageInstance(conn, ui, &b.config), NewStepTerminateServerInstance(conn, ui), } } diff --git a/builder/ncloud/config.go b/builder/ncloud/config.go index b1f7f4416..aca3e04d8 100644 --- a/builder/ncloud/config.go +++ b/builder/ncloud/config.go @@ -60,8 +60,7 @@ type Config struct { } // NewConfig checks parameters -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { warnings := []string{} err := config.Decode(c, &config.DecodeOpts{ @@ -71,7 +70,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, warnings, err + return warnings, err } var errs *packer.MultiError @@ -115,7 +114,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.BlockStorageSize < 10 || c.BlockStorageSize > 2000 { errs = packer.MultiErrorAppend(errs, errors.New("The size of BlockStorageSize is at least 10 GB and up to 2000GB")) } else if int(c.BlockStorageSize/10)*10 != c.BlockStorageSize { - return nil, nil, errors.New("BlockStorageSize must be a multiple of 10 GB") + return nil, errors.New("BlockStorageSize must be a multiple of 10 GB") } } @@ -136,8 +135,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/ncloud/config.hcl2spec.go b/builder/ncloud/config.hcl2spec.go index f72c02456..90559be6e 100644 --- a/builder/ncloud/config.hcl2spec.go +++ b/builder/ncloud/config.hcl2spec.go @@ -58,8 +58,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -73,10 +73,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/ncloud/config_test.go b/builder/ncloud/config_test.go index 7ccc23478..0c793b05e 100644 --- a/builder/ncloud/config_test.go +++ b/builder/ncloud/config_test.go @@ -42,7 +42,8 @@ func testConfigForMemberServerImage() map[string]interface{} { func TestConfigWithServerImageProductCode(t *testing.T) { raw := testConfig() - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.AccessKey != "access_key" { t.Errorf("Expected 'access_key' to be set to '%s', but got '%s'.", raw["access_key"], c.AccessKey) @@ -76,7 +77,8 @@ func TestConfigWithServerImageProductCode(t *testing.T) { func TestConfigWithMemberServerImageCode(t *testing.T) { raw := testConfigForMemberServerImage() - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.AccessKey != "access_key" { t.Errorf("Expected 'access_key' to be set to '%s', but got '%s'.", raw["access_key"], c.AccessKey) @@ -110,7 +112,8 @@ func TestConfigWithMemberServerImageCode(t *testing.T) { func TestEmptyConfig(t *testing.T) { raw := new(map[string]interface{}) - _, _, err := NewConfig(raw) + var c Config + _, err := c.Prepare(raw) if err == nil { t.Error("Expected Config to require 'access_key', 'secret_key' and some mandatory fields, but it did not") @@ -138,7 +141,8 @@ func TestExistsBothServerImageProductCodeAndMemberServerImageNoConfig(t *testing "member_server_image_no": "2440", } - _, _, err := NewConfig(raw) + var c Config + _, err := c.Prepare(raw) if !strings.Contains(err.Error(), "Only one of server_image_product_code and member_server_image_no can be set") { t.Error("Expected Config to require Only one of 'server_image_product_code' and 'member_server_image_no' can be set, but it did not") diff --git a/builder/null/builder.go b/builder/null/builder.go index 5939cd024..d6d2f1c70 100644 --- a/builder/null/builder.go +++ b/builder/null/builder.go @@ -3,6 +3,7 @@ package null import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -12,16 +13,17 @@ import ( const BuilderId = "fnoeding.null" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/null/config.go b/builder/null/config.go index a589b297b..79ed530b4 100644 --- a/builder/null/config.go +++ b/builder/null/config.go @@ -18,15 +18,14 @@ type Config struct { CommConfig communicator.Config `mapstructure:",squash"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&c, &config.DecodeOpts{ Interpolate: true, InterpolateFilter: &interpolate.RenderFilter{}, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -60,8 +59,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return &c, nil, nil + return nil, nil } diff --git a/builder/null/config.hcl2spec.go b/builder/null/config.hcl2spec.go index cceed507b..fb2f31145 100644 --- a/builder/null/config.hcl2spec.go +++ b/builder/null/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -61,10 +61,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/null/config_test.go b/builder/null/config_test.go index 29114806b..4a922d629 100644 --- a/builder/null/config_test.go +++ b/builder/null/config_test.go @@ -16,7 +16,8 @@ func testConfig() map[string]interface{} { } func testConfigStruct(t *testing.T) *Config { - c, warns, errs := NewConfig(testConfig()) + var c Config + warns, errs := c.Prepare(testConfig()) if len(warns) > 0 { t.Fatalf("bad: %#v", len(warns)) } @@ -24,7 +25,7 @@ func testConfigStruct(t *testing.T) *Config { t.Fatalf("bad: %#v", errs) } - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error) { @@ -50,7 +51,8 @@ func TestConfigPrepare_port(t *testing.T) { // default port should be 22 delete(raw, "port") - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if c.CommConfig.SSHPort != 22 { t.Fatalf("bad: port should default to 22, not %d", c.CommConfig.SSHPort) } @@ -62,12 +64,13 @@ func TestConfigPrepare_host(t *testing.T) { // No host delete(raw, "ssh_host") - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs) // Good host raw["ssh_host"] = "good" - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) } @@ -77,12 +80,12 @@ func TestConfigPrepare_sshCredential(t *testing.T) { // no ssh_password and no ssh_private_key_file delete(raw, "ssh_password") delete(raw, "ssh_private_key_file") - _, warns, errs := NewConfig(raw) + warns, errs := (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) // only ssh_password raw["ssh_password"] = "good" - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // only ssh_private_key_file @@ -90,11 +93,11 @@ func TestConfigPrepare_sshCredential(t *testing.T) { defer os.Remove(testFile) raw["ssh_private_key_file"] = testFile delete(raw, "ssh_password") - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // both ssh_password and ssh_private_key_file set raw["ssh_password"] = "bad" - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) } diff --git a/builder/oneandone/builder.go b/builder/oneandone/builder.go index 5ea7674bc..759f9bd00 100644 --- a/builder/oneandone/builder.go +++ b/builder/oneandone/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -14,16 +15,17 @@ import ( const BuilderId = "packer.oneandone" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/oneandone/config.go b/builder/oneandone/config.go index 68f2203f3..73914a13f 100644 --- a/builder/oneandone/config.go +++ b/builder/oneandone/config.go @@ -31,11 +31,10 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, Interpolate: true, InterpolateContext: &c.ctx, @@ -46,7 +45,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -107,8 +106,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Token) - return &c, nil, nil + return nil, nil } diff --git a/builder/oneandone/config.hcl2spec.go b/builder/oneandone/config.hcl2spec.go index ebed17e63..ed73682a2 100644 --- a/builder/oneandone/config.hcl2spec.go +++ b/builder/oneandone/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -69,10 +69,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index 8411f803d..ca4c31e5a 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -9,6 +9,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -35,6 +36,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/openstack/builder.hcl2spec.go b/builder/openstack/builder.hcl2spec.go index 7977cee1f..567b30688 100644 --- a/builder/openstack/builder.hcl2spec.go +++ b/builder/openstack/builder.hcl2spec.go @@ -73,8 +73,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -118,10 +118,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -241,10 +244,13 @@ type FlatImageFilter struct { // FlatMapstructure returns a new FlatImageFilter. // FlatImageFilter is an auto-generated flat version of ImageFilter. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ImageFilter) FlatMapstructure() interface{} { return new(FlatImageFilter) } +func (*ImageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatImageFilter) +} -// HCL2Spec returns the hcldec.Spec of a FlatImageFilter. -// This spec is used by HCL to read the fields of FlatImageFilter. +// HCL2Spec returns the hcl spec of a ImageFilter. +// This spec is used by HCL to read the fields of ImageFilter. +// The decoded values from this spec will then be applied to a FlatImageFilter. func (*FlatImageFilter) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockSpec{TypeName: "filters", Nested: hcldec.ObjectSpec((*FlatImageFilterOptions)(nil).HCL2Spec())}, @@ -266,10 +272,13 @@ type FlatImageFilterOptions struct { // FlatMapstructure returns a new FlatImageFilterOptions. // FlatImageFilterOptions is an auto-generated flat version of ImageFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ImageFilterOptions) FlatMapstructure() interface{} { return new(FlatImageFilterOptions) } +func (*ImageFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatImageFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatImageFilterOptions. -// This spec is used by HCL to read the fields of FlatImageFilterOptions. +// HCL2Spec returns the hcl spec of a ImageFilterOptions. +// This spec is used by HCL to read the fields of ImageFilterOptions. +// The decoded values from this spec will then be applied to a FlatImageFilterOptions. func (*FlatImageFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index d97b3be10..e91717c75 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -1,3 +1,5 @@ +//go:generate mapstructure-to-hcl2 -type Config + package classic import ( @@ -8,6 +10,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/go-oracle-terraform/opc" + "github.com/hashicorp/hcl/v2/hcldec" ocommon "github.com/hashicorp/packer/builder/oracle/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -20,16 +23,17 @@ const BuilderId = "packer.oracle.classic" // Builder is a builder implementation that creates Oracle OCI custom images. type Builder struct { - config *Config + config Config runner multistep.Runner } -func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { - config, err := NewConfig(rawConfig...) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + err := b.config.Prepare(raws...) if err != nil { return nil, err } - b.config = config var errs *packer.MultiError diff --git a/builder/oracle/classic/config.hcl2spec.go b/builder/oracle/classic/builder.hcl2spec.go similarity index 96% rename from builder/oracle/classic/config.hcl2spec.go rename to builder/oracle/classic/builder.hcl2spec.go index a65bc849e..d8bcf0079 100644 --- a/builder/oracle/classic/config.hcl2spec.go +++ b/builder/oracle/classic/builder.hcl2spec.go @@ -53,8 +53,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -82,10 +82,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 4fc1ddb15..6f66d7a81 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -1,5 +1,3 @@ -//go:generate mapstructure-to-hcl2 -type Config - package classic import ( @@ -57,8 +55,7 @@ func (c *Config) Identifier(s string) string { return fmt.Sprintf("/Compute-%s/%s/%s", c.IdentityDomain, c.Username, s) } -func NewConfig(raws ...interface{}) (*Config, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) error { // Decode from template err := config.Decode(c, &config.DecodeOpts{ @@ -66,12 +63,12 @@ func NewConfig(raws ...interface{}) (*Config, error) { InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) + return fmt.Errorf("Failed to mapstructure Config: %+v", err) } c.apiEndpointURL, err = url.Parse(c.APIEndpoint) if err != nil { - return nil, fmt.Errorf("Error parsing API Endpoint: %s", err) + return fmt.Errorf("Error parsing API Endpoint: %s", err) } // Set default source list if c.SSHSourceList == "" { @@ -127,7 +124,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } // unpack attributes from json into config @@ -155,5 +152,5 @@ func NewConfig(raws ...interface{}) (*Config, error) { c.attribs = data } - return c, nil + return nil } diff --git a/builder/oracle/classic/config_test.go b/builder/oracle/classic/config_test.go index b120e70a3..8d55a1626 100644 --- a/builder/oracle/classic/config_test.go +++ b/builder/oracle/classic/config_test.go @@ -22,7 +22,8 @@ func testConfig() map[string]interface{} { func TestConfigAutoFillsSourceList(t *testing.T) { tc := testConfig() - conf, err := NewConfig(tc) + var conf Config + err := conf.Prepare(tc) if err != nil { t.Fatalf("Should not have error: %s", err.Error()) } @@ -47,7 +48,8 @@ func TestConfigValidationCatchesMissing(t *testing.T) { for _, key := range required { tc := testConfig() delete(tc, key) - _, err := NewConfig(tc) + var c Config + err := c.Prepare(tc) if err == nil { t.Fatalf("Test should have failed when config lacked %s!", key) } @@ -68,7 +70,8 @@ func TestConfigValidatesObjects(t *testing.T) { for _, tt := range objectTests { tc := testConfig() tc[s] = tt.object - _, err := NewConfig(tc) + var c Config + err := c.Prepare(tc) if tt.valid { assert.NoError(t, err, tt.object) } else { diff --git a/builder/oracle/oci/builder.go b/builder/oracle/oci/builder.go index a4b31b8e6..5b0922018 100644 --- a/builder/oracle/oci/builder.go +++ b/builder/oracle/oci/builder.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" ocommon "github.com/hashicorp/packer/builder/oracle/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -22,22 +23,23 @@ const ociAPIVersion = "20160918" // Builder is a builder implementation that creates Oracle OCI custom images. type Builder struct { - config *Config + config Config runner multistep.Runner } -func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { - config, err := NewConfig(rawConfig...) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + err := b.config.Prepare(raws...) if err != nil { return nil, err } - b.config = config return nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { - driver, err := NewDriverOCI(b.config) + driver, err := NewDriverOCI(&b.config) if err != nil { return nil, err } diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index 2d11cb05a..52229cc48 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -75,8 +75,7 @@ func (c *Config) ConfigProvider() ocicommon.ConfigurationProvider { return c.configProvider } -func NewConfig(raws ...interface{}) (*Config, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) error { // Decode from template err := config.Decode(c, &config.DecodeOpts{ @@ -84,7 +83,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) + return fmt.Errorf("Failed to mapstructure Config: %+v", err) } // Determine where the SDK config is located @@ -103,13 +102,13 @@ func NewConfig(raws ...interface{}) (*Config, error) { if c.KeyFile != "" { path, err := packer.ExpandUser(c.KeyFile) if err != nil { - return nil, err + return err } // Read API signing key keyContent, err = ioutil.ReadFile(path) if err != nil { - return nil, err + return err } } @@ -135,7 +134,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Load API access configuration from SDK configProvider, err := ocicommon.ComposingConfigurationProvider(providers) if err != nil { - return nil, err + return err } var errs *packer.MultiError @@ -249,10 +248,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return c, nil + return nil } // getDefaultOCISettingsPath uses os/user to compute the default diff --git a/builder/oracle/oci/config.hcl2spec.go b/builder/oracle/oci/config.hcl2spec.go index 78980b963..107c1ae43 100644 --- a/builder/oracle/oci/config.hcl2spec.go +++ b/builder/oracle/oci/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -82,10 +82,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/oracle/oci/config_test.go b/builder/oracle/oci/config_test.go index 37d23a258..117f1190d 100644 --- a/builder/oracle/oci/config_test.go +++ b/builder/oracle/oci/config_test.go @@ -69,7 +69,8 @@ func TestConfig(t *testing.T) { // Config tests t.Run("BaseConfig", func(t *testing.T) { raw := testConfig(cfgFile) - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) @@ -80,7 +81,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) delete(raw, "access_cfg_file") - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) expectedErrors := []string{ "'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'", @@ -102,7 +104,8 @@ func TestConfig(t *testing.T) { raw["fingerprint"] = "00:00..." raw["key_file"] = keyFile.Name() - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("err: %+v", errs) @@ -112,7 +115,8 @@ func TestConfig(t *testing.T) { t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) { raw := testConfig(cfgFile) - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -131,7 +135,8 @@ func TestConfig(t *testing.T) { t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) { raw := testConfig(cfgFile) - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -156,7 +161,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) delete(raw, k) - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if !strings.Contains(errs.Error(), k) { t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k) @@ -168,7 +174,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) delete(raw, "image_name") - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -183,7 +190,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["user_ocid"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -199,7 +207,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["tenancy_ocid"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -215,7 +224,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["region"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -231,7 +241,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["fingerprint"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration: %+v", errs) } diff --git a/builder/oracle/oci/step_test.go b/builder/oracle/oci/step_test.go index 55afba523..deeff1b1a 100644 --- a/builder/oracle/oci/step_test.go +++ b/builder/oracle/oci/step_test.go @@ -16,7 +16,8 @@ func baseTestConfig() *Config { panic(err) } - cfg, err := NewConfig(map[string]interface{}{ + var c Config + err = c.Prepare(map[string]interface{}{ "availability_domain": "aaaa:US-ASHBURN-AD-1", // Image @@ -46,7 +47,7 @@ func baseTestConfig() *Config { if err != nil { panic(err) } - return cfg + return &c } func testState() multistep.StateBag { diff --git a/builder/osc/bsu/builder.go b/builder/osc/bsu/builder.go index ab4280417..bc93a7a58 100644 --- a/builder/osc/bsu/builder.go +++ b/builder/osc/bsu/builder.go @@ -13,6 +13,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -42,6 +43,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/osc/bsu/builder.hcl2spec.go b/builder/osc/bsu/builder.hcl2spec.go index 8bc75d0f7..4da22cf6a 100644 --- a/builder/osc/bsu/builder.hcl2spec.go +++ b/builder/osc/bsu/builder.hcl2spec.go @@ -98,8 +98,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -115,10 +115,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -151,8 +154,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, "snapshot_account_ids": &hcldec.AttrSpec{Name: "snapshot_account_ids", Type: cty.List(cty.String), Required: false}, "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, - "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, - "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "subregion_name": &hcldec.AttrSpec{Name: "subregion_name", Type: cty.String, Required: false}, "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index 1186a5bdc..120f81413 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -1,3 +1,5 @@ +//go:generate mapstructure-to-hcl2 -type Config,RootBlockDevice + // Package bsusurrogate contains a packer.Builder implementation that // builds a new EBS-backed OMI using an ephemeral instance. package bsusurrogate @@ -9,6 +11,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -39,6 +42,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs diff --git a/builder/osc/bsusurrogate/builder.hcl2spec.go b/builder/osc/bsusurrogate/builder.hcl2spec.go new file mode 100644 index 000000000..aa4ddf94e --- /dev/null +++ b/builder/osc/bsusurrogate/builder.hcl2spec.go @@ -0,0 +1,264 @@ +// Code generated by "mapstructure-to-hcl2 -type Config,RootBlockDevice"; DO NOT EDIT. +package bsusurrogate + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/osc/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" cty:"access_key"` + CustomEndpointOAPI *string `mapstructure:"custom_endpoint_oapi" cty:"custom_endpoint_oapi"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" cty:"profile"` + RawRegion *string `mapstructure:"region" cty:"region"` + SecretKey *string `mapstructure:"secret_key" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" cty:"token"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" cty:"associate_public_ip_address"` + Subregion *string `mapstructure:"subregion_name" cty:"subregion_name"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" cty:"block_duration_minutes"` + DisableStopVm *bool `mapstructure:"disable_stop_vm" cty:"disable_stop_vm"` + BsuOptimized *bool `mapstructure:"bsu_optimized" cty:"bsu_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" cty:"enable_t2_unlimited"` + IamVmProfile *string `mapstructure:"iam_vm_profile" cty:"iam_vm_profile"` + VmInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" cty:"shutdown_behavior"` + VmType *string `mapstructure:"vm_type" cty:"vm_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" cty:"security_group_ids"` + SourceOmi *string `mapstructure:"source_omi" cty:"source_omi"` + SourceOmiFilter *common.FlatOmiFilterOptions `mapstructure:"source_omi_filter" cty:"source_omi_filter"` + SpotPrice *string `mapstructure:"spot_price" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"` + TemporarySGSourceCidr *string `mapstructure:"temporary_security_group_source_cidr" cty:"temporary_security_group_source_cidr"` + UserData *string `mapstructure:"user_data" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" cty:"user_data_file"` + NetFilter *common.FlatNetFilterOptions `mapstructure:"net_filter" cty:"net_filter"` + NetId *string `mapstructure:"net_id" cty:"net_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + OMIMappings []common.FlatBlockDevice `mapstructure:"omi_block_device_mappings" cty:"omi_block_device_mappings"` + LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" cty:"launch_block_device_mappings"` + OMIName *string `mapstructure:"omi_name" cty:"omi_name"` + OMIDescription *string `mapstructure:"omi_description" cty:"omi_description"` + OMIVirtType *string `mapstructure:"omi_virtualization_type" cty:"omi_virtualization_type"` + OMIAccountIDs []string `mapstructure:"omi_account_ids" cty:"omi_account_ids"` + OMIGroups []string `mapstructure:"omi_groups" cty:"omi_groups"` + OMIProductCodes []string `mapstructure:"omi_product_codes" cty:"omi_product_codes"` + OMIRegions []string `mapstructure:"omi_regions" cty:"omi_regions"` + OMITags common.TagMap `mapstructure:"tags" cty:"tags"` + OMIForceDeregister *bool `mapstructure:"force_deregister" cty:"force_deregister"` + OMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" cty:"force_delete_snapshot"` + SnapshotTags common.TagMap `mapstructure:"snapshot_tags" cty:"snapshot_tags"` + SnapshotAccountIDs []string `mapstructure:"snapshot_account_ids" cty:"snapshot_account_ids"` + SnapshotGroups []string `mapstructure:"snapshot_groups" cty:"snapshot_groups"` + RootDevice *FlatRootBlockDevice `mapstructure:"omi_root_device" cty:"omi_root_device"` + VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_oapi": &hcldec.AttrSpec{Name: "custom_endpoint_oapi", Type: cty.String, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "subregion_name": &hcldec.AttrSpec{Name: "subregion_name", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_vm": &hcldec.AttrSpec{Name: "disable_stop_vm", Type: cty.Bool, Required: false}, + "bsu_optimized": &hcldec.AttrSpec{Name: "bsu_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_vm_profile": &hcldec.AttrSpec{Name: "iam_vm_profile", Type: cty.String, Required: false}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "vm_type": &hcldec.AttrSpec{Name: "vm_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_omi": &hcldec.AttrSpec{Name: "source_omi", Type: cty.String, Required: false}, + "source_omi_filter": &hcldec.BlockSpec{TypeName: "source_omi_filter", Nested: hcldec.ObjectSpec((*common.FlatOmiFilterOptions)(nil).HCL2Spec())}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidr": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidr", Type: cty.String, Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "net_filter": &hcldec.BlockSpec{TypeName: "net_filter", Nested: hcldec.ObjectSpec((*common.FlatNetFilterOptions)(nil).HCL2Spec())}, + "net_id": &hcldec.AttrSpec{Name: "net_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "omi_name": &hcldec.AttrSpec{Name: "omi_name", Type: cty.String, Required: false}, + "omi_description": &hcldec.AttrSpec{Name: "omi_description", Type: cty.String, Required: false}, + "omi_virtualization_type": &hcldec.AttrSpec{Name: "omi_virtualization_type", Type: cty.String, Required: false}, + "omi_account_ids": &hcldec.AttrSpec{Name: "omi_account_ids", Type: cty.List(cty.String), Required: false}, + "omi_groups": &hcldec.AttrSpec{Name: "omi_groups", Type: cty.List(cty.String), Required: false}, + "omi_product_codes": &hcldec.AttrSpec{Name: "omi_product_codes", Type: cty.List(cty.String), Required: false}, + "omi_regions": &hcldec.AttrSpec{Name: "omi_regions", Type: cty.List(cty.String), Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, + "force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false}, + "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "snapshot_account_ids": &hcldec.AttrSpec{Name: "snapshot_account_ids", Type: cty.List(cty.String), Required: false}, + "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, + "omi_root_device": &hcldec.BlockSpec{TypeName: "omi_root_device", Nested: hcldec.ObjectSpec((*FlatRootBlockDevice)(nil).HCL2Spec())}, + "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + } + return s +} + +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRootBlockDevice struct { + SourceDeviceName *string `mapstructure:"source_device_name" cty:"source_device_name"` + DeviceName *string `mapstructure:"device_name" cty:"device_name"` + DeleteOnVmDeletion *bool `mapstructure:"delete_on_vm_deletion" cty:"delete_on_vm_deletion"` + IOPS *int64 `mapstructure:"iops" cty:"iops"` + VolumeType *string `mapstructure:"volume_type" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" cty:"volume_size"` +} + +// FlatMapstructure returns a new FlatRootBlockDevice. +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RootBlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRootBlockDevice) +} + +// HCL2Spec returns the hcl spec of a RootBlockDevice. +// This spec is used by HCL to read the fields of RootBlockDevice. +// The decoded values from this spec will then be applied to a FlatRootBlockDevice. +func (*FlatRootBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "source_device_name": &hcldec.AttrSpec{Name: "source_device_name", Type: cty.String, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "delete_on_vm_deletion": &hcldec.AttrSpec{Name: "delete_on_vm_deletion", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/osc/bsuvolume/builder.go b/builder/osc/bsuvolume/builder.go index e0b5a8fa7..b40503f60 100644 --- a/builder/osc/bsuvolume/builder.go +++ b/builder/osc/bsuvolume/builder.go @@ -11,6 +11,7 @@ import ( "log" "net/http" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -44,6 +45,8 @@ type EngineVarsTemplate struct { SourceOMI string } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs // Create passthrough for {{ .BuildRegion }} and {{ .SourceOMI }} variables diff --git a/builder/osc/bsuvolume/builder.hcl2spec.go b/builder/osc/bsuvolume/builder.hcl2spec.go index 70789c634..ea8881b61 100644 --- a/builder/osc/bsuvolume/builder.hcl2spec.go +++ b/builder/osc/bsuvolume/builder.hcl2spec.go @@ -24,10 +24,13 @@ type FlatBlockDevice struct { // FlatMapstructure returns a new FlatBlockDevice. // FlatBlockDevice is an auto-generated flat version of BlockDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) } +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice. -// This spec is used by HCL to read the fields of FlatBlockDevice. +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "delete_on_vm_deletion": &hcldec.AttrSpec{Name: "delete_on_vm_deletion", Type: cty.Bool, Required: false}, @@ -119,8 +122,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -136,10 +139,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -226,7 +232,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, - "bsu_volumes": &hcldec.BlockListSpec{TypeName: "bsu_volumes", Nested: &hcldec.BlockSpec{TypeName: "bsu_volumes", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}}, + "bsu_volumes": &hcldec.BlockListSpec{TypeName: "bsu_volumes", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}, } return s } diff --git a/builder/osc/chroot/builder.go b/builder/osc/chroot/builder.go index e6813d263..939ef4f4f 100644 --- a/builder/osc/chroot/builder.go +++ b/builder/osc/chroot/builder.go @@ -13,6 +13,7 @@ import ( "net/http" "runtime" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -63,6 +64,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/osc/chroot/builder.hcl2spec.go b/builder/osc/chroot/builder.hcl2spec.go index 0404b12c1..b8286e162 100644 --- a/builder/osc/chroot/builder.hcl2spec.go +++ b/builder/osc/chroot/builder.hcl2spec.go @@ -63,10 +63,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -76,7 +79,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "omi_name": &hcldec.AttrSpec{Name: "omi_name", Type: cty.String, Required: false}, "omi_description": &hcldec.AttrSpec{Name: "omi_description", Type: cty.String, Required: false}, "omi_virtualization_type": &hcldec.AttrSpec{Name: "omi_virtualization_type", Type: cty.String, Required: false}, diff --git a/builder/osc/common/run_config.hcl2spec.go b/builder/osc/common/run_config.hcl2spec.go index 53edc1f9f..a61eaac64 100644 --- a/builder/osc/common/run_config.hcl2spec.go +++ b/builder/osc/common/run_config.hcl2spec.go @@ -22,10 +22,13 @@ type FlatBlockDevice struct { // FlatMapstructure returns a new FlatBlockDevice. // FlatBlockDevice is an auto-generated flat version of BlockDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) } +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice. -// This spec is used by HCL to read the fields of FlatBlockDevice. +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "delete_on_vm_deletion": &hcldec.AttrSpec{Name: "delete_on_vm_deletion", Type: cty.Bool, Required: false}, @@ -49,10 +52,13 @@ type FlatNetFilterOptions struct { // FlatMapstructure returns a new FlatNetFilterOptions. // FlatNetFilterOptions is an auto-generated flat version of NetFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*NetFilterOptions) FlatMapstructure() interface{} { return new(FlatNetFilterOptions) } +func (*NetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatNetFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatNetFilterOptions. -// This spec is used by HCL to read the fields of FlatNetFilterOptions. +// HCL2Spec returns the hcl spec of a NetFilterOptions. +// This spec is used by HCL to read the fields of NetFilterOptions. +// The decoded values from this spec will then be applied to a FlatNetFilterOptions. func (*FlatNetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -71,10 +77,13 @@ type FlatOmiFilterOptions struct { // FlatMapstructure returns a new FlatOmiFilterOptions. // FlatOmiFilterOptions is an auto-generated flat version of OmiFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*OmiFilterOptions) FlatMapstructure() interface{} { return new(FlatOmiFilterOptions) } +func (*OmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatOmiFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatOmiFilterOptions. -// This spec is used by HCL to read the fields of FlatOmiFilterOptions. +// HCL2Spec returns the hcl spec of a OmiFilterOptions. +// This spec is used by HCL to read the fields of OmiFilterOptions. +// The decoded values from this spec will then be applied to a FlatOmiFilterOptions. func (*FlatOmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -93,12 +102,13 @@ type FlatSecurityGroupFilterOptions struct { // FlatMapstructure returns a new FlatSecurityGroupFilterOptions. // FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SecurityGroupFilterOptions) FlatMapstructure() interface{} { +func (*SecurityGroupFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSecurityGroupFilterOptions) } -// HCL2Spec returns the hcldec.Spec of a FlatSecurityGroupFilterOptions. -// This spec is used by HCL to read the fields of FlatSecurityGroupFilterOptions. +// HCL2Spec returns the hcl spec of a SecurityGroupFilterOptions. +// This spec is used by HCL to read the fields of SecurityGroupFilterOptions. +// The decoded values from this spec will then be applied to a FlatSecurityGroupFilterOptions. func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -117,10 +127,13 @@ type FlatSubnetFilterOptions struct { // FlatMapstructure returns a new FlatSubnetFilterOptions. // FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SubnetFilterOptions) FlatMapstructure() interface{} { return new(FlatSubnetFilterOptions) } +func (*SubnetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSubnetFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatSubnetFilterOptions. -// This spec is used by HCL to read the fields of FlatSubnetFilterOptions. +// HCL2Spec returns the hcl spec of a SubnetFilterOptions. +// This spec is used by HCL to read the fields of SubnetFilterOptions. +// The decoded values from this spec will then be applied to a FlatSubnetFilterOptions. func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, diff --git a/builder/parallels/iso/builder.go b/builder/parallels/iso/builder.go index b7ea4788f..e5afcc2c5 100644 --- a/builder/parallels/iso/builder.go +++ b/builder/parallels/iso/builder.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" parallelscommon "github.com/hashicorp/packer/builder/parallels/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -82,6 +83,8 @@ type Config struct { ctx interpolate.Context } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/parallels/iso/builder.hcl2spec.go b/builder/parallels/iso/builder.hcl2spec.go index e8e6e506c..0a3bf313b 100644 --- a/builder/parallels/iso/builder.hcl2spec.go +++ b/builder/parallels/iso/builder.hcl2spec.go @@ -72,8 +72,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -98,10 +98,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/parallels/pvm/builder.go b/builder/parallels/pvm/builder.go index a199650d1..41a118eeb 100644 --- a/builder/parallels/pvm/builder.go +++ b/builder/parallels/pvm/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" parallelscommon "github.com/hashicorp/packer/builder/parallels/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -15,17 +16,17 @@ import ( // Builder implements packer.Builder and builds the actual Parallels // images. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/parallels/pvm/config.go b/builder/parallels/pvm/config.go index 3fffe7b98..4f88ee193 100644 --- a/builder/parallels/pvm/config.go +++ b/builder/parallels/pvm/config.go @@ -49,8 +49,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -64,7 +63,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } if c.VMName == "" { @@ -102,8 +101,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/parallels/pvm/config.hcl2spec.go b/builder/parallels/pvm/config.hcl2spec.go index 77d76625f..d15c6e839 100644 --- a/builder/parallels/pvm/config.hcl2spec.go +++ b/builder/parallels/pvm/config.hcl2spec.go @@ -53,8 +53,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -81,10 +81,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/parallels/pvm/config_test.go b/builder/parallels/pvm/config_test.go index 8c869c9fd..e7a2a5d59 100644 --- a/builder/parallels/pvm/config_test.go +++ b/builder/parallels/pvm/config_test.go @@ -53,13 +53,13 @@ func TestNewConfig_sourcePath(t *testing.T) { // Bad c := testConfig(t) delete(c, "source_path") - _, warns, errs := NewConfig(c) + warns, errs := (&Config{}).Prepare(c) testConfigErr(t, warns, errs) // Bad c = testConfig(t) c["source_path"] = "/i/dont/exist" - _, warns, errs = NewConfig(c) + warns, errs = (&Config{}).Prepare(c) testConfigErr(t, warns, errs) // Good @@ -68,7 +68,7 @@ func TestNewConfig_sourcePath(t *testing.T) { c = testConfig(t) c["source_path"] = tf.Name() - _, warns, errs = NewConfig(c) + warns, errs = (&Config{}).Prepare(c) testConfigOk(t, warns, errs) } @@ -76,7 +76,7 @@ func TestNewConfig_FloppyFiles(t *testing.T) { c := testConfig(t) floppies_path := "../../../common/test-fixtures/floppies" c["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} - _, _, err := NewConfig(c) + _, err := (&Config{}).Prepare(c) if err != nil { t.Fatalf("should not have error: %s", err) } @@ -85,7 +85,7 @@ func TestNewConfig_FloppyFiles(t *testing.T) { func TestNewConfig_InvalidFloppies(t *testing.T) { c := testConfig(t) c["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} - _, _, errs := NewConfig(c) + _, errs := (&Config{}).Prepare(c) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -96,18 +96,18 @@ func TestNewConfig_InvalidFloppies(t *testing.T) { } func TestNewConfig_shutdown_timeout(t *testing.T) { - c := testConfig(t) + cfg := testConfig(t) tf := getTempFile(t) defer os.Remove(tf.Name()) // Expect this to fail - c["source_path"] = tf.Name() - c["shutdown_timeout"] = "NaN" - _, warns, errs := NewConfig(c) + cfg["source_path"] = tf.Name() + cfg["shutdown_timeout"] = "NaN" + warns, errs := (&Config{}).Prepare(cfg) testConfigErr(t, warns, errs) // Passes when given a valid time duration - c["shutdown_timeout"] = "10s" - _, warns, errs = NewConfig(c) + cfg["shutdown_timeout"] = "10s" + warns, errs = (&Config{}).Prepare(cfg) testConfigOk(t, warns, errs) } diff --git a/builder/profitbricks/builder.go b/builder/profitbricks/builder.go index 3ab9d18fe..6c5cc4e61 100644 --- a/builder/profitbricks/builder.go +++ b/builder/profitbricks/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -13,16 +14,17 @@ import ( const BuilderId = "packer.profitbricks" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/profitbricks/config.go b/builder/profitbricks/config.go index f3d366c60..275f7b5f2 100644 --- a/builder/profitbricks/config.go +++ b/builder/profitbricks/config.go @@ -34,8 +34,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(&c, &config.DecodeOpts{ @@ -49,7 +48,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -121,9 +120,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.PBUsername) - return &c, nil, nil + return nil, nil } diff --git a/builder/profitbricks/config.hcl2spec.go b/builder/profitbricks/config.hcl2spec.go index c0867c500..1dc8dc7c1 100644 --- a/builder/profitbricks/config.hcl2spec.go +++ b/builder/profitbricks/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -73,10 +73,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/proxmox/builder.go b/builder/proxmox/builder.go index 083cfeb3d..e671e67cb 100644 --- a/builder/proxmox/builder.go +++ b/builder/proxmox/builder.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -27,12 +28,14 @@ var _ packer.Builder = &Builder{} var pluginVersion = "1.0.0" +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *config + return nil, nil } diff --git a/builder/proxmox/config.go b/builder/proxmox/config.go index b3c08ecaf..00f082d07 100644 --- a/builder/proxmox/config.go +++ b/builder/proxmox/config.go @@ -71,8 +71,7 @@ type diskConfig struct { DiskFormat string `mapstructure:"format"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { // Agent defaults to true c.Agent = true @@ -88,7 +87,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -207,11 +206,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Password) - return c, nil, nil + return nil, nil } func contains(haystack []string, needle string) bool { diff --git a/builder/proxmox/config.hcl2spec.go b/builder/proxmox/config.hcl2spec.go index 797bf117b..e9c208c12 100644 --- a/builder/proxmox/config.hcl2spec.go +++ b/builder/proxmox/config.hcl2spec.go @@ -53,8 +53,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -89,10 +89,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -162,8 +165,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "cpu_type": &hcldec.AttrSpec{Name: "cpu_type", Type: cty.String, Required: false}, "sockets": &hcldec.AttrSpec{Name: "sockets", Type: cty.Number, Required: false}, "os": &hcldec.AttrSpec{Name: "os", Type: cty.String, Required: false}, - "network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: &hcldec.BlockSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatnicConfig)(nil).HCL2Spec())}}, - "disks": &hcldec.BlockListSpec{TypeName: "disks", Nested: &hcldec.BlockSpec{TypeName: "disks", Nested: hcldec.ObjectSpec((*FlatdiskConfig)(nil).HCL2Spec())}}, + "network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatnicConfig)(nil).HCL2Spec())}, + "disks": &hcldec.BlockListSpec{TypeName: "disks", Nested: hcldec.ObjectSpec((*FlatdiskConfig)(nil).HCL2Spec())}, "iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false}, "qemu_agent": &hcldec.AttrSpec{Name: "qemu_agent", Type: cty.Bool, Required: false}, "scsi_controller": &hcldec.AttrSpec{Name: "scsi_controller", Type: cty.String, Required: false}, @@ -188,10 +191,13 @@ type FlatdiskConfig struct { // FlatMapstructure returns a new FlatdiskConfig. // FlatdiskConfig is an auto-generated flat version of diskConfig. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*diskConfig) FlatMapstructure() interface{} { return new(FlatdiskConfig) } +func (*diskConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatdiskConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatdiskConfig. -// This spec is used by HCL to read the fields of FlatdiskConfig. +// HCL2Spec returns the hcl spec of a diskConfig. +// This spec is used by HCL to read the fields of diskConfig. +// The decoded values from this spec will then be applied to a FlatdiskConfig. func (*FlatdiskConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "type": &hcldec.AttrSpec{Name: "type", Type: cty.String, Required: false}, @@ -216,10 +222,13 @@ type FlatnicConfig struct { // FlatMapstructure returns a new FlatnicConfig. // FlatnicConfig is an auto-generated flat version of nicConfig. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*nicConfig) FlatMapstructure() interface{} { return new(FlatnicConfig) } +func (*nicConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatnicConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatnicConfig. -// This spec is used by HCL to read the fields of FlatnicConfig. +// HCL2Spec returns the hcl spec of a nicConfig. +// This spec is used by HCL to read the fields of nicConfig. +// The decoded values from this spec will then be applied to a FlatnicConfig. func (*FlatnicConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "model": &hcldec.AttrSpec{Name: "model", Type: cty.String, Required: false}, diff --git a/builder/proxmox/config_test.go b/builder/proxmox/config_test.go index 84dd13c6d..3fa496c3f 100644 --- a/builder/proxmox/config_test.go +++ b/builder/proxmox/config_test.go @@ -9,7 +9,8 @@ import ( ) func TestRequiredParameters(t *testing.T) { - _, _, err := NewConfig(make(map[string]interface{})) + var c Config + _, err := c.Prepare(make(map[string]interface{})) if err == nil { t.Fatal("Expected empty configuration to fail") } diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 6d85d2654..9cb2e54e6 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" "github.com/hashicorp/packer/common/shutdowncommand" @@ -336,6 +337,8 @@ type Config struct { ctx interpolate.Context } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/qemu/builder.hcl2spec.go b/builder/qemu/builder.hcl2spec.go index d82b4e464..5e08510d7 100644 --- a/builder/qemu/builder.hcl2spec.go +++ b/builder/qemu/builder.hcl2spec.go @@ -63,8 +63,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -115,10 +115,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 47ddc8376..751de3977 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -19,16 +20,17 @@ import ( const BuilderId = "hashicorp.scaleway" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return nil, nil } diff --git a/builder/scaleway/config.go b/builder/scaleway/config.go index 6fd862974..8a8e17adc 100644 --- a/builder/scaleway/config.go +++ b/builder/scaleway/config.go @@ -71,8 +71,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ @@ -86,7 +85,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } c.UserAgent = useragent.String() @@ -161,9 +160,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Token) - return c, nil, nil + return nil, nil } diff --git a/builder/scaleway/config.hcl2spec.go b/builder/scaleway/config.hcl2spec.go index 7d91dba08..89e5d85d5 100644 --- a/builder/scaleway/config.hcl2spec.go +++ b/builder/scaleway/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -73,10 +73,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 8934d0649..b180d873f 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -5,8 +5,8 @@ package cvm import ( "context" "fmt" - "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -31,6 +31,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -56,7 +58,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } packer.LogSecretFilter.Set(b.config.SecretId, b.config.SecretKey) - log.Printf("[DEBUG]packer config: %v", b.config) return nil, nil } diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index 4b3c3914b..97ef30c46 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -81,8 +81,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -97,10 +97,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -129,7 +132,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false}, - "data_disks": &hcldec.BlockListSpec{TypeName: "data_disks", Nested: &hcldec.BlockSpec{TypeName: "data_disks", Nested: hcldec.ObjectSpec((*FlattencentCloudDataDisk)(nil).HCL2Spec())}}, + "data_disks": &hcldec.BlockListSpec{TypeName: "data_disks", Nested: hcldec.ObjectSpec((*FlattencentCloudDataDisk)(nil).HCL2Spec())}, "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, "vpc_name": &hcldec.AttrSpec{Name: "vpc_name", Type: cty.String, Required: false}, "vpc_ip": &hcldec.AttrSpec{Name: "vpc_ip", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/run_config.hcl2spec.go b/builder/tencentcloud/cvm/run_config.hcl2spec.go index ea01fe69e..8a2dea24e 100644 --- a/builder/tencentcloud/cvm/run_config.hcl2spec.go +++ b/builder/tencentcloud/cvm/run_config.hcl2spec.go @@ -17,10 +17,13 @@ type FlattencentCloudDataDisk struct { // FlatMapstructure returns a new FlattencentCloudDataDisk. // FlattencentCloudDataDisk is an auto-generated flat version of tencentCloudDataDisk. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*tencentCloudDataDisk) FlatMapstructure() interface{} { return new(FlattencentCloudDataDisk) } +func (*tencentCloudDataDisk) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlattencentCloudDataDisk) +} -// HCL2Spec returns the hcldec.Spec of a FlattencentCloudDataDisk. -// This spec is used by HCL to read the fields of FlattencentCloudDataDisk. +// HCL2Spec returns the hcl spec of a tencentCloudDataDisk. +// This spec is used by HCL to read the fields of tencentCloudDataDisk. +// The decoded values from this spec will then be applied to a FlattencentCloudDataDisk. func (*FlattencentCloudDataDisk) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, diff --git a/builder/triton/builder.go b/builder/triton/builder.go index ba6e2baa8..c4bd477e1 100644 --- a/builder/triton/builder.go +++ b/builder/triton/builder.go @@ -4,6 +4,7 @@ import ( "context" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -20,6 +21,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs := &multierror.Error{} diff --git a/builder/triton/config.hcl2spec.go b/builder/triton/config.hcl2spec.go index b6ee44651..4f1b5a871 100644 --- a/builder/triton/config.hcl2spec.go +++ b/builder/triton/config.hcl2spec.go @@ -67,8 +67,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -82,10 +82,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/triton/source_machine_config.hcl2spec.go b/builder/triton/source_machine_config.hcl2spec.go index 7dfed2c69..a7ac52f84 100644 --- a/builder/triton/source_machine_config.hcl2spec.go +++ b/builder/triton/source_machine_config.hcl2spec.go @@ -22,10 +22,13 @@ type FlatMachineImageFilter struct { // FlatMapstructure returns a new FlatMachineImageFilter. // FlatMachineImageFilter is an auto-generated flat version of MachineImageFilter. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*MachineImageFilter) FlatMapstructure() interface{} { return new(FlatMachineImageFilter) } +func (*MachineImageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMachineImageFilter) +} -// HCL2Spec returns the hcldec.Spec of a FlatMachineImageFilter. -// This spec is used by HCL to read the fields of FlatMachineImageFilter. +// HCL2Spec returns the hcl spec of a MachineImageFilter. +// This spec is used by HCL to read the fields of MachineImageFilter. +// The decoded values from this spec will then be applied to a FlatMachineImageFilter. func (*FlatMachineImageFilter) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false}, diff --git a/builder/ucloud/common/artifact.go b/builder/ucloud/common/artifact.go index 961bd9399..4daa8f68a 100644 --- a/builder/ucloud/common/artifact.go +++ b/builder/ucloud/common/artifact.go @@ -2,11 +2,12 @@ package common import ( "fmt" - "github.com/hashicorp/packer/packer" - "github.com/ucloud/ucloud-sdk-go/ucloud" "log" "sort" "strings" + + "github.com/hashicorp/packer/packer" + "github.com/ucloud/ucloud-sdk-go/ucloud" ) type Artifact struct { diff --git a/builder/ucloud/common/artifact_test.go b/builder/ucloud/common/artifact_test.go index d3f0f12d7..8154766e3 100644 --- a/builder/ucloud/common/artifact_test.go +++ b/builder/ucloud/common/artifact_test.go @@ -1,9 +1,10 @@ package common import ( - "github.com/hashicorp/packer/packer" "reflect" "testing" + + "github.com/hashicorp/packer/packer" ) func TestArtifact_Impl(t *testing.T) { diff --git a/builder/ucloud/common/client.go b/builder/ucloud/common/client.go index 0e1cf0434..58452ca18 100644 --- a/builder/ucloud/common/client.go +++ b/builder/ucloud/common/client.go @@ -7,7 +7,7 @@ import ( "github.com/ucloud/ucloud-sdk-go/services/unet" "github.com/ucloud/ucloud-sdk-go/services/vpc" "github.com/ucloud/ucloud-sdk-go/ucloud" - "github.com/ucloud/ucloud-sdk-go/ucloud/error" + uerr "github.com/ucloud/ucloud-sdk-go/ucloud/error" ) type UCloudClient struct { diff --git a/builder/ucloud/common/image_config.hcl2spec.go b/builder/ucloud/common/image_config.hcl2spec.go index 9839cad4c..a26a1372a 100644 --- a/builder/ucloud/common/image_config.hcl2spec.go +++ b/builder/ucloud/common/image_config.hcl2spec.go @@ -18,10 +18,13 @@ type FlatImageDestination struct { // FlatMapstructure returns a new FlatImageDestination. // FlatImageDestination is an auto-generated flat version of ImageDestination. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ImageDestination) FlatMapstructure() interface{} { return new(FlatImageDestination) } +func (*ImageDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatImageDestination) +} -// HCL2Spec returns the hcldec.Spec of a FlatImageDestination. -// This spec is used by HCL to read the fields of FlatImageDestination. +// HCL2Spec returns the hcl spec of a ImageDestination. +// This spec is used by HCL to read the fields of ImageDestination. +// The decoded values from this spec will then be applied to a FlatImageDestination. func (*FlatImageDestination) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false}, diff --git a/builder/ucloud/common/utils.go b/builder/ucloud/common/utils.go index 1f58444d5..b922608d1 100644 --- a/builder/ucloud/common/utils.go +++ b/builder/ucloud/common/utils.go @@ -2,10 +2,11 @@ package common import ( "fmt" + "strings" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/services/uhost" - "strings" ) func CheckStringIn(val string, available []string) error { diff --git a/builder/ucloud/uhost/builder.go b/builder/ucloud/uhost/builder.go index a9903bf4a..5b9f0b32a 100644 --- a/builder/ucloud/uhost/builder.go +++ b/builder/ucloud/uhost/builder.go @@ -7,6 +7,7 @@ package uhost import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -33,6 +34,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/ucloud/uhost/builder.hcl2spec.go b/builder/ucloud/uhost/builder.hcl2spec.go index e35dee79e..262a64271 100644 --- a/builder/ucloud/uhost/builder.hcl2spec.go +++ b/builder/ucloud/uhost/builder.hcl2spec.go @@ -64,8 +64,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -80,10 +80,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -100,7 +103,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "base_url": &hcldec.AttrSpec{Name: "base_url", Type: cty.String, Required: false}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, - "image_copy_to_mappings": &hcldec.BlockListSpec{TypeName: "image_copy_to_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_copy_to_mappings", Nested: hcldec.ObjectSpec((*common.FlatImageDestination)(nil).HCL2Spec())}}, + "image_copy_to_mappings": &hcldec.BlockListSpec{TypeName: "image_copy_to_mappings", Nested: hcldec.ObjectSpec((*common.FlatImageDestination)(nil).HCL2Spec())}, "wait_image_ready_timeout": &hcldec.AttrSpec{Name: "wait_image_ready_timeout", Type: cty.Number, Required: false}, "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, diff --git a/builder/ucloud/uhost/builder_acc_test.go b/builder/ucloud/uhost/builder_acc_test.go index 0d5a82d6d..091ab8ab6 100644 --- a/builder/ucloud/uhost/builder_acc_test.go +++ b/builder/ucloud/uhost/builder_acc_test.go @@ -2,11 +2,12 @@ package uhost import ( "fmt" + "os" + "testing" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/packer" "github.com/stretchr/testify/assert" - "os" - "testing" builderT "github.com/hashicorp/packer/helper/builder/testing" ) diff --git a/builder/ucloud/uhost/builder_test.go b/builder/ucloud/uhost/builder_test.go index dca944bc5..7d0a2d5ce 100644 --- a/builder/ucloud/uhost/builder_test.go +++ b/builder/ucloud/uhost/builder_test.go @@ -1,10 +1,11 @@ package uhost import ( - ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" - "github.com/hashicorp/packer/packer" "reflect" "testing" + + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" + "github.com/hashicorp/packer/packer" ) func testBuilderConfig() map[string]interface{} { diff --git a/builder/ucloud/uhost/step_check_source_image.go b/builder/ucloud/uhost/step_check_source_image.go index fd77d7eee..66260df79 100644 --- a/builder/ucloud/uhost/step_check_source_image.go +++ b/builder/ucloud/uhost/step_check_source_image.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_config_security_group.go b/builder/ucloud/uhost/step_config_security_group.go index 66c4cd27c..f561bd003 100644 --- a/builder/ucloud/uhost/step_config_security_group.go +++ b/builder/ucloud/uhost/step_config_security_group.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_config_subnet.go b/builder/ucloud/uhost/step_config_subnet.go index b6bb4bd53..affb43ccd 100644 --- a/builder/ucloud/uhost/step_config_subnet.go +++ b/builder/ucloud/uhost/step_config_subnet.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_config_vpc.go b/builder/ucloud/uhost/step_config_vpc.go index e8e2bcec4..b6792318c 100644 --- a/builder/ucloud/uhost/step_config_vpc.go +++ b/builder/ucloud/uhost/step_config_vpc.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" diff --git a/builder/ucloud/uhost/step_copy_image.go b/builder/ucloud/uhost/step_copy_image.go index 690b9deca..60061a33c 100644 --- a/builder/ucloud/uhost/step_copy_image.go +++ b/builder/ucloud/uhost/step_copy_image.go @@ -3,11 +3,12 @@ package uhost import ( "context" "fmt" - ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" - "github.com/hashicorp/packer/common/retry" "strings" "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" + "github.com/hashicorp/packer/common/retry" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/ucloud" diff --git a/builder/ucloud/uhost/step_create_image.go b/builder/ucloud/uhost/step_create_image.go index 235cdb7ff..e0004a1ce 100644 --- a/builder/ucloud/uhost/step_create_image.go +++ b/builder/ucloud/uhost/step_create_image.go @@ -3,9 +3,10 @@ package uhost import ( "context" "fmt" + "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" - "time" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_create_instance.go b/builder/ucloud/uhost/step_create_instance.go index 3c60daef8..a4a312ce8 100644 --- a/builder/ucloud/uhost/step_create_instance.go +++ b/builder/ucloud/uhost/step_create_instance.go @@ -3,15 +3,16 @@ package uhost import ( "context" "fmt" + "math/rand" + "strings" + "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/ucloud" - "math/rand" - "strings" - "time" ) type stepCreateInstance struct { diff --git a/builder/ucloud/uhost/step_pre_validate.go b/builder/ucloud/uhost/step_pre_validate.go index 843616c10..becb80b29 100644 --- a/builder/ucloud/uhost/step_pre_validate.go +++ b/builder/ucloud/uhost/step_pre_validate.go @@ -2,6 +2,7 @@ package uhost import ( "context" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_stop_instance.go b/builder/ucloud/uhost/step_stop_instance.go index 463428d1a..d9376e15e 100644 --- a/builder/ucloud/uhost/step_stop_instance.go +++ b/builder/ucloud/uhost/step_stop_instance.go @@ -3,9 +3,10 @@ package uhost import ( "context" "fmt" + "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" - "time" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index 5fb189e13..984654c51 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" "github.com/hashicorp/packer/helper/communicator" @@ -24,7 +25,7 @@ import ( // Builder implements packer.Builder and builds the actual VirtualBox // images. type Builder struct { - config *Config + config Config runner multistep.Runner } @@ -131,9 +132,9 @@ type Config struct { ctx interpolate.Context } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - b.config = new(Config) err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, diff --git a/builder/vagrant/builder.hcl2spec.go b/builder/vagrant/builder.hcl2spec.go index 1a9dece69..70aba5f49 100644 --- a/builder/vagrant/builder.hcl2spec.go +++ b/builder/vagrant/builder.hcl2spec.go @@ -62,8 +62,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -100,10 +100,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vagrant/builder_test.go b/builder/vagrant/builder_test.go index 65c2e5e9f..5327706af 100644 --- a/builder/vagrant/builder_test.go +++ b/builder/vagrant/builder_test.go @@ -15,7 +15,6 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { } func TestBuilder_Prepare_ValidateSource(t *testing.T) { - b := &Builder{} type testCase struct { config map[string]interface{} errExpected bool @@ -83,7 +82,7 @@ func TestBuilder_Prepare_ValidateSource(t *testing.T) { } for _, tc := range cases { - _, err := b.Prepare(tc.config) + _, err := (&Builder{}).Prepare(tc.config) if (err != nil) != tc.errExpected { t.Fatalf("Unexpected behavior from test case %#v; %s.", tc.config, tc.reason) } diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 02c7aeeac..fe1ba87a2 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + "github.com/hashicorp/hcl/v2/hcldec" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -130,6 +131,8 @@ type Config struct { ctx interpolate.Context } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/virtualbox/iso/builder.hcl2spec.go b/builder/virtualbox/iso/builder.hcl2spec.go index effec9e79..8f7497ac8 100644 --- a/builder/virtualbox/iso/builder.hcl2spec.go +++ b/builder/virtualbox/iso/builder.hcl2spec.go @@ -73,8 +73,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -116,10 +116,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index b20b5b663..f0e92d5db 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -15,17 +16,17 @@ import ( // Builder implements packer.Builder and builds the actual VirtualBox // images. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 3a6ae041a..ce5659bfe 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -104,8 +104,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -120,7 +119,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -195,7 +194,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } // TODO: Write a packer fix and just remove import_opts @@ -203,5 +202,5 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { c.ImportFlags = append(c.ImportFlags, "--options", c.ImportOpts) } - return c, warnings, nil + return warnings, nil } diff --git a/builder/virtualbox/ovf/config.hcl2spec.go b/builder/virtualbox/ovf/config.hcl2spec.go index 4ac16c25a..e518e3cf7 100644 --- a/builder/virtualbox/ovf/config.hcl2spec.go +++ b/builder/virtualbox/ovf/config.hcl2spec.go @@ -62,8 +62,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -102,10 +102,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index 517eda04b..f00d5bdfc 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -31,19 +31,21 @@ func getTempFile(t *testing.T) *os.File { } func TestNewConfig_FloppyFiles(t *testing.T) { - c := testConfig(t) + cfg := testConfig(t) floppies_path := "../../../common/test-fixtures/floppies" - c["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} - _, _, err := NewConfig(c) + cfg["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} + var c Config + _, err := c.Prepare(cfg) if err != nil { t.Fatalf("should not have error: %s", err) } } func TestNewConfig_InvalidFloppies(t *testing.T) { - c := testConfig(t) - c["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} - _, _, errs := NewConfig(c) + cfg := testConfig(t) + cfg["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} + var c Config + _, errs := c.Prepare(cfg) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -55,9 +57,10 @@ func TestNewConfig_InvalidFloppies(t *testing.T) { func TestNewConfig_sourcePath(t *testing.T) { // Okay, because it gets caught during download - c := testConfig(t) - delete(c, "source_path") - _, warns, err := NewConfig(c) + cfg := testConfig(t) + delete(cfg, "source_path") + var c Config + warns, err := c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -69,9 +72,9 @@ func TestNewConfig_sourcePath(t *testing.T) { tf := getTempFile(t) defer os.Remove(tf.Name()) - c = testConfig(t) - c["source_path"] = tf.Name() - _, warns, err = NewConfig(c) + cfg = testConfig(t) + cfg["source_path"] = tf.Name() + warns, err = c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -81,14 +84,15 @@ func TestNewConfig_sourcePath(t *testing.T) { } func TestNewConfig_shutdown_timeout(t *testing.T) { - c := testConfig(t) + cfg := testConfig(t) tf := getTempFile(t) defer os.Remove(tf.Name()) // Expect this to fail - c["source_path"] = tf.Name() - c["shutdown_timeout"] = "NaN" - _, warns, err := NewConfig(c) + cfg["source_path"] = tf.Name() + cfg["shutdown_timeout"] = "NaN" + var c Config + warns, err := c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -97,8 +101,8 @@ func TestNewConfig_shutdown_timeout(t *testing.T) { } // Passes when given a valid time duration - c["shutdown_timeout"] = "10s" - _, warns, err = NewConfig(c) + cfg["shutdown_timeout"] = "10s" + warns, err = c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/virtualbox/ovf/step_import_test.go b/builder/virtualbox/ovf/step_import_test.go index ffaecc0ba..482bfdcdc 100644 --- a/builder/virtualbox/ovf/step_import_test.go +++ b/builder/virtualbox/ovf/step_import_test.go @@ -14,10 +14,11 @@ func TestStepImport_impl(t *testing.T) { func TestStepImport(t *testing.T) { state := testState(t) - c := testConfig(t) - config, _, _ := NewConfig(c) + cfg := testConfig(t) + var c Config + c.Prepare(cfg) state.Put("vm_path", "foo") - state.Put("config", config) + state.Put("config", &c) step := new(StepImport) step.Name = "bar" @@ -47,14 +48,14 @@ func TestStepImport(t *testing.T) { } // Test cleanup - config.KeepRegistered = true + c.KeepRegistered = true step.Cleanup(state) if driver.DeleteCalled { t.Fatal("delete should not be called") } - config.KeepRegistered = false + c.KeepRegistered = false step.Cleanup(state) if !driver.DeleteCalled { t.Fatal("delete should be called") diff --git a/builder/virtualbox/vm/builder.go b/builder/virtualbox/vm/builder.go index 2336e58d5..3deda5ee7 100644 --- a/builder/virtualbox/vm/builder.go +++ b/builder/virtualbox/vm/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -19,13 +20,13 @@ type Builder struct { runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/virtualbox/vm/config.go b/builder/virtualbox/vm/config.go index 14b57295d..dc1a1fd66 100644 --- a/builder/virtualbox/vm/config.go +++ b/builder/virtualbox/vm/config.go @@ -44,8 +44,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -60,7 +59,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -203,8 +202,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/virtualbox/vm/config.hcl2spec.go b/builder/virtualbox/vm/config.hcl2spec.go index eafdf2428..a180a65f6 100644 --- a/builder/virtualbox/vm/config.hcl2spec.go +++ b/builder/virtualbox/vm/config.hcl2spec.go @@ -62,8 +62,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -98,10 +98,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 7e94d9663..0c230d91b 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/hashicorp/hcl/v2/hcldec" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -18,15 +19,14 @@ type Builder struct { runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *c - return warnings, nil } diff --git a/builder/vmware/iso/config.go b/builder/vmware/iso/config.go index 44d577058..2062ee4e5 100644 --- a/builder/vmware/iso/config.go +++ b/builder/vmware/iso/config.go @@ -115,8 +115,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -128,7 +127,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Accumulate any errors and warnings @@ -263,10 +262,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } func (c *Config) checkForVMXTemplateAndVMXDataCollisions() string { diff --git a/builder/vmware/iso/config.hcl2spec.go b/builder/vmware/iso/config.hcl2spec.go index 6dce149ae..21795f95a 100644 --- a/builder/vmware/iso/config.hcl2spec.go +++ b/builder/vmware/iso/config.hcl2spec.go @@ -92,8 +92,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -131,10 +131,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vmware/iso/step_create_vmx_test.go b/builder/vmware/iso/step_create_vmx_test.go index 9bd4d6d4c..0c6fe6e97 100644 --- a/builder/vmware/iso/step_create_vmx_test.go +++ b/builder/vmware/iso/step_create_vmx_test.go @@ -137,17 +137,17 @@ func setupVMwareBuild(t *testing.T, builderConfig map[string]string, provisioner // create our config to test the vmware-iso builder components := packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - return &Builder{}, nil + BuilderStore: packer.MapOfBuilder{ + "vmware-iso": func() (packer.Builder, error) { return &Builder{}, nil }, }, Hook: func(n string) (packer.Hook, error) { return &packer.DispatchHook{}, nil }, - PostProcessor: func(n string) (packer.PostProcessor, error) { - return &packer.MockPostProcessor{}, nil + ProvisionerStore: packer.MapOfProvisioner{ + "shell": func() (packer.Provisioner, error) { return &shell.Provisioner{}, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - return &shell.Provisioner{}, nil + PostProcessorStore: packer.MapOfPostProcessor{ + "something": func() (packer.PostProcessor, error) { return &packer.MockPostProcessor{}, nil }, }, } config := packer.CoreConfig{ diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index e1e33dd7c..a37b0b313 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -7,6 +7,7 @@ import ( "log" "time" + "github.com/hashicorp/hcl/v2/hcldec" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -17,17 +18,17 @@ import ( // Builder implements packer.Builder and builds the actual VMware // images. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 7e5d8d1f0..4b8dbd8b4 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -55,8 +55,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -68,7 +67,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -148,8 +147,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/vmware/vmx/config.hcl2spec.go b/builder/vmware/vmx/config.hcl2spec.go index 15fc44b5e..72e0027e9 100644 --- a/builder/vmware/vmx/config.hcl2spec.go +++ b/builder/vmware/vmx/config.hcl2spec.go @@ -76,8 +76,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -107,10 +107,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vmware/vmx/config_test.go b/builder/vmware/vmx/config_test.go index 4ee0b9426..4b5c63483 100644 --- a/builder/vmware/vmx/config_test.go +++ b/builder/vmware/vmx/config_test.go @@ -34,15 +34,15 @@ func testConfigOk(t *testing.T, warns []string, err error) { func TestNewConfig_sourcePath(t *testing.T) { // Bad - c := testConfig(t) - delete(c, "source_path") - _, warns, errs := NewConfig(c) + cfg := testConfig(t) + delete(cfg, "source_path") + warns, errs := (&Config{}).Prepare(cfg) testConfigErr(t, warns, errs) // Bad - c = testConfig(t) - c["source_path"] = "/i/dont/exist" - _, warns, errs = NewConfig(c) + cfg = testConfig(t) + cfg["source_path"] = "/i/dont/exist" + warns, errs = (&Config{}).Prepare(cfg) testConfigErr(t, warns, errs) // Good @@ -53,8 +53,8 @@ func TestNewConfig_sourcePath(t *testing.T) { tf.Close() defer os.Remove(tf.Name()) - c = testConfig(t) - c["source_path"] = tf.Name() - _, warns, errs = NewConfig(c) + cfg = testConfig(t) + cfg["source_path"] = tf.Name() + warns, errs = (&Config{}).Prepare(cfg) testConfigOk(t, warns, errs) } diff --git a/builder/yandex/builder.go b/builder/yandex/builder.go index 69bb9469f..5f0b68102 100644 --- a/builder/yandex/builder.go +++ b/builder/yandex/builder.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/uuid" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -19,24 +20,24 @@ const BuilderID = "packer.yandex" // Builder represents a Packer Builder. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } // Run executes a yandex Packer build and returns a packer.Artifact // representing a Yandex.Cloud compute image. func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { - driver, err := NewDriverYC(ui, b.config) + driver, err := NewDriverYC(ui, &b.config) ctx = requestid.ContextWithClientTraceID(ctx, uuid.New().String()) if err != nil { @@ -45,7 +46,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack // Set up the state state := &multistep.BasicStateBag{} - state.Put("config", b.config) + state.Put("config", &b.config) state.Put("driver", driver) state.Put("sdk", driver.SDK()) state.Put("hook", hook) @@ -91,7 +92,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack artifact := &Artifact{ image: image.(*compute.Image), - config: b.config, + config: &b.config, } return artifact, nil } diff --git a/builder/yandex/config.go b/builder/yandex/config.go index 2319203e1..5d3952a6a 100644 --- a/builder/yandex/config.go +++ b/builder/yandex/config.go @@ -122,15 +122,14 @@ type Config struct { StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ctx.Funcs = TemplateFuncs err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -286,8 +285,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return c, nil, nil + return nil, nil } diff --git a/builder/yandex/config.hcl2spec.go b/builder/yandex/config.hcl2spec.go index 69f6695d7..1fb0db29b 100644 --- a/builder/yandex/config.hcl2spec.go +++ b/builder/yandex/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -94,10 +94,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/yandex/config_test.go b/builder/yandex/config_test.go index 7e777d0e7..611474718 100644 --- a/builder/yandex/config_test.go +++ b/builder/yandex/config_test.go @@ -135,7 +135,8 @@ func TestConfigPrepare(t *testing.T) { delete(raw, "token") } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, tc.Key) @@ -156,7 +157,8 @@ func TestConfigPrepareStartupScriptFile(t *testing.T) { "key": "file_not_exist", } - _, _, errs := NewConfig(config) + var c Config + _, errs := c.Prepare(config) if errs == nil || !strings.Contains(errs.Error(), "cannot access file 'file_not_exist' with content "+ "for value of metadata key 'key':") { @@ -183,10 +185,11 @@ func TestConfigDefaults(t *testing.T) { for _, tc := range cases { raw := testConfig(t) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigOk(t, warns, errs) - actual := tc.Read(c) + actual := tc.Read(&c) if actual != tc.Value { t.Fatalf("bad: %#v", actual) } @@ -196,7 +199,8 @@ func TestConfigDefaults(t *testing.T) { func TestImageName(t *testing.T) { raw := testConfig(t) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if !strings.HasPrefix(c.ImageName, "packer-") { t.Fatalf("ImageName should have 'packer-' prefix, found %s", c.ImageName) } @@ -208,7 +212,8 @@ func TestImageName(t *testing.T) { func TestZone(t *testing.T) { raw := testConfig(t) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.Zone != "ru-central1-a" { t.Fatalf("Zone should be 'ru-central1-a' given, but is '%s'", c.Zone) } @@ -218,7 +223,8 @@ func TestGpuDefaultPlatformID(t *testing.T) { raw := testConfig(t) raw["instance_gpus"] = 1 - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.PlatformID != "gpu-standard-v1" { t.Fatalf("expected 'gpu-standard-v1', but got '%s'", c.PlatformID) } @@ -229,7 +235,8 @@ func TestGpuWrongPlatformID(t *testing.T) { raw["instance_gpus"] = 1 raw["platform_id"] = "standard-v1" - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs, "incompatible GPU platform_id") } @@ -254,12 +261,13 @@ func testConfig(t *testing.T) (config map[string]interface{}) { func testConfigStruct(t *testing.T) *Config { raw := testConfig(t) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) require.True(t, len(warns) == 0, "bad: %#v", warns) require.NoError(t, errs, "should not have error: %s", errs) - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error, extra string) { diff --git a/cmd/hcl2-schema/hcl2-schema.go b/cmd/hcl2-schema/hcl2-schema.go deleted file mode 100644 index 7628e6185..000000000 --- a/cmd/hcl2-schema/hcl2-schema.go +++ /dev/null @@ -1,313 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "io" - "io/ioutil" - "log" - "os" - "regexp" - "strings" - "text/template" - - "github.com/fatih/structtag" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -var ( - typeNames = flag.String("type", "", "comma-separated list of type names; must be set") - output = flag.String("output", "", "output file name; default srcdir/_hcl2.go") - trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") -) - -// Usage is a replacement usage function for the flags package. -func Usage() { - fmt.Fprintf(os.Stderr, "Usage of hcl2-schema:\n") - fmt.Fprintf(os.Stderr, "\thcl2-schema [flags] -type T [directory]\n") - fmt.Fprintf(os.Stderr, "\thcl2-schema [flags] -type T files... # Must be a single package\n") - fmt.Fprintf(os.Stderr, "Flags:\n") - flag.PrintDefaults() -} - -func main() { - log.SetFlags(0) - log.SetPrefix("hcl2-schema: ") - flag.Usage = Usage - flag.Parse() - if len(*typeNames) == 0 { - flag.Usage() - os.Exit(2) - } - types := strings.Split(*typeNames, ",") - - // We accept either one directory or a list of files. Which do we have? - args := flag.Args() - if len(args) == 0 { - // Default: process whole package in current directory. - args = []string{os.Getenv("GOFILE")} - } - fname := args[0] - outputPath := fname[:len(fname)-2] + "hcl2spec.go" - - b, err := ioutil.ReadFile(fname) - if err != nil { - fmt.Printf("ReadFile: %+v", err) - os.Exit(1) - } - - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, fname, b, parser.ParseComments) - if err != nil { - fmt.Printf("ParseFile: %+v", err) - os.Exit(1) - } - output := Output{ - Args: strings.Join(os.Args[1:], " "), - Package: f.Name.String(), - } - - res := []StructDef{} - - for _, t := range types { - for _, decl := range f.Decls { - typeDecl, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - typeSpec, ok := typeDecl.Specs[0].(*ast.TypeSpec) - if !ok { - continue - } - structDecl, ok := typeSpec.Type.(*ast.StructType) - if !ok { - continue - } - if typeSpec.Name.String() != t { - continue - } - sd := StructDef{StructName: t} - fields := structDecl.Fields.List - for _, field := range fields { - - fieldType := string(b[field.Type.Pos()-1 : field.Type.End()-1]) - fieldName := fieldType[strings.Index(fieldType, ".")+1:] - if len(field.Names) > 0 { - fieldName = field.Names[0].Name - } - - if ast.IsExported(fieldName) { - continue - } - if strings.Contains(fieldType, "func") { - continue - } - fd := FieldDef{Name: fieldName} - - squash := false - accessor := ToSnakeCase(fieldName) - if field.Tag != nil { - tag := field.Tag.Value[1:] - tag = tag[:len(tag)-1] - tags, err := structtag.Parse(tag) - if err != nil { - log.Fatalf("structtag.Parse(%s): err: %v", field.Tag.Value, err) - } - if hsg, err := tags.Get("hcl2-schema-generator"); err == nil { - if len(hsg.Options) > 0 && hsg.Options[0] == "direct" { - fieldType = "direct" - } - if hsg.Name != "" { - accessor = hsg.Name - } - } else if mstr, err := tags.Get("mapstructure"); err == nil { - if len(mstr.Options) > 0 && mstr.Options[0] == "squash" { - squash = true - } - if mstr.Name != "" { - accessor = mstr.Name - } - } - } - - switch fieldType { - case "direct": - fd.Spec = `(&` + sd.StructName + `{}).` + fieldName + `.HCL2Spec()` - case "[]string", "[]*string": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.List(cty.String), - Required: false, - }) - case "[]int", "[]uint", "[]int32", "[]int64": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.List(cty.Number), - Required: false, - }) - case "[]byte", "string", "*string", "time.Duration", "*url.URL": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.String, - Required: false, - }) - case "uint", "*int", "int", "int32", "int64", "float", "float32", "float64": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.Number, - Required: false, - }) - case "bool", "config.Trilean": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.Bool, - Required: false, - }) - case "osccommon.TagMap", "awscommon.TagMap", "TagMap", - "map[string]*string", "map[*string]*string", "map[string]string": - fd.Spec = fmt.Sprintf("%#v", &hcldec.BlockAttrsSpec{ - TypeName: accessor, - ElementType: cty.String, - Required: false, - }) - case "[][]string": - // TODO(azr): implement those - continue - case "communicator.Config": - // this one is manually set - continue - case "map[string]interface{}", "map[string]map[string]interface{}": - // probably never going to be supported - continue - case "common.PackerConfig": - // this one is deprecated ? - continue - default: // nested structures - if squash { - sd.Squashed = append(sd.Squashed, fieldName) - } else if strings.HasPrefix(fieldType, "[]") { - sd.NestedList = append(sd.NestedList, NestedFieldDef{ - FieldName: fieldName, - TypeName: fieldType, - Accessor: accessor, - }) - } else { - sd.Nested = append(sd.Nested, NestedFieldDef{ - FieldName: fieldName, - TypeName: fieldType, - Accessor: accessor, - }) - } - continue - } - - output.ImportCty = true - sd.Fields = append(sd.Fields, fd) - } - res = append(res, sd) - } - } - - output.StructDefs = res - - result := bytes.NewBuffer(nil) - - err = structDocsTemplate.Execute(result, output) - if err != nil { - log.Fatalf("err templating: %v", err) - } - - formattedBytes, err := format.Source(result.Bytes()) - if err != nil { - log.Printf("formatting err: %v", err) - formattedBytes = result.Bytes() - } - - outputFile, err := os.Create(outputPath) - if err != nil { - log.Fatalf("err: %v", err) - } - defer outputFile.Close() - - _, err = io.Copy(outputFile, bytes.NewBuffer(formattedBytes)) - if err != nil { - log.Fatalf("err: %v", err) - } -} - -type Output struct { - Args string - Package string - StructDefs []StructDef - ImportCty bool -} - -type FieldDef struct { - Name string - Spec string -} -type NestedFieldDef struct { - TypeName string - FieldName string - Accessor string -} - -type StructDef struct { - StructName string - Fields []FieldDef - Nested []NestedFieldDef - NestedList []NestedFieldDef - Squashed []string -} - -var structDocsTemplate = template.Must(template.New("structDocsTemplate"). - Funcs(template.FuncMap{ - // "indent": indent, - }). - Parse(`// Code generated by "hcl2-schema {{ .Args }}"; DO NOT EDIT.\n - -package {{ .Package }} - -import ( - "github.com/hashicorp/hcl/v2/hcldec" -{{- if .ImportCty }} - "github.com/zclconf/go-cty/cty" -{{end -}} -) -{{ range .StructDefs }} -{{ $StructName := .StructName}} -func (*{{ .StructName }}) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - {{- range .Fields}} - "{{ .Name }}": {{ .Spec }}, - {{- end }} - {{- range .Nested}} - "{{ .Accessor }}": &hcldec.BlockObjectSpec{TypeName: "{{ .TypeName }}", LabelNames: []string(nil), Nested: hcldec.ObjectSpec((&{{ $StructName }}{}).{{ .FieldName }}.HCL2Spec())}, - {{- end }} - {{- range .NestedList }} - "{{ .Accessor }}": &hcldec.BlockListSpec{TypeName: "{{ .TypeName }}", Nested: hcldec.ObjectSpec((&{{ $StructName }}{}).{{ .FieldName }}[0].HCL2Spec()) }, - {{- end}} - } - {{- range .Squashed }} - for k,v := range (&{{ $StructName }}{}).{{ . }}.HCL2Spec() { - s[k] = v - } - {{- end}} - return s -} -{{end}}`)) - -var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") -var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") - -func ToSnakeCase(str string) string { - snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") - snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") - return strings.ToLower(snake) -} diff --git a/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go b/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go index da3a507bf..58364cee9 100644 --- a/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go +++ b/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go @@ -125,7 +125,7 @@ func main() { newStructName := "Flat" + id.Name structs = append(structs, StructDef{ OriginalStructName: id.Name, - StructName: newStructName, + FlatStructName: newStructName, Struct: flatenedStruct, }) @@ -150,22 +150,23 @@ func main() { return structs[i].OriginalStructName < structs[j].OriginalStructName }) for _, flatenedStruct := range structs { - fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.StructName, flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.FlatStructName, flatenedStruct.OriginalStructName) fmt.Fprintf(out, "\n// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.") - fmt.Fprintf(out, "\ntype %s struct {\n", flatenedStruct.StructName) + fmt.Fprintf(out, "\ntype %s struct {\n", flatenedStruct.FlatStructName) outputStructFields(out, flatenedStruct.Struct) fmt.Fprint(out, "}\n") - fmt.Fprintf(out, "\n// FlatMapstructure returns a new %s.", flatenedStruct.StructName) - fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.StructName, flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// FlatMapstructure returns a new %s.", flatenedStruct.FlatStructName) + fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.FlatStructName, flatenedStruct.OriginalStructName) fmt.Fprintf(out, "\n// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.") - fmt.Fprintf(out, "\nfunc (*%s) FlatMapstructure() interface{} {", flatenedStruct.OriginalStructName) - fmt.Fprintf(out, "return new(%s)", flatenedStruct.StructName) + fmt.Fprintf(out, "\nfunc (*%s) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {", flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "return new(%s)", flatenedStruct.FlatStructName) fmt.Fprint(out, "}\n") - fmt.Fprintf(out, "\n// HCL2Spec returns the hcldec.Spec of a %s.", flatenedStruct.StructName) - fmt.Fprintf(out, "\n// This spec is used by HCL to read the fields of %s.", flatenedStruct.StructName) - fmt.Fprintf(out, "\nfunc (*%s) HCL2Spec() map[string]hcldec.Spec {\n", flatenedStruct.StructName) + fmt.Fprintf(out, "\n// HCL2Spec returns the hcl spec of a %s.", flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// This spec is used by HCL to read the fields of %s.", flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// The decoded values from this spec will then be applied to a %s.", flatenedStruct.FlatStructName) + fmt.Fprintf(out, "\nfunc (*%s) HCL2Spec() map[string]hcldec.Spec {\n", flatenedStruct.FlatStructName) outputStructHCL2SpecBody(out, flatenedStruct.Struct) fmt.Fprint(out, "}\n") } @@ -196,7 +197,7 @@ func main() { type StructDef struct { OriginalStructName string - StructName string + FlatStructName string Struct *types.Struct } @@ -250,7 +251,13 @@ func outputHCL2SpecField(w io.Writer, accessor string, fieldType types.Type, tag }) case *types.Named: b := bytes.NewBuffer(nil) - outputHCL2SpecField(b, accessor, elem, tag) + underlyingType := elem.Underlying() + switch underlyingType.(type) { + case *types.Struct: + fmt.Fprintf(b, `hcldec.ObjectSpec((*%s)(nil).HCL2Spec())`, elem.String()) + default: + outputHCL2SpecField(b, accessor, elem, tag) + } fmt.Fprintf(w, `&hcldec.BlockListSpec{TypeName: "%s", Nested: %s}`, accessor, b.String()) case *types.Slice: b := bytes.NewBuffer(nil) @@ -268,9 +275,6 @@ func outputHCL2SpecField(w io.Writer, accessor string, fieldType types.Type, tag default: outputHCL2SpecField(w, f.String(), underlyingType, tag) } - case *types.Struct: - fmt.Fprintf(w, `&hcldec.BlockObjectSpec{TypeName: "%s",`+ - ` Nested: hcldec.ObjectSpec((*%s)(nil).HCL2Spec())}`, accessor, fieldType.String()) default: fmt.Fprintf(w, `%#v`, &hcldec.AttrSpec{ Name: accessor, @@ -303,7 +307,9 @@ func basicKindToCtyType(kind types.BasicKind) cty.Type { func outputStructFields(w io.Writer, s *types.Struct) { for i := 0; i < s.NumFields(); i++ { field, tag := s.Field(i), s.Tag(i) - fmt.Fprintf(w, " %s `%s`\n", strings.Replace(field.String(), "field ", "", 1), tag) + fieldNameStr := field.String() + fieldNameStr = strings.Replace(fieldNameStr, "field ", "", 1) + fmt.Fprintf(w, " %s `%s`\n", fieldNameStr, tag) } } @@ -350,6 +356,9 @@ func getUsedImports(s *types.Struct) map[NamePath]*types.Package { continue } pkg := namedType.Obj().Pkg() + if pkg == nil { + continue + } res[NamePath{pkg.Name(), pkg.Path()}] = pkg } return res diff --git a/command/build.go b/command/build.go index 8dd245458..d7bedb91e 100644 --- a/command/build.go +++ b/command/build.go @@ -13,6 +13,9 @@ import ( "sync" "syscall" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/enumflag" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template" @@ -52,6 +55,7 @@ func (c *BuildCommand) Run(args []string) int { return c.RunContext(buildCtx, args) } +// Config is the command-configuration parsed from the command line. type Config struct { Color, Debug, Force, Timestamp bool ParallelBuilds int64 @@ -92,33 +96,67 @@ func (c *BuildCommand) ParseArgs(args []string) (Config, int) { return cfg, 0 } -func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { - cfg, ret := c.ParseArgs(args) - if ret != 0 { - return ret +func (c *BuildCommand) GetBuildsFromHCL(path string) ([]packer.Build, int) { + parser := &hcl2template.Parser{ + Parser: hclparse.NewParser(), + BuilderSchemas: c.CoreConfig.Components.BuilderStore, + ProvisionersSchemas: c.CoreConfig.Components.ProvisionerStore, + PostProcessorsSchemas: c.CoreConfig.Components.PostProcessorStore, } + builds, diags := parser.Parse(path) + { + // write HCL errors/diagnostics if any. + b := bytes.NewBuffer(nil) + err := hcl.NewDiagnosticTextWriter(b, parser.Files(), 80, false).WriteDiagnostics(diags) + if err != nil { + c.Ui.Error("could not write diagnostic: " + err.Error()) + return nil, 1 + } + if b.Len() != 0 { + c.Ui.Message(b.String()) + } + } + ret := 0 + if diags.HasErrors() { + ret = 1 + } + + return builds, ret +} + +func (c *BuildCommand) GetBuilds(path string) ([]packer.Build, int) { + + isHCLLoaded, err := isHCLLoaded(path) + if path != "-" && err != nil { + c.Ui.Error(fmt.Sprintf("Could not tell wether %s is hcl enabled: %s", path, err)) + return nil, 1 + } + if isHCLLoaded { + return c.GetBuildsFromHCL(path) + } + + c.Ui.Say(`Legacy JSON Configuration Will Be Used. +The template will be parsed in the legacy configuration style. This style +Will continue to work but users are encouraged to move to the new style, +See: https://packer.io/guides/hcl2 .`) + // Parse the template var tpl *template.Template - var err error - tpl, err = template.ParseFile(cfg.Path) + tpl, err = template.ParseFile(path) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) - return 1 + return nil, 1 } // Get the core core, err := c.Meta.Core(tpl) if err != nil { c.Ui.Error(err.Error()) - return 1 + return nil, 1 } - // Get the builds we care about - var errors = struct { - sync.RWMutex - m map[string]error - }{m: make(map[string]error)} + ret := 0 buildNames := c.Meta.BuildNames(core) builds := make([]packer.Build, 0, len(buildNames)) for _, n := range buildNames { @@ -127,12 +165,22 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { c.Ui.Error(fmt.Sprintf( "Failed to initialize build '%s': %s", n, err)) - errors.m[n] = err + ret = 1 continue } builds = append(builds, b) } + return builds, ret +} + +func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { + cfg, ret := c.ParseArgs(args) + if ret != 0 { + return ret + } + + builds, ret := c.GetBuilds(cfg.Path) if cfg.Debug { c.Ui.Say("Debug mode enabled. Builds will not be parallelized.") @@ -146,18 +194,17 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { packer.UiColorYellow, packer.UiColorBlue, } - buildUis := make(map[string]packer.Ui) - for i, b := range buildNames { - var ui packer.Ui - ui = c.Ui + buildUis := make(map[packer.Build]packer.Ui) + for i := range builds { + ui := c.Ui if cfg.Color { ui = &packer.ColoredUi{ Color: colors[i%len(colors)], Ui: ui, } if _, ok := c.Ui.(*packer.MachineReadableUi); !ok { - ui.Say(fmt.Sprintf("%s output will be in this color.", b)) - if i+1 == len(buildNames) { + ui.Say(fmt.Sprintf("%s: output will be in this color.", builds[i].Name())) + if i+1 == len(builds) { // Add a newline between the color output and the actual output c.Ui.Say("") } @@ -170,7 +217,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { } } - buildUis[b] = ui + buildUis[builds[i]] = ui } log.Printf("Build debug mode: %v", cfg.Debug) @@ -178,7 +225,8 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { log.Printf("On error: %v", cfg.OnError) // Set the debug and force mode and prepare all the builds - for _, b := range builds { + for i := range builds { + b := builds[i] log.Printf("Preparing build: %s", b.Name()) b.SetDebug(cfg.Debug) b.SetForce(cfg.Force) @@ -190,7 +238,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { return 1 } if len(warnings) > 0 { - ui := buildUis[b.Name()] + ui := buildUis[b] ui.Say(fmt.Sprintf("Warnings for build '%s':\n", b.Name())) for _, warning := range warnings { ui.Say(fmt.Sprintf("* %s", warning)) @@ -205,6 +253,11 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { sync.RWMutex m map[string][]packer.Artifact }{m: make(map[string][]packer.Artifact)} + // Get the builds we care about + var errors = struct { + sync.RWMutex + m map[string]error + }{m: make(map[string]error)} limitParallel := semaphore.NewWeighted(cfg.ParallelBuilds) for i := range builds { if err := buildCtx.Err(); err != nil { @@ -214,7 +267,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { b := builds[i] name := b.Name() - ui := buildUis[name] + ui := buildUis[b] if err := limitParallel.Acquire(buildCtx, 1); err != nil { ui.Error(fmt.Sprintf("Build '%s' failed to acquire semaphore: %s", name, err)) errors.Lock() @@ -338,10 +391,10 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { if len(errors.m) > 0 { // If any errors occurred, exit with a non-zero exit status - return 1 + ret = 1 } - return 0 + return ret } func (*BuildCommand) Help() string { diff --git a/command/build_parallel_test.go b/command/build_parallel_test.go index a2dc240ae..4634f5191 100644 --- a/command/build_parallel_test.go +++ b/command/build_parallel_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "path/filepath" "sync" "testing" @@ -28,6 +29,7 @@ type ParallelTestBuilder struct { wg sync.WaitGroup } +func (b *ParallelTestBuilder) ConfigSpec() hcldec.ObjectSpec { return nil } func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, error) { return nil, nil } func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { @@ -39,6 +41,7 @@ func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer // LockedBuilder wont run until unlock is called type LockedBuilder struct{ unlock chan interface{} } +func (b *LockedBuilder) ConfigSpec() hcldec.ObjectSpec { return nil } func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, error) { return nil, nil } func (b *LockedBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { @@ -57,25 +60,13 @@ func testMetaParallel(t *testing.T, builder *ParallelTestBuilder, locked *Locked return Meta{ CoreConfig: &packer.CoreConfig{ Components: packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - switch n { - case "parallel-test": - return builder, nil - case "file": - return &file.Builder{}, nil - case "lock": - return locked, nil - default: - panic(n) - } + BuilderStore: packer.MapOfBuilder{ + "parallel-test": func() (packer.Builder, error) { return builder, nil }, + "file": func() (packer.Builder, error) { return &file.Builder{}, nil }, + "lock": func() (packer.Builder, error) { return locked, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - switch n { - case "sleep": - return &sleep.Provisioner{}, nil - default: - panic(n) - } + ProvisionerStore: packer.MapOfProvisioner{ + "sleep": func() (packer.Provisioner, error) { return &sleep.Provisioner{}, nil }, }, }, }, diff --git a/command/build_test.go b/command/build_test.go index b68ef068c..ca8252715 100644 --- a/command/build_test.go +++ b/command/build_test.go @@ -12,9 +12,9 @@ import ( "github.com/hashicorp/packer/builder/file" "github.com/hashicorp/packer/builder/null" "github.com/hashicorp/packer/packer" - shell_local "github.com/hashicorp/packer/post-processor/shell-local" + shell_local_pp "github.com/hashicorp/packer/post-processor/shell-local" "github.com/hashicorp/packer/provisioner/shell" - sl "github.com/hashicorp/packer/provisioner/shell-local" + shell_local "github.com/hashicorp/packer/provisioner/shell-local" ) func TestBuildOnlyFileCommaFlags(t *testing.T) { @@ -166,7 +166,7 @@ func TestBuildExceptFileCommaFlags(t *testing.T) { } } -func TestBuildExceptNonExistingBuilder(t *testing.T) { +func TestBuildWithNonExistingBuilder(t *testing.T) { c := &BuildCommand{ Meta: testMetaFile(t), } @@ -202,25 +202,16 @@ func fileExists(filename string) bool { // available. This allows us to test a builder that writes files to disk. func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig { components := packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - if n == "file" { - return &file.Builder{}, nil - } - if n == "non-existing" { - return nil, fmt.Errorf("builder type not found") - } - return &null.Builder{}, nil + BuilderStore: packer.MapOfBuilder{ + "file": func() (packer.Builder, error) { return &file.Builder{}, nil }, + "null": func() (packer.Builder, error) { return &null.Builder{}, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - if n == "shell" { - return &shell.Provisioner{}, nil - } else if n == "shell-local" { - return &sl.Provisioner{}, nil - } - return nil, fmt.Errorf("requested provisioner not implemented in this test") + ProvisionerStore: packer.MapOfProvisioner{ + "shell-local": func() (packer.Provisioner, error) { return &shell_local.Provisioner{}, nil }, + "shell": func() (packer.Provisioner, error) { return &shell.Provisioner{}, nil }, }, - PostProcessor: func(n string) (packer.PostProcessor, error) { - return &shell_local.PostProcessor{}, nil + PostProcessorStore: packer.MapOfPostProcessor{ + "shell-local": func() (packer.PostProcessor, error) { return &shell_local_pp.PostProcessor{}, nil }, }, } return &packer.CoreConfig{ diff --git a/command/build_timeout_test.go b/command/build_timeout_test.go index bba3bbca7..5657a37b0 100644 --- a/command/build_timeout_test.go +++ b/command/build_timeout_test.go @@ -15,23 +15,12 @@ import ( // available. This allows us to test a builder that writes files to disk. func testCoreConfigSleepBuilder(t *testing.T) *packer.CoreConfig { components := packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - switch n { - case "file": - return &file.Builder{}, nil - default: - panic(n) - } + BuilderStore: packer.MapOfBuilder{ + "file": func() (packer.Builder, error) { return &file.Builder{}, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - switch n { - case "shell-local": - return &shell_local.Provisioner{}, nil - case "sleep": - return &sleep.Provisioner{}, nil - default: - panic(n) - } + ProvisionerStore: packer.MapOfProvisioner{ + "sleep": func() (packer.Provisioner, error) { return &sleep.Provisioner{}, nil }, + "shell-local": func() (packer.Provisioner, error) { return &shell_local.Provisioner{}, nil }, }, } return &packer.CoreConfig{ diff --git a/command/utils.go b/command/utils.go new file mode 100644 index 000000000..1123a2f0a --- /dev/null +++ b/command/utils.go @@ -0,0 +1,22 @@ +package command + +import ( + "os" + "strings" +) + +func isDir(name string) (bool, error) { + s, err := os.Stat(name) + if err != nil { + return false, err + } + return s.IsDir(), nil +} + +func isHCLLoaded(name string) (bool, error) { + if strings.HasSuffix(name, ".pkr.hcl") || + strings.HasSuffix(name, ".pkr.json") { + return true, nil + } + return isDir(name) +} diff --git a/common/shell-local/config.go b/common/shell-local/config.go index e4c808de9..2e6064258 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -50,7 +50,7 @@ func Decode(config *Config, raws ...interface{}) error { WinRMPassword: `{{.WinRMPassword}}`, } - err := configHelper.Decode(&config, &configHelper.DecodeOpts{ + err := configHelper.Decode(config, &configHelper.DecodeOpts{ Interpolate: true, InterpolateContext: &config.ctx, InterpolateFilter: &interpolate.RenderFilter{ diff --git a/common/shell-local/config.hcl2spec.go b/common/shell-local/config.hcl2spec.go index 257cd390d..e65a3a89d 100644 --- a/common/shell-local/config.hcl2spec.go +++ b/common/shell-local/config.hcl2spec.go @@ -33,10 +33,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/config.go b/config.go index 173d4ece0..f99761979 100644 --- a/config.go +++ b/config.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "runtime" + "sort" "strings" "github.com/hashicorp/packer/command" @@ -27,9 +28,9 @@ type config struct { PluginMinPort int PluginMaxPort int - Builders map[string]string - PostProcessors map[string]string `json:"post-processors"` - Provisioners map[string]string + Builders packer.MapOfBuilder + Provisioners packer.MapOfProvisioner + PostProcessors packer.MapOfPostProcessor `json:"post-processors"` } // Decodes configuration in JSON format from the given io.Reader into @@ -58,7 +59,7 @@ func (c *config) Discover() error { if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) } else { - if err := c.discover(filepath.Dir(exePath)); err != nil { + if err := c.discoverExternalComponents(filepath.Dir(exePath)); err != nil { return err } } @@ -68,19 +69,19 @@ func (c *config) Discover() error { if err != nil { log.Printf("[ERR] Error loading config directory: %s", err) } else { - if err := c.discover(filepath.Join(dir, "plugins")); err != nil { + if err := c.discoverExternalComponents(filepath.Join(dir, "plugins")); err != nil { return err } } // Next, look in the CWD. - if err := c.discover("."); err != nil { + if err := c.discoverExternalComponents("."); err != nil { return err } // Finally, try to use an internal plugin. Note that this will not override // any previously-loaded plugins. - if err := c.discoverInternal(); err != nil { + if err := c.discoverInternalComponents(); err != nil { return err } @@ -89,51 +90,33 @@ func (c *config) Discover() error { // This is a proper packer.BuilderFunc that can be used to load packer.Builder // implementations from the defined plugins. -func (c *config) LoadBuilder(name string) (packer.Builder, error) { +func (c *config) StartBuilder(name string) (packer.Builder, error) { log.Printf("Loading builder: %s\n", name) - bin, ok := c.Builders[name] - if !ok { - log.Printf("Builder not found: %s\n", name) - return nil, nil - } - - return c.pluginClient(bin).Builder() + return c.Builders.Start(name) } // This is a proper implementation of packer.HookFunc that can be used // to load packer.Hook implementations from the defined plugins. -func (c *config) LoadHook(name string) (packer.Hook, error) { +func (c *config) StarHook(name string) (packer.Hook, error) { log.Printf("Loading hook: %s\n", name) return c.pluginClient(name).Hook() } // This is a proper packer.PostProcessorFunc that can be used to load // packer.PostProcessor implementations from defined plugins. -func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) { +func (c *config) StartPostProcessor(name string) (packer.PostProcessor, error) { log.Printf("Loading post-processor: %s", name) - bin, ok := c.PostProcessors[name] - if !ok { - log.Printf("Post-processor not found: %s", name) - return nil, nil - } - - return c.pluginClient(bin).PostProcessor() + return c.PostProcessors.Start(name) } // This is a proper packer.ProvisionerFunc that can be used to load // packer.Provisioner implementations from defined plugins. -func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) { +func (c *config) StartProvisioner(name string) (packer.Provisioner, error) { log.Printf("Loading provisioner: %s\n", name) - bin, ok := c.Provisioners[name] - if !ok { - log.Printf("Provisioner not found: %s\n", name) - return nil, nil - } - - return c.pluginClient(bin).Provisioner() + return c.Provisioners.Start(name) } -func (c *config) discover(path string) error { +func (c *config) discoverExternalComponents(path string) error { var err error if !filepath.IsAbs(path) { @@ -142,32 +125,69 @@ func (c *config) discover(path string) error { return err } } + externallyUsed := []string{} - err = c.discoverSingle( - filepath.Join(path, "packer-builder-*"), &c.Builders) + pluginPaths, err := c.discoverSingle(filepath.Join(path, "packer-builder-*")) if err != nil { return err } + for plugin := range pluginPaths { + plugin := plugin + c.Builders[plugin] = func() (packer.Builder, error) { + return c.pluginClient(pluginPaths[plugin]).Builder() + } + externallyUsed = append(externallyUsed, plugin) + } + if len(externallyUsed) > 0 { + sort.Strings(externallyUsed) + log.Printf("using external builders %v", externallyUsed) + externallyUsed = nil + } - err = c.discoverSingle( - filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors) + pluginPaths, err = c.discoverSingle(filepath.Join(path, "packer-post-processor-*")) if err != nil { return err } + for plugin := range pluginPaths { + plugin := plugin + c.PostProcessors[plugin] = func() (packer.PostProcessor, error) { + return c.pluginClient(pluginPaths[plugin]).PostProcessor() + } + externallyUsed = append(externallyUsed, plugin) + } + if len(externallyUsed) > 0 { + sort.Strings(externallyUsed) + log.Printf("using external post-processors %v", externallyUsed) + externallyUsed = nil + } - return c.discoverSingle( - filepath.Join(path, "packer-provisioner-*"), &c.Provisioners) + pluginPaths, err = c.discoverSingle(filepath.Join(path, "packer-provisioner-*")) + if err != nil { + return err + } + for plugin := range pluginPaths { + plugin := plugin + c.Provisioners[plugin] = func() (packer.Provisioner, error) { + return c.pluginClient(pluginPaths[plugin]).Provisioner() + } + externallyUsed = append(externallyUsed, plugin) + } + if len(externallyUsed) > 0 { + sort.Strings(externallyUsed) + log.Printf("using external provisioners %v", externallyUsed) + externallyUsed = nil + } + + return nil } -func (c *config) discoverSingle(glob string, m *map[string]string) error { +func (c *config) discoverSingle(glob string) (map[string]string, error) { matches, err := filepath.Glob(glob) if err != nil { - return err + return nil, err } - if *m == nil { - *m = make(map[string]string) - } + res := make(map[string]string) prefix := filepath.Base(glob) prefix = prefix[:strings.Index(prefix, "*")] @@ -191,13 +211,13 @@ func (c *config) discoverSingle(glob string, m *map[string]string) error { // Look for foo-bar-baz. The plugin name is "baz" plugin := file[len(prefix):] log.Printf("[DEBUG] Discovered plugin: %s = %s", plugin, match) - (*m)[plugin] = match + res[plugin] = match } - return nil + return res, nil } -func (c *config) discoverInternal() error { +func (c *config) discoverInternalComponents() error { // Get the packer binary path packerPath, err := osext.Executable() if err != nil { @@ -206,34 +226,38 @@ func (c *config) discoverInternal() error { } for builder := range command.Builders { + builder := builder _, found := (c.Builders)[builder] if !found { - (c.Builders)[builder] = fmt.Sprintf("%s%splugin%spacker-builder-%s", - packerPath, PACKERSPACE, PACKERSPACE, builder) - } else { - log.Printf("Using external plugin for %s", builder) + c.Builders[builder] = func() (packer.Builder, error) { + bin := fmt.Sprintf("%s%splugin%spacker-builder-%s", + packerPath, PACKERSPACE, PACKERSPACE, builder) + return c.pluginClient(bin).Builder() + } } } for provisioner := range command.Provisioners { + provisioner := provisioner _, found := (c.Provisioners)[provisioner] if !found { - (c.Provisioners)[provisioner] = fmt.Sprintf( - "%s%splugin%spacker-provisioner-%s", - packerPath, PACKERSPACE, PACKERSPACE, provisioner) - } else { - log.Printf("Using external plugin for %s", provisioner) + c.Provisioners[provisioner] = func() (packer.Provisioner, error) { + bin := fmt.Sprintf("%s%splugin%spacker-provisioner-%s", + packerPath, PACKERSPACE, PACKERSPACE, provisioner) + return c.pluginClient(bin).Provisioner() + } } } for postProcessor := range command.PostProcessors { + postProcessor := postProcessor _, found := (c.PostProcessors)[postProcessor] if !found { - (c.PostProcessors)[postProcessor] = fmt.Sprintf( - "%s%splugin%spacker-post-processor-%s", - packerPath, PACKERSPACE, PACKERSPACE, postProcessor) - } else { - log.Printf("Using external plugin for %s", postProcessor) + c.PostProcessors[postProcessor] = func() (packer.PostProcessor, error) { + bin := fmt.Sprintf("%s%splugin%spacker-post-processor-%s", + packerPath, PACKERSPACE, PACKERSPACE, postProcessor) + return c.pluginClient(bin).PostProcessor() + } } } @@ -248,7 +272,7 @@ func (c *config) pluginClient(path string) *plugin.Client { if err != nil { // If that doesn't work, look for it in the same directory // as the `packer` executable (us). - log.Printf("Plugin could not be found. Checking same directory as executable.") + log.Printf("Plugin could not be found at %s (%v). Checking same directory as executable.", originalPath, err) exePath, err := osext.Executable() if err != nil { log.Printf("Couldn't get current exe path: %s", err) diff --git a/go.mod b/go.mod index 27db76320..d6f811f73 100644 --- a/go.mod +++ b/go.mod @@ -81,6 +81,7 @@ require ( github.com/hashicorp/golang-lru v0.5.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl/v2 v2.0.0 + github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 github.com/hashicorp/serf v0.8.2 // indirect github.com/hashicorp/vault v1.1.0 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d @@ -124,8 +125,6 @@ require ( github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b - github.com/onsi/ginkgo v1.7.0 // indirect - github.com/onsi/gomega v1.4.3 // indirect github.com/oracle/oci-go-sdk v1.8.0 github.com/outscale/osc-go v0.0.1 github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a @@ -157,7 +156,7 @@ require ( github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829 - github.com/zclconf/go-cty v1.1.0 + github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af go.opencensus.io v0.22.2 // indirect golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e golang.org/x/exp v0.0.0-20191129062945-2f5052295587 // indirect diff --git a/go.sum b/go.sum index 434ccc196..f6676f405 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,7 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 h1:3b+p838vN4sc37brz9W2HDphtSwZFcXZwFLyzm5Vk28= github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3/go.mod h1:YOY5xnRf7Jz2SZCLSKgVfyqNzbRgyTznM3HyDqQMxcU= +github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae h1:2Zmk+8cNvAGuY8AyvZuWpUdpQUAXwfom4ReVMe/CTIo= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= @@ -222,6 +223,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJ github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw= github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= +github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8= @@ -234,6 +236,7 @@ github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxB github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE= @@ -266,6 +269,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8= github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= +github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 h1:PFfGModn55JA0oBsvFghhj0v93me+Ctr3uHC/UmFAls= +github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= @@ -290,6 +295,7 @@ github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 h1:a2 github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -476,6 +482,7 @@ github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvA github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311 h1:s5pyxd5S6wRs2WpEE0xRfWUF46Wbz44h203KnbX0ecI= github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= @@ -485,8 +492,13 @@ github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e h1:hzwq5G github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829 h1:2FGwbx03GpP1Ulzg/L46tSoKh9t4yg8BhMKQl/Ff1x8= github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829/go.mod h1:Eml0jFLU4VVHgIN8zPHMuNwZXVzUMILyO6lQZSfz854= +github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.1.1 h1:Shl2p9Dat0cqJfXu0DZa+cOTRPhXQjK8IYWD6GVfiqo= +github.com/zclconf/go-cty v1.1.1/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af h1:4arg31xOP/qIUV1YVbCWJtChPGzwGzgmlucVbddUq+Y= +github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= @@ -552,6 +564,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= @@ -707,6 +720,7 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/hcl2template/common_test.go b/hcl2template/common_test.go new file mode 100644 index 000000000..3feae5347 --- /dev/null +++ b/hcl2template/common_test.go @@ -0,0 +1,236 @@ +package hcl2template + +import ( + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" + "github.com/zclconf/go-cty/cty" +) + +func getBasicParser() *Parser { + return &Parser{ + Parser: hclparse.NewParser(), + BuilderSchemas: mapOfBuilder(map[string]packer.Builder{ + "amazon-ebs": &MockBuilder{}, + "virtualbox-iso": &MockBuilder{}, + }), + ProvisionersSchemas: mapOfProvisioner(map[string]packer.Provisioner{ + "shell": &MockProvisioner{}, + "file": &MockProvisioner{}, + }), + PostProcessorsSchemas: mapOfPostProcessor(map[string]packer.PostProcessor{ + "amazon-import": &MockPostProcessor{}, + }), + } +} + +type mapOfBuilder map[string]packer.Builder + +func (mob mapOfBuilder) Has(builder string) bool { + _, res := mob[builder] + return res +} + +func (mob mapOfBuilder) Start(builder string) (packer.Builder, error) { + d, found := mob[builder] + var err error + if !found { + err = fmt.Errorf("Unknown entry %s", builder) + } + return d, err +} + +func (mob mapOfBuilder) List() []string { + res := []string{} + for k := range mob { + res = append(res, k) + } + return res +} + +type mapOfCommunicator map[string]packer.ConfigurableCommunicator + +func (mob mapOfCommunicator) Start(communicator string) (packer.ConfigurableCommunicator, error) { + c, found := mob[communicator] + var err error + if !found { + err = fmt.Errorf("Unknown entry %s", communicator) + } + return c, err +} + +type mapOfProvisioner map[string]packer.Provisioner + +func (mop mapOfProvisioner) Has(provisioner string) bool { + _, res := mop[provisioner] + return res +} + +func (mop mapOfProvisioner) Start(provisioner string) (packer.Provisioner, error) { + p, found := mop[provisioner] + var err error + if !found { + err = fmt.Errorf("Unknown provisioner %s", provisioner) + } + return p, err +} + +func (mod mapOfProvisioner) List() []string { + res := []string{} + for k := range mod { + res = append(res, k) + } + return res +} + +type mapOfPostProcessor map[string]packer.PostProcessor + +func (mop mapOfPostProcessor) Has(provisioner string) bool { + _, res := mop[provisioner] + return res +} + +func (mop mapOfPostProcessor) Start(postProcessor string) (packer.PostProcessor, error) { + p, found := mop[postProcessor] + var err error + if !found { + err = fmt.Errorf("Unknown post-processor %s", postProcessor) + } + return p, err +} + +func (mod mapOfPostProcessor) List() []string { + res := []string{} + for k := range mod { + res = append(res, k) + } + return res +} + +type parseTestArgs struct { + filename string +} + +type parseTest struct { + name string + parser *Parser + args parseTestArgs + + parseWantCfg *PackerConfig + parseWantDiags bool + parseWantDiagHasErrors bool + + getBuildsWantBuilds []packer.Build + getBuildsWantDiags bool + // getBuildsWantDiagHasErrors bool +} + +func testParse(t *testing.T, tests []parseTest) { + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotCfg, gotDiags := tt.parser.parse(tt.args.filename) + if tt.parseWantDiags == (gotDiags == nil) { + t.Fatalf("Parser.parse() unexpected diagnostics. %s", gotDiags) + } + if tt.parseWantDiagHasErrors != gotDiags.HasErrors() { + t.Fatalf("Parser.parse() unexpected diagnostics HasErrors. %s", gotDiags) + } + if diff := cmp.Diff(tt.parseWantCfg, gotCfg, + cmpopts.IgnoreUnexported(cty.Value{}, Source{}, ProvisionerBlock{}, PostProcessorBlock{}), + cmpopts.IgnoreTypes(HCL2Ref{}), + cmpopts.IgnoreTypes([]hcl.Range{}), + cmpopts.IgnoreTypes(hcl.Range{}), + cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}), + cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}), + ); diff != "" { + t.Fatalf("Parser.parse() wrong packer config. %s", diff) + } + if gotDiags.HasErrors() { + return + } + + gotBuilds, gotDiags := tt.parser.getBuilds(gotCfg) + if tt.getBuildsWantDiags == (gotDiags == nil) { + t.Fatalf("Parser.getBuilds() unexpected diagnostics. %s", gotDiags) + } + if diff := cmp.Diff(tt.getBuildsWantBuilds, gotBuilds, + cmpopts.IgnoreUnexported(packer.CoreBuild{}, + packer.CoreBuildProvisioner{}, + packer.CoreBuildPostProcessor{}, + ), + ); diff != "" { + t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff) + } + }) + } +} + +var ( + // everything in the tests is a basicNestedMockConfig this allow to test + // each known type to packer ( and embedding ) in one go. + basicNestedMockConfig = NestedMockConfig{ + String: "string", + Int: 42, + Int64: 43, + Bool: true, + Trilean: config.TriTrue, + Duration: 10 * time.Second, + MapStringString: map[string]string{ + "a": "b", + "c": "d", + }, + SliceString: []string{ + "a", + "b", + "c", + }, + } + + basicMockBuilder = &MockBuilder{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + basicNestedMockConfig, + basicNestedMockConfig, + }, + }, + } + + basicMockProvisioner = &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + {}, + }, + }, + } + basicMockPostProcessor = &MockPostProcessor{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + {}, + }, + }, + } + basicMockCommunicator = &MockCommunicator{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + {}, + }, + }, + } +) diff --git a/hcl2template/config_load.go b/hcl2template/config_load.go deleted file mode 100644 index 61bd281aa..000000000 --- a/hcl2template/config_load.go +++ /dev/null @@ -1,101 +0,0 @@ -package hcl2template - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" -) - -type Artifacts map[ArtifactRef]*Artifact - -type Artifact struct { - Type string - Name string - - DeclRange hcl.Range - - Config hcl.Body -} - -func (a *Artifact) Ref() ArtifactRef { - return ArtifactRef{ - Type: a.Type, - Name: a.Name, - } -} - -type ArtifactRef struct { - Type string - Name string -} - -// NoArtifact is the zero value of ArtifactRef, representing the absense of an -// artifact. -var NoArtifact ArtifactRef - -func artifactRefFromAbsTraversal(t hcl.Traversal) (ArtifactRef, hcl.Diagnostics) { - var diags hcl.Diagnostics - if len(t) != 3 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "An artifact reference must have three parts separated by periods: the keyword \"artifact\", the builder type name, and the artifact name.", - Subject: t.SourceRange().Ptr(), - }) - return NoArtifact, diags - } - - if t.RootName() != "artifact" { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "The first part of an artifact reference must be the keyword \"artifact\".", - Subject: t[0].SourceRange().Ptr(), - }) - return NoArtifact, diags - } - btStep, ok := t[1].(hcl.TraverseAttr) - if !ok { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "The second part of an artifact reference must be an identifier giving the builder type of the artifact.", - Subject: t[1].SourceRange().Ptr(), - }) - return NoArtifact, diags - } - nameStep, ok := t[2].(hcl.TraverseAttr) - if !ok { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "The third part of an artifact reference must be an identifier giving the name of the artifact.", - Subject: t[2].SourceRange().Ptr(), - }) - return NoArtifact, diags - } - - return ArtifactRef{ - Type: btStep.Name, - Name: nameStep.Name, - }, diags -} - -func (r ArtifactRef) String() string { - return fmt.Sprintf("%s.%s", r.Type, r.Name) -} - -// decodeBodyWithoutSchema is a generic alternative to hcldec.Decode that -// just extracts whatever attributes are present and rejects any nested blocks, -// for compatibility with legacy builders that can't provide explicit schema. -func decodeBodyWithoutSchema(body hcl.Body, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { - attrs, diags := body.JustAttributes() - vals := make(map[string]cty.Value) - for name, attr := range attrs { - val, moreDiags := attr.Expr.Value(ctx) - diags = append(diags, moreDiags...) - vals[name] = val - } - return cty.ObjectVal(vals), diags -} diff --git a/hcl2template/decode.go b/hcl2template/decode.go new file mode 100644 index 000000000..e7be2136e --- /dev/null +++ b/hcl2template/decode.go @@ -0,0 +1,15 @@ +package hcl2template + +import ( + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +type Decodable interface { + ConfigSpec() hcldec.ObjectSpec +} + +func decodeHCL2Spec(block *hcl.Block, ctx *hcl.EvalContext, dec Decodable) (cty.Value, hcl.Diagnostics) { + return hcldec.Decode(block.Body, dec.ConfigSpec(), ctx) +} diff --git a/hcl2template/load_test.go b/hcl2template/load_test.go deleted file mode 100644 index 0f4d6f91d..000000000 --- a/hcl2template/load_test.go +++ /dev/null @@ -1,352 +0,0 @@ -package hcl2template - -import ( - "testing" - - awscommon "github.com/hashicorp/packer/builder/amazon/common" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclparse" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/packer/helper/communicator" - - amazonebs "github.com/hashicorp/packer/builder/amazon/ebs" - "github.com/hashicorp/packer/builder/virtualbox/iso" - - "github.com/hashicorp/packer/provisioner/file" - "github.com/hashicorp/packer/provisioner/shell" - - amazon_import "github.com/hashicorp/packer/post-processor/amazon-import" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func getBasicParser() *Parser { - return &Parser{ - Parser: hclparse.NewParser(), - ProvisionersSchemas: map[string]Decodable{ - "shell": &shell.Config{}, - "file": &file.Config{}, - }, - PostProvisionersSchemas: map[string]Decodable{ - "amazon-import": &amazon_import.Config{}, - }, - CommunicatorSchemas: map[string]Decodable{ - "ssh": &communicator.SSH{}, - "winrm": &communicator.WinRM{}, - }, - SourceSchemas: map[string]Decodable{ - "amazon-ebs": &amazonebs.Config{}, - "virtualbox-iso": &iso.Config{}, - }, - } -} - -func TestParser_ParseFile(t *testing.T) { - defaultParser := getBasicParser() - - type fields struct { - Parser *hclparse.Parser - } - type args struct { - filename string - cfg *PackerConfig - } - tests := []struct { - name string - parser *Parser - args args - wantPackerConfig *PackerConfig - wantDiags bool - }{ - { - "valid " + sourceLabel + " load", - defaultParser, - args{"testdata/sources/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - ISOChecksum: strPtr("769474248a3897f4865817446f9a4a53"), - ISOChecksumType: strPtr("md5"), - RawSingleISOUrl: strPtr("http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso"), - BootCommand: []string{"..."}, - ShutdownCommand: strPtr("echo 'vagrant' | sudo -S shutdown -P now"), - BootWait: strPtr("10s"), - VBoxManage: [][]string{}, - VBoxManagePost: [][]string{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "ubuntu-1604", - }: { - Type: "amazon-ebs", - Name: "ubuntu-1604", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - Filters: map[string]string{ - "name": "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*", - "root-device-type": "ebs", - "virtualization-type": "hvm", - }, - Owners: []string{"099720109477"}, - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - }: { - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - MostRecent: boolPtr(true), - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - }, - }, - false, - }, - - { - "valid " + communicatorLabel + " load", - defaultParser, - args{"testdata/communicator/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Communicators: map[CommunicatorRef]*Communicator{ - {Type: "ssh", Name: "vagrant"}: { - Type: "ssh", Name: "vagrant", - Cfg: &communicator.FlatSSH{ - SSHUsername: strPtr("vagrant"), - SSHPassword: strPtr("s3cr4t"), - SSHClearAuthorizedKeys: boolPtr(true), - SSHHost: strPtr("sssssh.hashicorp.io"), - SSHHandshakeAttempts: intPtr(32), - SSHPort: intPtr(42), - SSHFileTransferMethod: strPtr("scp"), - SSHPrivateKeyFile: strPtr("file.pem"), - SSHPty: boolPtr(false), - SSHTimeout: strPtr("5m"), - SSHAgentAuth: boolPtr(false), - SSHDisableAgentForwarding: boolPtr(true), - SSHBastionHost: strPtr(""), - SSHBastionPort: intPtr(0), - SSHBastionAgentAuth: boolPtr(true), - SSHBastionUsername: strPtr(""), - SSHBastionPassword: strPtr(""), - SSHBastionPrivateKeyFile: strPtr(""), - SSHProxyHost: strPtr("ninja-potatoes.com"), - SSHProxyPort: intPtr(42), - SSHProxyUsername: strPtr("dark-father"), - SSHProxyPassword: strPtr("pickle-rick"), - SSHKeepAliveInterval: strPtr("10s"), - SSHReadWriteTimeout: strPtr("5m"), - }, - }, - }, - }, - false, - }, - - { - "duplicate " + sourceLabel, defaultParser, - args{"testdata/sources/basic.pkr.hcl", &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - }, - }, - }, - }, - }, - &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "ubuntu-1604", - }: { - Type: "amazon-ebs", - Name: "ubuntu-1604", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - Filters: map[string]string{ - "name": "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*", - "root-device-type": "ebs", - "virtualization-type": "hvm", - }, - Owners: []string{"099720109477"}, - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - }: { - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - MostRecent: boolPtr(true), - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - }, - }, - true, - }, - - {"valid variables load", defaultParser, - args{"testdata/variables/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Variables: PackerV1Variables{ - "image_name": "foo-image-{{user `my_secret`}}", - "key": "value", - "my_secret": "foo", - }, - }, - false, - }, - - {"valid " + buildLabel + " load", defaultParser, - args{"testdata/build/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Builds: Builds{ - { - Froms: BuildFromList{ - { - Src: SourceRef{"amazon-ebs", "ubuntu-1604"}, - }, - { - Src: SourceRef{"virtualbox-iso", "ubuntu-1204"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - CommunicatorRef: CommunicatorRef{"ssh", "vagrant"}, - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo '{{user `my_secret`}}' :D"}, - }}, - {Cfg: &shell.FlatConfig{ - Scripts: []string{"script-1.sh", "script-2.sh"}, - ValidExitCodes: []int{0, 42}, - }}, - {Cfg: &file.FlatConfig{ - Source: strPtr("app.tar.gz"), - Destination: strPtr("/tmp/app.tar.gz"), - }}, - }, - }, - }, - PostProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &amazon_import.FlatConfig{ - Name: strPtr("that-ubuntu-1.0"), - }}, - }, - }, - }, - }, - &Build{ - Froms: BuildFromList{ - { - Src: SourceRef{"amazon", "that-ubuntu-1"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo HOLY GUACAMOLE !"}, - }}, - }, - }, - }, - }, - }, - }, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p := tt.parser - f, moreDiags := p.ParseHCLFile(tt.args.filename) - if moreDiags != nil { - t.Fatalf("diags: %s", moreDiags) - } - diags := p.ParseFile(f, tt.args.cfg) - if tt.wantDiags == (diags == nil) { - for _, diag := range diags { - t.Errorf("PackerConfig.Load() unexpected diagnostics. %v", diag) - } - t.Error("") - } - if diff := cmp.Diff(tt.wantPackerConfig, tt.args.cfg, - cmpopts.IgnoreUnexported(cty.Value{}), - cmpopts.IgnoreTypes(HCL2Ref{}), - cmpopts.IgnoreTypes([]hcl.Range{}), - cmpopts.IgnoreTypes(hcl.Range{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}), - ); diff != "" { - t.Errorf("PackerConfig.Load() wrong packer config. %s", diff) - } - if t.Failed() { - t.Fatal() - } - }) - } -} - -func strPtr(s string) *string { return &s } -func intPtr(i int) *int { return &i } -func boolPtr(b bool) *bool { return &b } diff --git a/hcl2template/mock.go b/hcl2template/mock.go new file mode 100644 index 000000000..09fbf0c19 --- /dev/null +++ b/hcl2template/mock.go @@ -0,0 +1,120 @@ +//go:generate mapstructure-to-hcl2 -type MockConfig,NestedMockConfig + +package hcl2template + +import ( + "context" + "time" + + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" +) + +type NestedMockConfig struct { + String string `mapstructure:"string"` + Int int `mapstructure:"int"` + Int64 int64 `mapstructure:"int64"` + Bool bool `mapstructure:"bool"` + Trilean config.Trilean `mapstructure:"trilean"` + Duration time.Duration `mapstructure:"duration"` + MapStringString map[string]string `mapstructure:"map_string_string"` + SliceString []string `mapstructure:"slice_string"` +} + +type MockConfig struct { + NestedMockConfig `mapstructure:",squash"` + Nested NestedMockConfig `mapstructure:"nested"` + NestedSlice []NestedMockConfig `mapstructure:"nested_slice"` +} + +////// +// MockBuilder +////// + +type MockBuilder struct { + Config MockConfig +} + +var _ packer.Builder = new(MockBuilder) + +func (b *MockBuilder) ConfigSpec() hcldec.ObjectSpec { return b.Config.FlatMapstructure().HCL2Spec() } + +func (b *MockBuilder) Prepare(raws ...interface{}) ([]string, error) { + return nil, config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} + +func (b *MockBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { + return nil, nil +} + +////// +// MockProvisioner +////// + +type MockProvisioner struct { + Config MockConfig +} + +var _ packer.Provisioner = new(MockProvisioner) + +func (b *MockProvisioner) ConfigSpec() hcldec.ObjectSpec { + return b.Config.FlatMapstructure().HCL2Spec() +} + +func (b *MockProvisioner) Prepare(raws ...interface{}) error { + return config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} + +func (b *MockProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { + return nil +} + +////// +// MockPostProcessor +////// + +type MockPostProcessor struct { + Config MockConfig +} + +var _ packer.PostProcessor = new(MockPostProcessor) + +func (b *MockPostProcessor) ConfigSpec() hcldec.ObjectSpec { + return b.Config.FlatMapstructure().HCL2Spec() +} + +func (b *MockPostProcessor) Configure(raws ...interface{}) error { + return config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} + +func (b *MockPostProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, bool, error) { + return nil, false, false, nil +} + +////// +// MockCommunicator +////// + +type MockCommunicator struct { + Config MockConfig + packer.Communicator +} + +var _ packer.ConfigurableCommunicator = new(MockCommunicator) + +func (b *MockCommunicator) ConfigSpec() hcldec.ObjectSpec { + return b.Config.FlatMapstructure().HCL2Spec() +} + +func (b *MockCommunicator) Configure(raws ...interface{}) ([]string, error) { + return nil, config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} diff --git a/hcl2template/mock.hcl2spec.go b/hcl2template/mock.hcl2spec.go new file mode 100644 index 000000000..99def008f --- /dev/null +++ b/hcl2template/mock.hcl2spec.go @@ -0,0 +1,85 @@ +// Code generated by "mapstructure-to-hcl2 -type MockConfig,NestedMockConfig"; DO NOT EDIT. +package hcl2template + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatMockConfig is an auto-generated flat version of MockConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockConfig struct { + String *string `mapstructure:"string" cty:"string"` + Int *int `mapstructure:"int" cty:"int"` + Int64 *int64 `mapstructure:"int64" cty:"int64"` + Bool *bool `mapstructure:"bool" cty:"bool"` + Trilean *bool `mapstructure:"trilean" cty:"trilean"` + Duration *string `mapstructure:"duration" cty:"duration"` + MapStringString map[string]string `mapstructure:"map_string_string" cty:"map_string_string"` + SliceString []string `mapstructure:"slice_string" cty:"slice_string"` + Nested *FlatNestedMockConfig `mapstructure:"nested" cty:"nested"` + NestedSlice []FlatNestedMockConfig `mapstructure:"nested_slice" cty:"nested_slice"` +} + +// FlatMapstructure returns a new FlatMockConfig. +// FlatMockConfig is an auto-generated flat version of MockConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockConfig) +} + +// HCL2Spec returns the hcl spec of a MockConfig. +// This spec is used by HCL to read the fields of MockConfig. +// The decoded values from this spec will then be applied to a FlatMockConfig. +func (*FlatMockConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "string": &hcldec.AttrSpec{Name: "string", Type: cty.String, Required: false}, + "int": &hcldec.AttrSpec{Name: "int", Type: cty.Number, Required: false}, + "int64": &hcldec.AttrSpec{Name: "int64", Type: cty.Number, Required: false}, + "bool": &hcldec.AttrSpec{Name: "bool", Type: cty.Bool, Required: false}, + "trilean": &hcldec.AttrSpec{Name: "trilean", Type: cty.Bool, Required: false}, + "duration": &hcldec.AttrSpec{Name: "duration", Type: cty.String, Required: false}, + "map_string_string": &hcldec.BlockAttrsSpec{TypeName: "map_string_string", ElementType: cty.String, Required: false}, + "slice_string": &hcldec.AttrSpec{Name: "slice_string", Type: cty.List(cty.String), Required: false}, + "nested": &hcldec.BlockSpec{TypeName: "nested", Nested: hcldec.ObjectSpec((*FlatNestedMockConfig)(nil).HCL2Spec())}, + "nested_slice": &hcldec.BlockListSpec{TypeName: "nested_slice", Nested: hcldec.ObjectSpec((*FlatNestedMockConfig)(nil).HCL2Spec())}, + } + return s +} + +// FlatNestedMockConfig is an auto-generated flat version of NestedMockConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatNestedMockConfig struct { + String *string `mapstructure:"string" cty:"string"` + Int *int `mapstructure:"int" cty:"int"` + Int64 *int64 `mapstructure:"int64" cty:"int64"` + Bool *bool `mapstructure:"bool" cty:"bool"` + Trilean *bool `mapstructure:"trilean" cty:"trilean"` + Duration *string `mapstructure:"duration" cty:"duration"` + MapStringString map[string]string `mapstructure:"map_string_string" cty:"map_string_string"` + SliceString []string `mapstructure:"slice_string" cty:"slice_string"` +} + +// FlatMapstructure returns a new FlatNestedMockConfig. +// FlatNestedMockConfig is an auto-generated flat version of NestedMockConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*NestedMockConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatNestedMockConfig) +} + +// HCL2Spec returns the hcl spec of a NestedMockConfig. +// This spec is used by HCL to read the fields of NestedMockConfig. +// The decoded values from this spec will then be applied to a FlatNestedMockConfig. +func (*FlatNestedMockConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "string": &hcldec.AttrSpec{Name: "string", Type: cty.String, Required: false}, + "int": &hcldec.AttrSpec{Name: "int", Type: cty.Number, Required: false}, + "int64": &hcldec.AttrSpec{Name: "int64", Type: cty.Number, Required: false}, + "bool": &hcldec.AttrSpec{Name: "bool", Type: cty.Bool, Required: false}, + "trilean": &hcldec.AttrSpec{Name: "trilean", Type: cty.Bool, Required: false}, + "duration": &hcldec.AttrSpec{Name: "duration", Type: cty.String, Required: false}, + "map_string_string": &hcldec.BlockAttrsSpec{TypeName: "map_string_string", ElementType: cty.String, Required: false}, + "slice_string": &hcldec.AttrSpec{Name: "slice_string", Type: cty.List(cty.String), Required: false}, + } + return s +} diff --git a/hcl2template/parser.go b/hcl2template/parser.go index 97bd1e8d6..770c7209a 100644 --- a/hcl2template/parser.go +++ b/hcl2template/parser.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/packer/packer" ) const ( @@ -29,26 +30,24 @@ var configSchema = &hcl.BodySchema{ type Parser struct { *hclparse.Parser - ProvisionersSchemas map[string]Decodable + BuilderSchemas packer.BuilderStore - PostProvisionersSchemas map[string]Decodable + ProvisionersSchemas packer.ProvisionerStore - CommunicatorSchemas map[string]Decodable - - SourceSchemas map[string]Decodable + PostProcessorsSchemas packer.PostProcessorStore } const hcl2FileExt = ".pkr.hcl" -func (p *Parser) Parse(filename string) (*PackerConfig, hcl.Diagnostics) { +func (p *Parser) parse(filename string) (*PackerConfig, hcl.Diagnostics) { var diags hcl.Diagnostics hclFiles := []string{} jsonFiles := []string{} if strings.HasSuffix(filename, hcl2FileExt) { - hclFiles = append(hclFiles, hcl2FileExt) + hclFiles = append(hclFiles, filename) } else if strings.HasSuffix(filename, ".json") { - jsonFiles = append(jsonFiles, hcl2FileExt) + jsonFiles = append(jsonFiles, filename) } else { fileInfos, err := ioutil.ReadDir(filename) if err != nil { @@ -89,24 +88,21 @@ func (p *Parser) Parse(filename string) (*PackerConfig, hcl.Diagnostics) { cfg := &PackerConfig{} for _, file := range files { - moreDiags := p.ParseFile(file, cfg) + moreDiags := p.parseFile(file, cfg) diags = append(diags, moreDiags...) } - if diags.HasErrors() { - return cfg, diags - } - return cfg, nil + return cfg, diags } -// ParseFile filename content into cfg. +// parseFile filename content into cfg. // -// ParseFile may be called multiple times with the same cfg on a different file. +// parseFile may be called multiple times with the same cfg on a different file. // -// ParseFile returns as complete a config as we can manage, even if there are +// parseFile returns as complete a config as we can manage, even if there are // errors, since a partial result can be useful for careful analysis by // development tools such as text editor extensions. -func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { +func (p *Parser) parseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { var diags hcl.Diagnostics content, moreDiags := f.Body.Content(configSchema) @@ -115,12 +111,11 @@ func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { for _, block := range content.Blocks { switch block.Type { case sourceLabel: - if cfg.Sources == nil { - cfg.Sources = map[SourceRef]*Source{} - } - - source, moreDiags := p.decodeSource(block, p.SourceSchemas) + source, moreDiags := p.decodeSource(block) diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } ref := source.Ref() if existing := cfg.Sources[ref]; existing != nil { @@ -130,11 +125,15 @@ func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { Detail: fmt.Sprintf("This "+sourceLabel+" block has the "+ "same builder type and name as a previous block declared "+ "at %s. Each "+sourceLabel+" must have a unique name per builder type.", - existing.HCL2Ref.DeclRange), - Subject: &source.HCL2Ref.DeclRange, + existing.block.DefRange.Ptr()), + Subject: source.block.DefRange.Ptr(), }) continue } + + if cfg.Sources == nil { + cfg.Sources = map[SourceRef]*Source{} + } cfg.Sources[ref] = source case variablesLabel: @@ -143,35 +142,18 @@ func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { } moreDiags := cfg.Variables.decodeConfig(block) + if moreDiags.HasErrors() { + continue + } diags = append(diags, moreDiags...) case buildLabel: build, moreDiags := p.decodeBuildConfig(block) diags = append(diags, moreDiags...) - cfg.Builds = append(cfg.Builds, build) - - case communicatorLabel: - if cfg.Communicators == nil { - cfg.Communicators = map[CommunicatorRef]*Communicator{} - } - communicator, moreDiags := p.decodeCommunicatorConfig(block) - diags = append(diags, moreDiags...) - - ref := communicator.Ref() - - if existing := cfg.Communicators[ref]; existing != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate " + communicatorLabel + " block", - Detail: fmt.Sprintf("This "+communicatorLabel+" block has the "+ - "same type and name as a previous block declared "+ - "at %s. Each "+communicatorLabel+" must have a unique name per type.", - existing.HCL2Ref.DeclRange), - Subject: &communicator.HCL2Ref.DeclRange, - }) + if moreDiags.HasErrors() { continue } - cfg.Communicators[ref] = communicator + cfg.Builds = append(cfg.Builds, build) default: panic(fmt.Sprintf("unexpected block type %q", block.Type)) // TODO(azr): err diff --git a/hcl2template/parser_test.go b/hcl2template/parser_test.go deleted file mode 100644 index 1056153d6..000000000 --- a/hcl2template/parser_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package hcl2template - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - awscommon "github.com/hashicorp/packer/builder/amazon/common" - amazonebs "github.com/hashicorp/packer/builder/amazon/ebs" - "github.com/hashicorp/packer/builder/virtualbox/iso" - - "github.com/hashicorp/packer/helper/communicator" - - amazon_import "github.com/hashicorp/packer/post-processor/amazon-import" - - "github.com/hashicorp/packer/provisioner/file" - "github.com/hashicorp/packer/provisioner/shell" -) - -func TestParser_Parse(t *testing.T) { - defaultParser := getBasicParser() - - type args struct { - filename string - } - tests := []struct { - name string - parser *Parser - args args - wantCfg *PackerConfig - wantDiags bool - }{ - {"complete", - defaultParser, - args{"testdata/complete"}, - &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - ISOChecksum: strPtr("769474248a3897f4865817446f9a4a53"), - ISOChecksumType: strPtr("md5"), - RawSingleISOUrl: strPtr("http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso"), - BootCommand: []string{"..."}, - ShutdownCommand: strPtr("echo 'vagrant' | sudo -S shutdown -P now"), - BootWait: strPtr("10s"), - VBoxManage: [][]string{}, - VBoxManagePost: [][]string{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "ubuntu-1604", - }: { - Type: "amazon-ebs", - Name: "ubuntu-1604", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - Filters: map[string]string{ - "name": "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*", - "root-device-type": "ebs", - "virtualization-type": "hvm", - }, - Owners: []string{"099720109477"}, - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - }: { - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - MostRecent: boolPtr(true), - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - }, - Communicators: map[CommunicatorRef]*Communicator{ - {Type: "ssh", Name: "vagrant"}: { - Type: "ssh", Name: "vagrant", - Cfg: &communicator.FlatSSH{ - SSHUsername: strPtr("vagrant"), - SSHPassword: strPtr("s3cr4t"), - SSHClearAuthorizedKeys: boolPtr(true), - SSHHost: strPtr("sssssh.hashicorp.io"), - SSHHandshakeAttempts: intPtr(32), - SSHPort: intPtr(42), - SSHFileTransferMethod: strPtr("scp"), - SSHPrivateKeyFile: strPtr("file.pem"), - SSHPty: boolPtr(false), - SSHTimeout: strPtr("5m"), - SSHAgentAuth: boolPtr(false), - SSHDisableAgentForwarding: boolPtr(true), - SSHBastionHost: strPtr(""), - SSHBastionPort: intPtr(0), - SSHBastionAgentAuth: boolPtr(true), - SSHBastionUsername: strPtr(""), - SSHBastionPassword: strPtr(""), - SSHBastionPrivateKeyFile: strPtr(""), - SSHProxyHost: strPtr("ninja-potatoes.com"), - SSHProxyPort: intPtr(42), - SSHProxyUsername: strPtr("dark-father"), - SSHProxyPassword: strPtr("pickle-rick"), - SSHKeepAliveInterval: strPtr("10s"), - SSHReadWriteTimeout: strPtr("5m"), - }, - }, - }, - Variables: PackerV1Variables{ - "image_name": "foo-image-{{user `my_secret`}}", - "key": "value", - "my_secret": "foo", - }, - Builds: Builds{ - { - Froms: BuildFromList{ - { - Src: SourceRef{"amazon-ebs", "ubuntu-1604"}, - }, - { - Src: SourceRef{"virtualbox-iso", "ubuntu-1204"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - CommunicatorRef: CommunicatorRef{"ssh", "vagrant"}, - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo '{{user `my_secret`}}' :D"}, - }}, - {Cfg: &shell.FlatConfig{ - Scripts: []string{"script-1.sh", "script-2.sh"}, - ValidExitCodes: []int{0, 42}, - }}, - {Cfg: &file.FlatConfig{ - Source: strPtr("app.tar.gz"), - Destination: strPtr("/tmp/app.tar.gz"), - }}, - }, - }, - }, - PostProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &amazon_import.FlatConfig{ - Name: strPtr("that-ubuntu-1.0"), - }}, - }, - }, - }, - }, - &Build{ - Froms: BuildFromList{ - { - Src: SourceRef{"amazon", "that-ubuntu-1"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo HOLY GUACAMOLE !"}, - }}, - }, - }, - }, - }, - }, - }, false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotCfg, gotDiags := tt.parser.Parse(tt.args.filename) - if tt.wantDiags == (gotDiags == nil) { - t.Errorf("Parser.Parse() unexpected diagnostics. %s", gotDiags) - } - if diff := cmp.Diff(tt.wantCfg, gotCfg, - cmpopts.IgnoreUnexported(cty.Value{}), - cmpopts.IgnoreTypes(HCL2Ref{}), - cmpopts.IgnoreTypes([]hcl.Range{}), - cmpopts.IgnoreTypes(hcl.Range{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}), - ); diff != "" { - t.Errorf("Parser.Parse() wrong packer config. %s", diff) - } - - }) - } -} diff --git a/hcl2template/testdata/build/basic.pkr.hcl b/hcl2template/testdata/build/basic.pkr.hcl index c48847c94..34d84edb9 100644 --- a/hcl2template/testdata/build/basic.pkr.hcl +++ b/hcl2template/testdata/build/basic.pkr.hcl @@ -1,66 +1,125 @@ // starts resources to provision them. build { - from "src.amazon-ebs.ubuntu-1604" { - ami_name = "that-ubuntu-1.0" - } + sources = [ + "source.amazon-ebs.ubuntu-1604", + "source.virtualbox-iso.ubuntu-1204", + ] - from "src.virtualbox-iso.ubuntu-1204" { - // build name is defaulted from the label "src.virtualbox-iso.ubuntu-1204" - outout_dir = "path/" - } + provisioner "shell" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - provision { - communicator = "comm.ssh.vagrant" - - shell { - inline = [ - "echo '{{user `my_secret`}}' :D" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", ] } - shell { - valid_exit_codes = [ - 0, - 42, - ] - scripts = [ - "script-1.sh", - "script-2.sh", - ] - // override "vmware-iso" { // TODO(azr): handle common fields - // execute_command = "echo 'password' | sudo -S bash {{.Path}}" - // } + nested_slice { } - - file { - source = "app.tar.gz" - destination = "/tmp/app.tar.gz" - // timeout = "5s" // TODO(azr): handle common fields - } - } - post_provision { - amazon-import { - // only = ["src.virtualbox-iso.ubuntu-1204"] // TODO(azr): handle common fields - ami_name = "that-ubuntu-1.0" + provisioner "file" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { + } + } + + post-processor "amazon-import" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { } } } - -build { - // build an ami using the ami from the previous build block. - from "src.amazon.that-ubuntu-1.0" { - ami_name = "fooooobaaaar" - } - - provision { - - shell { - inline = [ - "echo HOLY GUACAMOLE !" - ] - } - } -} \ No newline at end of file diff --git a/hcl2template/testdata/build/post-processor_inexistent.pkr.hcl b/hcl2template/testdata/build/post-processor_inexistent.pkr.hcl new file mode 100644 index 000000000..179ff3475 --- /dev/null +++ b/hcl2template/testdata/build/post-processor_inexistent.pkr.hcl @@ -0,0 +1,6 @@ + +build { + post-processor "inexistant" { + foo = "bar" + } +} diff --git a/hcl2template/testdata/build/post-processor_untyped.pkr.hcl b/hcl2template/testdata/build/post-processor_untyped.pkr.hcl new file mode 100644 index 000000000..4b723360e --- /dev/null +++ b/hcl2template/testdata/build/post-processor_untyped.pkr.hcl @@ -0,0 +1,6 @@ + +build { + post-process { + foo = "bar" + } +} diff --git a/hcl2template/testdata/build/provisioner_inexistent.pkr.hcl b/hcl2template/testdata/build/provisioner_inexistent.pkr.hcl new file mode 100644 index 000000000..59b4dcbc5 --- /dev/null +++ b/hcl2template/testdata/build/provisioner_inexistent.pkr.hcl @@ -0,0 +1,6 @@ + +build { + provisioner "inexistant" { + foo = "bar" + } +} diff --git a/hcl2template/testdata/build/provisioner_untyped.pkr.hcl b/hcl2template/testdata/build/provisioner_untyped.pkr.hcl new file mode 100644 index 000000000..33a9fdb99 --- /dev/null +++ b/hcl2template/testdata/build/provisioner_untyped.pkr.hcl @@ -0,0 +1,6 @@ + +build { + provision { + foo = "bar" + } +} diff --git a/hcl2template/testdata/communicator/basic.pkr.hcl b/hcl2template/testdata/communicator/basic.pkr.hcl index b752333e9..4f5d8104e 100644 --- a/hcl2template/testdata/communicator/basic.pkr.hcl +++ b/hcl2template/testdata/communicator/basic.pkr.hcl @@ -1,27 +1,39 @@ communicator "ssh" "vagrant" { - ssh_password = "s3cr4t" - ssh_username = "vagrant" - ssh_agent_auth = false - ssh_bastion_agent_auth = true - ssh_bastion_host = "" - ssh_bastion_password = "" - ssh_bastion_port = 0 - ssh_bastion_private_key_file = "" - ssh_bastion_username = "" - ssh_clear_authorized_keys = true - ssh_disable_agent_forwarding = true - ssh_file_transfer_method = "scp" - ssh_handshake_attempts = 32 - ssh_host = "sssssh.hashicorp.io" - ssh_port = 42 - ssh_keep_alive_interval = "10s" - ssh_private_key_file = "file.pem" - ssh_proxy_host = "ninja-potatoes.com" - ssh_proxy_password = "pickle-rick" - ssh_proxy_port = "42" - ssh_proxy_username = "dark-father" - ssh_pty = false - ssh_read_write_timeout = "5m" - ssh_timeout = "5m" + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { + } } diff --git a/hcl2template/testdata/complete/build.pkr.hcl b/hcl2template/testdata/complete/build.pkr.hcl index c48847c94..de67e1c39 100644 --- a/hcl2template/testdata/complete/build.pkr.hcl +++ b/hcl2template/testdata/complete/build.pkr.hcl @@ -1,66 +1,124 @@ // starts resources to provision them. build { - from "src.amazon-ebs.ubuntu-1604" { - ami_name = "that-ubuntu-1.0" - } + sources = [ + "source.virtualbox-iso.ubuntu-1204", + ] - from "src.virtualbox-iso.ubuntu-1204" { - // build name is defaulted from the label "src.virtualbox-iso.ubuntu-1204" - outout_dir = "path/" - } + provisioner "shell" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - provision { - communicator = "comm.ssh.vagrant" - - shell { - inline = [ - "echo '{{user `my_secret`}}' :D" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", ] } - shell { - valid_exit_codes = [ - 0, - 42, - ] - scripts = [ - "script-1.sh", - "script-2.sh", - ] - // override "vmware-iso" { // TODO(azr): handle common fields - // execute_command = "echo 'password' | sudo -S bash {{.Path}}" - // } + nested_slice { } - - file { - source = "app.tar.gz" - destination = "/tmp/app.tar.gz" - // timeout = "5s" // TODO(azr): handle common fields - } - } - post_provision { - amazon-import { - // only = ["src.virtualbox-iso.ubuntu-1204"] // TODO(azr): handle common fields - ami_name = "that-ubuntu-1.0" + provisioner "file" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { + } + } + + post-processor "amazon-import" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { } } } - -build { - // build an ami using the ami from the previous build block. - from "src.amazon.that-ubuntu-1.0" { - ami_name = "fooooobaaaar" - } - - provision { - - shell { - inline = [ - "echo HOLY GUACAMOLE !" - ] - } - } -} \ No newline at end of file diff --git a/hcl2template/testdata/complete/communicator.pkr.hcl b/hcl2template/testdata/complete/communicator.pkr.hcl deleted file mode 100644 index b752333e9..000000000 --- a/hcl2template/testdata/complete/communicator.pkr.hcl +++ /dev/null @@ -1,27 +0,0 @@ - -communicator "ssh" "vagrant" { - ssh_password = "s3cr4t" - ssh_username = "vagrant" - ssh_agent_auth = false - ssh_bastion_agent_auth = true - ssh_bastion_host = "" - ssh_bastion_password = "" - ssh_bastion_port = 0 - ssh_bastion_private_key_file = "" - ssh_bastion_username = "" - ssh_clear_authorized_keys = true - ssh_disable_agent_forwarding = true - ssh_file_transfer_method = "scp" - ssh_handshake_attempts = 32 - ssh_host = "sssssh.hashicorp.io" - ssh_port = 42 - ssh_keep_alive_interval = "10s" - ssh_private_key_file = "file.pem" - ssh_proxy_host = "ninja-potatoes.com" - ssh_proxy_password = "pickle-rick" - ssh_proxy_port = "42" - ssh_proxy_username = "dark-father" - ssh_pty = false - ssh_read_write_timeout = "5m" - ssh_timeout = "5m" -} diff --git a/hcl2template/testdata/complete/sources.pkr.hcl b/hcl2template/testdata/complete/sources.pkr.hcl index 43e3af082..b95308b5a 100644 --- a/hcl2template/testdata/complete/sources.pkr.hcl +++ b/hcl2template/testdata/complete/sources.pkr.hcl @@ -1,37 +1,71 @@ -// a source represents a reusable setting for a system boot/start. source "virtualbox-iso" "ubuntu-1204" { - iso_url = "http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso" - iso_checksum = "769474248a3897f4865817446f9a4a53" - iso_checksum_type = "md5" + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - boot_wait = "10s" - http_directory = "xxx" - boot_command = ["..."] - - shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" -} - -source "amazon-ebs" "ubuntu-1604" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - filters { - virtualization-type = "hvm" - name = "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*" - root-device-type = "ebs" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" } - owners = [ - "099720109477" + slice_string = [ + "a", + "b", + "c", ] } -} -source "amazon-ebs" "that-ubuntu-1.0" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - most_recent = true + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] } -} + + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } +} \ No newline at end of file diff --git a/hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl b/hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl deleted file mode 100644 index 6f2d3076c..000000000 --- a/hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl +++ /dev/null @@ -1,61 +0,0 @@ - -// starts resources to provision them. -build { - from "src.amazon-ebs.ubuntu-1604" { - ami_name = "that-ubuntu-1.0" - } - - from "src.virtualbox-iso.ubuntu-1204" { - // build name is defaulted from the label "src.virtualbox-iso.ubuntu-1204" - outout_dir = "path/" - } - - provision { - communicator = comm.ssh.vagrant - - shell { - inline = [ - "echo '{{user `my_secret`}}' :D" - ] - } - - shell { - script = [ - "script-1.sh", - "script-2.sh", - ] - override "vmware-iso" { - execute_command = "echo 'password' | sudo -S bash {{.Path}}" - } - } - - upload "log.go" "/tmp" { - timeout = "5s" - } - - } - - post_provision { - amazon-import { - only = ["src.virtualbox-iso.ubuntu-1204"] - ami_name = "that-ubuntu-1.0" - } - } -} - -build { - // build an ami using the ami from the previous build block. - from "src.amazon.that-ubuntu-1.0" { - ami_name = "fooooobaaaar" - } - - provision { - communicator = comm.ssh.vagrant - - shell { - inline = [ - "echo HOLY GUACAMOLE !" - ] - } - } -} \ No newline at end of file diff --git a/hcl2template/testdata/complete/variables.pkr.hcl b/hcl2template/testdata/complete/variables.pkr.hcl deleted file mode 100644 index d4e651247..000000000 --- a/hcl2template/testdata/complete/variables.pkr.hcl +++ /dev/null @@ -1,6 +0,0 @@ - -variables { - key = "value" - my_secret = "foo" - image_name = "foo-image-{{user `my_secret`}}" -} diff --git a/hcl2template/testdata/sources/basic.pkr.hcl b/hcl2template/testdata/sources/basic.pkr.hcl index 43e3af082..58d3164e5 100644 --- a/hcl2template/testdata/sources/basic.pkr.hcl +++ b/hcl2template/testdata/sources/basic.pkr.hcl @@ -1,37 +1,72 @@ // a source represents a reusable setting for a system boot/start. source "virtualbox-iso" "ubuntu-1204" { - iso_url = "http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso" - iso_checksum = "769474248a3897f4865817446f9a4a53" - iso_checksum_type = "md5" + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - boot_wait = "10s" - http_directory = "xxx" - boot_command = ["..."] - - shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" -} - -source "amazon-ebs" "ubuntu-1604" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - filters { - virtualization-type = "hvm" - name = "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*" - root-device-type = "ebs" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" } - owners = [ - "099720109477" + slice_string = [ + "a", + "b", + "c", ] } -} -source "amazon-ebs" "that-ubuntu-1.0" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - most_recent = true + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] } -} + + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } +} \ No newline at end of file diff --git a/hcl2template/testdata/sources/duplicate.pkr.hcl b/hcl2template/testdata/sources/duplicate.pkr.hcl new file mode 100644 index 000000000..823d17e03 --- /dev/null +++ b/hcl2template/testdata/sources/duplicate.pkr.hcl @@ -0,0 +1,3 @@ + +source "virtualbox-iso" "ubuntu-1204" {} +source "virtualbox-iso" "ubuntu-1204" {} diff --git a/hcl2template/testdata/sources/inexistent.pkr.hcl b/hcl2template/testdata/sources/inexistent.pkr.hcl new file mode 100644 index 000000000..e9a112f17 --- /dev/null +++ b/hcl2template/testdata/sources/inexistent.pkr.hcl @@ -0,0 +1,4 @@ +// a source represents a reusable setting for a system boot/start. +source "inexistant" "ubuntu-1204" { + foo = "bar" +} \ No newline at end of file diff --git a/hcl2template/testdata/sources/unnamed.pkr.hcl b/hcl2template/testdata/sources/unnamed.pkr.hcl new file mode 100644 index 000000000..30adb0333 --- /dev/null +++ b/hcl2template/testdata/sources/unnamed.pkr.hcl @@ -0,0 +1,4 @@ +// a source represents a reusable setting for a system boot/start. +source "virtualbox-iso" { + foo = "bar" +} \ No newline at end of file diff --git a/hcl2template/testdata/sources/untyped.pkr.hcl b/hcl2template/testdata/sources/untyped.pkr.hcl new file mode 100644 index 000000000..28d4fad35 --- /dev/null +++ b/hcl2template/testdata/sources/untyped.pkr.hcl @@ -0,0 +1,4 @@ +// a source represents a reusable setting for a system boot/start. +source { + foo = "bar" +} \ No newline at end of file diff --git a/hcl2template/types.build.from.go b/hcl2template/types.build.from.go index 66d3b7645..b84b6ffc6 100644 --- a/hcl2template/types.build.from.go +++ b/hcl2template/types.build.from.go @@ -2,28 +2,15 @@ package hcl2template import ( "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2/hclsyntax" ) -type BuildFromList []BuildFrom - -type BuildFrom struct { - // source to take config from - Src SourceRef `hcl:"-"` - - HCL2Ref HCL2Ref -} - func sourceRefFromString(in string) SourceRef { args := strings.Split(in, ".") if len(args) < 2 { return NoSource } if len(args) > 2 { - // src.type.name + // source.type.name args = args[1:] } return SourceRef{ @@ -31,38 +18,3 @@ func sourceRefFromString(in string) SourceRef { Name: args[1], } } - -func (bf *BuildFrom) decodeConfig(block *hcl.Block) hcl.Diagnostics { - - bf.Src = sourceRefFromString(block.Labels[0]) - bf.HCL2Ref.DeclRange = block.DefRange - - var b struct { - Config hcl.Body `hcl:",remain"` - } - diags := gohcl.DecodeBody(block.Body, nil, &b) - - if bf.Src == NoSource { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid " + sourceLabel + " reference", - Detail: "A " + sourceLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes." + - "A valid source reference looks like: `src.type.name`", - Subject: &block.LabelRanges[0], - }) - } - if !hclsyntax.ValidIdentifier(bf.Src.Type) || - !hclsyntax.ValidIdentifier(bf.Src.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid " + sourceLabel + " reference", - Detail: "A " + sourceLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes." + - "A valid source reference looks like: `src.type.name`", - Subject: &block.LabelRanges[0], - }) - } - - return diags -} diff --git a/hcl2template/types.build.go b/hcl2template/types.build.go index d758bfc5a..c6a2f68e8 100644 --- a/hcl2template/types.build.go +++ b/hcl2template/types.build.go @@ -2,58 +2,96 @@ package hcl2template import ( "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/hcl/v2/hclsyntax" ) const ( buildFromLabel = "from" - buildProvisionnersLabel = "provision" + buildSourceLabel = "source" - buildPostProvisionnersLabel = "post_provision" + buildProvisionerLabel = "provisioner" + + buildPostProcessorLabel = "post-processor" ) var buildSchema = &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ - {Type: buildFromLabel, LabelNames: []string{"src"}}, - {Type: buildProvisionnersLabel}, - {Type: buildPostProvisionnersLabel}, + {Type: buildFromLabel, LabelNames: []string{"type"}}, + {Type: buildProvisionerLabel, LabelNames: []string{"type"}}, + {Type: buildPostProcessorLabel, LabelNames: []string{"type"}}, }, } -type Build struct { - // Ordered list of provisioner groups - ProvisionerGroups ProvisionerGroups +type BuildBlock struct { + ProvisionerBlocks []*ProvisionerBlock - // Ordered list of post-provisioner groups - PostProvisionerGroups ProvisionerGroups + PostProcessors []*PostProcessorBlock - // Ordered list of output stanzas - Froms BuildFromList + Froms []SourceRef HCL2Ref HCL2Ref } -type Builds []*Build +type Builds []*BuildBlock -func (p *Parser) decodeBuildConfig(block *hcl.Block) (*Build, hcl.Diagnostics) { - build := &Build{} +func (p *Parser) decodeBuildConfig(block *hcl.Block) (*BuildBlock, hcl.Diagnostics) { + build := &BuildBlock{} - content, diags := block.Body.Content(buildSchema) + var b struct { + FromSources []string `hcl:"sources"` + Config hcl.Body `hcl:",remain"` + } + diags := gohcl.DecodeBody(block.Body, nil, &b) + + for _, buildFrom := range b.FromSources { + ref := sourceRefFromString(buildFrom) + + if ref == NoSource { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid " + sourceLabel + " reference", + Detail: "A " + sourceLabel + " type must start with a letter and " + + "may contain only letters, digits, underscores, and dashes." + + "A valid source reference looks like: `source.type.name`", + Subject: &block.LabelRanges[0], + }) + continue + } + if !hclsyntax.ValidIdentifier(ref.Type) || + !hclsyntax.ValidIdentifier(ref.Name) { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid " + sourceLabel + " reference", + Detail: "A " + sourceLabel + " type must start with a letter and " + + "may contain only letters, digits, underscores, and dashes." + + "A valid source reference looks like: `source.type.name`", + Subject: &block.LabelRanges[0], + }) + continue + } + + build.Froms = append(build.Froms, ref) + } + + content, diags := b.Config.Content(buildSchema) for _, block := range content.Blocks { switch block.Type { - case buildFromLabel: - bf := BuildFrom{} - moreDiags := bf.decodeConfig(block) + case buildProvisionerLabel: + p, moreDiags := p.decodeProvisioner(block) diags = append(diags, moreDiags...) - build.Froms = append(build.Froms, bf) - case buildProvisionnersLabel: - pg, moreDiags := p.decodeProvisionerGroup(block, p.ProvisionersSchemas) + if moreDiags.HasErrors() { + continue + } + build.ProvisionerBlocks = append(build.ProvisionerBlocks, p) + case buildPostProcessorLabel: + pp, moreDiags := p.decodePostProcessorGroup(block) diags = append(diags, moreDiags...) - build.ProvisionerGroups = append(build.ProvisionerGroups, pg) - case buildPostProvisionnersLabel: - pg, moreDiags := p.decodeProvisionerGroup(block, p.PostProvisionersSchemas) - diags = append(diags, moreDiags...) - build.PostProvisionerGroups = append(build.PostProvisionerGroups, pg) + if moreDiags.HasErrors() { + continue + } + build.PostProcessors = append(build.PostProcessors, pp) } } diff --git a/hcl2template/types.build.post-processor.go b/hcl2template/types.build.post-processor.go new file mode 100644 index 000000000..7575b17c7 --- /dev/null +++ b/hcl2template/types.build.post-processor.go @@ -0,0 +1,61 @@ +package hcl2template + +import ( + "fmt" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/packer/packer" +) + +// PostProcessorBlock represents a parsed PostProcessorBlock +type PostProcessorBlock struct { + PType string + block *hcl.Block +} + +func (p *Parser) decodePostProcessorGroup(block *hcl.Block) (*PostProcessorBlock, hcl.Diagnostics) { + postProcessor := &PostProcessorBlock{ + PType: block.Labels[0], + block: block, + } + var diags hcl.Diagnostics + + if !p.PostProcessorsSchemas.Has(postProcessor.PType) { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Unknown " + buildPostProcessorLabel + " type " + postProcessor.PType, + Subject: block.LabelRanges[0].Ptr(), + Detail: fmt.Sprintf("known provisioners: %v", p.ProvisionersSchemas.List()), + Severity: hcl.DiagError, + }) + return nil, diags + } + + return postProcessor, diags +} + +func (p *Parser) StartPostProcessor(pp *PostProcessorBlock) (packer.PostProcessor, hcl.Diagnostics) { + var diags hcl.Diagnostics + + postProcessor, err := p.PostProcessorsSchemas.Start(pp.PType) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Failed loading " + pp.block.Type, + Subject: pp.block.LabelRanges[0].Ptr(), + Detail: err.Error(), + }) + return nil, diags + } + flatProvisinerCfg, moreDiags := decodeHCL2Spec(pp.block, nil, postProcessor) + diags = append(diags, moreDiags...) + err = postProcessor.Configure(flatProvisinerCfg) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Failed preparing " + pp.block.Type, + Detail: err.Error(), + Subject: pp.block.DefRange.Ptr(), + }) + return nil, diags + } + return postProcessor, diags +} diff --git a/hcl2template/types.build.provisioners.go b/hcl2template/types.build.provisioners.go index 77ec57c99..33d0fb324 100644 --- a/hcl2template/types.build.provisioners.go +++ b/hcl2template/types.build.provisioners.go @@ -1,70 +1,62 @@ package hcl2template import ( + "fmt" "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/packer/packer" ) -// Provisioner represents a parsed provisioner -type Provisioner struct { - // Cfg is a parsed config - Cfg interface{} +// ProvisionerBlock represents a parsed provisioner +type ProvisionerBlock struct { + PType string + block *hcl.Block } -type ProvisionerGroup struct { - CommunicatorRef CommunicatorRef - - Provisioners []Provisioner - HCL2Ref HCL2Ref -} - -// ProvisionerGroups is a slice of provision blocks; which contains -// provisioners -type ProvisionerGroups []*ProvisionerGroup - -func (p *Parser) decodeProvisionerGroup(block *hcl.Block, provisionerSpecs map[string]Decodable) (*ProvisionerGroup, hcl.Diagnostics) { - var b struct { - Communicator string `hcl:"communicator,optional"` - Remain hcl.Body `hcl:",remain"` +func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Diagnostics) { + provisioner := &ProvisionerBlock{ + PType: block.Labels[0], + block: block, } + var diags hcl.Diagnostics - diags := gohcl.DecodeBody(block.Body, nil, &b) - - pg := &ProvisionerGroup{} - pg.CommunicatorRef = communicatorRefFromString(b.Communicator) - pg.HCL2Ref.DeclRange = block.DefRange - - buildSchema := &hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{}, - } - for k := range provisionerSpecs { - buildSchema.Blocks = append(buildSchema.Blocks, hcl.BlockHeaderSchema{ - Type: k, + if !p.ProvisionersSchemas.Has(provisioner.PType) { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Unknown " + buildProvisionerLabel + " type " + provisioner.PType, + Subject: block.LabelRanges[0].Ptr(), + Detail: fmt.Sprintf("known provisioners: %v", p.ProvisionersSchemas.List()), + Severity: hcl.DiagError, }) + return nil, diags } + return provisioner, diags +} - content, moreDiags := b.Remain.Content(buildSchema) +func (p *Parser) StartProvisioner(pb *ProvisionerBlock) (packer.Provisioner, hcl.Diagnostics) { + var diags hcl.Diagnostics + + provisioner, err := p.ProvisionersSchemas.Start(pb.PType) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Failed loading " + pb.block.Type, + Subject: pb.block.LabelRanges[0].Ptr(), + Detail: err.Error(), + }) + return nil, diags + } + flatProvisinerCfg, moreDiags := decodeHCL2Spec(pb.block, nil, provisioner) diags = append(diags, moreDiags...) - for _, block := range content.Blocks { - provisioner, found := provisionerSpecs[block.Type] - if !found { - diags = append(diags, &hcl.Diagnostic{ - Summary: "Unknown " + buildProvisionnersLabel + " type", - Subject: &block.LabelRanges[0], - }) - continue - } - flatProvisinerCfg, moreDiags := decodeDecodable(block, nil, provisioner) - diags = append(diags, moreDiags...) - pg.Provisioners = append(pg.Provisioners, Provisioner{flatProvisinerCfg}) + if diags.HasErrors() { + return nil, diags } - - return pg, diags -} - -func (pgs ProvisionerGroups) FirstCommunicatorRef() CommunicatorRef { - if len(pgs) == 0 { - return NoCommunicator + err = provisioner.Prepare(flatProvisinerCfg) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Failed preparing " + pb.block.Type, + Detail: err.Error(), + Subject: pb.block.DefRange.Ptr(), + }) + return nil, diags } - return pgs[0].CommunicatorRef + return provisioner, diags } diff --git a/hcl2template/types.build_test.go b/hcl2template/types.build_test.go new file mode 100644 index 000000000..f5e1cea69 --- /dev/null +++ b/hcl2template/types.build_test.go @@ -0,0 +1,88 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestParse_build(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"basic build no src", + defaultParser, + parseTestArgs{"testdata/build/basic.pkr.hcl"}, + &PackerConfig{ + Builds: Builds{ + &BuildBlock{ + Froms: []SourceRef{ + { + Type: "amazon-ebs", + Name: "ubuntu-1604", + }, + ref, + }, + ProvisionerBlocks: []*ProvisionerBlock{ + { + PType: "shell", + }, + { + PType: "file", + }, + }, + PostProcessors: []*PostProcessorBlock{ + { + PType: "amazon-import", + }, + }, + }, + }, + }, + false, false, + []packer.Build{}, + true, + }, + {"untyped provisioner", + defaultParser, + parseTestArgs{"testdata/build/provisioner_untyped.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + nil, + false, + }, + {"inexistent provisioner", + defaultParser, + parseTestArgs{"testdata/build/provisioner_inexistent.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + []packer.Build{&packer.CoreBuild{}}, + false, + }, + {"untyped post-processor", + defaultParser, + parseTestArgs{"testdata/build/post-processor_untyped.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + []packer.Build{&packer.CoreBuild{}}, + false, + }, + {"inexistent post-processor", + defaultParser, + parseTestArgs{"testdata/build/post-processor_inexistent.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + []packer.Build{}, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/types.communicator.go b/hcl2template/types.communicator.go deleted file mode 100644 index 0cc0841c6..000000000 --- a/hcl2template/types.communicator.go +++ /dev/null @@ -1,88 +0,0 @@ -package hcl2template - -import ( - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" -) - -type Communicator struct { - // Type of communicator; ex: ssh - Type string - // Given name - Name string - - Cfg interface{} - - HCL2Ref HCL2Ref -} - -func (communicator *Communicator) Ref() CommunicatorRef { - return CommunicatorRef{ - Type: communicator.Type, - Name: communicator.Name, - } -} - -func (p *Parser) decodeCommunicatorConfig(block *hcl.Block) (*Communicator, hcl.Diagnostics) { - - output := &Communicator{} - output.Type = block.Labels[0] - output.Name = block.Labels[1] - output.HCL2Ref.DeclRange = block.DefRange - - diags := hcl.Diagnostics{} - - communicator, found := p.CommunicatorSchemas[output.Type] - if !found { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unknown " + communicatorLabel + " type " + output.Type, - Detail: "A " + communicatorLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes.", - Subject: &block.DefRange, - }) - return output, diags - } - - flatCommunicator, moreDiags := decodeDecodable(block, nil, communicator) - diags = append(diags, moreDiags...) - output.Cfg = flatCommunicator - - if !hclsyntax.ValidIdentifier(output.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid " + communicatorLabel + " name", - Detail: "A " + communicatorLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes.", - Subject: &block.DefRange, - }) - } - - return output, diags -} - -type CommunicatorRef struct { - Type string - Name string -} - -// NoCommunicator is the zero value of CommunicatorRef, representing the -// absense of Communicator. -var NoCommunicator CommunicatorRef - -func communicatorRefFromString(in string) CommunicatorRef { - args := strings.Split(in, ".") - if len(args) < 2 { - return NoCommunicator - } - if len(args) > 2 { - // comm.type.name - args = args[1:] - } - return CommunicatorRef{ - Type: args[0], - Name: args[1], - } -} diff --git a/hcl2template/types.decodable.go b/hcl2template/types.decodable.go deleted file mode 100644 index 18c7dfe46..000000000 --- a/hcl2template/types.decodable.go +++ /dev/null @@ -1,56 +0,0 @@ -package hcl2template - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" -) - -type Decodable interface { - FlatMapstructure() interface{} -} - -type SelfSpecified interface { - HCL2Spec() map[string]hcldec.Spec -} - -func decodeDecodable(block *hcl.Block, ctx *hcl.EvalContext, dec Decodable) (interface{}, hcl.Diagnostics) { - var diags hcl.Diagnostics - - flatCfg := dec.FlatMapstructure() - var spec hcldec.ObjectSpec - if ss, selfSpecified := flatCfg.(SelfSpecified); selfSpecified { - spec = hcldec.ObjectSpec(ss.HCL2Spec()) - } else { - diags = append(diags, &hcl.Diagnostic{ - Summary: "Unknown type", - Subject: &block.DefRange, - Detail: fmt.Sprintf("Cannot get spec from a %T", flatCfg), - }) - return nil, diags - } - val, moreDiags := hcldec.Decode(block.Body, spec, ctx) - diags = append(diags, moreDiags...) - - err := gocty.FromCtyValue(val, flatCfg) - if err != nil { - switch err := err.(type) { - case cty.PathError: - diags = append(diags, &hcl.Diagnostic{ - Summary: "gocty.FromCtyValue: " + err.Error(), - Subject: &block.DefRange, - Detail: fmt.Sprintf("%v", err.Path), - }) - default: - diags = append(diags, &hcl.Diagnostic{ - Summary: "gocty.FromCtyValue: " + err.Error(), - Subject: &block.DefRange, - Detail: fmt.Sprintf("%v", err), - }) - } - } - return flatCfg, diags -} diff --git a/hcl2template/types.hcl_ref.go b/hcl2template/types.hcl_ref.go index 06568fb11..3c8060298 100644 --- a/hcl2template/types.hcl_ref.go +++ b/hcl2template/types.hcl_ref.go @@ -12,9 +12,3 @@ type HCL2Ref struct { // remainder of unparsed body Remain hcl.Body } - -// func (hr *HCL2Ref) Blah() { -// // hr.Remain. -// ctyjson.Marshal(nil, nil) -// hr.DeclRange. -// } diff --git a/hcl2template/types.packer_config.go b/hcl2template/types.packer_config.go index a1d3371e8..623360388 100644 --- a/hcl2template/types.packer_config.go +++ b/hcl2template/types.packer_config.go @@ -2,7 +2,7 @@ package hcl2template import ( "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/packer/template" + "github.com/hashicorp/packer/packer" ) // PackerConfig represents a loaded packer config @@ -12,154 +12,91 @@ type PackerConfig struct { Variables PackerV1Variables Builds Builds - - Communicators map[CommunicatorRef]*Communicator } -type PackerV1Build struct { - Builders []*template.Builder - Provisioners []*template.Provisioner - PostProcessors []*template.PostProcessor -} - -func (pkrCfg *PackerConfig) ToV1Build() PackerV1Build { +func (p *Parser) CoreBuildProvisioners(blocks []*ProvisionerBlock) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) { var diags hcl.Diagnostics - res := PackerV1Build{} + res := []packer.CoreBuildProvisioner{} + for _, pb := range blocks { + provisioner, moreDiags := p.StartProvisioner(pb) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + res = append(res, packer.CoreBuildProvisioner{ + PType: pb.PType, + Provisioner: provisioner, + }) + } + return res, diags +} - for _, build := range pkrCfg.Builds { - communicator, _ := pkrCfg.Communicators[build.ProvisionerGroups.FirstCommunicatorRef()] +func (p *Parser) CoreBuildPostProcessors(blocks []*PostProcessorBlock) ([]packer.CoreBuildPostProcessor, hcl.Diagnostics) { + var diags hcl.Diagnostics + res := []packer.CoreBuildPostProcessor{} + for _, pp := range blocks { + postProcessor, moreDiags := p.StartPostProcessor(pp) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + res = append(res, packer.CoreBuildPostProcessor{ + PostProcessor: postProcessor, + PType: pp.PType, + }) + } + return res, diags +} +func (p *Parser) getBuilds(cfg *PackerConfig) ([]packer.Build, hcl.Diagnostics) { + res := []packer.Build{} + var diags hcl.Diagnostics + + for _, build := range cfg.Builds { for _, from := range build.Froms { - source, found := pkrCfg.Sources[from.Src] + src, found := cfg.Sources[from] if !found { diags = append(diags, &hcl.Diagnostic{ + Summary: "Unknown " + sourceLabel + " " + from.String(), + Subject: build.HCL2Ref.DeclRange.Ptr(), Severity: hcl.DiagError, - Summary: "Unknown " + sourceLabel + " reference", - Detail: "", - Subject: &from.HCL2Ref.DeclRange, }) - + continue + } + builder, moreDiags := p.StartBuilder(src) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + provisioners, moreDiags := p.CoreBuildProvisioners(build.ProvisionerBlocks) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + postProcessors, moreDiags := p.CoreBuildPostProcessors(build.PostProcessors) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { continue } - // provisioners := build.ProvisionerGroups.FlatProvisioners() - // postProcessors := build.PostProvisionerGroups.FlatProvisioners() - - _ = from - _ = source - _ = communicator - // _ = provisioners - // _ = postProcessors + pcb := &packer.CoreBuild{ + Type: src.Type, + Builder: builder, + Provisioners: provisioners, + PostProcessors: [][]packer.CoreBuildPostProcessor{postProcessors}, + Variables: cfg.Variables, + } + res = append(res, pcb) } } - return res + return res, diags } -func (pkrCfg *PackerConfig) ToTemplate() (*template.Template, error) { - var result template.Template - // var errs error +func (p *Parser) Parse(path string) ([]packer.Build, hcl.Diagnostics) { + cfg, diags := p.parse(path) + if diags.HasErrors() { + return nil, diags + } - result.Comments = nil - result.Variables = pkrCfg.Variables.Variables() - // TODO(azr): add sensitive variables - - builder := pkrCfg.ToV1Build() - _ = builder - - // // Gather all the post-processors - // if len(r.PostProcessors) > 0 { - // result.PostProcessors = make([][]*PostProcessor, 0, len(r.PostProcessors)) - // } - // for i, v := range r.PostProcessors { - // // Parse the configurations. We need to do this because post-processors - // // can take three different formats. - // configs, err := r.parsePostProcessor(i, v) - // if err != nil { - // errs = multierror.Append(errs, err) - // continue - // } - - // // Parse the PostProcessors out of the configs - // pps := make([]*PostProcessor, 0, len(configs)) - // for j, c := range configs { - // var pp PostProcessor - // if err := r.decoder(&pp, nil).Decode(c); err != nil { - // errs = multierror.Append(errs, fmt.Errorf( - // "post-processor %d.%d: %s", i+1, j+1, err)) - // continue - // } - - // // Type is required - // if pp.Type == "" { - // errs = multierror.Append(errs, fmt.Errorf( - // "post-processor %d.%d: type is required", i+1, j+1)) - // continue - // } - - // // Set the raw configuration and delete any special keys - // pp.Config = c - - // // The name defaults to the type if it isn't set - // if pp.Name == "" { - // pp.Name = pp.Type - // } - - // delete(pp.Config, "except") - // delete(pp.Config, "only") - // delete(pp.Config, "keep_input_artifact") - // delete(pp.Config, "type") - // delete(pp.Config, "name") - - // if len(pp.Config) == 0 { - // pp.Config = nil - // } - - // pps = append(pps, &pp) - // } - - // result.PostProcessors = append(result.PostProcessors, pps) - // } - - // // Gather all the provisioners - // if len(r.Provisioners) > 0 { - // result.Provisioners = make([]*Provisioner, 0, len(r.Provisioners)) - // } - // for i, v := range r.Provisioners { - // var p Provisioner - // if err := r.decoder(&p, nil).Decode(v); err != nil { - // errs = multierror.Append(errs, fmt.Errorf( - // "provisioner %d: %s", i+1, err)) - // continue - // } - - // // Type is required before any richer validation - // if p.Type == "" { - // errs = multierror.Append(errs, fmt.Errorf( - // "provisioner %d: missing 'type'", i+1)) - // continue - // } - - // // Set the raw configuration and delete any special keys - // p.Config = v.(map[string]interface{}) - - // delete(p.Config, "except") - // delete(p.Config, "only") - // delete(p.Config, "override") - // delete(p.Config, "pause_before") - // delete(p.Config, "type") - // delete(p.Config, "timeout") - - // if len(p.Config) == 0 { - // p.Config = nil - // } - - // result.Provisioners = append(result.Provisioners, &p) - // } - - // // If we have errors, return those with a nil result - // if errs != nil { - // return nil, errs - // } - - return &result, nil + return p.getBuilds(cfg) } diff --git a/hcl2template/types.packer_config_test.go b/hcl2template/types.packer_config_test.go new file mode 100644 index 000000000..b47ffba4a --- /dev/null +++ b/hcl2template/types.packer_config_test.go @@ -0,0 +1,55 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +var ref = SourceRef{Type: "virtualbox-iso", Name: "ubuntu-1204"} + +func TestParser_complete(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"working build", + defaultParser, + parseTestArgs{"testdata/complete"}, + &PackerConfig{ + Sources: map[SourceRef]*Source{ + ref: &Source{Type: "virtualbox-iso", Name: "ubuntu-1204"}, + }, + Builds: Builds{ + &BuildBlock{ + Froms: []SourceRef{ref}, + ProvisionerBlocks: []*ProvisionerBlock{ + {PType: "shell"}, + {PType: "file"}, + }, + PostProcessors: []*PostProcessorBlock{ + {PType: "amazon-import"}, + }, + }, + }, + }, + false, false, + []packer.Build{ + &packer.CoreBuild{ + Type: "virtualbox-iso", + Builder: basicMockBuilder, + Provisioners: []packer.CoreBuildProvisioner{ + {PType: "shell", Provisioner: basicMockProvisioner}, + {PType: "file", Provisioner: basicMockProvisioner}, + }, + PostProcessors: [][]packer.CoreBuildPostProcessor{ + { + {PType: "amazon-import", PostProcessor: basicMockPostProcessor}, + }, + }, + }, + }, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/types.source.go b/hcl2template/types.source.go index 17844b323..f86ab4b69 100644 --- a/hcl2template/types.source.go +++ b/hcl2template/types.source.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/packer/packer" ) // A source field in an HCL file will load into the Source type. @@ -14,36 +15,58 @@ type Source struct { // Given name; if any Name string - Cfg interface{} - - HCL2Ref HCL2Ref + block *hcl.Block } -func (p *Parser) decodeSource(block *hcl.Block, sourceSpecs map[string]Decodable) (*Source, hcl.Diagnostics) { +func (p *Parser) decodeSource(block *hcl.Block) (*Source, hcl.Diagnostics) { source := &Source{ - Type: block.Labels[0], - Name: block.Labels[1], + Type: block.Labels[0], + Name: block.Labels[1], + block: block, } - source.HCL2Ref.DeclRange = block.DefRange - var diags hcl.Diagnostics - sourceSpec, found := sourceSpecs[source.Type] - if !found { + if !p.BuilderSchemas.Has(source.Type) { diags = append(diags, &hcl.Diagnostic{ - Summary: "Unknown " + sourceLabel + " type", - Subject: &block.LabelRanges[0], + Summary: "Unknown " + buildSourceLabel + " type " + source.Type, + Subject: block.LabelRanges[0].Ptr(), + Detail: fmt.Sprintf("known builders: %v", p.BuilderSchemas.List()), + Severity: hcl.DiagError, }) - return source, diags + return nil, diags } - flatSource, moreDiags := decodeDecodable(block, nil, sourceSpec) - diags = append(diags, moreDiags...) - source.Cfg = flatSource - return source, diags } +func (p *Parser) StartBuilder(source *Source) (packer.Builder, hcl.Diagnostics) { + var diags hcl.Diagnostics + + // calling BuilderSchemas will start a new builder plugin to ask about + // the schema of the builder; but we do not know yet if the builder is + // actually going to be used. This also allows to call the same builder + // more than once. + builder, err := p.BuilderSchemas.Start(source.Type) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Failed to load " + sourceLabel + " type", + Detail: err.Error(), + Subject: &source.block.LabelRanges[0], + }) + return builder, diags + } + + decoded, moreDiags := decodeHCL2Spec(source.block, nil, builder) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + return nil, diags + } + warning, err := builder.Prepare(decoded) + moreDiags = warningErrorsToDiags(source.block, warning, err) + diags = append(diags, moreDiags...) + return builder, diags +} + func (source *Source) Ref() SourceRef { return SourceRef{ Type: source.Type, diff --git a/hcl2template/types.source_test.go b/hcl2template/types.source_test.go new file mode 100644 index 000000000..66d38860a --- /dev/null +++ b/hcl2template/types.source_test.go @@ -0,0 +1,75 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestParse_source(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"two basic sources", + defaultParser, + parseTestArgs{"testdata/sources/basic.pkr.hcl"}, + &PackerConfig{ + Sources: map[SourceRef]*Source{ + SourceRef{ + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }: { + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }, + }, + }, + false, false, + []packer.Build{}, + false, + }, + {"untyped source", + defaultParser, + parseTestArgs{"testdata/sources/untyped.pkr.hcl"}, + &PackerConfig{}, + true, true, + nil, + false, + }, + {"unnamed source", + defaultParser, + parseTestArgs{"testdata/sources/unnamed.pkr.hcl"}, + &PackerConfig{}, + true, true, + nil, + false, + }, + {"inexistent source", + defaultParser, + parseTestArgs{"testdata/sources/inexistent.pkr.hcl"}, + &PackerConfig{}, + true, true, + nil, + false, + }, + {"duplicate source", + defaultParser, + parseTestArgs{"testdata/sources/duplicate.pkr.hcl"}, + &PackerConfig{ + Sources: map[SourceRef]*Source{ + SourceRef{ + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }: { + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }, + }, + }, + true, true, + nil, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/types.variable.go b/hcl2template/types.variables.go similarity index 100% rename from hcl2template/types.variable.go rename to hcl2template/types.variables.go diff --git a/hcl2template/types.variables_test.go b/hcl2template/types.variables_test.go new file mode 100644 index 000000000..cf60f25ef --- /dev/null +++ b/hcl2template/types.variables_test.go @@ -0,0 +1,29 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestParse_variables(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"basic variables", + defaultParser, + parseTestArgs{"testdata/variables/basic.pkr.hcl"}, + &PackerConfig{ + Variables: PackerV1Variables{ + "image_name": "foo-image-{{user `my_secret`}}", + "key": "value", + "my_secret": "foo", + }, + }, + false, false, + []packer.Build{}, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/utils.go b/hcl2template/utils.go new file mode 100644 index 000000000..6edb52447 --- /dev/null +++ b/hcl2template/utils.go @@ -0,0 +1,23 @@ +package hcl2template + +import "github.com/hashicorp/hcl/v2" + +func warningErrorsToDiags(block *hcl.Block, warnings []string, err error) hcl.Diagnostics { + var diags hcl.Diagnostics + + for _, warning := range warnings { + diags = append(diags, &hcl.Diagnostic{ + Summary: warning, + Subject: &block.DefRange, + Severity: hcl.DiagWarning, + }) + } + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: err.Error(), + Subject: &block.DefRange, + Severity: hcl.DiagError, + }) + } + return diags +} diff --git a/helper/builder/testing/testing.go b/helper/builder/testing/testing.go index 223615fa3..df1bdad87 100644 --- a/helper/builder/testing/testing.go +++ b/helper/builder/testing/testing.go @@ -63,6 +63,13 @@ type TestT interface { Skip(args ...interface{}) } +type TestBuilderStore struct { + packer.BuilderStore + StartFn func(name string) (packer.Builder, error) +} + +func (tbs TestBuilderStore) Start(name string) (packer.Builder, error) { return tbs.StartFn(name) } + // Test performs an acceptance test on a backend with the given test case. // // Tests are not run unless an environmental variable "PACKER_ACC" is @@ -106,12 +113,14 @@ func Test(t TestT, c TestCase) { log.Printf("[DEBUG] Initializing core...") core, err := packer.NewCore(&packer.CoreConfig{ Components: packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - if n == "test" { - return c.Builder, nil - } + BuilderStore: TestBuilderStore{ + StartFn: func(n string) (packer.Builder, error) { + if n == "test" { + return c.Builder, nil + } - return nil, nil + return nil, nil + }, }, }, Template: tpl, diff --git a/helper/communicator/config.go b/helper/communicator/config.go index c6c5d880e..a2e262445 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -11,7 +11,9 @@ import ( "os" "time" + "github.com/hashicorp/hcl/v2/hcldec" packerssh "github.com/hashicorp/packer/communicator/ssh" + "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/packer" @@ -160,10 +162,28 @@ type SSH struct { SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels"` // SSH Internals - SSHPublicKey []byte - SSHPrivateKey []byte + SSHPublicKey []byte `mapstructure:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key"` } +func (c *SSH) ConfigSpec() hcldec.ObjectSpec { return c.FlatMapstructure().HCL2Spec() } +func (c *WinRM) ConfigSpec() hcldec.ObjectSpec { return c.FlatMapstructure().HCL2Spec() } + +func (c *SSH) Configure(raws ...interface{}) ([]string, error) { + err := config.Decode(c, nil, raws...) + return nil, err +} + +func (c *WinRM) Configure(raws ...interface{}) ([]string, error) { + err := config.Decode(c, nil, raws...) + return nil, err +} + +var ( + _ packer.ConfigurableCommunicator = new(SSH) + _ packer.ConfigurableCommunicator = new(WinRM) +) + type SSHInterface struct { // One of `public_ip`, `private_ip`, `public_dns`, or `private_dns`. If // set, either the public IP address, private IP address, public DNS name diff --git a/helper/communicator/config.hcl2spec.go b/helper/communicator/config.hcl2spec.go index bf726f3cd..977ac1d41 100644 --- a/helper/communicator/config.hcl2spec.go +++ b/helper/communicator/config.hcl2spec.go @@ -39,8 +39,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -54,10 +54,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, @@ -135,17 +138,18 @@ type FlatSSH struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` } // FlatMapstructure returns a new FlatSSH. // FlatSSH is an auto-generated flat version of SSH. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SSH) FlatMapstructure() interface{} { return new(FlatSSH) } +func (*SSH) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSSH) } -// HCL2Spec returns the hcldec.Spec of a FlatSSH. -// This spec is used by HCL to read the fields of FlatSSH. +// HCL2Spec returns the hcl spec of a SSH. +// This spec is used by HCL to read the fields of SSH. +// The decoded values from this spec will then be applied to a FlatSSH. func (*FlatSSH) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, @@ -198,10 +202,11 @@ type FlatWinRM struct { // FlatMapstructure returns a new FlatWinRM. // FlatWinRM is an auto-generated flat version of WinRM. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*WinRM) FlatMapstructure() interface{} { return new(FlatWinRM) } +func (*WinRM) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatWinRM) } -// HCL2Spec returns the hcldec.Spec of a FlatWinRM. -// This spec is used by HCL to read the fields of FlatWinRM. +// HCL2Spec returns the hcl spec of a WinRM. +// This spec is used by HCL to read the fields of WinRM. +// The decoded values from this spec will then be applied to a FlatWinRM. func (*FlatWinRM) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, diff --git a/helper/config/decode.go b/helper/config/decode.go index 43673e1dc..eec3bbdf7 100644 --- a/helper/config/decode.go +++ b/helper/config/decode.go @@ -1,14 +1,19 @@ package config import ( + "encoding/json" "fmt" "reflect" "sort" "strings" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/mapstructure" + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/gocty" + ctyjson "github.com/zclconf/go-cty/cty/json" ) // DecodeOpts are the options for decoding configuration. @@ -37,6 +42,36 @@ var DefaultDecodeHookFuncs = []mapstructure.DecodeHookFunc{ // Decode decodes the configuration into the target and optionally // automatically interpolates all the configuration as it goes. func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { + for i, raw := range raws { + // check for cty values and transform them to json then to a + // map[string]interface{} so that mapstructure can do its thing. + cval, ok := raw.(cty.Value) + if !ok { + continue + } + type flatConfigurer interface { + FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } + } + ctarget := target.(flatConfigurer) + flatCfg := ctarget.FlatMapstructure() + err := gocty.FromCtyValue(cval, flatCfg) + if err != nil { + switch err := err.(type) { + case cty.PathError: + return fmt.Errorf("%v: %v", err, err.Path) + } + return err + } + b, err := ctyjson.SimpleJSONValue{Value: cval}.MarshalJSON() + if err != nil { + return err + } + var raw map[string]interface{} + if err := json.Unmarshal(b, &raw); err != nil { + return err + } + raws[i] = raw + } if config == nil { config = &DecodeOpts{Interpolate: true} } diff --git a/main.go b/main.go index b24ee229b..bed90598c 100644 --- a/main.go +++ b/main.go @@ -141,16 +141,16 @@ func wrappedMain() int { log.SetFlags(0) } - log.Printf("[INFO] Packer version: %s", version.FormattedVersion()) - log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH) - log.Printf("Built with Go Version: %s", runtime.Version()) + log.Printf("[INFO] Packer version: %s [%s %s %s]", + version.FormattedVersion(), + runtime.Version(), + runtime.GOOS, runtime.GOARCH) config, err := loadConfig() if err != nil { fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err) return 1 } - log.Printf("Packer config: %+v", config) // Fire off the checkpoint. go runCheckpoint(config) @@ -214,10 +214,11 @@ func wrappedMain() int { CommandMeta = &command.Meta{ CoreConfig: &packer.CoreConfig{ Components: packer.ComponentFinder{ - Builder: config.LoadBuilder, - Hook: config.LoadHook, - PostProcessor: config.LoadPostProcessor, - Provisioner: config.LoadProvisioner, + Hook: config.StarHook, + + BuilderStore: config.Builders, + ProvisionerStore: config.Provisioners, + PostProcessorStore: config.PostProcessors, }, Version: version.Version, }, @@ -291,6 +292,9 @@ func loadConfig() (*config, error) { var config config config.PluginMinPort = 10000 config.PluginMaxPort = 25000 + config.Builders = packer.MapOfBuilder{} + config.PostProcessors = packer.MapOfPostProcessor{} + config.Provisioners = packer.MapOfProvisioner{} if err := config.Discover(); err != nil { return nil, err } diff --git a/packer/build.go b/packer/build.go index badd67c1f..526dc05e4 100644 --- a/packer/build.go +++ b/packer/build.go @@ -79,21 +79,21 @@ type Build interface { SetOnError(string) } -// A build struct represents a single build job, the result of which should +// A CoreBuild struct represents a single build job, the result of which should // be a single machine image artifact. This artifact may be comprised of -// multiple files, of course, but it should be for only a single provider -// (such as VirtualBox, EC2, etc.). -type coreBuild struct { - name string - builder Builder - builderConfig interface{} - builderType string +// multiple files, of course, but it should be for only a single provider (such +// as VirtualBox, EC2, etc.). +type CoreBuild struct { + Type string + Builder Builder + BuilderConfig interface{} + BuilderType string hooks map[string][]Hook - postProcessors [][]coreBuildPostProcessor - provisioners []coreBuildProvisioner - cleanupProvisioner coreBuildProvisioner - templatePath string - variables map[string]string + Provisioners []CoreBuildProvisioner + PostProcessors [][]CoreBuildPostProcessor + CleanupProvisioner CoreBuildProvisioner + TemplatePath string + Variables map[string]string debug bool force bool @@ -102,32 +102,32 @@ type coreBuild struct { prepareCalled bool } -// Keeps track of the post-processor and the configuration of the -// post-processor used within a build. -type coreBuildPostProcessor struct { - processor PostProcessor - processorType string +// CoreBuildPostProcessor Keeps track of the post-processor and the +// configuration of the post-processor used within a build. +type CoreBuildPostProcessor struct { + PostProcessor PostProcessor + PType string config map[string]interface{} keepInputArtifact *bool } -// Keeps track of the provisioner and the configuration of the provisioner -// within the build. -type coreBuildProvisioner struct { - pType string - provisioner Provisioner +// CoreBuildProvisioner keeps track of the provisioner and the configuration of +// the provisioner within the build. +type CoreBuildProvisioner struct { + PType string + Provisioner Provisioner config []interface{} } // Returns the name of the build. -func (b *coreBuild) Name() string { - return b.name +func (b *CoreBuild) Name() string { + return b.Type } // Prepare prepares the build by doing some initialization for the builder // and any hooks. This _must_ be called prior to Run. The parameter is the // overrides for the variables within the template (if any). -func (b *coreBuild) Prepare() (warn []string, err error) { +func (b *CoreBuild) Prepare() (warn []string, err error) { b.l.Lock() defer b.l.Unlock() @@ -138,48 +138,48 @@ func (b *coreBuild) Prepare() (warn []string, err error) { b.prepareCalled = true packerConfig := map[string]interface{}{ - BuildNameConfigKey: b.name, - BuilderTypeConfigKey: b.builderType, + BuildNameConfigKey: b.Type, + BuilderTypeConfigKey: b.BuilderType, DebugConfigKey: b.debug, ForceConfigKey: b.force, OnErrorConfigKey: b.onError, - TemplatePathKey: b.templatePath, - UserVariablesConfigKey: b.variables, + TemplatePathKey: b.TemplatePath, + UserVariablesConfigKey: b.Variables, } // Prepare the builder - warn, err = b.builder.Prepare(b.builderConfig, packerConfig) + warn, err = b.Builder.Prepare(b.BuilderConfig, packerConfig) if err != nil { - log.Printf("Build '%s' prepare failure: %s\n", b.name, err) + log.Printf("Build '%s' prepare failure: %s\n", b.Type, err) return } // Prepare the provisioners - for _, coreProv := range b.provisioners { + for _, coreProv := range b.Provisioners { configs := make([]interface{}, len(coreProv.config), len(coreProv.config)+1) copy(configs, coreProv.config) configs = append(configs, packerConfig) - if err = coreProv.provisioner.Prepare(configs...); err != nil { + if err = coreProv.Provisioner.Prepare(configs...); err != nil { return } } // Prepare the on-error-cleanup provisioner - if b.cleanupProvisioner.pType != "" { - configs := make([]interface{}, len(b.cleanupProvisioner.config), len(b.cleanupProvisioner.config)+1) - copy(configs, b.cleanupProvisioner.config) + if b.CleanupProvisioner.PType != "" { + configs := make([]interface{}, len(b.CleanupProvisioner.config), len(b.CleanupProvisioner.config)+1) + copy(configs, b.CleanupProvisioner.config) configs = append(configs, packerConfig) - err = b.cleanupProvisioner.provisioner.Prepare(configs...) + err = b.CleanupProvisioner.Provisioner.Prepare(configs...) if err != nil { return } } // Prepare the post-processors - for _, ppSeq := range b.postProcessors { + for _, ppSeq := range b.PostProcessors { for _, corePP := range ppSeq { - err = corePP.processor.Configure(corePP.config, packerConfig) + err = corePP.PostProcessor.Configure(corePP.config, packerConfig) if err != nil { return } @@ -190,7 +190,7 @@ func (b *coreBuild) Prepare() (warn []string, err error) { } // Runs the actual build. Prepare must be called prior to running this. -func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) { +func (b *CoreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) { if !b.prepareCalled { panic("Prepare must be called first") } @@ -203,24 +203,24 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) } // Add a hook for the provisioners if we have provisioners - if len(b.provisioners) > 0 { - hookedProvisioners := make([]*HookedProvisioner, len(b.provisioners)) - for i, p := range b.provisioners { + if len(b.Provisioners) > 0 { + hookedProvisioners := make([]*HookedProvisioner, len(b.Provisioners)) + for i, p := range b.Provisioners { var pConfig interface{} if len(p.config) > 0 { pConfig = p.config[0] } if b.debug { hookedProvisioners[i] = &HookedProvisioner{ - &DebuggedProvisioner{Provisioner: p.provisioner}, + &DebuggedProvisioner{Provisioner: p.Provisioner}, pConfig, - p.pType, + p.PType, } } else { hookedProvisioners[i] = &HookedProvisioner{ - p.provisioner, + p.Provisioner, pConfig, - p.pType, + p.PType, } } } @@ -234,11 +234,11 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) }) } - if b.cleanupProvisioner.pType != "" { + if b.CleanupProvisioner.PType != "" { hookedCleanupProvisioner := &HookedProvisioner{ - b.cleanupProvisioner.provisioner, - b.cleanupProvisioner.config, - b.cleanupProvisioner.pType, + b.CleanupProvisioner.Provisioner, + b.CleanupProvisioner.config, + b.CleanupProvisioner.PType, } hooks[HookCleanupProvision] = []Hook{&ProvisionHook{ Provisioners: []*HookedProvisioner{hookedCleanupProvisioner}, @@ -254,9 +254,9 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) Ui: originalUi, } - log.Printf("Running builder: %s", b.builderType) - ts := CheckpointReporter.AddSpan(b.builderType, "builder", b.builderConfig) - builderArtifact, err := b.builder.Run(ctx, builderUi, hook) + log.Printf("Running builder: %s", b.BuilderType) + ts := CheckpointReporter.AddSpan(b.BuilderType, "builder", b.BuilderConfig) + builderArtifact, err := b.Builder.Run(ctx, builderUi, hook) ts.End(err) if err != nil { return nil, err @@ -269,21 +269,21 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) } errors := make([]error, 0) - keepOriginalArtifact := len(b.postProcessors) == 0 + keepOriginalArtifact := len(b.PostProcessors) == 0 // Run the post-processors PostProcessorRunSeqLoop: - for _, ppSeq := range b.postProcessors { + for _, ppSeq := range b.PostProcessors { priorArtifact := builderArtifact for i, corePP := range ppSeq { ppUi := &TargetedUI{ - Target: fmt.Sprintf("%s (%s)", b.Name(), corePP.processorType), + Target: fmt.Sprintf("%s (%s)", b.Name(), corePP.PType), Ui: originalUi, } - builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType)) - ts := CheckpointReporter.AddSpan(corePP.processorType, "post-processor", corePP.config) - artifact, defaultKeep, forceOverride, err := corePP.processor.PostProcess(ctx, ppUi, priorArtifact) + builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.PType)) + ts := CheckpointReporter.AddSpan(corePP.PType, "post-processor", corePP.config) + artifact, defaultKeep, forceOverride, err := corePP.PostProcessor.PostProcess(ctx, ppUi, priorArtifact) ts.End(err) if err != nil { errors = append(errors, fmt.Errorf("Post-processor failed: %s", err)) @@ -308,7 +308,7 @@ PostProcessorRunSeqLoop: log.Printf("The %s post-processor forces "+ "keep_input_artifact=true to preserve integrity of the"+ "build chain. User-set keep_input_artifact=false will be"+ - "ignored.", corePP.processorType) + "ignored.", corePP.PType) } else { // User overrides default. keep = *corePP.keepInputArtifact @@ -321,7 +321,7 @@ PostProcessorRunSeqLoop: if !keepOriginalArtifact && keep { log.Printf( "Flagging to keep original artifact from post-processor '%s'", - corePP.processorType) + corePP.PType) keepOriginalArtifact = true } } else { @@ -330,10 +330,10 @@ PostProcessorRunSeqLoop: if keep { artifacts = append(artifacts, priorArtifact) } else { - log.Printf("Deleting prior artifact from post-processor '%s'", corePP.processorType) + log.Printf("Deleting prior artifact from post-processor '%s'", corePP.PType) if err := priorArtifact.Destroy(); err != nil { log.Printf("Error is %#v", err) - errors = append(errors, fmt.Errorf("Failed cleaning up prior artifact: %s; pp is %s", err, corePP.processorType)) + errors = append(errors, fmt.Errorf("Failed cleaning up prior artifact: %s; pp is %s", err, corePP.PType)) } } } @@ -352,7 +352,7 @@ PostProcessorRunSeqLoop: copy(artifacts[1:], artifacts) artifacts[0] = builderArtifact } else { - log.Printf("Deleting original artifact for build '%s'", b.name) + log.Printf("Deleting original artifact for build '%s'", b.Type) if err := builderArtifact.Destroy(); err != nil { errors = append(errors, fmt.Errorf("Error destroying builder artifact: %s; bad artifact: %#v", err, builderArtifact.Files())) } @@ -365,7 +365,7 @@ PostProcessorRunSeqLoop: return artifacts, err } -func (b *coreBuild) SetDebug(val bool) { +func (b *CoreBuild) SetDebug(val bool) { if b.prepareCalled { panic("prepare has already been called") } @@ -373,7 +373,7 @@ func (b *coreBuild) SetDebug(val bool) { b.debug = val } -func (b *coreBuild) SetForce(val bool) { +func (b *CoreBuild) SetForce(val bool) { if b.prepareCalled { panic("prepare has already been called") } @@ -381,7 +381,7 @@ func (b *coreBuild) SetForce(val bool) { b.force = val } -func (b *coreBuild) SetOnError(val string) { +func (b *CoreBuild) SetOnError(val string) { if b.prepareCalled { panic("prepare has already been called") } diff --git a/packer/build_test.go b/packer/build_test.go index 0c58b405d..2168214f2 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -10,24 +10,24 @@ func boolPointer(tf bool) *bool { return &tf } -func testBuild() *coreBuild { - return &coreBuild{ - name: "test", - builder: &MockBuilder{ArtifactId: "b"}, - builderConfig: 42, - builderType: "foo", +func testBuild() *CoreBuild { + return &CoreBuild{ + Type: "test", + Builder: &MockBuilder{ArtifactId: "b"}, + BuilderConfig: 42, + BuilderType: "foo", hooks: map[string][]Hook{ "foo": {&MockHook{}}, }, - provisioners: []coreBuildProvisioner{ + Provisioners: []CoreBuildProvisioner{ {"mock-provisioner", &MockProvisioner{}, []interface{}{42}}, }, - postProcessors: [][]coreBuildPostProcessor{ + PostProcessors: [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp"}, "testPP", make(map[string]interface{}), boolPointer(true)}, }, }, - variables: make(map[string]string), + Variables: make(map[string]string), onError: "cleanup", } } @@ -54,7 +54,7 @@ func TestBuild_Prepare(t *testing.T) { packerConfig := testDefaultPackerConfig() build := testBuild() - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) build.Prepare() if !builder.PrepareCalled { @@ -64,8 +64,8 @@ func TestBuild_Prepare(t *testing.T) { t.Fatalf("bad: %#v", builder.PrepareConfig) } - coreProv := build.provisioners[0] - prov := coreProv.provisioner.(*MockProvisioner) + coreProv := build.Provisioners[0] + prov := coreProv.Provisioner.(*MockProvisioner) if !prov.PrepCalled { t.Fatal("prep should be called") } @@ -73,8 +73,8 @@ func TestBuild_Prepare(t *testing.T) { t.Fatalf("bad: %#v", prov.PrepConfigs) } - corePP := build.postProcessors[0][0] - pp := corePP.processor.(*MockPostProcessor) + corePP := build.PostProcessors[0][0] + pp := corePP.PostProcessor.(*MockPostProcessor) if !pp.ConfigureCalled { t.Fatal("should be called") } @@ -111,7 +111,7 @@ func TestBuildPrepare_BuilderWarnings(t *testing.T) { expected := []string{"foo"} build := testBuild() - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) builder.PrepareWarnings = expected warn, err := build.Prepare() @@ -128,7 +128,7 @@ func TestBuild_Prepare_Debug(t *testing.T) { packerConfig[DebugConfigKey] = true build := testBuild() - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) build.SetDebug(true) build.Prepare() @@ -139,8 +139,8 @@ func TestBuild_Prepare_Debug(t *testing.T) { t.Fatalf("bad: %#v", builder.PrepareConfig) } - coreProv := build.provisioners[0] - prov := coreProv.provisioner.(*MockProvisioner) + coreProv := build.Provisioners[0] + prov := coreProv.Provisioner.(*MockProvisioner) if !prov.PrepCalled { t.Fatal("prepare should be called") } @@ -156,8 +156,8 @@ func TestBuildPrepare_variables_default(t *testing.T) { } build := testBuild() - build.variables["foo"] = "bar" - builder := build.builder.(*MockBuilder) + build.Variables["foo"] = "bar" + builder := build.Builder.(*MockBuilder) warn, err := build.Prepare() if len(warn) > 0 { @@ -191,7 +191,7 @@ func TestBuild_Run(t *testing.T) { } // Verify builder was run - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) if !builder.RunCalled { t.Fatal("should be called") } @@ -210,13 +210,13 @@ func TestBuild_Run(t *testing.T) { // Verify provisioners run dispatchHook.Run(ctx, HookProvision, nil, new(MockCommunicator), 42) - prov := build.provisioners[0].provisioner.(*MockProvisioner) + prov := build.Provisioners[0].Provisioner.(*MockProvisioner) if !prov.ProvCalled { t.Fatal("should be called") } // Verify post-processor was run - pp := build.postProcessors[0][0].processor.(*MockPostProcessor) + pp := build.PostProcessors[0][0].PostProcessor.(*MockPostProcessor) if !pp.PostProcessCalled { t.Fatal("should be called") } @@ -228,7 +228,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with no post-processors, we only get the // main build. build := testBuild() - build.postProcessors = [][]coreBuildPostProcessor{} + build.PostProcessors = [][]CoreBuildPostProcessor{} build.Prepare() artifacts, err := build.Run(context.Background(), ui) @@ -249,7 +249,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that doesn't keep // inputs, only that post-processors results are returned. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp"}, "pp", make(map[string]interface{}), boolPointer(false)}, }, @@ -274,7 +274,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with multiple post-processors, as long as one // keeps the original, the original is kept. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp1"}, "pp", make(map[string]interface{}), boolPointer(false)}, }, @@ -302,7 +302,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with sequences, intermediaries are kept if they // want to be. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp1a"}, "pp", make(map[string]interface{}), boolPointer(false)}, {&MockPostProcessor{ArtifactId: "pp1b"}, "pp", make(map[string]interface{}), boolPointer(true)}, @@ -332,7 +332,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that forcibly // keeps inputs, that the artifacts are kept. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { { &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: true}, "pp", make(map[string]interface{}), boolPointer(false), @@ -360,7 +360,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that non-forcibly // keeps inputs, that the artifacts are discarded if user overrides. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { { &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", make(map[string]interface{}), boolPointer(false), @@ -387,7 +387,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that non-forcibly // keeps inputs, that the artifacts are kept if user does not have preference. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { { &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", make(map[string]interface{}), nil, @@ -434,7 +434,7 @@ func TestBuild_Cancel(t *testing.T) { topCtx, topCtxCancel := context.WithCancel(context.Background()) - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) builder.RunFn = func(ctx context.Context) { topCtxCancel() diff --git a/packer/builder.go b/packer/builder.go index e24073128..19bfc7673 100644 --- a/packer/builder.go +++ b/packer/builder.go @@ -1,6 +1,8 @@ package packer -import "context" +import ( + "context" +) // Implementers of Builder are responsible for actually building images // on some platform given some configuration. @@ -13,6 +15,8 @@ import "context" // parallelism is strictly disabled, so it is safe to request input from // stdin and so on. type Builder interface { + HCL2Speccer + // Prepare is responsible for configuring the builder and validating // that configuration. Any setup should be done in this method. Note that // NO side effects should take place in prepare, it is meant as a state diff --git a/packer/builder_mock.go b/packer/builder_mock.go index 5ebd2d993..2fee11c77 100644 --- a/packer/builder_mock.go +++ b/packer/builder_mock.go @@ -1,8 +1,12 @@ +//go:generate mapstructure-to-hcl2 -type MockBuilder,MockCommunicator,RemoteCmd,MockProvisioner,MockPostProcessor + package packer import ( "context" "errors" + + "github.com/hashicorp/hcl/v2/hcldec" ) // MockBuilder is an implementation of Builder that can be used for tests. @@ -23,6 +27,10 @@ type MockBuilder struct { RunFn func(ctx context.Context) } +func (tb *MockBuilder) ConfigSpec() hcldec.ObjectSpec { return tb.FlatMapstructure().HCL2Spec() } + +func (tb *MockBuilder) FlatConfig() interface{} { return tb.FlatMapstructure() } + func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, error) { tb.PrepareCalled = true tb.PrepareConfig = config diff --git a/packer/builder_mock.hcl2spec.go b/packer/builder_mock.hcl2spec.go new file mode 100644 index 000000000..0baca99da --- /dev/null +++ b/packer/builder_mock.hcl2spec.go @@ -0,0 +1,207 @@ +// Code generated by "mapstructure-to-hcl2 -type MockBuilder,MockCommunicator,RemoteCmd,MockProvisioner,MockPostProcessor"; DO NOT EDIT. +package packer + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" + "io" +) + +// FlatMockBuilder is an auto-generated flat version of MockBuilder. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockBuilder struct { + ArtifactId *string `cty:"artifact_id"` + PrepareWarnings []string `cty:"prepare_warnings"` + RunErrResult *bool `cty:"run_err_result"` + RunNilResult *bool `cty:"run_nil_result"` + PrepareCalled *bool `cty:"prepare_called"` + PrepareConfig []interface{} `cty:"prepare_config"` + RunCalled *bool `cty:"run_called"` + RunHook Hook `cty:"run_hook"` + RunUi Ui `cty:"run_ui"` + CancelCalled *bool `cty:"cancel_called"` +} + +// FlatMapstructure returns a new FlatMockBuilder. +// FlatMockBuilder is an auto-generated flat version of MockBuilder. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockBuilder) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockBuilder) +} + +// HCL2Spec returns the hcl spec of a MockBuilder. +// This spec is used by HCL to read the fields of MockBuilder. +// The decoded values from this spec will then be applied to a FlatMockBuilder. +func (*FlatMockBuilder) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false}, + "prepare_warnings": &hcldec.AttrSpec{Name: "prepare_warnings", Type: cty.List(cty.String), Required: false}, + "run_err_result": &hcldec.AttrSpec{Name: "run_err_result", Type: cty.Bool, Required: false}, + "run_nil_result": &hcldec.AttrSpec{Name: "run_nil_result", Type: cty.Bool, Required: false}, + "prepare_called": &hcldec.AttrSpec{Name: "prepare_called", Type: cty.Bool, Required: false}, + "prepare_config": &hcldec.AttrSpec{Name: "prepare_config", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "run_called": &hcldec.AttrSpec{Name: "run_called", Type: cty.Bool, Required: false}, + "run_hook": &hcldec.AttrSpec{Name: "Hook", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "run_ui": &hcldec.AttrSpec{Name: "Ui", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "cancel_called": &hcldec.AttrSpec{Name: "cancel_called", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatMockCommunicator is an auto-generated flat version of MockCommunicator. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockCommunicator struct { + StartCalled *bool `cty:"start_called"` + StartCmd *FlatRemoteCmd `cty:"start_cmd"` + StartStderr *string `cty:"start_stderr"` + StartStdout *string `cty:"start_stdout"` + StartStdin *string `cty:"start_stdin"` + StartExitStatus *int `cty:"start_exit_status"` + UploadCalled *bool `cty:"upload_called"` + UploadPath *string `cty:"upload_path"` + UploadData *string `cty:"upload_data"` + UploadDirDst *string `cty:"upload_dir_dst"` + UploadDirSrc *string `cty:"upload_dir_src"` + UploadDirExclude []string `cty:"upload_dir_exclude"` + DownloadDirDst *string `cty:"download_dir_dst"` + DownloadDirSrc *string `cty:"download_dir_src"` + DownloadDirExclude []string `cty:"download_dir_exclude"` + DownloadCalled *bool `cty:"download_called"` + DownloadPath *string `cty:"download_path"` + DownloadData *string `cty:"download_data"` +} + +// FlatMapstructure returns a new FlatMockCommunicator. +// FlatMockCommunicator is an auto-generated flat version of MockCommunicator. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockCommunicator) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockCommunicator) +} + +// HCL2Spec returns the hcl spec of a MockCommunicator. +// This spec is used by HCL to read the fields of MockCommunicator. +// The decoded values from this spec will then be applied to a FlatMockCommunicator. +func (*FlatMockCommunicator) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "start_called": &hcldec.AttrSpec{Name: "start_called", Type: cty.Bool, Required: false}, + "start_cmd": &hcldec.BlockSpec{TypeName: "start_cmd", Nested: hcldec.ObjectSpec((*FlatRemoteCmd)(nil).HCL2Spec())}, + "start_stderr": &hcldec.AttrSpec{Name: "start_stderr", Type: cty.String, Required: false}, + "start_stdout": &hcldec.AttrSpec{Name: "start_stdout", Type: cty.String, Required: false}, + "start_stdin": &hcldec.AttrSpec{Name: "start_stdin", Type: cty.String, Required: false}, + "start_exit_status": &hcldec.AttrSpec{Name: "start_exit_status", Type: cty.Number, Required: false}, + "upload_called": &hcldec.AttrSpec{Name: "upload_called", Type: cty.Bool, Required: false}, + "upload_path": &hcldec.AttrSpec{Name: "upload_path", Type: cty.String, Required: false}, + "upload_data": &hcldec.AttrSpec{Name: "upload_data", Type: cty.String, Required: false}, + "upload_dir_dst": &hcldec.AttrSpec{Name: "upload_dir_dst", Type: cty.String, Required: false}, + "upload_dir_src": &hcldec.AttrSpec{Name: "upload_dir_src", Type: cty.String, Required: false}, + "upload_dir_exclude": &hcldec.AttrSpec{Name: "upload_dir_exclude", Type: cty.List(cty.String), Required: false}, + "download_dir_dst": &hcldec.AttrSpec{Name: "download_dir_dst", Type: cty.String, Required: false}, + "download_dir_src": &hcldec.AttrSpec{Name: "download_dir_src", Type: cty.String, Required: false}, + "download_dir_exclude": &hcldec.AttrSpec{Name: "download_dir_exclude", Type: cty.List(cty.String), Required: false}, + "download_called": &hcldec.AttrSpec{Name: "download_called", Type: cty.Bool, Required: false}, + "download_path": &hcldec.AttrSpec{Name: "download_path", Type: cty.String, Required: false}, + "download_data": &hcldec.AttrSpec{Name: "download_data", Type: cty.String, Required: false}, + } + return s +} + +// FlatMockPostProcessor is an auto-generated flat version of MockPostProcessor. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockPostProcessor struct { + ArtifactId *string `cty:"artifact_id"` + Keep *bool `cty:"keep"` + ForceOverride *bool `cty:"force_override"` + Error error `cty:"error"` + ConfigureCalled *bool `cty:"configure_called"` + ConfigureConfigs []interface{} `cty:"configure_configs"` + ConfigureError error `cty:"configure_error"` + PostProcessCalled *bool `cty:"post_process_called"` + PostProcessArtifact Artifact `cty:"post_process_artifact"` + PostProcessUi Ui `cty:"post_process_ui"` +} + +// FlatMapstructure returns a new FlatMockPostProcessor. +// FlatMockPostProcessor is an auto-generated flat version of MockPostProcessor. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockPostProcessor) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockPostProcessor) +} + +// HCL2Spec returns the hcl spec of a MockPostProcessor. +// This spec is used by HCL to read the fields of MockPostProcessor. +// The decoded values from this spec will then be applied to a FlatMockPostProcessor. +func (*FlatMockPostProcessor) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false}, + "keep": &hcldec.AttrSpec{Name: "keep", Type: cty.Bool, Required: false}, + "force_override": &hcldec.AttrSpec{Name: "force_override", Type: cty.Bool, Required: false}, + "error": &hcldec.AttrSpec{Name: "error", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "configure_called": &hcldec.AttrSpec{Name: "configure_called", Type: cty.Bool, Required: false}, + "configure_configs": &hcldec.AttrSpec{Name: "configure_configs", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "configure_error": &hcldec.AttrSpec{Name: "error", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "post_process_called": &hcldec.AttrSpec{Name: "post_process_called", Type: cty.Bool, Required: false}, + "post_process_artifact": &hcldec.AttrSpec{Name: "Artifact", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "post_process_ui": &hcldec.AttrSpec{Name: "Ui", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + } + return s +} + +// FlatMockProvisioner is an auto-generated flat version of MockProvisioner. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockProvisioner struct { + PrepCalled *bool `cty:"prep_called"` + PrepConfigs []interface{} `cty:"prep_configs"` + ProvCalled *bool `cty:"prov_called"` + ProvCommunicator Communicator `cty:"prov_communicator"` + ProvUi Ui `cty:"prov_ui"` +} + +// FlatMapstructure returns a new FlatMockProvisioner. +// FlatMockProvisioner is an auto-generated flat version of MockProvisioner. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockProvisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockProvisioner) +} + +// HCL2Spec returns the hcl spec of a MockProvisioner. +// This spec is used by HCL to read the fields of MockProvisioner. +// The decoded values from this spec will then be applied to a FlatMockProvisioner. +func (*FlatMockProvisioner) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "prep_called": &hcldec.AttrSpec{Name: "prep_called", Type: cty.Bool, Required: false}, + "prep_configs": &hcldec.AttrSpec{Name: "prep_configs", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "prov_called": &hcldec.AttrSpec{Name: "prov_called", Type: cty.Bool, Required: false}, + "prov_communicator": &hcldec.AttrSpec{Name: "Communicator", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "prov_ui": &hcldec.AttrSpec{Name: "Ui", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + } + return s +} + +// FlatRemoteCmd is an auto-generated flat version of RemoteCmd. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRemoteCmd struct { + Command *string `cty:"command"` + Stdin io.Reader `cty:"stdin"` + Stdout io.Writer `cty:"stdout"` + Stderr io.Writer `cty:"stderr"` +} + +// FlatMapstructure returns a new FlatRemoteCmd. +// FlatRemoteCmd is an auto-generated flat version of RemoteCmd. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RemoteCmd) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRemoteCmd) +} + +// HCL2Spec returns the hcl spec of a RemoteCmd. +// This spec is used by HCL to read the fields of RemoteCmd. +// The decoded values from this spec will then be applied to a FlatRemoteCmd. +func (*FlatRemoteCmd) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "command": &hcldec.AttrSpec{Name: "command", Type: cty.String, Required: false}, + "stdin": &hcldec.AttrSpec{Name: "io.Reader", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "stdout": &hcldec.AttrSpec{Name: "io.Writer", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "stderr": &hcldec.AttrSpec{Name: "io.Writer", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + } + return s +} diff --git a/packer/communicator.go b/packer/communicator.go index aef1775a0..026737d4b 100644 --- a/packer/communicator.go +++ b/packer/communicator.go @@ -37,7 +37,7 @@ type RemoteCmd struct { exitStatus int // This thing is a mutex, lock when making modifications concurrently - sync.Mutex + m sync.Mutex exitChInit sync.Once exitCh chan interface{} @@ -80,6 +80,11 @@ type Communicator interface { DownloadDir(src string, dst string, exclude []string) error } +type ConfigurableCommunicator interface { + HCL2Speccer + Configure(...interface{}) ([]string, error) +} + // RunWithUi runs the remote command and streams the output to any configured // Writers for stdout/stderr, while also writing each line as it comes to a Ui. // RunWithUi will not return until the command finishes or is cancelled. @@ -95,8 +100,8 @@ func (r *RemoteCmd) RunWithUi(ctx context.Context, c Communicator, ui Ui) error originalStdout := r.Stdout originalStderr := r.Stderr defer func() { - r.Lock() - defer r.Unlock() + r.m.Lock() + defer r.m.Unlock() r.Stdout = originalStdout r.Stderr = originalStderr @@ -169,9 +174,9 @@ OutputLoop: func (r *RemoteCmd) SetExited(status int) { r.initchan() - r.Lock() + r.m.Lock() r.exitStatus = status - r.Unlock() + r.m.Unlock() close(r.exitCh) } @@ -180,8 +185,8 @@ func (r *RemoteCmd) SetExited(status int) { func (r *RemoteCmd) Wait() int { r.initchan() <-r.exitCh - r.Lock() - defer r.Unlock() + r.m.Lock() + defer r.m.Unlock() return r.exitStatus } diff --git a/packer/config_file.go b/packer/config_file.go index 173766afa..bf6c8637c 100644 --- a/packer/config_file.go +++ b/packer/config_file.go @@ -24,12 +24,10 @@ func ConfigDir() (string, error) { func homeDir() (string, error) { // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 if home := os.Getenv("HOME"); home != "" { - log.Printf("Detected home directory from env var: %s", home) return home, nil } if home := os.Getenv("APPDATA"); home != "" { - log.Printf("Detected home directory from env var: %s", home) return home, nil } diff --git a/packer/core.go b/packer/core.go index 45912f070..46e6ad8eb 100644 --- a/packer/core.go +++ b/packer/core.go @@ -54,14 +54,36 @@ type PostProcessorFunc func(name string) (PostProcessor, error) // The function type used to lookup Provisioner implementations. type ProvisionerFunc func(name string) (Provisioner, error) +type BasicStore interface { + Has(name string) bool + List() (names []string) +} + +type BuilderStore interface { + BasicStore + Start(name string) (Builder, error) +} + +type ProvisionerStore interface { + BasicStore + Start(name string) (Provisioner, error) +} + +type PostProcessorStore interface { + BasicStore + Start(name string) (PostProcessor, error) +} + // ComponentFinder is a struct that contains the various function // pointers necessary to look up components of Packer such as builders, // commands, etc. type ComponentFinder struct { - Builder BuilderFunc - Hook HookFunc - PostProcessor PostProcessorFunc - Provisioner ProvisionerFunc + Hook HookFunc + + // For HCL2 + BuilderStore BuilderStore + ProvisionerStore ProvisionerStore + PostProcessorStore PostProcessorStore } // NewCore creates a new Core. @@ -112,10 +134,10 @@ func (c *Core) BuildNames() []string { return r } -func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName string) (coreBuildProvisioner, error) { +func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName string) (CoreBuildProvisioner, error) { // Get the provisioner - cbp := coreBuildProvisioner{} - provisioner, err := c.components.Provisioner(rawP.Type) + cbp := CoreBuildProvisioner{} + provisioner, err := c.components.ProvisionerStore.Start(rawP.Type) if err != nil { return cbp, fmt.Errorf( "error initializing provisioner '%s': %s", @@ -146,9 +168,9 @@ func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName Provisioner: provisioner, } } - cbp = coreBuildProvisioner{ - pType: rawP.Type, - provisioner: provisioner, + cbp = CoreBuildProvisioner{ + PType: rawP.Type, + Provisioner: provisioner, config: config, } @@ -162,7 +184,7 @@ func (c *Core) Build(n string) (Build, error) { if !ok { return nil, fmt.Errorf("no such build found: %s", n) } - builder, err := c.components.Builder(configBuilder.Type) + builder, err := c.components.BuilderStore.Start(configBuilder.Type) if err != nil { return nil, fmt.Errorf( "error initializing builder '%s': %s", @@ -177,7 +199,7 @@ func (c *Core) Build(n string) (Build, error) { rawName := configBuilder.Name // Setup the provisioners for this build - provisioners := make([]coreBuildProvisioner, 0, len(c.Template.Provisioners)) + provisioners := make([]CoreBuildProvisioner, 0, len(c.Template.Provisioners)) for _, rawP := range c.Template.Provisioners { // If we're skipping this, then ignore it if rawP.OnlyExcept.Skip(rawName) { @@ -191,7 +213,7 @@ func (c *Core) Build(n string) (Build, error) { provisioners = append(provisioners, cbp) } - var cleanupProvisioner coreBuildProvisioner + var cleanupProvisioner CoreBuildProvisioner if c.Template.CleanupProvisioner != nil { // This is a special instantiation of the shell-local provisioner that // is only run on error at end of provisioning step before other step @@ -203,9 +225,9 @@ func (c *Core) Build(n string) (Build, error) { } // Setup the post-processors - postProcessors := make([][]coreBuildPostProcessor, 0, len(c.Template.PostProcessors)) + postProcessors := make([][]CoreBuildPostProcessor, 0, len(c.Template.PostProcessors)) for _, rawPs := range c.Template.PostProcessors { - current := make([]coreBuildPostProcessor, 0, len(rawPs)) + current := make([]CoreBuildPostProcessor, 0, len(rawPs)) for _, rawP := range rawPs { if rawP.Skip(rawName) { continue @@ -222,7 +244,7 @@ func (c *Core) Build(n string) (Build, error) { } // Get the post-processor - postProcessor, err := c.components.PostProcessor(rawP.Type) + postProcessor, err := c.components.PostProcessorStore.Start(rawP.Type) if err != nil { return nil, fmt.Errorf( "error initializing post-processor '%s': %s", @@ -233,9 +255,9 @@ func (c *Core) Build(n string) (Build, error) { "post-processor type not found: %s", rawP.Type) } - current = append(current, coreBuildPostProcessor{ - processor: postProcessor, - processorType: rawP.Type, + current = append(current, CoreBuildPostProcessor{ + PostProcessor: postProcessor, + PType: rawP.Type, config: rawP.Config, keepInputArtifact: rawP.KeepInputArtifact, }) @@ -251,16 +273,16 @@ func (c *Core) Build(n string) (Build, error) { // TODO hooks one day - return &coreBuild{ - name: n, - builder: builder, - builderConfig: configBuilder.Config, - builderType: configBuilder.Type, - postProcessors: postProcessors, - provisioners: provisioners, - cleanupProvisioner: cleanupProvisioner, - templatePath: c.Template.Path, - variables: c.variables, + return &CoreBuild{ + Type: n, + Builder: builder, + BuilderConfig: configBuilder.Config, + BuilderType: configBuilder.Type, + PostProcessors: postProcessors, + Provisioners: provisioners, + CleanupProvisioner: cleanupProvisioner, + TemplatePath: c.Template.Path, + Variables: c.variables, }, nil } diff --git a/packer/hcl2spec.go b/packer/hcl2spec.go new file mode 100644 index 000000000..bbba47ec3 --- /dev/null +++ b/packer/hcl2spec.go @@ -0,0 +1,12 @@ +package packer + +import "github.com/hashicorp/hcl/v2/hcldec" + +// a struct (or type) implementing HCL2Speccer is a type that can tell it's own +// hcl2 conf/layout. +type HCL2Speccer interface { + // ConfigSpec should return the hcl object spec used to configure the + // builder. It will be used to tell the HCL parsing library how to + // validate/configure a configuration. + ConfigSpec() hcldec.ObjectSpec +} diff --git a/packer/maps.go b/packer/maps.go new file mode 100644 index 000000000..7cad0a014 --- /dev/null +++ b/packer/maps.go @@ -0,0 +1,74 @@ +package packer + +import ( + "fmt" +) + +type MapOfProvisioner map[string]func() (Provisioner, error) + +func (mop MapOfProvisioner) Has(provisioner string) bool { + _, res := mop[provisioner] + return res +} + +func (mop MapOfProvisioner) Start(provisioner string) (Provisioner, error) { + p, found := mop[provisioner] + if !found { + return nil, fmt.Errorf("Unknown provisioner %s", provisioner) + } + return p() +} + +func (mop MapOfProvisioner) List() []string { + res := []string{} + for k := range mop { + res = append(res, k) + } + return res +} + +type MapOfPostProcessor map[string]func() (PostProcessor, error) + +func (mopp MapOfPostProcessor) Has(postProcessor string) bool { + _, res := mopp[postProcessor] + return res +} + +func (mopp MapOfPostProcessor) Start(postProcessor string) (PostProcessor, error) { + p, found := mopp[postProcessor] + if !found { + return nil, fmt.Errorf("Unknown post-processor %s", postProcessor) + } + return p() +} + +func (mopp MapOfPostProcessor) List() []string { + res := []string{} + for k := range mopp { + res = append(res, k) + } + return res +} + +type MapOfBuilder map[string]func() (Builder, error) + +func (mob MapOfBuilder) Has(builder string) bool { + _, res := mob[builder] + return res +} + +func (mob MapOfBuilder) Start(builder string) (Builder, error) { + d, found := mob[builder] + if !found { + return nil, fmt.Errorf("Unknown builder %s", builder) + } + return d() +} + +func (mob MapOfBuilder) List() []string { + res := []string{} + for k := range mob { + res = append(res, k) + } + return res +} diff --git a/packer/plugin/builder.go b/packer/plugin/builder.go index 44af7dd67..8eaa09f0b 100644 --- a/packer/plugin/builder.go +++ b/packer/plugin/builder.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -12,6 +13,15 @@ type cmdBuilder struct { client *Client } +func (b *cmdBuilder) ConfigSpec() hcldec.ObjectSpec { + defer func() { + r := recover() + b.checkExit(r, nil) + }() + + return b.builder.ConfigSpec() +} + func (b *cmdBuilder) Prepare(config ...interface{}) ([]string, error) { defer func() { r := recover() diff --git a/packer/plugin/post_processor.go b/packer/plugin/post_processor.go index a08dc586a..98a956523 100644 --- a/packer/plugin/post_processor.go +++ b/packer/plugin/post_processor.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -12,6 +13,15 @@ type cmdPostProcessor struct { client *Client } +func (b *cmdPostProcessor) ConfigSpec() hcldec.ObjectSpec { + defer func() { + r := recover() + b.checkExit(r, nil) + }() + + return b.p.ConfigSpec() +} + func (c *cmdPostProcessor) Configure(config ...interface{}) error { defer func() { r := recover() diff --git a/packer/plugin/post_processor_test.go b/packer/plugin/post_processor_test.go index ee012819c..b28340773 100644 --- a/packer/plugin/post_processor_test.go +++ b/packer/plugin/post_processor_test.go @@ -5,11 +5,14 @@ import ( "os/exec" "testing" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) type helperPostProcessor byte +func (helperPostProcessor) ConfigSpec() hcldec.ObjectSpec { return nil } + func (helperPostProcessor) Configure(...interface{}) error { return nil } diff --git a/packer/plugin/provisioner.go b/packer/plugin/provisioner.go index 9c3b8fb42..fcc47aea4 100644 --- a/packer/plugin/provisioner.go +++ b/packer/plugin/provisioner.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -12,6 +13,15 @@ type cmdProvisioner struct { client *Client } +func (p *cmdProvisioner) ConfigSpec() hcldec.ObjectSpec { + defer func() { + r := recover() + p.checkExit(r, nil) + }() + + return p.p.ConfigSpec() +} + func (c *cmdProvisioner) Prepare(configs ...interface{}) error { defer func() { r := recover() diff --git a/packer/plugin/server.go b/packer/plugin/server.go index e30de6fb0..839edc153 100644 --- a/packer/plugin/server.go +++ b/packer/plugin/server.go @@ -110,8 +110,7 @@ func serverListener_tcp() (net.Listener, error) { return nil, err } - log.Printf("Plugin minimum port: %d\n", minPort) - log.Printf("Plugin maximum port: %d\n", maxPort) + log.Printf("Plugin port range: [%d,%d]", minPort, maxPort) for port := minPort; port <= maxPort; port++ { address := fmt.Sprintf("127.0.0.1:%d", port) diff --git a/packer/post_processor.go b/packer/post_processor.go index c9b07e037..fd7df4e27 100644 --- a/packer/post_processor.go +++ b/packer/post_processor.go @@ -8,6 +8,8 @@ import "context" // the result of a build, compresses it, and returns a new artifact containing // a single file of the prior artifact compressed. type PostProcessor interface { + HCL2Speccer + // Configure is responsible for setting up configuration, storing // the state for later, and returning and errors, such as validation // errors. diff --git a/packer/post_processor_mock.go b/packer/post_processor_mock.go index d31fdec4b..b03abd4eb 100644 --- a/packer/post_processor_mock.go +++ b/packer/post_processor_mock.go @@ -1,6 +1,10 @@ package packer -import "context" +import ( + "context" + + "github.com/hashicorp/hcl/v2/hcldec" +) // MockPostProcessor is an implementation of PostProcessor that can be // used for tests. @@ -19,6 +23,8 @@ type MockPostProcessor struct { PostProcessUi Ui } +func (t *MockPostProcessor) ConfigSpec() hcldec.ObjectSpec { return t.FlatMapstructure().HCL2Spec() } + func (t *MockPostProcessor) Configure(configs ...interface{}) error { t.ConfigureCalled = true t.ConfigureConfigs = configs diff --git a/packer/provisioner.go b/packer/provisioner.go index ff89c0c42..98c91268f 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -6,11 +6,15 @@ import ( "log" "sync" "time" + + "github.com/hashicorp/hcl/v2/hcldec" ) // A provisioner is responsible for installing and configuring software // on a machine prior to building the actual image. type Provisioner interface { + HCL2Speccer + // Prepare is called with a set of configurations to setup the // internal state of the provisioner. The multiple configurations // should be merged in some sane way. @@ -71,6 +75,8 @@ type PausedProvisioner struct { Provisioner Provisioner } +func (p *PausedProvisioner) ConfigSpec() hcldec.ObjectSpec { return p.ConfigSpec() } +func (p *PausedProvisioner) FlatConfig() interface{} { return p.FlatConfig() } func (p *PausedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } @@ -98,6 +104,8 @@ type DebuggedProvisioner struct { lock sync.Mutex } +func (p *DebuggedProvisioner) ConfigSpec() hcldec.ObjectSpec { return p.ConfigSpec() } +func (p *DebuggedProvisioner) FlatConfig() interface{} { return p.FlatConfig() } func (p *DebuggedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } diff --git a/packer/provisioner_mock.go b/packer/provisioner_mock.go index 9555c5f57..2096a007d 100644 --- a/packer/provisioner_mock.go +++ b/packer/provisioner_mock.go @@ -2,6 +2,8 @@ package packer import ( "context" + + "github.com/hashicorp/hcl/v2/hcldec" ) // MockProvisioner is an implementation of Provisioner that can be @@ -16,6 +18,10 @@ type MockProvisioner struct { ProvUi Ui } +func (tp *MockProvisioner) ConfigSpec() hcldec.ObjectSpec { return tp.FlatMapstructure().HCL2Spec() } + +func (tp *MockProvisioner) FlatConfig() interface{} { return tp.FlatMapstructure() } + func (t *MockProvisioner) Prepare(configs ...interface{}) error { t.PrepCalled = true t.PrepConfigs = configs diff --git a/packer/rpc/artifact.go b/packer/rpc/artifact.go index b1166afa7..ff64920bc 100644 --- a/packer/rpc/artifact.go +++ b/packer/rpc/artifact.go @@ -1,16 +1,13 @@ package rpc import ( - "net/rpc" - "github.com/hashicorp/packer/packer" ) // An implementation of packer.Artifact where the artifact is actually // available over an RPC connection. type artifact struct { - client *rpc.Client - endpoint string + commonClient } // ArtifactServer wraps a packer.Artifact implementation and makes it diff --git a/packer/rpc/build.go b/packer/rpc/build.go index 39b31ed67..02ac0f86d 100644 --- a/packer/rpc/build.go +++ b/packer/rpc/build.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.Build where the build is actually executed // over an RPC connection. type build struct { - client *rpc.Client - mux *muxBroker + commonClient } // BuildServer wraps a packer.Build implementation and makes it exportable diff --git a/packer/rpc/builder.go b/packer/rpc/builder.go index 4b95bea3e..f6f7265a5 100644 --- a/packer/rpc/builder.go +++ b/packer/rpc/builder.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.Builder where the builder is actually executed // over an RPC connection. type builder struct { - client *rpc.Client - mux *muxBroker + commonClient } // BuilderServer wraps a packer.Builder implementation and makes it exportable @@ -21,8 +19,8 @@ type BuilderServer struct { context context.Context contextCancel func() + commonServer builder packer.Builder - mux *muxBroker } type BuilderPrepareArgs struct { @@ -35,12 +33,16 @@ type BuilderPrepareResponse struct { } func (b *builder) Prepare(config ...interface{}) ([]string, error) { + config, err := encodeCTYValues(config) + if err != nil { + return nil, err + } var resp BuilderPrepareResponse - cerr := b.client.Call("Builder.Prepare", &BuilderPrepareArgs{config}, &resp) + cerr := b.client.Call(b.endpoint+".Prepare", &BuilderPrepareArgs{config}, &resp) if cerr != nil { return nil, cerr } - var err error = nil + if resp.Error != nil { err = resp.Error } @@ -61,7 +63,7 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack select { case <-ctx.Done(): log.Printf("Cancelling builder after context cancellation %v", ctx.Err()) - if err := b.client.Call("Builder.Cancel", new(interface{}), new(interface{})); err != nil { + if err := b.client.Call(b.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling builder: %s", err) } case <-done: @@ -70,7 +72,7 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack var responseId uint32 - if err := b.client.Call("Builder.Run", nextId, &responseId); err != nil { + if err := b.client.Call(b.endpoint+".Run", nextId, &responseId); err != nil { return nil, err } @@ -87,7 +89,11 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *BuilderPrepareResponse) error { - warnings, err := b.builder.Prepare(args.Configs...) + config, err := decodeCTYValues(args.Configs) + if err != nil { + return err + } + warnings, err := b.builder.Prepare(config...) *reply = BuilderPrepareResponse{ Warnings: warnings, Error: NewBasicError(err), diff --git a/packer/rpc/client.go b/packer/rpc/client.go index 53f88d52f..e8eadfbf5 100644 --- a/packer/rpc/client.go +++ b/packer/rpc/client.go @@ -69,56 +69,79 @@ func (c *Client) Close() error { func (c *Client) Artifact() packer.Artifact { return &artifact{ - client: c.client, - endpoint: DefaultArtifactEndpoint, + commonClient: commonClient{ + endpoint: DefaultArtifactEndpoint, + client: c.client, + }, } } func (c *Client) Build() packer.Build { return &build{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultBuildEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Builder() packer.Builder { return &builder{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultBuilderEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Communicator() packer.Communicator { return &communicator{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultCommunicatorEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Hook() packer.Hook { return &hook{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultHookEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) PostProcessor() packer.PostProcessor { return &postProcessor{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultPostProcessorEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Provisioner() packer.Provisioner { return &provisioner{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultProvisionerEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Ui() packer.Ui { return &Ui{ - client: c.client, + commonClient: commonClient{ + endpoint: DefaultUiEndpoint, + client: c.client, + }, endpoint: DefaultUiEndpoint, } } diff --git a/packer/rpc/client_test.go b/packer/rpc/client_test.go index 59a9389b9..ef91e6534 100644 --- a/packer/rpc/client_test.go +++ b/packer/rpc/client_test.go @@ -40,7 +40,10 @@ func testConn(t *testing.T) (net.Conn, net.Conn) { func testClientServer(t *testing.T) (*Client, *Server) { clientConn, serverConn := testConn(t) - server, _ := NewServer(serverConn) + server, err := NewServer(serverConn) + if err != nil { + t.Fatalf("err: %v", err) + } go server.Serve() client, err := NewClient(clientConn) diff --git a/packer/rpc/common.go b/packer/rpc/common.go new file mode 100644 index 000000000..7044ebcaf --- /dev/null +++ b/packer/rpc/common.go @@ -0,0 +1,71 @@ +package rpc + +import ( + "bytes" + "encoding/gob" + "fmt" + "net/rpc" + + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// commonClient allows to rpc call funcs that can be defined on the different +// build blocks of packer +type commonClient struct { + // endpoint is usually the type of build block we are connecting to. + // + // eg: Provisioner / PostProcessor / Builder / Artifact / Communicator + endpoint string + client *rpc.Client + mux *muxBroker +} + +type commonServer struct { + mux *muxBroker + selfConfigurable interface { + ConfigSpec() hcldec.ObjectSpec + } +} + +type ConfigSpecResponse struct { + ConfigSpec []byte +} + +func (p *commonClient) ConfigSpec() hcldec.ObjectSpec { + // TODO(azr): the RPC Call can fail but the ConfigSpec signature doesn't + // return an error; should we simply panic ? Logging this for now; will + // decide later. The correct approach would probably be to return an error + // in ConfigSpec but that will break a lot of things. + resp := &ConfigSpecResponse{} + cerr := p.client.Call(p.endpoint+".ConfigSpec", new(interface{}), resp) + if cerr != nil { + err := fmt.Errorf("ConfigSpec failed: %v", cerr) + panic(err.Error()) + } + + res := hcldec.ObjectSpec{} + err := gob.NewDecoder(bytes.NewReader(resp.ConfigSpec)).Decode(&res) + if err != nil { + panic("ici:" + err.Error()) + } + return res +} + +func (s *commonServer) ConfigSpec(_ interface{}, reply *ConfigSpecResponse) error { + spec := s.selfConfigurable.ConfigSpec() + b := bytes.NewBuffer(nil) + err := gob.NewEncoder(b).Encode(spec) + reply.ConfigSpec = b.Bytes() + + return err +} + +func init() { + gob.Register(new(hcldec.AttrSpec)) + gob.Register(new(hcldec.BlockSpec)) + gob.Register(new(hcldec.BlockAttrsSpec)) + gob.Register(new(hcldec.BlockListSpec)) + gob.Register(new(hcldec.BlockObjectSpec)) + gob.Register(new(cty.Value)) +} diff --git a/packer/rpc/communicator.go b/packer/rpc/communicator.go index 814dc05ca..55da58f24 100644 --- a/packer/rpc/communicator.go +++ b/packer/rpc/communicator.go @@ -15,15 +15,14 @@ import ( // An implementation of packer.Communicator where the communicator is actually // executed over an RPC connection. type communicator struct { - client *rpc.Client - mux *muxBroker + commonClient } // CommunicatorServer wraps a packer.Communicator implementation and makes // it exportable as part of a Golang RPC server. type CommunicatorServer struct { - c packer.Communicator - mux *muxBroker + commonServer + c packer.Communicator } type CommandFinished struct { @@ -62,7 +61,12 @@ type CommunicatorDownloadDirArgs struct { } func Communicator(client *rpc.Client) *communicator { - return &communicator{client: client} + return &communicator{ + commonClient: commonClient{ + client: client, + endpoint: DefaultCommunicatorEndpoint, + }, + } } func (c *communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) (err error) { @@ -123,7 +127,7 @@ func (c *communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) (err er cmd.SetExited(finished.ExitStatus) }() - err = c.client.Call("Communicator.Start", &args, new(interface{})) + err = c.client.Call(c.endpoint+".Start", &args, new(interface{})) return } @@ -141,7 +145,7 @@ func (c *communicator) Upload(path string, r io.Reader, fi *os.FileInfo) (err er args.FileInfo = NewFileInfo(*fi) } - err = c.client.Call("Communicator.Upload", &args, new(interface{})) + err = c.client.Call(c.endpoint+".Upload", &args, new(interface{})) return } @@ -153,7 +157,7 @@ func (c *communicator) UploadDir(dst string, src string, exclude []string) error } var reply error - err := c.client.Call("Communicator.UploadDir", args, &reply) + err := c.client.Call(c.endpoint+".UploadDir", args, &reply) if err == nil { err = reply } @@ -169,7 +173,7 @@ func (c *communicator) DownloadDir(src string, dst string, exclude []string) err } var reply error - err := c.client.Call("Communicator.DownloadDir", args, &reply) + err := c.client.Call(c.endpoint+".DownloadDir", args, &reply) if err == nil { err = reply } @@ -193,7 +197,7 @@ func (c *communicator) Download(path string, w io.Writer) (err error) { } // Start sending data to the RPC server - err = c.client.Call("Communicator.Download", &args, new(interface{})) + err = c.client.Call(c.endpoint+".Download", &args, new(interface{})) // Wait for the RPC server to finish receiving the data before we return <-waitServer diff --git a/packer/rpc/cty_encode.go b/packer/rpc/cty_encode.go new file mode 100644 index 000000000..570e0ee13 --- /dev/null +++ b/packer/rpc/cty_encode.go @@ -0,0 +1,35 @@ +package rpc + +import ( + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/json" +) + +// cty.Value is does not know how to encode itself through the wire so we +// transform it to bytes. +func encodeCTYValues(config []interface{}) ([]interface{}, error) { + for i := range config { + if v, ok := config[i].(cty.Value); ok { + b, err := json.Marshal(v, cty.DynamicPseudoType) + if err != nil { + return nil, err + } + config[i] = b + } + } + return config, nil +} + +// decodeCTYValues will try to decode a cty value when it finds a byte slice +func decodeCTYValues(config []interface{}) ([]interface{}, error) { + for i := range config { + if b, ok := config[i].([]byte); ok { + t, err := json.Unmarshal(b, cty.DynamicPseudoType) + if err != nil { + return nil, err + } + config[i] = t + } + } + return config, nil +} diff --git a/packer/rpc/hook.go b/packer/rpc/hook.go index c5ac329f6..12334b1d3 100644 --- a/packer/rpc/hook.go +++ b/packer/rpc/hook.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "sync" "github.com/hashicorp/packer/packer" @@ -12,8 +11,7 @@ import ( // An implementation of packer.Hook where the hook is actually executed // over an RPC connection. type hook struct { - client *rpc.Client - mux *muxBroker + commonClient } // HookServer wraps a packer.Hook implementation and makes it exportable @@ -46,7 +44,7 @@ func (h *hook) Run(ctx context.Context, name string, ui packer.Ui, comm packer.C select { case <-ctx.Done(): log.Printf("Cancelling hook after context cancellation %v", ctx.Err()) - if err := h.client.Call("Hook.Cancel", new(interface{}), new(interface{})); err != nil { + if err := h.client.Call(h.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling builder: %s", err) } case <-done: @@ -59,7 +57,7 @@ func (h *hook) Run(ctx context.Context, name string, ui packer.Ui, comm packer.C StreamId: nextId, } - return h.client.Call("Hook.Run", &args, new(interface{})) + return h.client.Call(h.endpoint+".Run", &args, new(interface{})) } func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error { diff --git a/packer/rpc/post_processor.go b/packer/rpc/post_processor.go index 55e6363f3..4b36f2dd4 100644 --- a/packer/rpc/post_processor.go +++ b/packer/rpc/post_processor.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.PostProcessor where the PostProcessor is actually // executed over an RPC connection. type postProcessor struct { - client *rpc.Client - mux *muxBroker + commonClient } // PostProcessorServer wraps a packer.PostProcessor implementation and makes it @@ -21,8 +19,8 @@ type PostProcessorServer struct { context context.Context contextCancel func() - mux *muxBroker - p packer.PostProcessor + commonServer + p packer.PostProcessor } type PostProcessorConfigureArgs struct { @@ -36,13 +34,13 @@ type PostProcessorProcessResponse struct { StreamId uint32 } -func (p *postProcessor) Configure(raw ...interface{}) (err error) { - args := &PostProcessorConfigureArgs{Configs: raw} - if cerr := p.client.Call("PostProcessor.Configure", args, new(interface{})); cerr != nil { - err = cerr +func (p *postProcessor) Configure(raw ...interface{}) error { + raw, err := encodeCTYValues(raw) + if err != nil { + return err } - - return + args := &PostProcessorConfigureArgs{Configs: raw} + return p.client.Call(p.endpoint+".Configure", args, new(interface{})) } func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, bool, error) { @@ -59,7 +57,7 @@ func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer. select { case <-ctx.Done(): log.Printf("Cancelling post-processor after context cancellation %v", ctx.Err()) - if err := p.client.Call("PostProcessor.Cancel", new(interface{}), new(interface{})); err != nil { + if err := p.client.Call(p.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling post-processor: %s", err) } case <-done: @@ -67,7 +65,7 @@ func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer. }() var response PostProcessorProcessResponse - if err := p.client.Call("PostProcessor.PostProcess", nextId, &response); err != nil { + if err := p.client.Call(p.endpoint+".PostProcess", nextId, &response); err != nil { return nil, false, false, err } @@ -87,8 +85,12 @@ func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer. return client.Artifact(), response.Keep, response.ForceOverride, nil } -func (p *PostProcessorServer) Configure(args *PostProcessorConfigureArgs, reply *interface{}) error { - err := p.p.Configure(args.Configs...) +func (p *PostProcessorServer) Configure(args *PostProcessorConfigureArgs, reply *interface{}) (err error) { + config, err := decodeCTYValues(args.Configs) + if err != nil { + return err + } + err = p.p.Configure(config...) return err } diff --git a/packer/rpc/post_processor_test.go b/packer/rpc/post_processor_test.go index 3338915d2..e6f7235a1 100644 --- a/packer/rpc/post_processor_test.go +++ b/packer/rpc/post_processor_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -21,6 +22,8 @@ type TestPostProcessor struct { postProcessFn func(context.Context) error } +func (*TestPostProcessor) ConfigSpec() hcldec.ObjectSpec { return nil } + func (pp *TestPostProcessor) Configure(v ...interface{}) error { pp.configCalled = true pp.configVal = v @@ -81,7 +84,7 @@ func TestPostProcessorRPC(t *testing.T) { } if p.ppArtifactId != "ppTestId" { - t.Fatalf("unknown artifact: %s", p.ppArtifact.Id()) + t.Fatalf("unknown artifact: '%s'", p.ppArtifact.Id()) } if artifact.Id() != "id" { diff --git a/packer/rpc/provisioner.go b/packer/rpc/provisioner.go index faad6f434..3ef1ea6b4 100644 --- a/packer/rpc/provisioner.go +++ b/packer/rpc/provisioner.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.Provisioner where the provisioner is actually // executed over an RPC connection. type provisioner struct { - client *rpc.Client - mux *muxBroker + commonClient } // ProvisionerServer wraps a packer.Provisioner implementation and makes it @@ -21,21 +19,21 @@ type ProvisionerServer struct { context context.Context contextCancel func() - p packer.Provisioner - mux *muxBroker + commonServer + p packer.Provisioner } type ProvisionerPrepareArgs struct { Configs []interface{} } -func (p *provisioner) Prepare(configs ...interface{}) (err error) { - args := &ProvisionerPrepareArgs{configs} - if cerr := p.client.Call("Provisioner.Prepare", args, new(interface{})); cerr != nil { - err = cerr +func (p *provisioner) Prepare(configs ...interface{}) error { + configs, err := encodeCTYValues(configs) + if err != nil { + return err } - - return + args := &ProvisionerPrepareArgs{configs} + return p.client.Call(p.endpoint+".Prepare", args, new(interface{})) } func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { @@ -52,18 +50,22 @@ func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C select { case <-ctx.Done(): log.Printf("Cancelling provisioner after context cancellation %v", ctx.Err()) - if err := p.client.Call("Provisioner.Cancel", new(interface{}), new(interface{})); err != nil { + if err := p.client.Call(p.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling provisioner: %s", err) } case <-done: } }() - return p.client.Call("Provisioner.Provision", nextId, new(interface{})) + return p.client.Call(p.endpoint+".Provision", nextId, new(interface{})) } func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *interface{}) error { - return p.p.Prepare(args.Configs...) + config, err := decodeCTYValues(args.Configs) + if err != nil { + return err + } + return p.p.Prepare(config...) } func (p *ProvisionerServer) Provision(streamId uint32, reply *interface{}) error { diff --git a/packer/rpc/server.go b/packer/rpc/server.go index e0b90d261..b583e2945 100644 --- a/packer/rpc/server.go +++ b/packer/rpc/server.go @@ -76,15 +76,20 @@ func (s *Server) RegisterBuild(b packer.Build) error { func (s *Server) RegisterBuilder(b packer.Builder) error { return s.server.RegisterName(DefaultBuilderEndpoint, &BuilderServer{ + commonServer: commonServer{ + selfConfigurable: b, + mux: s.mux, + }, builder: b, - mux: s.mux, }) } func (s *Server) RegisterCommunicator(c packer.Communicator) error { return s.server.RegisterName(DefaultCommunicatorEndpoint, &CommunicatorServer{ - c: c, - mux: s.mux, + c: c, + commonServer: commonServer{ + mux: s.mux, + }, }) } @@ -97,15 +102,21 @@ func (s *Server) RegisterHook(h packer.Hook) error { func (s *Server) RegisterPostProcessor(p packer.PostProcessor) error { return s.server.RegisterName(DefaultPostProcessorEndpoint, &PostProcessorServer{ - mux: s.mux, - p: p, + commonServer: commonServer{ + selfConfigurable: p, + mux: s.mux, + }, + p: p, }) } func (s *Server) RegisterProvisioner(p packer.Provisioner) error { return s.server.RegisterName(DefaultProvisionerEndpoint, &ProvisionerServer{ - mux: s.mux, - p: p, + commonServer: commonServer{ + selfConfigurable: p, + mux: s.mux, + }, + p: p, }) } diff --git a/packer/rpc/ui.go b/packer/rpc/ui.go index d86301673..adcee28b5 100644 --- a/packer/rpc/ui.go +++ b/packer/rpc/ui.go @@ -2,7 +2,6 @@ package rpc import ( "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -10,7 +9,7 @@ import ( // An implementation of packer.Ui where the Ui is actually executed // over an RPC connection. type Ui struct { - client *rpc.Client + commonClient endpoint string } diff --git a/packer/testing.go b/packer/testing.go index 7217a5083..000c57e70 100644 --- a/packer/testing.go +++ b/packer/testing.go @@ -9,12 +9,8 @@ import ( func TestCoreConfig(t *testing.T) *CoreConfig { // Create some test components components := ComponentFinder{ - Builder: func(n string) (Builder, error) { - if n != "test" { - return nil, nil - } - - return &MockBuilder{}, nil + BuilderStore: MapOfBuilder{ + "test": func() (Builder, error) { return &MockBuilder{}, nil }, }, } @@ -46,12 +42,8 @@ func TestUi(t *testing.T) Ui { func TestBuilder(t *testing.T, c *CoreConfig, n string) *MockBuilder { var b MockBuilder - c.Components.Builder = func(actual string) (Builder, error) { - if actual != n { - return nil, nil - } - - return &b, nil + c.Components.BuilderStore = MapOfBuilder{ + n: func() (Builder, error) { return &b, nil }, } return &b @@ -62,12 +54,8 @@ func TestBuilder(t *testing.T, c *CoreConfig, n string) *MockBuilder { func TestProvisioner(t *testing.T, c *CoreConfig, n string) *MockProvisioner { var b MockProvisioner - c.Components.Provisioner = func(actual string) (Provisioner, error) { - if actual != n { - return nil, nil - } - - return &b, nil + c.Components.ProvisionerStore = MapOfProvisioner{ + n: func() (Provisioner, error) { return &b, nil }, } return &b @@ -78,12 +66,8 @@ func TestProvisioner(t *testing.T, c *CoreConfig, n string) *MockProvisioner { func TestPostProcessor(t *testing.T, c *CoreConfig, n string) *MockPostProcessor { var b MockPostProcessor - c.Components.PostProcessor = func(actual string) (PostProcessor, error) { - if actual != n { - return nil, nil - } - - return &b, nil + c.Components.PostProcessorStore = MapOfPostProcessor{ + n: func() (PostProcessor, error) { return &b, nil }, } return &b diff --git a/post-processor/alicloud-import/post-processor.go b/post-processor/alicloud-import/post-processor.go index 42882c6df..a38ad7b13 100644 --- a/post-processor/alicloud-import/post-processor.go +++ b/post-processor/alicloud-import/post-processor.go @@ -16,6 +16,7 @@ import ( "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/aliyun/alibaba-cloud-sdk-go/services/ram" "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/hashicorp/hcl/v2/hcldec" packerecs "github.com/hashicorp/packer/builder/alicloud/ecs" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -82,7 +83,8 @@ type PostProcessor struct { ramClient *ram.Client } -// Entry point for configuration parsing when we've defined +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/alicloud-import/post-processor.hcl2spec.go b/post-processor/alicloud-import/post-processor.hcl2spec.go index aeeed2435..d92036608 100644 --- a/post-processor/alicloud-import/post-processor.hcl2spec.go +++ b/post-processor/alicloud-import/post-processor.hcl2spec.go @@ -88,8 +88,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -112,10 +112,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -144,7 +147,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_ignore_data_disks": &hcldec.AttrSpec{Name: "image_ignore_data_disks", Type: cty.Bool, Required: false}, "tags": &hcldec.BlockAttrsSpec{TypeName: "tags", ElementType: cty.String, Required: false}, "system_disk_mapping": &hcldec.BlockSpec{TypeName: "system_disk_mapping", Nested: hcldec.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}, - "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}}, + "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "zone_id": &hcldec.AttrSpec{Name: "zone_id", Type: cty.String, Required: false}, "io_optimized": &hcldec.AttrSpec{Name: "io_optimized", Type: cty.Bool, Required: false}, diff --git a/post-processor/amazon-import/post-processor.go b/post-processor/amazon-import/post-processor.go index f8f055a27..f64decbe0 100644 --- a/post-processor/amazon-import/post-processor.go +++ b/post-processor/amazon-import/post-processor.go @@ -13,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -51,7 +52,8 @@ type PostProcessor struct { config Config } -// Entry point for configuration parsing when we've defined +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&p.config, &config.DecodeOpts{ diff --git a/post-processor/amazon-import/post-processor.hcl2spec.go b/post-processor/amazon-import/post-processor.hcl2spec.go index ef2134711..60ecb8f08 100644 --- a/post-processor/amazon-import/post-processor.hcl2spec.go +++ b/post-processor/amazon-import/post-processor.hcl2spec.go @@ -49,10 +49,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/artifice/post-processor.go b/post-processor/artifice/post-processor.go index fafa915e7..3ab566dea 100644 --- a/post-processor/artifice/post-processor.go +++ b/post-processor/artifice/post-processor.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -32,6 +33,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/artifice/post-processor.hcl2spec.go b/post-processor/artifice/post-processor.hcl2spec.go index c81b3eefe..a2ae49b96 100644 --- a/post-processor/artifice/post-processor.hcl2spec.go +++ b/post-processor/artifice/post-processor.hcl2spec.go @@ -23,10 +23,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/checksum/post-processor.go b/post-processor/checksum/post-processor.go index 9b3cec76f..3495a633f 100644 --- a/post-processor/checksum/post-processor.go +++ b/post-processor/checksum/post-processor.go @@ -14,6 +14,7 @@ import ( "os" "path/filepath" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -58,6 +59,8 @@ func getHash(t string) hash.Hash { return h } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/checksum/post-processor.hcl2spec.go b/post-processor/checksum/post-processor.hcl2spec.go index 1efec6c80..b136faac0 100644 --- a/post-processor/checksum/post-processor.hcl2spec.go +++ b/post-processor/checksum/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/compress/post-processor.go b/post-processor/compress/post-processor.go index 0bc89b9a3..8777cab49 100644 --- a/post-processor/compress/post-processor.go +++ b/post-processor/compress/post-processor.go @@ -14,6 +14,7 @@ import ( "runtime" "github.com/biogo/hts/bgzf" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -54,6 +55,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/compress/post-processor.hcl2spec.go b/post-processor/compress/post-processor.hcl2spec.go index 4e8db0497..e1e9e55ac 100644 --- a/post-processor/compress/post-processor.hcl2spec.go +++ b/post-processor/compress/post-processor.hcl2spec.go @@ -26,10 +26,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/digitalocean-import/post-processor.go b/post-processor/digitalocean-import/post-processor.go index 57e82f668..50f70013a 100644 --- a/post-processor/digitalocean-import/post-processor.go +++ b/post-processor/digitalocean-import/post-processor.go @@ -19,6 +19,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/digitalocean/godo" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/digitalocean" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -72,6 +73,8 @@ func (l logger) Log(args ...interface{}) { l.logger.Println(args...) } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/digitalocean-import/post-processor.hcl2spec.go b/post-processor/digitalocean-import/post-processor.hcl2spec.go index 00e764e83..fb2a4d8ac 100644 --- a/post-processor/digitalocean-import/post-processor.hcl2spec.go +++ b/post-processor/digitalocean-import/post-processor.hcl2spec.go @@ -34,10 +34,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-import/post-processor.go b/post-processor/docker-import/post-processor.go index fc0309a3c..089aed122 100644 --- a/post-processor/docker-import/post-processor.go +++ b/post-processor/docker-import/post-processor.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -30,6 +31,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-import/post-processor.hcl2spec.go b/post-processor/docker-import/post-processor.hcl2spec.go index 80a0ff36c..fc90b5adf 100644 --- a/post-processor/docker-import/post-processor.hcl2spec.go +++ b/post-processor/docker-import/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-push/post-processor.go b/post-processor/docker-push/post-processor.go index cd0b950e1..a276b0fca 100644 --- a/post-processor/docker-push/post-processor.go +++ b/post-processor/docker-push/post-processor.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -36,6 +37,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-push/post-processor.hcl2spec.go b/post-processor/docker-push/post-processor.hcl2spec.go index 096aebdda..cbd5cf726 100644 --- a/post-processor/docker-push/post-processor.hcl2spec.go +++ b/post-processor/docker-push/post-processor.hcl2spec.go @@ -30,10 +30,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-save/post-processor.go b/post-processor/docker-save/post-processor.go index 26198142c..8cf3306d7 100644 --- a/post-processor/docker-save/post-processor.go +++ b/post-processor/docker-save/post-processor.go @@ -7,6 +7,7 @@ import ( "fmt" "os" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -32,6 +33,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-save/post-processor.hcl2spec.go b/post-processor/docker-save/post-processor.hcl2spec.go index e7c71ed51..59bee1e9b 100644 --- a/post-processor/docker-save/post-processor.hcl2spec.go +++ b/post-processor/docker-save/post-processor.hcl2spec.go @@ -22,10 +22,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-tag/post-processor.go b/post-processor/docker-tag/post-processor.go index f2ba1beb3..4c1f03054 100644 --- a/post-processor/docker-tag/post-processor.go +++ b/post-processor/docker-tag/post-processor.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -32,6 +33,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-tag/post-processor.hcl2spec.go b/post-processor/docker-tag/post-processor.hcl2spec.go index 22c20a420..2ec63f2b5 100644 --- a/post-processor/docker-tag/post-processor.hcl2spec.go +++ b/post-processor/docker-tag/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/exoscale-import/post-processor.go b/post-processor/exoscale-import/post-processor.go index 3e8cc261a..ac190191e 100644 --- a/post-processor/exoscale-import/post-processor.go +++ b/post-processor/exoscale-import/post-processor.go @@ -17,6 +17,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/exoscale/egoscale" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/file" "github.com/hashicorp/packer/builder/qemu" "github.com/hashicorp/packer/common" @@ -57,6 +58,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.TemplateZone = defaultTemplateZone p.config.APIEndpoint = defaultAPIEndpoint diff --git a/post-processor/exoscale-import/post-processor.hcl2spec.go b/post-processor/exoscale-import/post-processor.hcl2spec.go index 3dbd4740d..243b54166 100644 --- a/post-processor/exoscale-import/post-processor.hcl2spec.go +++ b/post-processor/exoscale-import/post-processor.hcl2spec.go @@ -33,10 +33,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/googlecompute-export/post-processor.go b/post-processor/googlecompute-export/post-processor.go index 133e47dc8..16453742a 100644 --- a/post-processor/googlecompute-export/post-processor.go +++ b/post-processor/googlecompute-export/post-processor.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/googlecompute" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -41,6 +42,8 @@ type PostProcessor struct { runner multistep.Runner } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/googlecompute-export/post-processor.hcl2spec.go b/post-processor/googlecompute-export/post-processor.hcl2spec.go index 1fce9eab7..5df63b9eb 100644 --- a/post-processor/googlecompute-export/post-processor.hcl2spec.go +++ b/post-processor/googlecompute-export/post-processor.hcl2spec.go @@ -31,10 +31,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/googlecompute-import/post-processor.go b/post-processor/googlecompute-import/post-processor.go index fd949fef3..1eaf9f954 100644 --- a/post-processor/googlecompute-import/post-processor.go +++ b/post-processor/googlecompute-import/post-processor.go @@ -14,6 +14,7 @@ import ( "google.golang.org/api/compute/v1" "google.golang.org/api/storage/v1" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/googlecompute" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -46,6 +47,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/googlecompute-import/post-processor.hcl2spec.go b/post-processor/googlecompute-import/post-processor.hcl2spec.go index 39d10924a..cc9e3a5e6 100644 --- a/post-processor/googlecompute-import/post-processor.hcl2spec.go +++ b/post-processor/googlecompute-import/post-processor.hcl2spec.go @@ -32,10 +32,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/manifest/post-processor.go b/post-processor/manifest/post-processor.go index d10919d28..698c12b01 100644 --- a/post-processor/manifest/post-processor.go +++ b/post-processor/manifest/post-processor.go @@ -12,6 +12,7 @@ import ( "path/filepath" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -36,6 +37,8 @@ type ManifestFile struct { LastRunUUID string `json:"last_run_uuid"` } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/manifest/post-processor.hcl2spec.go b/post-processor/manifest/post-processor.hcl2spec.go index a7e831e30..08be1e1e9 100644 --- a/post-processor/manifest/post-processor.hcl2spec.go +++ b/post-processor/manifest/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/shell-local/post-processor.go b/post-processor/shell-local/post-processor.go index e23d31648..0924314ff 100644 --- a/post-processor/shell-local/post-processor.go +++ b/post-processor/shell-local/post-processor.go @@ -3,6 +3,7 @@ package shell_local import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" sl "github.com/hashicorp/packer/common/shell-local" "github.com/hashicorp/packer/packer" ) @@ -16,6 +17,8 @@ type ExecuteCommandTemplate struct { Script string } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := sl.Decode(&p.config, raws...) if err != nil { diff --git a/post-processor/ucloud-import/post-processor.go b/post-processor/ucloud-import/post-processor.go index 69dded174..a0ec219ed 100644 --- a/post-processor/ucloud-import/post-processor.go +++ b/post-processor/ucloud-import/post-processor.go @@ -1,8 +1,16 @@ +//go:generate mapstructure-to-hcl2 -type Config + package ucloudimport import ( "context" "fmt" + "log" + "net/url" + "strings" + "time" + + "github.com/hashicorp/hcl/v2/hcldec" ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" @@ -13,10 +21,6 @@ import ( "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/ucloud" ufsdk "github.com/ufilesdk-dev/ufile-gosdk" - "log" - "net/url" - "strings" - "time" ) const ( @@ -57,7 +61,8 @@ type PostProcessor struct { config Config } -// Entry point for configuration parsing when we've defined +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/ucloud-import/post-processor.hcl2spec.go b/post-processor/ucloud-import/post-processor.hcl2spec.go new file mode 100644 index 000000000..ec78de713 --- /dev/null +++ b/post-processor/ucloud-import/post-processor.hcl2spec.go @@ -0,0 +1,70 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package ucloudimport + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + PublicKey *string `mapstructure:"public_key" cty:"public_key"` + PrivateKey *string `mapstructure:"private_key" cty:"private_key"` + Region *string `mapstructure:"region" cty:"region"` + ProjectId *string `mapstructure:"project_id" cty:"project_id"` + BaseUrl *string `mapstructure:"base_url" cty:"base_url"` + UFileBucket *string `mapstructure:"ufile_bucket_name" cty:"ufile_bucket_name"` + UFileKey *string `mapstructure:"ufile_key_name" cty:"ufile_key_name"` + SkipClean *bool `mapstructure:"skip_clean" cty:"skip_clean"` + ImageName *string `mapstructure:"image_name" cty:"image_name"` + ImageDescription *string `mapstructure:"image_description" cty:"image_description"` + OSType *string `mapstructure:"image_os_type" cty:"image_os_type"` + OSName *string `mapstructure:"image_os_name" cty:"image_os_name"` + Format *string `mapstructure:"format" cty:"format"` + WaitImageReadyTimeout *int `mapstructure:"wait_image_ready_timeout" cty:"wait_image_ready_timeout"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "public_key": &hcldec.AttrSpec{Name: "public_key", Type: cty.String, Required: false}, + "private_key": &hcldec.AttrSpec{Name: "private_key", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false}, + "base_url": &hcldec.AttrSpec{Name: "base_url", Type: cty.String, Required: false}, + "ufile_bucket_name": &hcldec.AttrSpec{Name: "ufile_bucket_name", Type: cty.String, Required: false}, + "ufile_key_name": &hcldec.AttrSpec{Name: "ufile_key_name", Type: cty.String, Required: false}, + "skip_clean": &hcldec.AttrSpec{Name: "skip_clean", Type: cty.Bool, Required: false}, + "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, + "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, + "image_os_type": &hcldec.AttrSpec{Name: "image_os_type", Type: cty.String, Required: false}, + "image_os_name": &hcldec.AttrSpec{Name: "image_os_name", Type: cty.String, Required: false}, + "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, + "wait_image_ready_timeout": &hcldec.AttrSpec{Name: "wait_image_ready_timeout", Type: cty.Number, Required: false}, + } + return s +} diff --git a/post-processor/vagrant-cloud/post-processor.go b/post-processor/vagrant-cloud/post-processor.go index 9401b8374..74a72d074 100644 --- a/post-processor/vagrant-cloud/post-processor.go +++ b/post-processor/vagrant-cloud/post-processor.go @@ -18,6 +18,7 @@ import ( "os" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" @@ -63,6 +64,8 @@ type PostProcessor struct { insecureSkipTLSVerify bool } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/vagrant-cloud/post-processor.hcl2spec.go b/post-processor/vagrant-cloud/post-processor.hcl2spec.go index a8216aeff..515a06914 100644 --- a/post-processor/vagrant-cloud/post-processor.hcl2spec.go +++ b/post-processor/vagrant-cloud/post-processor.hcl2spec.go @@ -29,10 +29,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index 0a4527026..0e57829f2 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -14,6 +14,7 @@ import ( "path/filepath" "text/template" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -58,6 +59,11 @@ type PostProcessor struct { configs map[string]*Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { + panic("not implemented yet") + // return p.config.FlatMapstructure().HCL2Spec() +} + func (p *PostProcessor) Configure(raws ...interface{}) error { p.configs = make(map[string]*Config) p.configs[""] = new(Config) diff --git a/post-processor/vagrant/post-processor.hcl2spec.go b/post-processor/vagrant/post-processor.hcl2spec.go index 541c4d493..ea0b1a5af 100644 --- a/post-processor/vagrant/post-processor.hcl2spec.go +++ b/post-processor/vagrant/post-processor.hcl2spec.go @@ -27,10 +27,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index ca4b8393c..83f9c8865 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -45,6 +46,8 @@ type PostProcessor struct { url *url.URL } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/vsphere-template/post-processor.hcl2spec.go b/post-processor/vsphere-template/post-processor.hcl2spec.go index 0ec10a50b..c45780821 100644 --- a/post-processor/vsphere-template/post-processor.hcl2spec.go +++ b/post-processor/vsphere-template/post-processor.hcl2spec.go @@ -30,10 +30,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index c4da09a8e..6bfdda5b7 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -15,6 +15,7 @@ import ( "runtime" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -59,6 +60,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/vsphere/post-processor.hcl2spec.go b/post-processor/vsphere/post-processor.hcl2spec.go index ecf70776a..09373442b 100644 --- a/post-processor/vsphere/post-processor.hcl2spec.go +++ b/post-processor/vsphere/post-processor.hcl2spec.go @@ -36,10 +36,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 4575115ac..59fec5eac 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/config" @@ -76,6 +77,8 @@ type Provisioner struct { playbookFiles []string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/ansible-local/provisioner.hcl2spec.go b/provisioner/ansible-local/provisioner.hcl2spec.go index 7424c095f..9c162d6ba 100644 --- a/provisioner/ansible-local/provisioner.hcl2spec.go +++ b/provisioner/ansible-local/provisioner.hcl2spec.go @@ -36,10 +36,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 6de5bba5f..729cc61e0 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -28,6 +28,7 @@ import ( "golang.org/x/crypto/ssh" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/adapter" commonhelper "github.com/hashicorp/packer/helper/common" @@ -81,6 +82,8 @@ type PassthroughTemplate struct { WinRMPassword string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) diff --git a/provisioner/ansible/provisioner.hcl2spec.go b/provisioner/ansible/provisioner.hcl2spec.go index 1e659cece..dbee849a1 100644 --- a/provisioner/ansible/provisioner.hcl2spec.go +++ b/provisioner/ansible/provisioner.hcl2spec.go @@ -41,10 +41,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/breakpoint/provisioner.go b/provisioner/breakpoint/provisioner.go index 8ff752065..4cb138cba 100644 --- a/provisioner/breakpoint/provisioner.go +++ b/provisioner/breakpoint/provisioner.go @@ -8,6 +8,7 @@ import ( "golang.org/x/sync/errgroup" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -27,6 +28,8 @@ type Provisioner struct { config Config } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/breakpoint/provisioner.hcl2spec.go b/provisioner/breakpoint/provisioner.hcl2spec.go index d22e893ae..d56a14362 100644 --- a/provisioner/breakpoint/provisioner.hcl2spec.go +++ b/provisioner/breakpoint/provisioner.hcl2spec.go @@ -23,10 +23,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 59e433c3f..c8d54968e 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -15,6 +15,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/uuid" commonhelper "github.com/hashicorp/packer/helper/common" @@ -125,6 +126,8 @@ type KnifeTemplate struct { Args string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it diff --git a/provisioner/chef-client/provisioner.hcl2spec.go b/provisioner/chef-client/provisioner.hcl2spec.go index 59378b356..5fb6a4716 100644 --- a/provisioner/chef-client/provisioner.hcl2spec.go +++ b/provisioner/chef-client/provisioner.hcl2spec.go @@ -49,10 +49,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index 39d59b36b..cab586dc4 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -15,6 +15,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -101,6 +102,8 @@ type InstallChefTemplate struct { Version string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/chef-solo/provisioner.hcl2spec.go b/provisioner/chef-solo/provisioner.hcl2spec.go index df801aa29..86f602b64 100644 --- a/provisioner/chef-solo/provisioner.hcl2spec.go +++ b/provisioner/chef-solo/provisioner.hcl2spec.go @@ -39,10 +39,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/converge/provisioner.go b/provisioner/converge/provisioner.go index c0c5cbeb4..99033a3c5 100644 --- a/provisioner/converge/provisioner.go +++ b/provisioner/converge/provisioner.go @@ -15,6 +15,7 @@ import ( "encoding/json" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -56,7 +57,8 @@ type Provisioner struct { config Config } -// Prepare provisioner somehow. TODO: actual docs +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode( &p.config, diff --git a/provisioner/converge/provisioner.hcl2spec.go b/provisioner/converge/provisioner.hcl2spec.go index 1594f8f14..1d35a429f 100644 --- a/provisioner/converge/provisioner.hcl2spec.go +++ b/provisioner/converge/provisioner.hcl2spec.go @@ -31,10 +31,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -48,7 +51,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false}, "bootstrap_command": &hcldec.AttrSpec{Name: "bootstrap_command", Type: cty.String, Required: false}, "prevent_bootstrap_sudo": &hcldec.AttrSpec{Name: "prevent_bootstrap_sudo", Type: cty.Bool, Required: false}, - "module_dirs": &hcldec.BlockListSpec{TypeName: "module_dirs", Nested: &hcldec.BlockSpec{TypeName: "module_dirs", Nested: hcldec.ObjectSpec((*FlatModuleDir)(nil).HCL2Spec())}}, + "module_dirs": &hcldec.BlockListSpec{TypeName: "module_dirs", Nested: hcldec.ObjectSpec((*FlatModuleDir)(nil).HCL2Spec())}, "module": &hcldec.AttrSpec{Name: "module", Type: cty.String, Required: false}, "working_directory": &hcldec.AttrSpec{Name: "working_directory", Type: cty.String, Required: false}, "params": &hcldec.BlockAttrsSpec{TypeName: "params", ElementType: cty.String, Required: false}, @@ -69,10 +72,13 @@ type FlatModuleDir struct { // FlatMapstructure returns a new FlatModuleDir. // FlatModuleDir is an auto-generated flat version of ModuleDir. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ModuleDir) FlatMapstructure() interface{} { return new(FlatModuleDir) } +func (*ModuleDir) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatModuleDir) +} -// HCL2Spec returns the hcldec.Spec of a FlatModuleDir. -// This spec is used by HCL to read the fields of FlatModuleDir. +// HCL2Spec returns the hcl spec of a ModuleDir. +// This spec is used by HCL to read the fields of ModuleDir. +// The decoded values from this spec will then be applied to a FlatModuleDir. func (*FlatModuleDir) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false}, diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index 9fc73b950..e7dc1c15a 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -40,6 +41,8 @@ type Provisioner struct { config Config } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/file/provisioner.hcl2spec.go b/provisioner/file/provisioner.hcl2spec.go index 7a7c90d78..85a536403 100644 --- a/provisioner/file/provisioner.hcl2spec.go +++ b/provisioner/file/provisioner.hcl2spec.go @@ -26,10 +26,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/inspec/provisioner.go b/provisioner/inspec/provisioner.go index 0b7d047aa..099ca2e87 100644 --- a/provisioner/inspec/provisioner.go +++ b/provisioner/inspec/provisioner.go @@ -27,6 +27,7 @@ import ( "golang.org/x/crypto/ssh" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/adapter" "github.com/hashicorp/packer/helper/config" @@ -68,6 +69,8 @@ type Provisioner struct { inspecMajVersion uint } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) diff --git a/provisioner/inspec/provisioner.hcl2spec.go b/provisioner/inspec/provisioner.hcl2spec.go index cc6411db5..8afc07f0f 100644 --- a/provisioner/inspec/provisioner.hcl2spec.go +++ b/provisioner/inspec/provisioner.hcl2spec.go @@ -34,10 +34,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 5ff6cbe93..d07190179 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" @@ -97,6 +98,7 @@ func (p *Provisioner) defaultExecuteCommand() string { return fmt.Sprintf(`powershell -executionpolicy %s "%s"`, p.config.ExecutionPolicy, baseCmd) } } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know diff --git a/provisioner/powershell/provisioner.hcl2spec.go b/provisioner/powershell/provisioner.hcl2spec.go index e1c543f62..81db6dc4f 100644 --- a/provisioner/powershell/provisioner.hcl2spec.go +++ b/provisioner/powershell/provisioner.hcl2spec.go @@ -37,10 +37,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index d66e54baa..ed43bc427 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" @@ -150,6 +151,8 @@ type EnvVarsTemplate struct { WinRMPassword string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it diff --git a/provisioner/puppet-masterless/provisioner.hcl2spec.go b/provisioner/puppet-masterless/provisioner.hcl2spec.go index d0deb5920..07ce05c12 100644 --- a/provisioner/puppet-masterless/provisioner.hcl2spec.go +++ b/provisioner/puppet-masterless/provisioner.hcl2spec.go @@ -37,10 +37,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index e4fc2adff..91b0f4ca6 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" @@ -144,6 +145,8 @@ type EnvVarsTemplate struct { WinRMPassword string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it diff --git a/provisioner/puppet-server/provisioner.hcl2spec.go b/provisioner/puppet-server/provisioner.hcl2spec.go index 3c2eb2cde..5535856c9 100644 --- a/provisioner/puppet-server/provisioner.hcl2spec.go +++ b/provisioner/puppet-server/provisioner.hcl2spec.go @@ -37,10 +37,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index 5531d76b9..2fddb034f 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -13,6 +13,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -108,6 +109,8 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{ }, } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/salt-masterless/provisioner.hcl2spec.go b/provisioner/salt-masterless/provisioner.hcl2spec.go index 9d918990b..4bd24fd11 100644 --- a/provisioner/salt-masterless/provisioner.hcl2spec.go +++ b/provisioner/salt-masterless/provisioner.hcl2spec.go @@ -38,10 +38,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index f92f93c70..186644a42 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -3,6 +3,7 @@ package shell import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" sl "github.com/hashicorp/packer/common/shell-local" "github.com/hashicorp/packer/packer" ) @@ -11,6 +12,8 @@ type Provisioner struct { config sl.Config } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := sl.Decode(&p.config, raws...) if err != nil { diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index 824225aee..f3a36c11c 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -17,6 +17,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" @@ -75,6 +76,8 @@ type ExecuteCommandTemplate struct { Path string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/shell/provisioner.hcl2spec.go b/provisioner/shell/provisioner.hcl2spec.go index 9b48d3c3f..6e6d88a85 100644 --- a/provisioner/shell/provisioner.hcl2spec.go +++ b/provisioner/shell/provisioner.hcl2spec.go @@ -38,10 +38,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/sleep/provisioner.go b/provisioner/sleep/provisioner.go index 646f1c4dd..d87152149 100644 --- a/provisioner/sleep/provisioner.go +++ b/provisioner/sleep/provisioner.go @@ -6,6 +6,7 @@ import ( "context" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" ) @@ -16,6 +17,10 @@ type Provisioner struct { var _ packer.Provisioner = new(Provisioner) +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.FlatMapstructure().HCL2Spec() } + +func (p *Provisioner) FlatConfig() interface{} { return p.FlatMapstructure() } + func (p *Provisioner) Prepare(raws ...interface{}) error { return config.Decode(&p, &config.DecodeOpts{}, raws...) } diff --git a/provisioner/sleep/provisioner.hcl2spec.go b/provisioner/sleep/provisioner.hcl2spec.go index 60f3ed89d..fbdec1b02 100644 --- a/provisioner/sleep/provisioner.hcl2spec.go +++ b/provisioner/sleep/provisioner.hcl2spec.go @@ -15,10 +15,13 @@ type FlatProvisioner struct { // FlatMapstructure returns a new FlatProvisioner. // FlatProvisioner is an auto-generated flat version of Provisioner. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Provisioner) FlatMapstructure() interface{} { return new(FlatProvisioner) } +func (*Provisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatProvisioner) +} -// HCL2Spec returns the hcldec.Spec of a FlatProvisioner. -// This spec is used by HCL to read the fields of FlatProvisioner. +// HCL2Spec returns the hcl spec of a Provisioner. +// This spec is used by HCL to read the fields of Provisioner. +// The decoded values from this spec will then be applied to a FlatProvisioner. func (*FlatProvisioner) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "duration": &hcldec.AttrSpec{Name: "duration", Type: cty.String, Required: false}, diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index f2bc01d30..69f073690 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -13,6 +13,7 @@ import ( "sync" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/config" @@ -63,6 +64,8 @@ type Provisioner struct { cancelLock sync.Mutex } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/windows-restart/provisioner.hcl2spec.go b/provisioner/windows-restart/provisioner.hcl2spec.go index eda8f486e..e2de58e75 100644 --- a/provisioner/windows-restart/provisioner.hcl2spec.go +++ b/provisioner/windows-restart/provisioner.hcl2spec.go @@ -26,10 +26,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/windows-shell/provisioner.go b/provisioner/windows-shell/provisioner.go index 2f42b145d..c5a1289e0 100644 --- a/provisioner/windows-shell/provisioner.go +++ b/provisioner/windows-shell/provisioner.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" @@ -51,6 +52,8 @@ type ExecuteCommandTemplate struct { Path string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/windows-shell/provisioner.hcl2spec.go b/provisioner/windows-shell/provisioner.hcl2spec.go index ee62b6bf2..af5d7c5ec 100644 --- a/provisioner/windows-shell/provisioner.hcl2spec.go +++ b/provisioner/windows-shell/provisioner.hcl2spec.go @@ -31,10 +31,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/template/template.hcl2spec.go b/template/template.hcl2spec.go index 988b6fc79..c5b7d11a5 100644 --- a/template/template.hcl2spec.go +++ b/template/template.hcl2spec.go @@ -21,10 +21,13 @@ type FlatProvisioner struct { // FlatMapstructure returns a new FlatProvisioner. // FlatProvisioner is an auto-generated flat version of Provisioner. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Provisioner) FlatMapstructure() interface{} { return new(FlatProvisioner) } +func (*Provisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatProvisioner) +} -// HCL2Spec returns the hcldec.Spec of a FlatProvisioner. -// This spec is used by HCL to read the fields of FlatProvisioner. +// HCL2Spec returns the hcl spec of a Provisioner. +// This spec is used by HCL to read the fields of Provisioner. +// The decoded values from this spec will then be applied to a FlatProvisioner. func (*FlatProvisioner) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "only": &hcldec.AttrSpec{Name: "only", Type: cty.List(cty.String), Required: false}, diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go index e0dbf491e..0d6fae964 100644 --- a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go +++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go @@ -1,6 +1,8 @@ package convert import ( + "strings" + "github.com/zclconf/go-cty/cty" ) @@ -41,7 +43,14 @@ var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{ case "false", "0": return cty.False, nil default: - return cty.NilVal, path.NewErrorf("a bool is required") + switch strings.ToLower(val.AsString()) { + case "true": + return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"true\"") + case "false": + return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"false\"") + default: + return cty.NilVal, path.NewErrorf("a bool is required") + } } }, }, diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go index 664790b46..834e9b6fc 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go @@ -80,6 +80,7 @@ var FormatListFunc = function.New(&function.Spec{ lenChooser := -1 iterators := make([]cty.ElementIterator, len(args)) singleVals := make([]cty.Value, len(args)) + unknowns := make([]bool, len(args)) for i, arg := range args { argTy := arg.Type() switch { @@ -87,7 +88,8 @@ var FormatListFunc = function.New(&function.Spec{ if !argTy.IsTupleType() && !arg.IsKnown() { // We can't iterate this one at all yet then, so we can't // yet produce a result. - return cty.UnknownVal(retType), nil + unknowns[i] = true + continue } thisLen := arg.LengthInt() if iterLen == -1 { @@ -103,12 +105,26 @@ var FormatListFunc = function.New(&function.Spec{ ) } } + if !arg.IsKnown() { + // We allowed an unknown tuple value to fall through in + // our initial check above so that we'd be able to run + // the above error checks against it, but we still can't + // iterate it if the checks pass. + unknowns[i] = true + continue + } iterators[i] = arg.ElementIterator() default: singleVals[i] = arg } } + for _, isUnk := range unknowns { + if isUnk { + return cty.UnknownVal(retType), nil + } + } + if iterLen == 0 { // If our sequences are all empty then our result must be empty. return cty.ListValEmpty(cty.String), nil diff --git a/vendor/github.com/zclconf/go-cty/cty/gob.go b/vendor/github.com/zclconf/go-cty/cty/gob.go index a77dace27..6c972d7de 100644 --- a/vendor/github.com/zclconf/go-cty/cty/gob.go +++ b/vendor/github.com/zclconf/go-cty/cty/gob.go @@ -5,6 +5,8 @@ import ( "encoding/gob" "fmt" "math/big" + + "github.com/zclconf/go-cty/cty/set" ) // GobEncode is an implementation of the gob.GobEncoder interface, which @@ -46,11 +48,12 @@ func (val *Value) GobDecode(buf []byte) error { return fmt.Errorf("unsupported cty.Value encoding version %d; only 0 is supported", gv.Version) } - // big.Float seems to, for some reason, lose its "pointerness" when we - // round-trip it, so we'll fix that here. - if bf, ok := gv.V.(big.Float); ok { - gv.V = &bf - } + // Because big.Float.GobEncode is implemented with a pointer reciever, + // gob encoding of an interface{} containing a *big.Float value does not + // round-trip correctly, emerging instead as a non-pointer big.Float. + // The rest of cty expects all number values to be represented by + // *big.Float, so we'll fix that up here. + gv.V = gobDecodeFixNumberPtr(gv.V, gv.Ty) val.ty = gv.Ty val.v = gv.V @@ -123,3 +126,74 @@ type gobType struct { type gobCapsuleTypeImpl struct { } + +// goDecodeFixNumberPtr fixes an unfortunate quirk of round-tripping cty.Number +// values through gob: the big.Float.GobEncode method is implemented on a +// pointer receiver, and so it loses the "pointer-ness" of the value on +// encode, causing the values to emerge the other end as big.Float rather than +// *big.Float as we expect elsewhere in cty. +// +// The implementation of gobDecodeFixNumberPtr mutates the given raw value +// during its work, and may either return the same value mutated or a new +// value. Callers must no longer use whatever value they pass as "raw" after +// this function is called. +func gobDecodeFixNumberPtr(raw interface{}, ty Type) interface{} { + // Unfortunately we need to work recursively here because number values + // might be embedded in structural or collection type values. + + switch { + case ty.Equals(Number): + if bf, ok := raw.(big.Float); ok { + return &bf // wrap in pointer + } + case ty.IsMapType() && ty.ElementType().Equals(Number): + if m, ok := raw.(map[string]interface{}); ok { + for k, v := range m { + m[k] = gobDecodeFixNumberPtr(v, ty.ElementType()) + } + } + case ty.IsListType() && ty.ElementType().Equals(Number): + if s, ok := raw.([]interface{}); ok { + for i, v := range s { + s[i] = gobDecodeFixNumberPtr(v, ty.ElementType()) + } + } + case ty.IsSetType() && ty.ElementType().Equals(Number): + if s, ok := raw.(set.Set); ok { + newS := set.NewSet(s.Rules()) + for it := s.Iterator(); it.Next(); { + newV := gobDecodeFixNumberPtr(it.Value(), ty.ElementType()) + newS.Add(newV) + } + return newS + } + case ty.IsObjectType(): + if m, ok := raw.(map[string]interface{}); ok { + for k, v := range m { + aty := ty.AttributeType(k) + m[k] = gobDecodeFixNumberPtr(v, aty) + } + } + case ty.IsTupleType(): + if s, ok := raw.([]interface{}); ok { + for i, v := range s { + ety := ty.TupleElementType(i) + s[i] = gobDecodeFixNumberPtr(v, ety) + } + } + } + + return raw +} + +// gobDecodeFixNumberPtrVal is a helper wrapper around gobDecodeFixNumberPtr +// that works with already-constructed values. This is primarily for testing, +// to fix up intentionally-invalid number values for the parts of the test +// code that need them to be valid, such as calling GoString on them. +func gobDecodeFixNumberPtrVal(v Value) Value { + raw := gobDecodeFixNumberPtr(v.v, v.ty) + return Value{ + v: raw, + ty: v.ty, + } +} diff --git a/vendor/github.com/zclconf/go-cty/cty/set_internals.go b/vendor/github.com/zclconf/go-cty/cty/set_internals.go index 3fd4fb2df..f1ec98556 100644 --- a/vendor/github.com/zclconf/go-cty/cty/set_internals.go +++ b/vendor/github.com/zclconf/go-cty/cty/set_internals.go @@ -147,6 +147,17 @@ func appendSetHashBytes(val Value, buf *bytes.Buffer) { switch val.ty { case Number: + // Due to an unfortunate quirk of gob encoding for big.Float, we end up + // with non-pointer values immediately after a gob round-trip, and + // we end up in here before we've had a chance to run + // gobDecodeFixNumberPtr on the inner values of a gob-encoded set, + // and so sadly we must make a special effort to handle that situation + // here just so that we can get far enough along to fix it up for + // everything else in this package. + if bf, ok := val.v.(big.Float); ok { + buf.WriteString(bf.String()) + return + } buf.WriteString(val.v.(*big.Float).String()) return case Bool: diff --git a/vendor/modules.txt b/vendor/modules.txt index 632ccc2ba..fdb69eaed 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -618,7 +618,7 @@ github.com/yandex-cloud/go-sdk/pkg/retry github.com/yandex-cloud/go-sdk/pkg/sdkerrors github.com/yandex-cloud/go-sdk/pkg/singleflight github.com/yandex-cloud/go-sdk/sdkresolvers -# github.com/zclconf/go-cty v1.1.0 +# github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af github.com/zclconf/go-cty/cty github.com/zclconf/go-cty/cty/convert github.com/zclconf/go-cty/cty/function diff --git a/website/source/guides/hcl/from-json-v1/index.html.md.erb b/website/source/guides/hcl/from-json-v1/index.html.md.erb new file mode 100644 index 000000000..01a8e2278 --- /dev/null +++ b/website/source/guides/hcl/from-json-v1/index.html.md.erb @@ -0,0 +1,179 @@ +--- +layout: guides +page_title: Transforming Packer v1 files for Packer v1.5.0 +sidebar_current: hcl +description: |- + Learn how to manually move from a Packer v1 working JSON build file to a + working v1.5.0 HCL file. +--- + +# Transforming Packer v1 files for Packer v1.5 + +-> **Note:** Starting from version **1.5.0** Packer can read HCL2 files. + +We will soon provide a programatical a way to transpose a v1 buildfile to a v1.5 +HCL file. In the meantime we will show how to manually do it. + +The following file : + +```json +{ + "builders": [ + { + "ami_name": "packer-test", + "region": "us-east-1" + "instance_type": "t2.micro" + + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": ["amazon"], + "most_recent": true + }, + + "ssh_username": "ubuntu", + "type": "amazon-ebs" + } + ], + "provisioners": [ + { + "type": "shell", + "inline": [ + "sleep 5" + ] + } + ] +} +``` + +Becomes: + +```hcl +# the source block is what was defined in the builders section and represents a +# reusable way to start a machine. You build your images from that source. All +# sources have a 1:1 correspondance to what currently is a builder. The +# argument name (ie: ami_name) must be unquoted and can be set using the equal +# sign operator (=). +source "amazon-ebs" "example" { + ami_name = "packer-test" + region = "us-east-1" + instance_type = "t2.micro" + + source_ami_filter { + filters { + virtualization-type = "hvm" + name = "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*" + root-device-type = "ebs" + } + owners = ["amazon"] + most_recent = true + } + + communicator = "ssh" + ssh_username = "ec2-user" +} + +# A build starts sources and runs provisioning steps on those sources. +build { + sources = [ + # there can be multiple sources per build + "source.amazon-ebs.example" + ] + + # All provisioners and post-processors have a 1:1 correspondence to their + # current layout. The argument name (ie: inline) must to be unquoted + # and can be set using the equal sign operator (=). + provisioner "shell" { + inline = ["sleep 5"] + } + + # post-processors work too, example: `post-processor "shell-local" {}`. +} + +``` + +### 1:1 correspondence of components ... except : + +All fields of builders, provisioners and post-processors have a 1:1 +correspondance except for the following: + +* builders: + * aws ami_block_device_mappings + * aws launch_block_device_mappings + * aws run_volume_tags + * alicloud image_disk_mappings + * osc omi_block_device_mappings + * osc launch_block_device_mappings + * proxmox network_adapters + * proxmox disks + * tencentcloud data_disks + * ucloud image_copy_to_mappings + + +* provisioner: + * converge module_dirs + +* post-processor: + * alicloud-import image_disk_mappings + +One could think that these are defined as "arrays of blocks" - they are in fact +repeatable blocks with the same identifier. For example: + +```json +"builders": [ + { + "type": "amazon-ebs", + "launch_block_device_mappings": [ + { + "device_name": "/dev/xvda", + "volume_size": "20", + "volume_type": "gp2", + "delete_on_termination": "true" + }, + { + "device_name": "/dev/xvdf", + "volume_size": "500", + "volume_type": "gp2", + "delete_on_termination": "true", + "encrypted": true + } + ], + } +``` + +Becomes: + +```hcl +source "amazon-ebs" "example" { + launch_block_device_mappings { + device_name = "/dev/xvda" + volume_size = 20 + volume_type = "gp2" + delete_on_termination = true + } + launch_block_device_mappings { + device_name = "/dev/xvdf" + volume_size = 500 + volume_type = "gp2" + delete_on_termination = true + encrypted = true + } +``` + +There is soon going to be a PR to drop the `s` at the end of these fields. + +### Deprecation + +The current layout of buildfiles will be supported until we and the community +love the new format. Only then the v1 format will be carefully deprecated. + +-> **Note:** The HCL parsing library can read JSON and if it is your +configuration format of predilection, you will still be able to do it. You will +have to tweak a few things in order to use future versions of Packer that have +deprecated the current format. Sorry about that! Because the HCL reading code +is generated from the JSON parsing settings; every builder, provisioner and +post-processor setting should look and work the same. A config file transposer +is currently in the making. diff --git a/website/source/guides/hcl/index.html.md.erb b/website/source/guides/hcl/index.html.md.erb new file mode 100644 index 000000000..5d66496bf --- /dev/null +++ b/website/source/guides/hcl/index.html.md.erb @@ -0,0 +1,52 @@ +--- +layout: guides +sidebar_current: hcl +page_title: Getting started configuring Packer with HCL2 files +--- + +# Introduction to Packer HCL2 + +-> **Note:** Starting from version **1.5.0** Packer can read HCL2 files. + +It is not necessary to know all of the details of the HCL syntax in order to +use Packer, and so this page summarizes the most important details to get you +started. If you are interested, you can find a [full definition of HCL +syntax](https://github.com/hashicorp/hcl2/blob/master/hcl/hclsyntax/spec.md) in +the HCL native syntax specification. + +## Arguments and Blocks + +The HCL syntax is built around two key syntax constructs: arguments and blocks. + +```hcl +# block +source "amazon-ebs" "example" { + + # argument + ami_name = "abc123" +} +``` + +## Comments + +The HCL language supports three different syntaxes for comments: + +* ```#``` begins a single-line comment, ending at the end of the line. +* ```//``` also begins a single-line comment, as an alternative to ```#```. +* ```/*``` and ```*/``` are start and end delimiters for a comment that might + span over multiple lines. + +## Multi-line strings + +A multi-line string value can be provided using heredoc syntax. + +```hcl +variable "long_key" { + type = "string" + default = < <% content_for :sidebar do %>