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 %>