diff --git a/Makefile b/Makefile index a88399a81..440e5631e 100644 --- a/Makefile +++ b/Makefile @@ -121,7 +121,6 @@ generate: install-gen-deps ## Generate dynamically generated code @find post-processor common helper template builder provisioner -type f | xargs grep -l '^// Code generated' | xargs rm go generate ./... go fmt common/bootcommand/boot_command.go - go fmt command/plugin.go generate-check: generate ## Check go code generation is on par @echo "==> Checking that auto-generated code is not changed..." diff --git a/builder/alicloud/ecs/builder.hcl2spec.go b/builder/alicloud/ecs/builder.hcl2spec.go index 0fa1e5fe6..bc62b3875 100644 --- a/builder/alicloud/ecs/builder.hcl2spec.go +++ b/builder/alicloud/ecs/builder.hcl2spec.go @@ -3,6 +3,7 @@ package ecs import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -46,99 +47,100 @@ func (*FlatAlicloudDiskDevice) HCL2Spec() map[string]hcldec.Spec { // 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"` - AlicloudAccessKey *string `mapstructure:"access_key" required:"false" cty:"access_key"` - AlicloudSecretKey *string `mapstructure:"secret_key" required:"false" cty:"secret_key"` - AlicloudRegion *string `mapstructure:"region" required:"false" cty:"region"` - AlicloudSkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` - AlicloudSkipImageValidation *bool `mapstructure:"skip_image_validation" required:"false" cty:"skip_image_validation"` - AlicloudProfile *string `mapstructure:"profile" required:"false" cty:"profile"` - AlicloudSharedCredentialsFile *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file"` - SecurityToken *string `mapstructure:"security_token" required:"false" cty:"security_token"` - AlicloudImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"` - AlicloudImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version"` - AlicloudImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` - AlicloudImageShareAccounts []string `mapstructure:"image_share_account" required:"false" cty:"image_share_account"` - AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account" cty:"image_unshare_account"` - AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions"` - AlicloudImageDestinationNames []string `mapstructure:"image_copy_names" required:"false" cty:"image_copy_names"` - ImageEncrypted *bool `mapstructure:"image_encrypted" required:"false" cty:"image_encrypted"` - AlicloudImageForceDelete *bool `mapstructure:"image_force_delete" required:"false" cty:"image_force_delete"` - AlicloudImageForceDeleteSnapshots *bool `mapstructure:"image_force_delete_snapshots" required:"false" cty:"image_force_delete_snapshots"` - AlicloudImageForceDeleteInstances *bool `mapstructure:"image_force_delete_instances" cty:"image_force_delete_instances"` - AlicloudImageIgnoreDataDisks *bool `mapstructure:"image_ignore_data_disks" required:"false" cty:"image_ignore_data_disks"` - AlicloudImageTags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` - ECSSystemDiskMapping *FlatAlicloudDiskDevice `mapstructure:"system_disk_mapping" required:"false" cty:"system_disk_mapping"` - ECSImagesDiskMappings []FlatAlicloudDiskDevice `mapstructure:"image_disk_mappings" required:"false" cty:"image_disk_mappings"` - AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" cty:"associate_public_ip_address"` - ZoneId *string `mapstructure:"zone_id" required:"false" cty:"zone_id"` - IOOptimized *bool `mapstructure:"io_optimized" required:"false" cty:"io_optimized"` - InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` - Description *string `mapstructure:"description" cty:"description"` - AlicloudSourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image"` - ForceStopInstance *bool `mapstructure:"force_stop_instance" required:"false" cty:"force_stop_instance"` - DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` - SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` - SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name"` - UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` - UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` - VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` - VpcName *string `mapstructure:"vpc_name" required:"false" cty:"vpc_name"` - CidrBlock *string `mapstructure:"vpc_cidr_block" required:"false" cty:"vpc_cidr_block"` - VSwitchId *string `mapstructure:"vswitch_id" required:"false" cty:"vswitch_id"` - VSwitchName *string `mapstructure:"vswitch_name" required:"false" cty:"vswitch_name"` - InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name"` - InternetChargeType *string `mapstructure:"internet_charge_type" required:"false" cty:"internet_charge_type"` - InternetMaxBandwidthOut *int `mapstructure:"internet_max_bandwidth_out" required:"false" cty:"internet_max_bandwidth_out"` - WaitSnapshotReadyTimeout *int `mapstructure:"wait_snapshot_ready_timeout" required:"false" cty:"wait_snapshot_ready_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"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` - 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"` - SSHPrivateIp *bool `mapstructure:"ssh_private_ip" required:"false" cty:"ssh_private_ip"` + 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"` + AlicloudAccessKey *string `mapstructure:"access_key" required:"false" cty:"access_key"` + AlicloudSecretKey *string `mapstructure:"secret_key" required:"false" cty:"secret_key"` + AlicloudRegion *string `mapstructure:"region" required:"false" cty:"region"` + AlicloudSkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + AlicloudSkipImageValidation *bool `mapstructure:"skip_image_validation" required:"false" cty:"skip_image_validation"` + AlicloudProfile *string `mapstructure:"profile" required:"false" cty:"profile"` + AlicloudSharedCredentialsFile *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file"` + SecurityToken *string `mapstructure:"security_token" required:"false" cty:"security_token"` + AlicloudImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"` + AlicloudImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version"` + AlicloudImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` + AlicloudImageShareAccounts []string `mapstructure:"image_share_account" required:"false" cty:"image_share_account"` + AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account" cty:"image_unshare_account"` + AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions"` + AlicloudImageDestinationNames []string `mapstructure:"image_copy_names" required:"false" cty:"image_copy_names"` + ImageEncrypted *bool `mapstructure:"image_encrypted" required:"false" cty:"image_encrypted"` + AlicloudImageForceDelete *bool `mapstructure:"image_force_delete" required:"false" cty:"image_force_delete"` + AlicloudImageForceDeleteSnapshots *bool `mapstructure:"image_force_delete_snapshots" required:"false" cty:"image_force_delete_snapshots"` + AlicloudImageForceDeleteInstances *bool `mapstructure:"image_force_delete_instances" cty:"image_force_delete_instances"` + AlicloudImageIgnoreDataDisks *bool `mapstructure:"image_ignore_data_disks" required:"false" cty:"image_ignore_data_disks"` + AlicloudImageTags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + AlicloudImageTag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` + ECSSystemDiskMapping *FlatAlicloudDiskDevice `mapstructure:"system_disk_mapping" required:"false" cty:"system_disk_mapping"` + ECSImagesDiskMappings []FlatAlicloudDiskDevice `mapstructure:"image_disk_mappings" required:"false" cty:"image_disk_mappings"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" cty:"associate_public_ip_address"` + ZoneId *string `mapstructure:"zone_id" required:"false" cty:"zone_id"` + IOOptimized *bool `mapstructure:"io_optimized" required:"false" cty:"io_optimized"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + Description *string `mapstructure:"description" cty:"description"` + AlicloudSourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image"` + ForceStopInstance *bool `mapstructure:"force_stop_instance" required:"false" cty:"force_stop_instance"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + VpcName *string `mapstructure:"vpc_name" required:"false" cty:"vpc_name"` + CidrBlock *string `mapstructure:"vpc_cidr_block" required:"false" cty:"vpc_cidr_block"` + VSwitchId *string `mapstructure:"vswitch_id" required:"false" cty:"vswitch_id"` + VSwitchName *string `mapstructure:"vswitch_name" required:"false" cty:"vswitch_name"` + InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name"` + InternetChargeType *string `mapstructure:"internet_charge_type" required:"false" cty:"internet_charge_type"` + InternetMaxBandwidthOut *int `mapstructure:"internet_max_bandwidth_out" required:"false" cty:"internet_max_bandwidth_out"` + WaitSnapshotReadyTimeout *int `mapstructure:"wait_snapshot_ready_timeout" required:"false" cty:"wait_snapshot_ready_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"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` + SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` + 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"` + SSHPrivateIp *bool `mapstructure:"ssh_private_ip" required:"false" cty:"ssh_private_ip"` } // FlatMapstructure returns a new FlatConfig. @@ -181,6 +183,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_force_delete_instances": &hcldec.AttrSpec{Name: "image_force_delete_instances", Type: cty.Bool, Required: false}, "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}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, diff --git a/builder/alicloud/ecs/image_config.go b/builder/alicloud/ecs/image_config.go index a7e5f008c..76315e833 100644 --- a/builder/alicloud/ecs/image_config.go +++ b/builder/alicloud/ecs/image_config.go @@ -7,6 +7,7 @@ import ( "regexp" "strings" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/template/interpolate" ) @@ -189,14 +190,19 @@ type AlicloudImageConfig struct { // The region validation can be skipped // if this value is true, the default value is false. AlicloudImageSkipRegionValidation bool `mapstructure:"skip_region_validation" required:"false"` - // Tags applied to the destination - // image and relevant snapshots. - AlicloudImageTags map[string]string `mapstructure:"tags" required:"false"` + // Tags applied to the destination image and relevant snapshots. + AlicloudImageTags map[string]string `mapstructure:"tags" required:"false"` + // Same as [`tags`](#tags) but defined as a singular repeatable block + // containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + AlicloudImageTag hcl2template.NameValues `mapstructure:"tag" required:"false"` AlicloudDiskDevices `mapstructure:",squash"` } func (c *AlicloudImageConfig) Prepare(ctx *interpolate.Context) []error { var errs []error + errs = append(errs, c.AlicloudImageTag.CopyOn(&c.AlicloudImageTags)...) if c.AlicloudImageName == "" { errs = append(errs, fmt.Errorf("image_name must be specified")) } else if len(c.AlicloudImageName) < 2 || len(c.AlicloudImageName) > 128 { @@ -228,9 +234,5 @@ func (c *AlicloudImageConfig) Prepare(ctx *interpolate.Context) []error { c.AlicloudImageDestinationRegions = regions } - if len(errs) > 0 { - return errs - } - - return nil + return errs } diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index e0c1e1882..e66adc95f 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -18,6 +18,7 @@ import ( awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/chroot" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -162,7 +163,12 @@ type Config struct { // Tags to apply to the volumes that are *launched*. This is a [template // engine](/docs/templates/engine.html), see [Build template // data](#build-template-data) for more information. - RootVolumeTags awscommon.TagMap `mapstructure:"root_volume_tags" required:"false"` + RootVolumeTags map[string]string `mapstructure:"root_volume_tags" required:"false"` + // Same as [`root_volume_tags`](#root_volume_tags) but defined as a + // singular block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + RootVolumeTag hcl2template.NameValues `mapstructure:"root_volume_tag" required:"false"` // what architecture to use when registering the final AMI; valid options // are "x86_64" or "arm64". Defaults to "x86_64". Architecture string `mapstructure:"ami_architecture" required:"false"` @@ -253,6 +259,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { var errs *packer.MultiError var warns []string + errs = packer.MultiErrorAppend(errs, b.config.RootVolumeTag.CopyOn(&b.config.RootVolumeTags)...) + errs = packer.MultiErrorAppend(errs, b.config.SourceAmiFilter.Prepare()...) + errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.AccessConfig, &b.config.ctx)...) diff --git a/builder/amazon/chroot/builder.hcl2spec.go b/builder/amazon/chroot/builder.hcl2spec.go index 5565acc2c..4e3187986 100644 --- a/builder/amazon/chroot/builder.hcl2spec.go +++ b/builder/amazon/chroot/builder.hcl2spec.go @@ -4,6 +4,7 @@ package chroot import ( "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/amazon/common" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -25,7 +26,8 @@ type FlatConfig struct { AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"` AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"` AMISkipRegionValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` - AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` + AMITags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + AMITag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` 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"` @@ -34,7 +36,8 @@ type FlatConfig struct { 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"` + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotTag []hcl2template.FlatNameValue `mapstructure:"snapshot_tag" required:"false" cty:"snapshot_tag"` SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"` SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` @@ -66,7 +69,8 @@ type FlatConfig struct { RootVolumeType *string `mapstructure:"root_volume_type" required:"false" cty:"root_volume_type"` SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` - RootVolumeTags common.TagMap `mapstructure:"root_volume_tags" required:"false" cty:"root_volume_tags"` + RootVolumeTags map[string]string `mapstructure:"root_volume_tags" required:"false" cty:"root_volume_tags"` + RootVolumeTag []hcl2template.FlatNameValue `mapstructure:"root_volume_tag" required:"false" cty:"root_volume_tag"` Architecture *string `mapstructure:"ami_architecture" required:"false" cty:"ami_architecture"` } @@ -98,6 +102,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, "tags": &hcldec.BlockAttrsSpec{TypeName: "tags", ElementType: cty.String, Required: false}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -107,6 +112,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "snapshot_tags", ElementType: cty.String, Required: false}, + "snapshot_tag": &hcldec.BlockListSpec{TypeName: "snapshot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, @@ -139,6 +145,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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())}, "root_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "root_volume_tags", ElementType: cty.String, Required: false}, + "root_volume_tag": &hcldec.BlockListSpec{TypeName: "root_volume_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "ami_architecture": &hcldec.AttrSpec{Name: "ami_architecture", Type: cty.String, Required: false}, } return s diff --git a/builder/amazon/chroot/step_create_volume.go b/builder/amazon/chroot/step_create_volume.go index 72f1c270f..64758e9cd 100644 --- a/builder/amazon/chroot/step_create_volume.go +++ b/builder/amazon/chroot/step_create_volume.go @@ -23,7 +23,7 @@ type StepCreateVolume struct { volumeId string RootVolumeSize int64 RootVolumeType string - RootVolumeTags awscommon.TagMap + RootVolumeTags map[string]string Ctx interpolate.Context } @@ -33,7 +33,7 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) - volTags, err := s.RootVolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + volTags, err := awscommon.TagMap(s.RootVolumeTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error tagging volumes: %s", err) state.Put("error", err) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index 8a5b83a67..8d421e453 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -7,6 +7,7 @@ import ( "log" "regexp" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/template/interpolate" ) @@ -49,7 +50,12 @@ type AMIConfig struct { // Tags applied to the AMI. This is a // [template engine](/docs/templates/engine.html), see [Build template // data](#build-template-data) for more information. - AMITags TagMap `mapstructure:"tags" required:"false"` + AMITags map[string]string `mapstructure:"tags" required:"false"` + // Same as [`tags`](#tags) but defined as a singular repeatable block + // containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + AMITag hcl2template.NameValues `mapstructure:"tag" required:"false"` // Enable enhanced networking (ENA but not SriovNetSupport) on // HVM-compatible AMIs. If set, add `ec2:ModifyInstanceAttribute` to your // AWS IAM policy. @@ -115,7 +121,12 @@ type AMIConfig struct { // They will override AMI tags if already applied to snapshot. This is a // [template engine](../templates/engine.html), see [Build template // data](#build-template-data) for more information. - SnapshotTags TagMap `mapstructure:"snapshot_tags" required:"false"` + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false"` + // Same as [`snapshot_tags`](#snapshot_tags) but defined as a singular + // repeatable block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + SnapshotTag hcl2template.NameValues `mapstructure:"snapshot_tag" required:"false"` // A list of account IDs that have // access to create volumes from the snapshot(s). By default no additional // users other than the user creating the AMI has permissions to create @@ -140,6 +151,9 @@ func stringInSlice(s []string, searchstr string) bool { func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context) []error { var errs []error + errs = append(errs, c.SnapshotTag.CopyOn(&c.SnapshotTags)...) + errs = append(errs, c.AMITag.CopyOn(&c.AMITags)...) + if c.AMIName == "" { errs = append(errs, fmt.Errorf("ami_name must be specified")) } @@ -189,7 +203,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context } for _, kmsKey := range kmsKeys { if !validateKmsKey(kmsKey) { - errs = append(errs, fmt.Errorf("%s is not a valid KMS Key Id.", kmsKey)) + errs = append(errs, fmt.Errorf("%q is not a valid KMS Key Id.", kmsKey)) } } diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 479144f7b..5d33a006b 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -12,6 +12,7 @@ import ( "time" "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/template/interpolate" ) @@ -19,9 +20,9 @@ import ( var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$") type AmiFilterOptions struct { - Filters map[string]string - Owners []string - MostRecent bool `mapstructure:"most_recent"` + hcl2template.KVFilter `mapstructure:",squash"` + Owners []string + MostRecent bool `mapstructure:"most_recent"` } func (d *AmiFilterOptions) GetOwners() []*string { @@ -34,7 +35,7 @@ func (d *AmiFilterOptions) GetOwners() []*string { } func (d *AmiFilterOptions) Empty() bool { - return len(d.Owners) == 0 && len(d.Filters) == 0 + return len(d.Owners) == 0 && d.KVFilter.Empty() } func (d *AmiFilterOptions) NoOwner() bool { @@ -42,17 +43,13 @@ func (d *AmiFilterOptions) NoOwner() bool { } type SubnetFilterOptions struct { - Filters map[string]string - MostFree bool `mapstructure:"most_free"` - Random bool `mapstructure:"random"` -} - -func (d *SubnetFilterOptions) Empty() bool { - return len(d.Filters) == 0 + hcl2template.KVFilter `mapstructure:",squash"` + MostFree bool `mapstructure:"most_free"` + Random bool `mapstructure:"random"` } type VpcFilterOptions struct { - Filters map[string]string + hcl2template.KVFilter `mapstructure:",squash"` } type Statement struct { @@ -66,16 +63,8 @@ type PolicyDocument struct { Statement []Statement } -func (d *VpcFilterOptions) Empty() bool { - return len(d.Filters) == 0 -} - type SecurityGroupFilterOptions struct { - Filters map[string]string -} - -func (d *SecurityGroupFilterOptions) Empty() bool { - return len(d.Filters) == 0 + hcl2template.KVFilter `mapstructure:",squash"` } // RunConfig contains configuration for running an instance from a source @@ -198,6 +187,11 @@ type RunConfig struct { // EBS volumes. This is a [template engine](/docs/templates/engine.html), // see [Build template data](#build-template-data) for more information. RunTags map[string]string `mapstructure:"run_tags" required:"false"` + // Same as [`run_tags`](#run_tags) but defined as a singular repeatable + // block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + RunTag hcl2template.NameValues `mapstructure:"run_tag" required:"false"` // The ID (not the name) of the security // group to assign to the instance. By default this is not set and Packer will // automatically create a new temporary security group to allow SSH access. @@ -287,9 +281,14 @@ type RunConfig struct { // Windows, Linux/UNIX (Amazon VPC), SUSE Linux (Amazon VPC), // Windows (Amazon VPC) SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product" required:"false"` - // Requires spot_price to be - // set. This tells Packer to apply tags to the spot request that is issued. + // Requires spot_price to be set. This tells Packer to apply tags to the + // spot request that is issued. SpotTags map[string]string `mapstructure:"spot_tags" required:"false"` + // Same as [`spot_tags`](#spot_tags) but defined as a singular repeatable block + // containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + SpotTag hcl2template.NameValues `mapstructure:"spot_tag" required:"false"` // Filters used to populate the `subnet_id` field. // Example: // @@ -423,6 +422,19 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) + // Copy singular tag maps + errs = append(errs, c.RunTag.CopyOn(&c.RunTags)...) + errs = append(errs, c.SpotTag.CopyOn(&c.SpotTags)...) + + for _, preparer := range []interface{ Prepare() []error }{ + &c.SourceAmiFilter, + &c.SecurityGroupFilter, + &c.SubnetFilter, + &c.VpcFilter, + } { + errs = append(errs, preparer.Prepare()...) + } + // Validating ssh_interface if c.SSHInterface != "public_ip" && c.SSHInterface != "private_ip" && diff --git a/builder/amazon/common/run_config.hcl2spec.go b/builder/amazon/common/run_config.hcl2spec.go index d27b01148..9e0f8bf3c 100644 --- a/builder/amazon/common/run_config.hcl2spec.go +++ b/builder/amazon/common/run_config.hcl2spec.go @@ -3,15 +3,17 @@ package common import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) // FlatAmiFilterOptions is an auto-generated flat version of AmiFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatAmiFilterOptions struct { - Filters map[string]string `cty:"filters"` - Owners []string `cty:"owners"` - MostRecent *bool `mapstructure:"most_recent" cty:"most_recent"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` + Owners []string `cty:"owners"` + MostRecent *bool `mapstructure:"most_recent" cty:"most_recent"` } // FlatMapstructure returns a new FlatAmiFilterOptions. @@ -27,6 +29,7 @@ func (*AmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcl func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "owners": &hcldec.AttrSpec{Name: "owners", Type: cty.List(cty.String), Required: false}, "most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false}, } @@ -61,7 +64,8 @@ func (*FlatPolicyDocument) HCL2Spec() map[string]hcldec.Spec { // FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatSecurityGroupFilterOptions struct { - Filters map[string]string `cty:"filters"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` } // FlatMapstructure returns a new FlatSecurityGroupFilterOptions. @@ -77,6 +81,7 @@ func (*SecurityGroupFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[ func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, } return s } @@ -111,9 +116,10 @@ func (*FlatStatement) HCL2Spec() map[string]hcldec.Spec { // FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatSubnetFilterOptions struct { - Filters map[string]string `cty:"filters"` - MostFree *bool `mapstructure:"most_free" cty:"most_free"` - Random *bool `mapstructure:"random" cty:"random"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` + MostFree *bool `mapstructure:"most_free" cty:"most_free"` + Random *bool `mapstructure:"random" cty:"random"` } // FlatMapstructure returns a new FlatSubnetFilterOptions. @@ -129,6 +135,7 @@ func (*SubnetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string] func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "most_free": &hcldec.AttrSpec{Name: "most_free", Type: cty.Bool, Required: false}, "random": &hcldec.AttrSpec{Name: "random", Type: cty.Bool, Required: false}, } @@ -138,7 +145,8 @@ func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { // FlatVpcFilterOptions is an auto-generated flat version of VpcFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatVpcFilterOptions struct { - Filters map[string]string `cty:"filters"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` } // FlatMapstructure returns a new FlatVpcFilterOptions. @@ -154,6 +162,7 @@ func (*VpcFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcl func (*FlatVpcFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, } return s } diff --git a/builder/amazon/common/run_config_test.go b/builder/amazon/common/run_config_test.go index c01b31ebb..ddd208db6 100644 --- a/builder/amazon/common/run_config_test.go +++ b/builder/amazon/common/run_config_test.go @@ -6,6 +6,7 @@ import ( "regexp" "testing" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" ) @@ -73,7 +74,7 @@ func TestRunConfigPrepare_SourceAmiFilterOwnersBlank(t *testing.T) { c := testConfigFilter() filter_key := "name" filter_value := "foo" - c.SourceAmiFilter = AmiFilterOptions{Filters: map[string]string{filter_key: filter_value}} + c.SourceAmiFilter.Filters = map[string]string{filter_key: filter_value} if err := c.Prepare(nil); len(err) != 1 { t.Fatalf("Should error if Owners is not specified)") } @@ -84,7 +85,12 @@ func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) { owner := "123" filter_key := "name" filter_value := "foo" - goodFilter := AmiFilterOptions{Owners: []string{owner}, Filters: map[string]string{filter_key: filter_value}} + goodFilter := AmiFilterOptions{ + Owners: []string{owner}, + KVFilter: hcl2template.KVFilter{ + Filters: map[string]string{filter_key: filter_value}, + }, + } c.SourceAmiFilter = goodFilter if err := c.Prepare(nil); len(err) != 0 { t.Fatalf("err: %s", err) diff --git a/builder/amazon/common/step_create_tags.go b/builder/amazon/common/step_create_tags.go index 0e785a202..1a6257c18 100644 --- a/builder/amazon/common/step_create_tags.go +++ b/builder/amazon/common/step_create_tags.go @@ -15,8 +15,8 @@ import ( ) type StepCreateTags struct { - Tags TagMap - SnapshotTags TagMap + Tags map[string]string + SnapshotTags map[string]string Ctx interpolate.Context } @@ -26,7 +26,7 @@ func (s *StepCreateTags) Run(ctx context.Context, state multistep.StateBag) mult ui := state.Get("ui").(packer.Ui) amis := state.Get("amis").(map[string]string) - if !s.Tags.IsSet() && !s.SnapshotTags.IsSet() { + if len(s.Tags) == 0 && len(s.SnapshotTags) == 0 { return multistep.ActionContinue } @@ -72,7 +72,7 @@ func (s *StepCreateTags) Run(ctx context.Context, state multistep.StateBag) mult // Convert tags to ec2.Tag format ui.Say("Creating AMI tags") - amiTags, err := s.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + amiTags, err := TagMap(s.Tags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { state.Put("error", err) ui.Error(err.Error()) @@ -81,7 +81,7 @@ func (s *StepCreateTags) Run(ctx context.Context, state multistep.StateBag) mult amiTags.Report(ui) ui.Say("Creating snapshot tags") - snapshotTags, err := s.SnapshotTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + snapshotTags, err := TagMap(s.SnapshotTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 56d43e9d6..7b8610c06 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -31,10 +31,10 @@ type StepRunSourceInstance struct { InstanceType string IsRestricted bool SourceAMI string - Tags TagMap + Tags map[string]string UserData string UserDataFile string - VolumeTags TagMap + VolumeTags map[string]string NoEphemeral bool instanceId string @@ -88,7 +88,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa s.Tags["Name"] = "Packer Builder" } - ec2Tags, err := s.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error tagging source instance: %s", err) state.Put("error", err) @@ -96,7 +96,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa return multistep.ActionHalt } - volTags, err := s.VolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + volTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error tagging volumes: %s", err) state.Put("error", err) @@ -322,10 +322,10 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa } } - if len(volumeIds) > 0 && s.VolumeTags.IsSet() { + if len(volumeIds) > 0 && len(s.VolumeTags) > 0 { ui.Say("Adding tags to source EBS Volumes") - volumeTags, err := s.VolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + volumeTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err) state.Put("error", err) diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index dd2297e97..a50da505f 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -34,10 +34,10 @@ type StepRunSpotInstance struct { InstanceType string SourceAMI string SpotPrice string - SpotTags TagMap + SpotTags map[string]string SpotInstanceTypes []string - Tags TagMap - VolumeTags TagMap + Tags map[string]string + VolumeTags map[string]string UserData string UserDataFile string Ctx interpolate.Context @@ -194,7 +194,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } // Convert tags from the tag map provided by the user into *ec2.Tag s - ec2Tags, err := s.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error generating tags for source instance: %s", err) state.Put("error", err) @@ -336,7 +336,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) instance := describeOutput.Reservations[0].Instances[0] // Tag the spot instance request (not the eventual spot instance) - spotTags, err := s.SpotTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + spotTags, err := TagMap(s.SpotTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error generating tags for spot request: %s", err) state.Put("error", err) @@ -344,7 +344,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - if len(spotTags) > 0 && s.SpotTags.IsSet() { + if len(spotTags) > 0 && len(s.SpotTags) > 0 { spotTags.Report(ui) // Use the instance ID to find out the SIR, so that we can tag the spot // request associated with this instance. @@ -400,10 +400,10 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } } - if len(volumeIds) > 0 && s.VolumeTags.IsSet() { + if len(volumeIds) > 0 && len(s.VolumeTags) > 0 { ui.Say("Adding tags to source EBS Volumes") - volumeTags, err := s.VolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + volumeTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err) state.Put("error", err) diff --git a/builder/amazon/common/step_run_spot_instance_test.go b/builder/amazon/common/step_run_spot_instance_test.go index 3122ef9d9..9d4fa8a0d 100644 --- a/builder/amazon/common/step_run_spot_instance_test.go +++ b/builder/amazon/common/step_run_spot_instance_test.go @@ -44,9 +44,9 @@ func getBasicStep() *StepRunSpotInstance { InstanceType: "t2.micro", SourceAMI: "", SpotPrice: "auto", - SpotTags: TagMap(nil), - Tags: TagMap{}, - VolumeTags: TagMap(nil), + SpotTags: nil, + Tags: map[string]string{}, + VolumeTags: nil, UserData: "", UserDataFile: "", } diff --git a/builder/amazon/common/tags.go b/builder/amazon/common/tags.go index 6a171d759..5b0a4d276 100644 --- a/builder/amazon/common/tags.go +++ b/builder/amazon/common/tags.go @@ -21,10 +21,6 @@ func (t EC2Tags) Report(ui packer.Ui) { } } -func (t TagMap) IsSet() bool { - return len(t) > 0 -} - func (t TagMap) EC2Tags(ictx interpolate.Context, region string, state multistep.StateBag) (EC2Tags, error) { var ec2Tags []*ec2.Tag generatedData := builder.GeneratedData{State: state} diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 6d3e7f89a..7ae72b906 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -211,7 +211,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack AMISkipBuildRegion: b.config.AMISkipBuildRegion, VpcId: b.config.VpcId, SubnetId: b.config.SubnetId, - HasSubnetFilter: len(b.config.SubnetFilter.Filters) > 0, + HasSubnetFilter: !b.config.SubnetFilter.Empty(), }, &awscommon.StepSourceAMIInfo{ SourceAmi: b.config.SourceAmi, diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index c78b9b290..ce76fdd2d 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -4,6 +4,7 @@ package ebs import ( "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/amazon/common" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -37,7 +38,8 @@ type FlatConfig struct { 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"` + AMITags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + AMITag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` 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"` @@ -46,7 +48,8 @@ type FlatConfig struct { 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"` + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotTag []hcl2template.FlatNameValue `mapstructure:"snapshot_tag" required:"false" cty:"snapshot_tag"` 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"` @@ -62,6 +65,7 @@ type FlatConfig struct { 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"` + RunTag []hcl2template.FlatNameValue `mapstructure:"run_tag" required:"false" cty:"run_tag"` 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"` @@ -70,6 +74,7 @@ type FlatConfig struct { 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"` + SpotTag []hcl2template.FlatNameValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag"` 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"` @@ -166,6 +171,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "tags", ElementType: cty.String, Required: false}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -175,6 +181,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "snapshot_tags", ElementType: cty.String, Required: false}, + "snapshot_tag": &hcldec.BlockListSpec{TypeName: "snapshot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -190,6 +197,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "run_tag": &hcldec.BlockListSpec{TypeName: "run_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -198,6 +206,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 1a94b7c28..e0373a172 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -234,7 +234,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack ForceDeregister: b.config.AMIForceDeregister, VpcId: b.config.VpcId, SubnetId: b.config.SubnetId, - HasSubnetFilter: len(b.config.SubnetFilter.Filters) > 0, + HasSubnetFilter: !b.config.SubnetFilter.Empty(), }, &awscommon.StepSourceAMIInfo{ SourceAmi: b.config.SourceAmi, diff --git a/builder/amazon/ebssurrogate/builder.hcl2spec.go b/builder/amazon/ebssurrogate/builder.hcl2spec.go index 93817d55b..14393e83e 100644 --- a/builder/amazon/ebssurrogate/builder.hcl2spec.go +++ b/builder/amazon/ebssurrogate/builder.hcl2spec.go @@ -4,6 +4,7 @@ package ebssurrogate import ( "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/amazon/common" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -86,6 +87,7 @@ type FlatConfig struct { 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"` + RunTag []hcl2template.FlatNameValue `mapstructure:"run_tag" required:"false" cty:"run_tag"` 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"` @@ -94,6 +96,7 @@ type FlatConfig struct { 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"` + SpotTag []hcl2template.FlatNameValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag"` 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"` @@ -151,7 +154,8 @@ type FlatConfig struct { 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"` + AMITags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + AMITag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` 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"` @@ -160,7 +164,8 @@ type FlatConfig struct { 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"` + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotTag []hcl2template.FlatNameValue `mapstructure:"snapshot_tag" required:"false" cty:"snapshot_tag"` 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"` @@ -215,6 +220,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "run_tag": &hcldec.BlockListSpec{TypeName: "run_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -223,6 +229,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -281,6 +288,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "tags", ElementType: cty.String, Required: false}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -290,6 +298,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "snapshot_tags", ElementType: cty.String, Required: false}, + "snapshot_tag": &hcldec.BlockListSpec{TypeName: "snapshot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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())}, diff --git a/builder/amazon/ebsvolume/block_device.go b/builder/amazon/ebsvolume/block_device.go index 37c0dd28d..0b0c7f74e 100644 --- a/builder/amazon/ebsvolume/block_device.go +++ b/builder/amazon/ebsvolume/block_device.go @@ -5,6 +5,7 @@ package ebsvolume import ( "github.com/aws/aws-sdk-go/service/ec2" awscommon "github.com/hashicorp/packer/builder/amazon/common" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/template/interpolate" ) @@ -13,7 +14,12 @@ type BlockDevice struct { // Tags to apply to the volume. These are retained after the builder // completes. This is a [template engine](/docs/templates/engine.html), see // [Build template data](#build-template-data) for more information. - Tags awscommon.TagMap `mapstructure:"tags" required:"false"` + Tags map[string]string `mapstructure:"tags" required:"false"` + // Same as [`tags`](#tags) but defined as a singular repeatable block + // containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + Tag hcl2template.NameValues `mapstructure:"tag" required:"false"` } type BlockDevices []BlockDevice @@ -28,10 +34,15 @@ func (bds BlockDevices) BuildEC2BlockDeviceMappings() []*ec2.BlockDeviceMapping } func (bds BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) { + for _, block := range bds { + + errs = append(errs, block.Tag.CopyOn(&block.Tags)...) + if err := block.Prepare(ctx); err != nil { errs = append(errs, err) } + } return errs } diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index d7bf8c590..f9af503ab 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" @@ -64,7 +65,13 @@ type Config struct { // created. Packer will replace all tags on the volume with the tags // configured in the `ebs_volumes` section as soon as the instance is // reported as 'ready'. - VolumeRunTags awscommon.TagMap `mapstructure:"run_volume_tags"` + VolumeRunTags map[string]string `mapstructure:"run_volume_tags"` + // Same as [`run_volume_tags`](#run_volume_tags) but defined as a singular + // repeatable block containing a `name` and a `value` field. In HCL2 mode + // the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + VolumeRunTag hcl2template.NameValues `mapstructure:"run_volume_tag"` launchBlockDevices BlockDevices @@ -102,6 +109,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { // Accumulate any errors var errs *packer.MultiError var warns []string + errs = packer.MultiErrorAppend(errs, b.config.VolumeRunTag.CopyOn(&b.config.VolumeRunTags)...) errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.launchBlockDevices.Prepare(&b.config.ctx)...) diff --git a/builder/amazon/ebsvolume/builder.hcl2spec.go b/builder/amazon/ebsvolume/builder.hcl2spec.go index 44804fd87..4b385186e 100644 --- a/builder/amazon/ebsvolume/builder.hcl2spec.go +++ b/builder/amazon/ebsvolume/builder.hcl2spec.go @@ -4,23 +4,25 @@ package ebsvolume import ( "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/amazon/common" + "github.com/hashicorp/packer/hcl2template" "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"` + 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 map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + Tag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` } // FlatMapstructure returns a new FlatBlockDevice. @@ -46,6 +48,7 @@ func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { "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: "tags", ElementType: cty.String, Required: false}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, } return s } @@ -86,6 +89,7 @@ type FlatConfig struct { 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"` + RunTag []hcl2template.FlatNameValue `mapstructure:"run_tag" required:"false" cty:"run_tag"` 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"` @@ -94,6 +98,7 @@ type FlatConfig struct { 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"` + SpotTag []hcl2template.FlatNameValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag"` 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"` @@ -147,7 +152,8 @@ type FlatConfig struct { 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"` + VolumeRunTags map[string]string `mapstructure:"run_volume_tags" cty:"run_volume_tags"` + VolumeRunTag []hcl2template.FlatNameValue `mapstructure:"run_volume_tag" cty:"run_volume_tag"` } // FlatMapstructure returns a new FlatConfig. @@ -195,6 +201,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "run_tag": &hcldec.BlockListSpec{TypeName: "run_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -203,6 +210,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -257,6 +265,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "run_volume_tags", ElementType: cty.String, Required: false}, + "run_volume_tag": &hcldec.BlockListSpec{TypeName: "run_volume_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, } return s } diff --git a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go index d98848d70..5d8bea217 100644 --- a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go +++ b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" @@ -43,11 +44,11 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m // volume will have had these tags applied when the instance was // created. We now need to remove these tags to ensure only the EBS // volume tags are applied (if any) - if config.VolumeRunTags.IsSet() { + if len(config.VolumeRunTags) > 0 { ui.Say("Removing any tags applied to EBS volumes when the source instance was created...") ui.Message("Compiling list of existing tags to remove...") - existingTags, err := config.VolumeRunTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + existingTags, err := awscommon.TagMap(config.VolumeRunTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error generating list of tags to remove: %s", err) state.Put("error", err) @@ -91,7 +92,7 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m } ui.Message(fmt.Sprintf("Compiling list of tags to apply to volume on %s...", mapping.DeviceName)) - tags, err := mapping.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state) + tags, err := awscommon.TagMap(mapping.Tags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state) if err != nil { err := fmt.Errorf("Error generating tags for device %s: %s", mapping.DeviceName, err) state.Put("error", err) diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 763960150..cad338300 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -296,7 +296,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack ForceDeregister: b.config.AMIForceDeregister, VpcId: b.config.VpcId, SubnetId: b.config.SubnetId, - HasSubnetFilter: len(b.config.SubnetFilter.Filters) > 0, + HasSubnetFilter: !b.config.SubnetFilter.Empty(), }, &awscommon.StepSourceAMIInfo{ SourceAmi: b.config.SourceAmi, diff --git a/builder/amazon/instance/builder.hcl2spec.go b/builder/amazon/instance/builder.hcl2spec.go index ffa9d2077..4c84e8949 100644 --- a/builder/amazon/instance/builder.hcl2spec.go +++ b/builder/amazon/instance/builder.hcl2spec.go @@ -4,6 +4,7 @@ package instance import ( "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/amazon/common" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -37,7 +38,8 @@ type FlatConfig struct { 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"` + AMITags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + AMITag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` 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"` @@ -46,7 +48,8 @@ type FlatConfig struct { 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"` + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotTag []hcl2template.FlatNameValue `mapstructure:"snapshot_tag" required:"false" cty:"snapshot_tag"` 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"` @@ -62,6 +65,7 @@ type FlatConfig struct { 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"` + RunTag []hcl2template.FlatNameValue `mapstructure:"run_tag" required:"false" cty:"run_tag"` 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"` @@ -70,6 +74,7 @@ type FlatConfig struct { 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"` + SpotTag []hcl2template.FlatNameValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag"` 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"` @@ -173,6 +178,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "tags", ElementType: cty.String, Required: false}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -182,6 +188,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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: "snapshot_tags", ElementType: cty.String, Required: false}, + "snapshot_tag": &hcldec.BlockListSpec{TypeName: "snapshot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -197,6 +204,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "run_tag": &hcldec.BlockListSpec{TypeName: "run_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, @@ -205,6 +213,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index ba7a26f5d..d87ff3426 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/pkcs12" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -240,6 +241,11 @@ type Config struct { // 256 characters. Tags are applied to every resource deployed by a Packer // build, i.e. Resource Group, VM, NIC, VNET, Public IP, KeyVault, etc. AzureTags map[string]*string `mapstructure:"azure_tags" required:"false"` + // Same as [`azure_tags`](#azure_tags) but defined as a singular repeatable block + // containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + AzureTag hcl2template.NameValues `mapstructure:"azure_tag" required:"false"` // Resource group under which the final artifact will be stored. ResourceGroupName string `mapstructure:"resource_group_name"` // Storage account under which the final artifact will be stored. @@ -523,6 +529,13 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { provideDefaultValues(c) setRuntimeValues(c) setUserNamePassword(c) + + // copy singular blocks + for _, kv := range c.AzureTag { + v := kv.Value + c.AzureTags[kv.Name] = &v + } + err = c.ClientConfig.SetDefaultValues() if err != nil { return nil, err diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index 1babfcaa8..def5180b7 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -3,6 +3,7 @@ package arm import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -48,6 +49,7 @@ type FlatConfig struct { ManagedImageDataDiskSnapshotPrefix *string `mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" cty:"managed_image_data_disk_snapshot_prefix"` ManagedImageZoneResilient *bool `mapstructure:"managed_image_zone_resilient" required:"false" cty:"managed_image_zone_resilient"` AzureTags map[string]*string `mapstructure:"azure_tags" required:"false" cty:"azure_tags"` + AzureTag []hcl2template.FlatNameValue `mapstructure:"azure_tag" required:"false" cty:"azure_tag"` ResourceGroupName *string `mapstructure:"resource_group_name" cty:"resource_group_name"` StorageAccount *string `mapstructure:"storage_account" cty:"storage_account"` TempComputeName *string `mapstructure:"temp_compute_name" required:"false" cty:"temp_compute_name"` @@ -162,6 +164,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "managed_image_data_disk_snapshot_prefix": &hcldec.AttrSpec{Name: "managed_image_data_disk_snapshot_prefix", Type: cty.String, Required: false}, "managed_image_zone_resilient": &hcldec.AttrSpec{Name: "managed_image_zone_resilient", Type: cty.Bool, Required: false}, "azure_tags": &hcldec.BlockAttrsSpec{TypeName: "azure_tags", ElementType: cty.String, Required: false}, + "azure_tag": &hcldec.BlockListSpec{TypeName: "azure_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "resource_group_name": &hcldec.AttrSpec{Name: "resource_group_name", Type: cty.String, Required: false}, "storage_account": &hcldec.AttrSpec{Name: "storage_account", Type: cty.String, Required: false}, "temp_compute_name": &hcldec.AttrSpec{Name: "temp_compute_name", Type: cty.String, Required: false}, diff --git a/builder/hyperone/config.go b/builder/hyperone/config.go index 9c6a4144f..a6daedf97 100644 --- a/builder/hyperone/config.go +++ b/builder/hyperone/config.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/json" "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" @@ -60,9 +61,13 @@ type Config struct { ImageName string `mapstructure:"image_name" required:"false"` // The description of the resulting image. ImageDescription string `mapstructure:"image_description" required:"false"` - // Key/value pair tags to - // add to the created image. + // Key/value pair tags to add to the created image. ImageTags map[string]string `mapstructure:"image_tags" required:"false"` + // Same as [`image_tags`](#image_tags) but defined as a singular repeatable + // block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + ImageTag hcl2template.NameValues `mapstructure:"image_tag" required:"false"` // The service of the resulting image. ImageService string `mapstructure:"image_service" required:"false"` // ID or name of the type this server should be created with. @@ -72,6 +77,11 @@ type Config struct { // Key/value pair tags to // add to the created server. VmTags map[string]string `mapstructure:"vm_tags" required:"false"` + // Same as [`vm_tags`](#vm_tags) but defined as a singular repeatable block + // containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + VmTag hcl2template.NameValues `mapstructure:"vm_tag" required:"false"` // The name of the created disk. DiskName string `mapstructure:"disk_name" required:"false"` // The type of the created disk. Defaults to ssd. @@ -257,6 +267,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { // Validation var errs *packer.MultiError + errs = packer.MultiErrorAppend(errs, c.ImageTag.CopyOn(&c.ImageTags)...) + errs = packer.MultiErrorAppend(errs, c.VmTag.CopyOn(&c.VmTags)...) + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } diff --git a/builder/hyperone/config.hcl2spec.go b/builder/hyperone/config.hcl2spec.go index ed69c7f0f..c63b79b55 100644 --- a/builder/hyperone/config.hcl2spec.go +++ b/builder/hyperone/config.hcl2spec.go @@ -3,93 +3,96 @@ package hyperone import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "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"` - 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"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` - 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"` - APIURL *string `mapstructure:"api_url" required:"false" cty:"api_url"` - Token *string `mapstructure:"token" required:"true" cty:"token"` - Project *string `mapstructure:"project" required:"true" cty:"project"` - TokenLogin *string `mapstructure:"token_login" required:"false" cty:"token_login"` - StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout"` - SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image"` - ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name"` - ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` - ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags"` - ImageService *string `mapstructure:"image_service" required:"false" cty:"image_service"` - VmType *string `mapstructure:"vm_type" required:"true" cty:"vm_type"` - VmName *string `mapstructure:"vm_name" required:"false" cty:"vm_name"` - VmTags map[string]string `mapstructure:"vm_tags" required:"false" cty:"vm_tags"` - DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name"` - DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type"` - DiskSize *float32 `mapstructure:"disk_size" required:"true" cty:"disk_size"` - Network *string `mapstructure:"network" required:"false" cty:"network"` - PrivateIP *string `mapstructure:"private_ip" required:"false" cty:"private_ip"` - PublicIP *string `mapstructure:"public_ip" required:"false" cty:"public_ip"` - PublicNetAdpService *string `mapstructure:"public_netadp_service" required:"false" cty:"public_netadp_service"` - ChrootDisk *bool `mapstructure:"chroot_disk" cty:"chroot_disk"` - ChrootDiskSize *float32 `mapstructure:"chroot_disk_size" cty:"chroot_disk_size"` - ChrootDiskType *string `mapstructure:"chroot_disk_type" cty:"chroot_disk_type"` - ChrootMountPath *string `mapstructure:"chroot_mount_path" cty:"chroot_mount_path"` - ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts"` - ChrootCopyFiles []string `mapstructure:"chroot_copy_files" cty:"chroot_copy_files"` - ChrootCommandWrapper *string `mapstructure:"chroot_command_wrapper" cty:"chroot_command_wrapper"` - MountOptions []string `mapstructure:"mount_options" cty:"mount_options"` - MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition"` - PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands"` - PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands"` - SSHKeys []string `mapstructure:"ssh_keys" required:"false" cty:"ssh_keys"` - UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + 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"` + 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"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` + SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` + 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"` + APIURL *string `mapstructure:"api_url" required:"false" cty:"api_url"` + Token *string `mapstructure:"token" required:"true" cty:"token"` + Project *string `mapstructure:"project" required:"true" cty:"project"` + TokenLogin *string `mapstructure:"token_login" required:"false" cty:"token_login"` + StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout"` + SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image"` + ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name"` + ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` + ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags"` + ImageTag []hcl2template.FlatNameValue `mapstructure:"image_tag" required:"false" cty:"image_tag"` + ImageService *string `mapstructure:"image_service" required:"false" cty:"image_service"` + VmType *string `mapstructure:"vm_type" required:"true" cty:"vm_type"` + VmName *string `mapstructure:"vm_name" required:"false" cty:"vm_name"` + VmTags map[string]string `mapstructure:"vm_tags" required:"false" cty:"vm_tags"` + VmTag []hcl2template.FlatNameValue `mapstructure:"vm_tag" required:"false" cty:"vm_tag"` + DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name"` + DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type"` + DiskSize *float32 `mapstructure:"disk_size" required:"true" cty:"disk_size"` + Network *string `mapstructure:"network" required:"false" cty:"network"` + PrivateIP *string `mapstructure:"private_ip" required:"false" cty:"private_ip"` + PublicIP *string `mapstructure:"public_ip" required:"false" cty:"public_ip"` + PublicNetAdpService *string `mapstructure:"public_netadp_service" required:"false" cty:"public_netadp_service"` + ChrootDisk *bool `mapstructure:"chroot_disk" cty:"chroot_disk"` + ChrootDiskSize *float32 `mapstructure:"chroot_disk_size" cty:"chroot_disk_size"` + ChrootDiskType *string `mapstructure:"chroot_disk_type" cty:"chroot_disk_type"` + ChrootMountPath *string `mapstructure:"chroot_mount_path" cty:"chroot_mount_path"` + ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts"` + ChrootCopyFiles []string `mapstructure:"chroot_copy_files" cty:"chroot_copy_files"` + ChrootCommandWrapper *string `mapstructure:"chroot_command_wrapper" cty:"chroot_command_wrapper"` + MountOptions []string `mapstructure:"mount_options" cty:"mount_options"` + MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition"` + PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands"` + PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands"` + SSHKeys []string `mapstructure:"ssh_keys" required:"false" cty:"ssh_keys"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` } // FlatMapstructure returns a new FlatConfig. @@ -161,10 +164,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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_tags": &hcldec.BlockAttrsSpec{TypeName: "image_tags", ElementType: cty.String, Required: false}, + "image_tag": &hcldec.BlockListSpec{TypeName: "image_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "image_service": &hcldec.AttrSpec{Name: "image_service", Type: cty.String, Required: false}, "vm_type": &hcldec.AttrSpec{Name: "vm_type", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "vm_tags": &hcldec.BlockAttrsSpec{TypeName: "vm_tags", ElementType: cty.String, Required: false}, + "vm_tag": &hcldec.BlockListSpec{TypeName: "vm_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "disk_name": &hcldec.AttrSpec{Name: "disk_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}, diff --git a/builder/osc/common/run_config.go b/builder/osc/common/run_config.go index 55683caa2..112bcd069 100644 --- a/builder/osc/common/run_config.go +++ b/builder/osc/common/run_config.go @@ -11,6 +11,7 @@ import ( "time" "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/template/interpolate" ) @@ -18,13 +19,13 @@ import ( var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$") type OmiFilterOptions struct { - Filters map[string]string - Owners []string - MostRecent bool `mapstructure:"most_recent"` + hcl2template.KVFilter `mapstructure:",squash"` + Owners []string + MostRecent bool `mapstructure:"most_recent"` } func (d *OmiFilterOptions) Empty() bool { - return len(d.Owners) == 0 && len(d.Filters) == 0 + return len(d.Owners) == 0 && d.KVFilter.Empty() } func (d *OmiFilterOptions) NoOwner() bool { @@ -32,29 +33,17 @@ func (d *OmiFilterOptions) NoOwner() bool { } type SubnetFilterOptions struct { - Filters map[string]string - MostFree bool `mapstructure:"most_free"` - Random bool `mapstructure:"random"` -} - -func (d *SubnetFilterOptions) Empty() bool { - return len(d.Filters) == 0 + hcl2template.KVFilter `mapstructure:",squash"` + MostFree bool `mapstructure:"most_free"` + Random bool `mapstructure:"random"` } type NetFilterOptions struct { - Filters map[string]string -} - -func (d *NetFilterOptions) Empty() bool { - return len(d.Filters) == 0 + hcl2template.KVFilter `mapstructure:",squash"` } type SecurityGroupFilterOptions struct { - Filters map[string]string -} - -func (d *SecurityGroupFilterOptions) Empty() bool { - return len(d.Filters) == 0 + hcl2template.KVFilter `mapstructure:",squash"` } // RunConfig contains configuration for running an vm from a source @@ -115,6 +104,15 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) + for _, preparer := range []interface{ Prepare() []error }{ + &c.SourceOmiFilter, + &c.SecurityGroupFilter, + &c.SubnetFilter, + &c.NetFilter, + } { + errs = append(errs, preparer.Prepare()...) + } + // Validating ssh_interface if c.SSHInterface != "public_ip" && c.SSHInterface != "private_ip" && diff --git a/builder/osc/common/run_config.hcl2spec.go b/builder/osc/common/run_config.hcl2spec.go index a61eaac64..d08073cdb 100644 --- a/builder/osc/common/run_config.hcl2spec.go +++ b/builder/osc/common/run_config.hcl2spec.go @@ -3,6 +3,7 @@ package common import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -46,7 +47,8 @@ func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { // FlatNetFilterOptions is an auto-generated flat version of NetFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatNetFilterOptions struct { - Filters map[string]string `cty:"filters"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` } // FlatMapstructure returns a new FlatNetFilterOptions. @@ -62,6 +64,7 @@ func (*NetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcl func (*FlatNetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, } return s } @@ -69,9 +72,10 @@ func (*FlatNetFilterOptions) HCL2Spec() map[string]hcldec.Spec { // FlatOmiFilterOptions is an auto-generated flat version of OmiFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatOmiFilterOptions struct { - Filters map[string]string `cty:"filters"` - Owners []string `cty:"owners"` - MostRecent *bool `mapstructure:"most_recent" cty:"most_recent"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` + Owners []string `cty:"owners"` + MostRecent *bool `mapstructure:"most_recent" cty:"most_recent"` } // FlatMapstructure returns a new FlatOmiFilterOptions. @@ -87,6 +91,7 @@ func (*OmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcl func (*FlatOmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "owners": &hcldec.AttrSpec{Name: "owners", Type: cty.List(cty.String), Required: false}, "most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false}, } @@ -96,7 +101,8 @@ func (*FlatOmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { // FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatSecurityGroupFilterOptions struct { - Filters map[string]string `cty:"filters"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` } // FlatMapstructure returns a new FlatSecurityGroupFilterOptions. @@ -112,6 +118,7 @@ func (*SecurityGroupFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[ func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, } return s } @@ -119,9 +126,10 @@ func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { // FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatSubnetFilterOptions struct { - Filters map[string]string `cty:"filters"` - MostFree *bool `mapstructure:"most_free" cty:"most_free"` - Random *bool `mapstructure:"random" cty:"random"` + Filters map[string]string `cty:"filters"` + Filter []hcl2template.FlatNameValue `cty:"filter"` + MostFree *bool `mapstructure:"most_free" cty:"most_free"` + Random *bool `mapstructure:"random" cty:"random"` } // FlatMapstructure returns a new FlatSubnetFilterOptions. @@ -137,6 +145,7 @@ func (*SubnetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string] func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "most_free": &hcldec.AttrSpec{Name: "most_free", Type: cty.Bool, Required: false}, "random": &hcldec.AttrSpec{Name: "random", Type: cty.Bool, Required: false}, } diff --git a/builder/osc/common/run_config_test.go b/builder/osc/common/run_config_test.go index a33280eae..d1faa7f81 100644 --- a/builder/osc/common/run_config_test.go +++ b/builder/osc/common/run_config_test.go @@ -6,6 +6,7 @@ import ( "regexp" "testing" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" ) @@ -72,7 +73,11 @@ func TestRunConfigPrepare_SourceOmiFilterOwnersBlank(t *testing.T) { c := testConfigFilter() filter_key := "name" filter_value := "foo" - c.SourceOmiFilter = OmiFilterOptions{Filters: map[string]string{filter_key: filter_value}} + c.SourceOmiFilter = OmiFilterOptions{ + KVFilter: hcl2template.KVFilter{ + Filters: map[string]string{filter_key: filter_value}, + }, + } if err := c.Prepare(nil); len(err) != 1 { t.Fatalf("Should error if Owners is not specified)") } @@ -83,7 +88,12 @@ func TestRunConfigPrepare_SourceOmiFilterGood(t *testing.T) { owner := "123" filter_key := "name" filter_value := "foo" - goodFilter := OmiFilterOptions{Owners: []string{owner}, Filters: map[string]string{filter_key: filter_value}} + goodFilter := OmiFilterOptions{ + Owners: []string{owner}, + KVFilter: hcl2template.KVFilter{ + Filters: map[string]string{filter_key: filter_value}, + }, + } c.SourceOmiFilter = goodFilter if err := c.Prepare(nil); len(err) != 0 { t.Fatalf("err: %s", err) diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index fd9a54748..f896f2f82 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -3,96 +3,98 @@ package cvm import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "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"` - SecretId *string `mapstructure:"secret_id" required:"true" cty:"secret_id"` - SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` - Region *string `mapstructure:"region" required:"true" cty:"region"` - Zone *string `mapstructure:"zone" required:"true" cty:"zone"` - SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` - ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"` - ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` - Reboot *bool `mapstructure:"reboot" required:"false" cty:"reboot"` - ForcePoweroff *bool `mapstructure:"force_poweroff" required:"false" cty:"force_poweroff"` - Sysprep *bool `mapstructure:"sysprep" required:"false" cty:"sysprep"` - ImageForceDelete *bool `mapstructure:"image_force_delete" cty:"image_force_delete"` - ImageCopyRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions"` - ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false" cty:"image_share_accounts"` - AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` - SourceImageId *string `mapstructure:"source_image_id" required:"true" cty:"source_image_id"` - InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` - InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name"` - DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type"` - DiskSize *int64 `mapstructure:"disk_size" required:"false" cty:"disk_size"` - DataDisks []FlattencentCloudDataDisk `mapstructure:"data_disks" cty:"data_disks"` - VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` - VpcName *string `mapstructure:"vpc_name" required:"false" cty:"vpc_name"` - VpcIp *string `mapstructure:"vpc_ip" cty:"vpc_ip"` - SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` - SubnetName *string `mapstructure:"subnet_name" required:"false" cty:"subnet_name"` - CidrBlock *string `mapstructure:"cidr_block" required:"false" cty:"cidr_block"` - SubnectCidrBlock *string `mapstructure:"subnect_cidr_block" required:"false" cty:"subnect_cidr_block"` - InternetChargeType *string `mapstructure:"internet_charge_type" cty:"internet_charge_type"` - InternetMaxBandwidthOut *int64 `mapstructure:"internet_max_bandwidth_out" required:"false" cty:"internet_max_bandwidth_out"` - SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` - SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name"` - UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` - UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` - HostName *string `mapstructure:"host_name" required:"false" cty:"host_name"` - RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` - 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"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` - 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"` - SSHPrivateIp *bool `mapstructure:"ssh_private_ip" cty:"ssh_private_ip"` + 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"` + SecretId *string `mapstructure:"secret_id" required:"true" cty:"secret_id"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + Region *string `mapstructure:"region" required:"true" cty:"region"` + Zone *string `mapstructure:"zone" required:"true" cty:"zone"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"` + ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` + Reboot *bool `mapstructure:"reboot" required:"false" cty:"reboot"` + ForcePoweroff *bool `mapstructure:"force_poweroff" required:"false" cty:"force_poweroff"` + Sysprep *bool `mapstructure:"sysprep" required:"false" cty:"sysprep"` + ImageForceDelete *bool `mapstructure:"image_force_delete" cty:"image_force_delete"` + ImageCopyRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions"` + ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false" cty:"image_share_accounts"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + SourceImageId *string `mapstructure:"source_image_id" required:"true" cty:"source_image_id"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name"` + DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type"` + DiskSize *int64 `mapstructure:"disk_size" required:"false" cty:"disk_size"` + DataDisks []FlattencentCloudDataDisk `mapstructure:"data_disks" cty:"data_disks"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + VpcName *string `mapstructure:"vpc_name" required:"false" cty:"vpc_name"` + VpcIp *string `mapstructure:"vpc_ip" cty:"vpc_ip"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + SubnetName *string `mapstructure:"subnet_name" required:"false" cty:"subnet_name"` + CidrBlock *string `mapstructure:"cidr_block" required:"false" cty:"cidr_block"` + SubnectCidrBlock *string `mapstructure:"subnect_cidr_block" required:"false" cty:"subnect_cidr_block"` + InternetChargeType *string `mapstructure:"internet_charge_type" cty:"internet_charge_type"` + InternetMaxBandwidthOut *int64 `mapstructure:"internet_max_bandwidth_out" required:"false" cty:"internet_max_bandwidth_out"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + HostName *string `mapstructure:"host_name" required:"false" cty:"host_name"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + RunTag []hcl2template.FlatNameValue `mapstructure:"run_tag" required:"false" cty:"run_tag"` + 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"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` + SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` + 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"` + SSHPrivateIp *bool `mapstructure:"ssh_private_ip" cty:"ssh_private_ip"` } // FlatMapstructure returns a new FlatConfig. @@ -149,6 +151,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, "host_name": &hcldec.AttrSpec{Name: "host_name", Type: cty.String, Required: false}, "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "run_tag": &hcldec.BlockListSpec{TypeName: "run_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index 719d7caa3..cf6739c6a 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/template/interpolate" "github.com/pkg/errors" @@ -83,6 +84,11 @@ type TencentCloudRunConfig struct { // Tags to apply to the instance that is *launched* to create the image. // These tags are *not* applied to the resulting image. RunTags map[string]string `mapstructure:"run_tags" required:"false"` + // Same as [`run_tags`](#run_tags) but defined as a singular repeatable + // block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + RunTag hcl2template.NameValues `mapstructure:"run_tag" required:"false"` // Communicator settings Comm communicator.Config `mapstructure:",squash"` @@ -183,6 +189,8 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { cf.RunTags = make(map[string]string) } + errs = append(errs, cf.RunTag.CopyOn(&cf.RunTags)...) + return errs } diff --git a/builder/triton/config.hcl2spec.go b/builder/triton/config.hcl2spec.go index 08641e906..1ba657fd4 100644 --- a/builder/triton/config.hcl2spec.go +++ b/builder/triton/config.hcl2spec.go @@ -3,81 +3,84 @@ package triton import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/hcl2template" "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"` - Endpoint *string `mapstructure:"triton_url" required:"false" cty:"triton_url"` - Account *string `mapstructure:"triton_account" required:"true" cty:"triton_account"` - Username *string `mapstructure:"triton_user" required:"false" cty:"triton_user"` - KeyID *string `mapstructure:"triton_key_id" required:"true" cty:"triton_key_id"` - KeyMaterial *string `mapstructure:"triton_key_material" required:"false" cty:"triton_key_material"` - InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` - MachineName *string `mapstructure:"source_machine_name" required:"false" cty:"source_machine_name"` - MachinePackage *string `mapstructure:"source_machine_package" required:"true" cty:"source_machine_package"` - MachineImage *string `mapstructure:"source_machine_image" required:"true" cty:"source_machine_image"` - MachineNetworks []string `mapstructure:"source_machine_networks" required:"false" cty:"source_machine_networks"` - MachineMetadata map[string]string `mapstructure:"source_machine_metadata" required:"false" cty:"source_machine_metadata"` - MachineTags map[string]string `mapstructure:"source_machine_tags" required:"false" cty:"source_machine_tags"` - MachineFirewallEnabled *bool `mapstructure:"source_machine_firewall_enabled" required:"false" cty:"source_machine_firewall_enabled"` - MachineImageFilters *FlatMachineImageFilter `mapstructure:"source_machine_image_filter" required:"false" cty:"source_machine_image_filter"` - ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"` - ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version"` - ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` - ImageHomepage *string `mapstructure:"image_homepage" required:"false" cty:"image_homepage"` - ImageEULA *string `mapstructure:"image_eula_url" required:"false" cty:"image_eula_url"` - ImageACL []string `mapstructure:"image_acls" required:"false" cty:"image_acls"` - ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags"` - 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"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` - 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"` + 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"` + Endpoint *string `mapstructure:"triton_url" required:"false" cty:"triton_url"` + Account *string `mapstructure:"triton_account" required:"true" cty:"triton_account"` + Username *string `mapstructure:"triton_user" required:"false" cty:"triton_user"` + KeyID *string `mapstructure:"triton_key_id" required:"true" cty:"triton_key_id"` + KeyMaterial *string `mapstructure:"triton_key_material" required:"false" cty:"triton_key_material"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MachineName *string `mapstructure:"source_machine_name" required:"false" cty:"source_machine_name"` + MachinePackage *string `mapstructure:"source_machine_package" required:"true" cty:"source_machine_package"` + MachineImage *string `mapstructure:"source_machine_image" required:"true" cty:"source_machine_image"` + MachineNetworks []string `mapstructure:"source_machine_networks" required:"false" cty:"source_machine_networks"` + MachineMetadata map[string]string `mapstructure:"source_machine_metadata" required:"false" cty:"source_machine_metadata"` + MachineTags map[string]string `mapstructure:"source_machine_tags" required:"false" cty:"source_machine_tags"` + MachineTag []hcl2template.FlatNameValue `mapstructure:"source_machine_tag" required:"false" cty:"source_machine_tag"` + MachineFirewallEnabled *bool `mapstructure:"source_machine_firewall_enabled" required:"false" cty:"source_machine_firewall_enabled"` + MachineImageFilters *FlatMachineImageFilter `mapstructure:"source_machine_image_filter" required:"false" cty:"source_machine_image_filter"` + ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"` + ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version"` + ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` + ImageHomepage *string `mapstructure:"image_homepage" required:"false" cty:"image_homepage"` + ImageEULA *string `mapstructure:"image_eula_url" required:"false" cty:"image_eula_url"` + ImageACL []string `mapstructure:"image_acls" required:"false" cty:"image_acls"` + ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags"` + ImageTag []hcl2template.FlatNameValue `mapstructure:"image_tag" required:"false" cty:"image_tag"` + 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"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_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"` + SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"` + 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"` } // FlatMapstructure returns a new FlatConfig. @@ -111,6 +114,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "source_machine_networks": &hcldec.AttrSpec{Name: "source_machine_networks", Type: cty.List(cty.String), Required: false}, "source_machine_metadata": &hcldec.BlockAttrsSpec{TypeName: "source_machine_metadata", ElementType: cty.String, Required: false}, "source_machine_tags": &hcldec.BlockAttrsSpec{TypeName: "source_machine_tags", ElementType: cty.String, Required: false}, + "source_machine_tag": &hcldec.BlockListSpec{TypeName: "source_machine_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "source_machine_firewall_enabled": &hcldec.AttrSpec{Name: "source_machine_firewall_enabled", Type: cty.Bool, Required: false}, "source_machine_image_filter": &hcldec.BlockSpec{TypeName: "source_machine_image_filter", Nested: hcldec.ObjectSpec((*FlatMachineImageFilter)(nil).HCL2Spec())}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, @@ -120,6 +124,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_eula_url": &hcldec.AttrSpec{Name: "image_eula_url", Type: cty.String, Required: false}, "image_acls": &hcldec.AttrSpec{Name: "image_acls", Type: cty.List(cty.String), Required: false}, "image_tags": &hcldec.BlockAttrsSpec{TypeName: "image_tags", ElementType: cty.String, Required: false}, + "image_tag": &hcldec.BlockListSpec{TypeName: "image_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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}, diff --git a/builder/triton/source_machine_config.go b/builder/triton/source_machine_config.go index 906b5c38f..da8e9419d 100644 --- a/builder/triton/source_machine_config.go +++ b/builder/triton/source_machine_config.go @@ -6,6 +6,7 @@ package triton import ( "fmt" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/template/interpolate" ) @@ -52,9 +53,13 @@ type SourceMachineConfig struct { // set the user-script metadata key to have Triton start a user supplied // script after the VM has booted. MachineMetadata map[string]string `mapstructure:"source_machine_metadata" required:"false"` - // Tags applied to the - // VM used to create the image. + // Tags applied to the VM used to create the image. MachineTags map[string]string `mapstructure:"source_machine_tags" required:"false"` + // Same as [`source_machine_tags`](#source_machine_tags) but defined as a + // singular block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + MachineTag hcl2template.NameValues `mapstructure:"source_machine_tag" required:"false"` // Whether or not the firewall // of the VM used to create an image of is enabled. The Triton firewall only // filters inbound traffic to the VM. All outbound traffic is always allowed. @@ -107,9 +112,7 @@ func (c *SourceMachineConfig) Prepare(ctx *interpolate.Context) []error { c.MachineTags = make(map[string]string) } - if len(errs) > 0 { - return errs - } + errs = append(errs, c.MachineTag.CopyOn(&c.MachineTags)...) - return nil + return errs } diff --git a/builder/triton/target_image_config.go b/builder/triton/target_image_config.go index a0a5c73ef..67ad946f2 100644 --- a/builder/triton/target_image_config.go +++ b/builder/triton/target_image_config.go @@ -5,6 +5,7 @@ package triton import ( "fmt" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/template/interpolate" ) @@ -36,12 +37,19 @@ type TargetImageConfig struct { ImageACL []string `mapstructure:"image_acls" required:"false"` // Tag applied to the image. ImageTags map[string]string `mapstructure:"image_tags" required:"false"` + // Same as [`image_tags`](#image_tags) but defined as a singular repeatable + // block containing a `name` and a `value` field. In HCL2 mode the + // [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + // will allow you to create those programatically. + ImageTag hcl2template.NameValues `mapstructure:"image_tag" required:"false"` } // Prepare performs basic validation on a TargetImageConfig struct. func (c *TargetImageConfig) Prepare(ctx *interpolate.Context) []error { var errs []error + errs = append(errs, c.ImageTag.CopyOn(&c.ImageTags)...) + if c.ImageName == "" { errs = append(errs, fmt.Errorf("An image_name must be specified")) } diff --git a/builder/vmware/common/driver_parser.go b/builder/vmware/common/driver_parser.go index b464ffbc1..f7ea454d5 100644 --- a/builder/vmware/common/driver_parser.go +++ b/builder/vmware/common/driver_parser.go @@ -437,7 +437,9 @@ type pParameterClientMatch struct { data string } -func (e pParameterClientMatch) repr() string { return fmt.Sprintf("match-client:%s=%s", e.name, e.data) } +func (e pParameterClientMatch) repr() string { + return fmt.Sprintf("match-client:%s=%s", e.name, e.data) +} // range 127.0.0.1 127.0.0.255 type pParameterRange4 struct { diff --git a/builder/vsphere/iso/step_create.hcl2spec.go b/builder/vsphere/iso/step_create.hcl2spec.go index 2bdc5384f..9f1e31fdf 100644 --- a/builder/vsphere/iso/step_create.hcl2spec.go +++ b/builder/vsphere/iso/step_create.hcl2spec.go @@ -92,7 +92,9 @@ type FlatNIC struct { // FlatMapstructure returns a new FlatNIC. // FlatNIC is an auto-generated flat version of NIC. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*NIC) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatNIC) } +func (*NIC) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatNIC) +} // HCL2Spec returns the hcl spec of a NIC. // This spec is used by HCL to read the fields of NIC. diff --git a/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go b/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go index 92ad43dda..6917c6d20 100644 --- a/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go +++ b/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go @@ -41,6 +41,8 @@ import ( "golang.org/x/tools/imports" ) +const mapstructureToHCL2 = "mapstructure-to-hcl2" + 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") @@ -49,15 +51,15 @@ var ( // Usage is a replacement usage function for the flags package. func Usage() { - fmt.Fprintf(os.Stderr, "Usage of mapstructure-to-hcl2:\n") - fmt.Fprintf(os.Stderr, "\tflatten-mapstructure [flags] -type T[,T...] pkg\n") + fmt.Fprintf(os.Stderr, "Usage of "+mapstructureToHCL2+":\n") + fmt.Fprintf(os.Stderr, "\t"+mapstructureToHCL2+" [flags] -type T[,T...] pkg\n") fmt.Fprintf(os.Stderr, "Flags:\n") flag.PrintDefaults() } func main() { log.SetFlags(0) - log.SetPrefix("mapstructure-to-hcl2: ") + log.SetPrefix(mapstructureToHCL2 + ": ") flag.Usage = Usage flag.Parse() if len(*typeNames) == 0 { @@ -76,7 +78,7 @@ func main() { if goFile := os.Getenv("GOFILE"); goFile != "" { outputPath = goFile[:len(goFile)-2] + "hcl2spec.go" } - log.SetPrefix(fmt.Sprintf("mapstructure-to-hcl2: %s.%v: ", os.Getenv("GOPACKAGE"), typeNames)) + log.SetPrefix(fmt.Sprintf(mapstructureToHCL2+": %s.%v: ", os.Getenv("GOPACKAGE"), typeNames)) cfg := &packages.Config{ Mode: packages.LoadSyntax, @@ -138,7 +140,7 @@ func main() { out := bytes.NewBuffer(nil) - fmt.Fprintf(out, `// Code generated by "mapstructure-to-hcl2 %s"; DO NOT EDIT.`, strings.Join(os.Args[1:], " ")) + fmt.Fprintf(out, `// Code generated by "%s %s"; DO NOT EDIT.`, mapstructureToHCL2, strings.Join(os.Args[1:], " ")) fmt.Fprintf(out, "\npackage %s\n", topPkg.Name) delete(usedImports, NamePath{topPkg.Name, topPkg.PkgPath}) @@ -160,8 +162,8 @@ func main() { 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{ HCL2Spec() map[string]hcldec.Spec } {", flatenedStruct.OriginalStructName) - fmt.Fprintf(out, "return new(%s)", flatenedStruct.FlatStructName) - fmt.Fprint(out, "}\n") + fmt.Fprintf(out, "\nreturn new(%s)", flatenedStruct.FlatStructName) + fmt.Fprint(out, "\n}\n") 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) @@ -225,7 +227,7 @@ func outputStructHCL2SpecBody(w io.Writer, s *types.Struct) { // outputHCL2SpecField writes the values of the `map[string]hcldec.Spec` map // supposed to define the HCL spec of a struct. func outputHCL2SpecField(w io.Writer, accessor string, fieldType types.Type, tag *structtag.Tags) { - if m2h, err := tag.Get(""); err == nil && m2h.HasOption("self-defined") { + if m2h, err := tag.Get(mapstructureToHCL2); err == nil && m2h.HasOption("self-defined") { fmt.Fprintf(w, `(&%s{}).HCL2Spec()`, fieldType.String()) return } diff --git a/cmd/struct-markdown/main.go b/cmd/struct-markdown/main.go index 1474be1a9..724bde2da 100644 --- a/cmd/struct-markdown/main.go +++ b/cmd/struct-markdown/main.go @@ -117,6 +117,8 @@ func main() { fieldType = `duration string | ex: "1h5m2s"` case "config.Trilean": fieldType = `boolean` + case "hcl2template.NameValues": + fieldType = `[]{name string, value string}` } field := Field{ diff --git a/command/build_parallel_test.go b/command/build_parallel_test.go index d4802de43..cf4e7b40a 100644 --- a/command/build_parallel_test.go +++ b/command/build_parallel_test.go @@ -47,7 +47,9 @@ type LockedBuilder struct{ unlock chan interface{} } func (b *LockedBuilder) ConfigSpec() hcldec.ObjectSpec { return nil } -func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, []string, error) { return nil, nil, nil } +func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, []string, error) { + return nil, nil, nil +} func (b *LockedBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { ui.Say("locking build") diff --git a/hcl2template/types.kv.go b/hcl2template/types.kv.go new file mode 100644 index 000000000..cd1e63007 --- /dev/null +++ b/hcl2template/types.kv.go @@ -0,0 +1,37 @@ +//go:generate mapstructure-to-hcl2 -type NameValue,NameValues,KVFilter + +package hcl2template + +type NameValue struct { + Name string + Value string +} + +type NameValues []NameValue + +func (kvs NameValues) CopyOn(to *map[string]string) []error { + if len(kvs) == 0 { + return nil + } + if *to == nil { + *to = map[string]string{} + } + for _, kv := range kvs { + (*to)[kv.Name] = kv.Value + } + return nil +} + +type KVFilter struct { + Filters map[string]string + Filter NameValues +} + +func (kvf *KVFilter) Prepare() []error { + kvf.Filter.CopyOn(&kvf.Filters) + return nil +} + +func (kvf *KVFilter) Empty() bool { + return len(kvf.Filters) == 0 +} diff --git a/hcl2template/types.kv.hcl2spec.go b/hcl2template/types.kv.hcl2spec.go new file mode 100644 index 000000000..09c30840f --- /dev/null +++ b/hcl2template/types.kv.hcl2spec.go @@ -0,0 +1,57 @@ +// Code generated by "mapstructure-to-hcl2 -type NameValue,NameValues,KVFilter"; DO NOT EDIT. +package hcl2template + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatKVFilter is an auto-generated flat version of KVFilter. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatKVFilter struct { + Filters map[string]string `cty:"filters"` + Filter []FlatNameValue `cty:"filter"` +} + +// FlatMapstructure returns a new FlatKVFilter. +// FlatKVFilter is an auto-generated flat version of KVFilter. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*KVFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatKVFilter) +} + +// HCL2Spec returns the hcl spec of a KVFilter. +// This spec is used by HCL to read the fields of KVFilter. +// The decoded values from this spec will then be applied to a FlatKVFilter. +func (*FlatKVFilter) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, + "filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*FlatNameValue)(nil).HCL2Spec())}, + } + return s +} + +// FlatNameValue is an auto-generated flat version of NameValue. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatNameValue struct { + Name *string `cty:"name"` + Value *string `cty:"value"` +} + +// FlatMapstructure returns a new FlatNameValue. +// FlatNameValue is an auto-generated flat version of NameValue. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*NameValue) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatNameValue) +} + +// HCL2Spec returns the hcl spec of a NameValue. +// This spec is used by HCL to read the fields of NameValue. +// The decoded values from this spec will then be applied to a FlatNameValue. +func (*FlatNameValue) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, + "value": &hcldec.AttrSpec{Name: "value", Type: cty.String, Required: false}, + } + return s +} diff --git a/helper/communicator/config.hcl2spec.go b/helper/communicator/config.hcl2spec.go index 30a9beece..20b89368c 100644 --- a/helper/communicator/config.hcl2spec.go +++ b/helper/communicator/config.hcl2spec.go @@ -148,7 +148,9 @@ type FlatSSH struct { // 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{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSSH) } +func (*SSH) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSSH) +} // HCL2Spec returns the hcl spec of a SSH. // This spec is used by HCL to read the fields of SSH. @@ -206,7 +208,9 @@ 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{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatWinRM) } +func (*WinRM) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatWinRM) +} // HCL2Spec returns the hcl spec of a WinRM. // This spec is used by HCL to read the fields of WinRM. diff --git a/post-processor/alicloud-import/post-processor.go b/post-processor/alicloud-import/post-processor.go index fd5da203c..f2e4bb76b 100644 --- a/post-processor/alicloud-import/post-processor.go +++ b/post-processor/alicloud-import/post-processor.go @@ -107,6 +107,8 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { errs, fmt.Errorf("Error parsing oss_key_name template: %s", err)) } + errs = packer.MultiErrorAppend(errs, p.config.AlicloudImageTag.CopyOn(&p.config.AlicloudImageTags)...) + // Check we have alicloud access variables defined somewhere errs = packer.MultiErrorAppend(errs, p.config.AlicloudAccessConfig.Prepare(&p.config.ctx)...) diff --git a/post-processor/alicloud-import/post-processor.hcl2spec.go b/post-processor/alicloud-import/post-processor.hcl2spec.go index a070a6268..0d48af77c 100644 --- a/post-processor/alicloud-import/post-processor.hcl2spec.go +++ b/post-processor/alicloud-import/post-processor.hcl2spec.go @@ -4,6 +4,7 @@ package alicloudimport import ( "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/alicloud/ecs" + "github.com/hashicorp/packer/hcl2template" "github.com/zclconf/go-cty/cty" ) @@ -38,6 +39,7 @@ type FlatConfig struct { AlicloudImageForceDeleteInstances *bool `mapstructure:"image_force_delete_instances" cty:"image_force_delete_instances"` AlicloudImageIgnoreDataDisks *bool `mapstructure:"image_ignore_data_disks" required:"false" cty:"image_ignore_data_disks"` AlicloudImageTags map[string]string `mapstructure:"tags" required:"false" cty:"tags"` + AlicloudImageTag []hcl2template.FlatNameValue `mapstructure:"tag" required:"false" cty:"tag"` ECSSystemDiskMapping *ecs.FlatAlicloudDiskDevice `mapstructure:"system_disk_mapping" required:"false" cty:"system_disk_mapping"` ECSImagesDiskMappings []ecs.FlatAlicloudDiskDevice `mapstructure:"image_disk_mappings" required:"false" cty:"image_disk_mappings"` AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" cty:"associate_public_ip_address"` @@ -153,6 +155,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_force_delete_instances": &hcldec.AttrSpec{Name: "image_force_delete_instances", Type: cty.Bool, Required: false}, "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}, + "tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatNameValue)(nil).HCL2Spec())}, "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.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, diff --git a/provisioner/shell/provisioner_test.go b/provisioner/shell/provisioner_test.go index 9d4a8847c..e720d0731 100644 --- a/provisioner/shell/provisioner_test.go +++ b/provisioner/shell/provisioner_test.go @@ -491,14 +491,14 @@ func TestProvisioner_RemoteFileDefaultsToScriptnnnn(t *testing.T) { t.Fatalf("should not have error: %s", err) } - remoteFileRegex := regexp.MustCompile("script_[0-9]{4}.sh") + remoteFileRegex := regexp.MustCompile("script_[0-9]{1,4}.sh") if !remoteFileRegex.MatchString(p.config.RemoteFile) { - t.Fatalf("remote_file did not default to script_nnnn.sh") + t.Fatalf("remote_file did not default to script_nnnn.sh: %q", p.config.RemoteFile) } if !remoteFileRegex.MatchString(p.config.RemotePath) { - t.Fatalf("remote_path did not match script_nnnn.sh") + t.Fatalf("remote_path did not match script_nnnn.sh: %q", p.config.RemotePath) } } diff --git a/scripts/generate-plugins.go b/scripts/generate-plugins.go index 784046f7e..9b79e3ee2 100644 --- a/scripts/generate-plugins.go +++ b/scripts/generate-plugins.go @@ -15,6 +15,8 @@ import ( "path/filepath" "sort" "strings" + + "golang.org/x/tools/imports" ) const target = "command/plugin.go" @@ -58,6 +60,8 @@ func main() { } defer file.Close() + output = string(goFmt(target, []byte(output))) + _, err = file.WriteString(output) if err != nil { log.Fatalf("Failed writing to %s: %s", target, err) @@ -66,6 +70,15 @@ func main() { log.Printf("Generated %s", target) } +func goFmt(filename string, b []byte) []byte { + fb, err := imports.Process(filename, b, nil) + if err != nil { + log.Printf("formatting err: %v", err) + return b + } + return fb +} + type plugin struct { Package string // This plugin's package name (iso) PluginName string // Name of plugin (vmware-iso) diff --git a/website/source/partials/builder/alicloud/ecs/_AlicloudImageConfig-not-required.html.md b/website/source/partials/builder/alicloud/ecs/_AlicloudImageConfig-not-required.html.md index 1aaffe8f3..7b868b25f 100644 --- a/website/source/partials/builder/alicloud/ecs/_AlicloudImageConfig-not-required.html.md +++ b/website/source/partials/builder/alicloud/ecs/_AlicloudImageConfig-not-required.html.md @@ -49,6 +49,10 @@ - `skip_region_validation` (bool) - The region validation can be skipped if this value is true, the default value is false. -- `tags` (map[string]string) - Tags applied to the destination - image and relevant snapshots. +- `tags` (map[string]string) - Tags applied to the destination image and relevant snapshots. + +- `tag` ([]{name string, value string}) - Same as [`tags`](#tags) but defined as a singular repeatable block + containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. \ No newline at end of file diff --git a/website/source/partials/builder/amazon/chroot/_Config-not-required.html.md b/website/source/partials/builder/amazon/chroot/_Config-not-required.html.md index 5272dc712..a2519bee2 100644 --- a/website/source/partials/builder/amazon/chroot/_Config-not-required.html.md +++ b/website/source/partials/builder/amazon/chroot/_Config-not-required.html.md @@ -121,10 +121,15 @@ criteria provided in `source_ami_filter`; this pins the AMI returned by the filter, but will cause Packer to fail if the `source_ami` does not exist. -- `root_volume_tags` (awscommon.TagMap) - Tags to apply to the volumes that are *launched*. This is a [template +- `root_volume_tags` (map[string]string) - Tags to apply to the volumes that are *launched*. This is a [template engine](/docs/templates/engine.html), see [Build template data](#build-template-data) for more information. +- `root_volume_tag` ([]{name string, value string}) - Same as [`root_volume_tags`](#root_volume_tags) but defined as a + singular block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `ami_architecture` (string) - what architecture to use when registering the final AMI; valid options are "x86_64" or "arm64". Defaults to "x86_64". \ No newline at end of file diff --git a/website/source/partials/builder/amazon/common/_AMIConfig-not-required.html.md b/website/source/partials/builder/amazon/common/_AMIConfig-not-required.html.md index 6cd6c65ff..b2d595896 100644 --- a/website/source/partials/builder/amazon/common/_AMIConfig-not-required.html.md +++ b/website/source/partials/builder/amazon/common/_AMIConfig-not-required.html.md @@ -28,10 +28,15 @@ - `skip_region_validation` (bool) - Set to true if you want to skip validation of the ami_regions configuration option. Default false. -- `tags` (TagMap) - Tags applied to the AMI. This is a +- `tags` (map[string]string) - Tags applied to the AMI. This is a [template engine](/docs/templates/engine.html), see [Build template data](#build-template-data) for more information. +- `tag` ([]{name string, value string}) - Same as [`tags`](#tags) but defined as a singular repeatable block + containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `ena_support` (boolean) - Enable enhanced networking (ENA but not SriovNetSupport) on HVM-compatible AMIs. If set, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy. @@ -93,11 +98,16 @@ the intermediary AMI into any regions provided in `ami_regions`, then delete the intermediary AMI. Default `false`. -- `snapshot_tags` (TagMap) - Tags to apply to snapshot. +- `snapshot_tags` (map[string]string) - Tags to apply to snapshot. They will override AMI tags if already applied to snapshot. This is a [template engine](../templates/engine.html), see [Build template data](#build-template-data) for more information. +- `snapshot_tag` ([]{name string, value string}) - Same as [`snapshot_tags`](#snapshot_tags) but defined as a singular + repeatable block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `snapshot_users` ([]string) - A list of account IDs that have access to create volumes from the snapshot(s). By default no additional users other than the user creating the AMI has permissions to create diff --git a/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md b/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md index 9a62ee69c..c79d15ca0 100644 --- a/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md +++ b/website/source/partials/builder/amazon/common/_RunConfig-not-required.html.md @@ -113,6 +113,11 @@ EBS volumes. This is a [template engine](/docs/templates/engine.html), see [Build template data](#build-template-data) for more information. +- `run_tag` ([]{name string, value string}) - Same as [`run_tags`](#run_tags) but defined as a singular repeatable + block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `security_group_id` (string) - The ID (not the name) of the security group to assign to the instance. By default this is not set and Packer will automatically create a new temporary security group to allow SSH access. @@ -197,8 +202,13 @@ Windows, Linux/UNIX (Amazon VPC), SUSE Linux (Amazon VPC), Windows (Amazon VPC) -- `spot_tags` (map[string]string) - Requires spot_price to be - set. This tells Packer to apply tags to the spot request that is issued. +- `spot_tags` (map[string]string) - Requires spot_price to be set. This tells Packer to apply tags to the + spot request that is issued. + +- `spot_tag` ([]{name string, value string}) - Same as [`spot_tags`](#spot_tags) but defined as a singular repeatable block + containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. - `subnet_filter` (SubnetFilterOptions) - Filters used to populate the `subnet_id` field. Example: diff --git a/website/source/partials/builder/amazon/ebsvolume/_BlockDevice-not-required.html.md b/website/source/partials/builder/amazon/ebsvolume/_BlockDevice-not-required.html.md index bd0fb9e1e..56fe38e38 100644 --- a/website/source/partials/builder/amazon/ebsvolume/_BlockDevice-not-required.html.md +++ b/website/source/partials/builder/amazon/ebsvolume/_BlockDevice-not-required.html.md @@ -1,6 +1,11 @@ -- `tags` (awscommon.TagMap) - Tags to apply to the volume. These are retained after the builder +- `tags` (map[string]string) - Tags to apply to the volume. These are retained after the builder completes. This is a [template engine](/docs/templates/engine.html), see [Build template data](#build-template-data) for more information. + +- `tag` ([]{name string, value string}) - Same as [`tags`](#tags) but defined as a singular repeatable block + containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. \ No newline at end of file diff --git a/website/source/partials/builder/amazon/ebsvolume/_Config-not-required.html.md b/website/source/partials/builder/amazon/ebsvolume/_Config-not-required.html.md index e49c6aca0..bc4464a25 100644 --- a/website/source/partials/builder/amazon/ebsvolume/_Config-not-required.html.md +++ b/website/source/partials/builder/amazon/ebsvolume/_Config-not-required.html.md @@ -23,7 +23,7 @@ source instance. See the [BlockDevices](#block-devices-configuration) documentation for fields. -- `run_volume_tags` (awscommon.TagMap) - Tags to apply to the volumes of the instance that is *launched* to +- `run_volume_tags` (map[string]string) - Tags to apply to the volumes of the instance that is *launched* to create EBS Volumes. These tags will *not* appear in the tags of the resulting EBS volumes unless they're duplicated under `tags` in the `ebs_volumes` setting. This is a [template @@ -35,4 +35,10 @@ created. Packer will replace all tags on the volume with the tags configured in the `ebs_volumes` section as soon as the instance is reported as 'ready'. + +- `run_volume_tag` ([]{name string, value string}) - Same as [`run_volume_tags`](#run_volume_tags) but defined as a singular + repeatable block containing a `name` and a `value` field. In HCL2 mode + the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. \ No newline at end of file diff --git a/website/source/partials/builder/azure/arm/_Config-not-required.html.md b/website/source/partials/builder/azure/arm/_Config-not-required.html.md index b6f549fc6..b4ee7aecc 100644 --- a/website/source/partials/builder/azure/arm/_Config-not-required.html.md +++ b/website/source/partials/builder/azure/arm/_Config-not-required.html.md @@ -100,6 +100,11 @@ 256 characters. Tags are applied to every resource deployed by a Packer build, i.e. Resource Group, VM, NIC, VNET, Public IP, KeyVault, etc. +- `azure_tag` ([]{name string, value string}) - Same as [`azure_tags`](#azure_tags) but defined as a singular repeatable block + containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `resource_group_name` (string) - Resource group under which the final artifact will be stored. - `storage_account` (string) - Storage account under which the final artifact will be stored. diff --git a/website/source/partials/builder/hyperone/_Config-not-required.html.md b/website/source/partials/builder/hyperone/_Config-not-required.html.md index 89bb59ca7..30ec224f7 100644 --- a/website/source/partials/builder/hyperone/_Config-not-required.html.md +++ b/website/source/partials/builder/hyperone/_Config-not-required.html.md @@ -15,8 +15,12 @@ - `image_description` (string) - The description of the resulting image. -- `image_tags` (map[string]string) - Key/value pair tags to - add to the created image. +- `image_tags` (map[string]string) - Key/value pair tags to add to the created image. + +- `image_tag` ([]{name string, value string}) - Same as [`image_tags`](#image_tags) but defined as a singular repeatable + block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. - `image_service` (string) - The service of the resulting image. @@ -25,6 +29,11 @@ - `vm_tags` (map[string]string) - Key/value pair tags to add to the created server. +- `vm_tag` ([]{name string, value string}) - Same as [`vm_tags`](#vm_tags) but defined as a singular repeatable block + containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `disk_name` (string) - The name of the created disk. - `disk_type` (string) - The type of the created disk. Defaults to ssd. diff --git a/website/source/partials/builder/tencentcloud/cvm/_TencentCloudRunConfig-not-required.html.md b/website/source/partials/builder/tencentcloud/cvm/_TencentCloudRunConfig-not-required.html.md index 3a6953e58..73e31c9fb 100644 --- a/website/source/partials/builder/tencentcloud/cvm/_TencentCloudRunConfig-not-required.html.md +++ b/website/source/partials/builder/tencentcloud/cvm/_TencentCloudRunConfig-not-required.html.md @@ -55,4 +55,9 @@ - `run_tags` (map[string]string) - Tags to apply to the instance that is *launched* to create the image. These tags are *not* applied to the resulting image. +- `run_tag` ([]{name string, value string}) - Same as [`run_tags`](#run_tags) but defined as a singular repeatable + block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. + - `ssh_private_ip` (bool) - SSH Private Ip \ No newline at end of file diff --git a/website/source/partials/builder/triton/_SourceMachineConfig-not-required.html.md b/website/source/partials/builder/triton/_SourceMachineConfig-not-required.html.md index a3e6d821e..8e2fb1d1c 100644 --- a/website/source/partials/builder/triton/_SourceMachineConfig-not-required.html.md +++ b/website/source/partials/builder/triton/_SourceMachineConfig-not-required.html.md @@ -23,8 +23,12 @@ set the user-script metadata key to have Triton start a user supplied script after the VM has booted. -- `source_machine_tags` (map[string]string) - Tags applied to the - VM used to create the image. +- `source_machine_tags` (map[string]string) - Tags applied to the VM used to create the image. + +- `source_machine_tag` ([]{name string, value string}) - Same as [`source_machine_tags`](#source_machine_tags) but defined as a + singular block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. - `source_machine_firewall_enabled` (bool) - Whether or not the firewall of the VM used to create an image of is enabled. The Triton firewall only diff --git a/website/source/partials/builder/triton/_TargetImageConfig-not-required.html.md b/website/source/partials/builder/triton/_TargetImageConfig-not-required.html.md index c66a0fef6..9aa28adbf 100644 --- a/website/source/partials/builder/triton/_TargetImageConfig-not-required.html.md +++ b/website/source/partials/builder/triton/_TargetImageConfig-not-required.html.md @@ -14,4 +14,9 @@ credentials are used) will have access to the image. - `image_tags` (map[string]string) - Tag applied to the image. + +- `image_tag` ([]{name string, value string}) - Same as [`image_tags`](#image_tags) but defined as a singular repeatable + block containing a `name` and a `value` field. In HCL2 mode the + [`dynamic_block`](https://packer.io/docs/configuration/from-1.5/expressions.html#dynamic-blocks) + will allow you to create those programatically. \ No newline at end of file