Merge branch 'master' of github.com:hashicorp/packer into google-impersonation

This commit is contained in:
upodroid 2020-10-01 23:19:15 +01:00
commit b1c74f9df0
184 changed files with 23281 additions and 2579 deletions

View File

@ -7,11 +7,11 @@ version: 2.1
executors: executors:
golang: golang:
docker: docker:
- image: circleci/golang:1.13 - image: circleci/golang:1.15
resource_class: medium+ resource_class: medium+
darwin: darwin:
macos: macos:
xcode: "9.0" xcode: "12.0.0"
commands: commands:
install-go-run-tests-unix: install-go-run-tests-unix:
@ -67,7 +67,7 @@ jobs:
steps: steps:
- install-go-run-tests-unix: - install-go-run-tests-unix:
GOOS: darwin GOOS: darwin
GOVERSION: "1.13" GOVERSION: "1.15"
- codecov/upload: - codecov/upload:
file: coverage.txt file: coverage.txt
test-windows: test-windows:
@ -76,7 +76,7 @@ jobs:
shell: bash.exe shell: bash.exe
steps: steps:
- install-go-run-tests-windows: - install-go-run-tests-windows:
GOVERSION: "1.13" GOVERSION: "1.15"
- codecov/upload: - codecov/upload:
file: coverage.txt file: coverage.txt
check-lint: check-lint:

View File

@ -13,5 +13,6 @@ coverage:
project: off project: off
patch: off patch: off
ignore: # ignore hcl2spec generated code for coverage ignore: # ignore hcl2spec generated code for coverage and mocks
- "**/*.hcl2spec.go" - "**/*.hcl2spec.go"
- "**/*_mock.go"

View File

@ -1,13 +1,37 @@
## 1.6.3 (Upcoming) ## 1.6.5 (Upcoming)
## 1.6.4 (September 30, 2020)
### BUG FIXES:
* builder/amazon: Fix authentication issue when using instance profiles or
assumed roles for loading session-derived credentials. [GH-10007]
* builder/azure: Fix crash when using `azure_tag` or `azure_tags` configuration
options. [GH-10014]
* builder/qemu: Ensure `qemu_img_args` are honored during the disk convert
step. [GH-10001]
## 1.6.3 (September 25, 2020)
### IMPROVEMENTS: ### IMPROVEMENTS:
* builder/amazon: Add `pause_before_ssm` option to pause for some time before
establishing a Session Manager session; defaults to 10s. [GH-9988]
* builder/amazon: Implement assume_role option that matches Terraform behavior.
[GH-9981]
* builder/azure: Support publishing to a Shared Image Gallery with a different * builder/azure: Support publishing to a Shared Image Gallery with a different
subscription id [GH-9875] subscription id [GH-9875]
* builder/openstack: Add `external_source_image_url` and
`external_source_image_format` to support building images from external
source URLs. [GH-9992]
* builder/openstack: Include API requests and responses as part of the debug
log output. [GH-9972]
* builder/oracle-oci: Add `create_vnic_details` option for launch details. * builder/oracle-oci: Add `create_vnic_details` option for launch details.
[GH-9856] [GH-9856]
* builder/oracle-oci: Allow freeform and defined tags to be added instance. * builder/oracle-oci: Allow freeform and defined tags to be added to an instance.
[GH-9802] [GH-9802]
* builder/proxmox: Add ability to specify interfaces for http_directroy and VM. * builder/proxmox: Add `io_thread` option for supporting io threads when using
a `virtio-scsi-single` controller with a `scsi` or `virtio` disk type.
[GH-9969]
* builder/proxmox: Add ability to specify interfaces for http_directory and VM.
[GH-9874] [GH-9874]
* builder/proxmox: Allow the mounting of multiple ISOs via the `cd_drive` * builder/proxmox: Allow the mounting of multiple ISOs via the `cd_drive`
option. [GH-9653] option. [GH-9653]
@ -16,17 +40,22 @@
to qemu-img [GH-9956] to qemu-img [GH-9956]
* builder/qemu: Add `skip_resize_disk` option to skip the resizing of QCOW2 * builder/qemu: Add `skip_resize_disk` option to skip the resizing of QCOW2
images. [GH-9896] [GH-9860] images. [GH-9896] [GH-9860]
* builder/qemu: Skip qemu-img convert on MacOS to prevent the creation * builder/qemu: Skip qemu-img convert on MacOS to prevent the creation of
of corrupt images [QEMU corrupt images [QEMU
#1776920](https://bugs.launchpad.net/qemu/+bug/1776920) [GH-9949] #1776920](https://bugs.launchpad.net/qemu/+bug/1776920) [GH-9949]
* builder/scaleway: Change default boottype to local. [GH-9853] * builder/scaleway: Change default boottype to local. [GH-9853]
* builder/scaleway: Update scaleway to use non-deprecated sdk. [GH-9902] * builder/scaleway: Update scaleway to use non-deprecated sdk. [GH-9902]
* builder/vmware: Add `vnc_over_websocket` to allow the sending of a * builder/vmware: Add `vnc_over_websocket` to allow the sending of a
`boot_command` to hosts running ESXi 6.7 and above. [GH-9938] `boot_command` to hosts running ESXi 6.7 and above. [GH-9938]
* builder/vmware: Allow user to set vmware tools source path. [GH-9983]
* builder/vsphere-clone: Add ability to set `mac_address` [GH-9930] * builder/vsphere-clone: Add ability to set `mac_address` [GH-9930]
* builder/vsphere-clone: Add floppy_files, cd_files, and iso_paths options.
[GH-9963]
* builder/vsphere-iso: Add NVMe controller support. [GH-9880] * builder/vsphere-iso: Add NVMe controller support. [GH-9880]
* builder/vsphere: Look for a default resource pool when root resource pool is * builder/vsphere: Look for a default resource pool when root resource pool is
not found. [GH-9809] not found. [GH-9809]
* core: Add support for running cygwin/msys2 based cd/iso creation tool
[GH-9954]
* core: New `cd_files` option to mount iso for modern OSes which don't support * core: New `cd_files` option to mount iso for modern OSes which don't support
floppies. [GH-9796] [GH-9919] [GH-9928] [GH-9932] [GH-9941] floppies. [GH-9796] [GH-9919] [GH-9928] [GH-9932] [GH-9941]
* HCL2: When the type of a variable is not known evaluate setting as a literal * HCL2: When the type of a variable is not known evaluate setting as a literal
@ -41,6 +70,9 @@
error was creating multiple spot instances. [GH-9946] error was creating multiple spot instances. [GH-9946]
* builder/amazon-ebssurrogate: Fix issue where builder defaults to AWS managed * builder/amazon-ebssurrogate: Fix issue where builder defaults to AWS managed
key even when custom `kms_key_id` is set. [GH-9959] key even when custom `kms_key_id` is set. [GH-9959]
* builder/amazon: Update ssm_driver log polling logic to prevent infinite loops
when SSM driver is terminated outside of Packer. [GH-9991]
* builder/azure: Fix crash when using HCL2 configs. [GH-9984] [GH-9985]
* builder/qemu: Fix hardcoded lowerbound causing negative ports [GH-9905] * builder/qemu: Fix hardcoded lowerbound causing negative ports [GH-9905]
* builder/qemu: Skip compaction when backing file is used. [GH-9918] * builder/qemu: Skip compaction when backing file is used. [GH-9918]
* builder/scaleway: Add pre validate step to prevent the creation of multiple * builder/scaleway: Add pre validate step to prevent the creation of multiple
@ -50,7 +82,9 @@
* builder/vsphere: Fix overly strict iso_path validation regex. [GH-9855] * builder/vsphere: Fix overly strict iso_path validation regex. [GH-9855]
* command/console: Prevent failure when there are unknown vars. [GH-9864] * command/console: Prevent failure when there are unknown vars. [GH-9864]
* command/inspect: Allow unset variables in HCL2 and JSON. [GH-9832] * command/inspect: Allow unset variables in HCL2 and JSON. [GH-9832]
* core: use $APPDATA over $HOME on Windows hosts when determining homedir. * core: Prevent the UI progressbar from hanging and crashing when there is no
TTY available. [GH-9974]
* core: Use $APPDATA over $HOME on Windows hosts when determining homedir.
[GH-9830] [GH-9830]
* post-processor/digitalocean-import: Fix crash caused by empty artifact.Files * post-processor/digitalocean-import: Fix crash caused by empty artifact.Files
slice. [GH-9857] slice. [GH-9857]

View File

@ -41,7 +41,9 @@ type FlatConfig struct {
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users" hcl:"snapshot_users"` SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users" hcl:"snapshot_users"`
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups" hcl:"snapshot_groups"` SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups" hcl:"snapshot_groups"`
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
@ -50,6 +52,7 @@ type FlatConfig struct {
RawRegion *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` RawRegion *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"`
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
@ -117,7 +120,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false}, "snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false},
"snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, "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}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
@ -126,6 +131,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},

View File

@ -1,5 +1,5 @@
//go:generate struct-markdown //go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions //go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions,AssumeRoleConfig
package common package common
@ -11,15 +11,67 @@ import (
"strings" "strings"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/aws-sdk-go/service/ec2/ec2iface"
awsbase "github.com/hashicorp/aws-sdk-go-base"
cleanhttp "github.com/hashicorp/go-cleanhttp" cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
vaultapi "github.com/hashicorp/vault/api" vaultapi "github.com/hashicorp/vault/api"
) )
// AssumeRoleConfig lets users set configuration options for assuming a special
// role when executing Packer.
//
// Usage example:
//
// HCL config example:
//
// ```HCL
// source "example" "amazon-ebs"{
// assume_role {
// role_arn = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME"
// session_name = "SESSION_NAME"
// external_id = "EXTERNAL_ID"
// }
// }
// ```
//
// JSON config example:
//
// ```json
// builder{
// "type": "amazon-ebs",
// "assume_role": {
// "role_arn" : "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME",
// "session_name": "SESSION_NAME",
// "external_id" : "EXTERNAL_ID"
// }
// }
// ```
type AssumeRoleConfig struct {
// Amazon Resource Name (ARN) of the IAM Role to assume.
AssumeRoleARN string `mapstructure:"role_arn" required:"false"`
// Number of seconds to restrict the assume role session duration.
AssumeRoleDurationSeconds int `mapstructure:"duration_seconds" required:"false"`
// The external ID to use when assuming the role. If omitted, no external
// ID is passed to the AssumeRole call.
AssumeRoleExternalID string `mapstructure:"external_id" required:"false"`
// IAM Policy JSON describing further restricting permissions for the IAM
// Role being assumed.
AssumeRolePolicy string `mapstructure:"policy" required:"false"`
// Set of Amazon Resource Names (ARNs) of IAM Policies describing further
// restricting permissions for the IAM Role being
AssumeRolePolicyARNs []string `mapstructure:"policy_arns" required:"false"`
// Session name to use when assuming the role.
AssumeRoleSessionName string `mapstructure:"session_name" required:"false"`
// Map of assume role session tags.
AssumeRoleTags map[string]string `mapstructure:"tags" required:"false"`
// Set of assume role session tag keys to pass to any subsequent sessions.
AssumeRoleTransitiveTagKeys []string `mapstructure:"transitive_tag_keys" required:"false"`
}
type VaultAWSEngineOptions struct { type VaultAWSEngineOptions struct {
Name string `mapstructure:"name"` Name string `mapstructure:"name"`
RoleARN string `mapstructure:"role_arn"` RoleARN string `mapstructure:"role_arn"`
@ -48,10 +100,17 @@ type AccessConfig struct {
// is not required if you are using `use_vault_aws_engine` for // is not required if you are using `use_vault_aws_engine` for
// authentication instead. // authentication instead.
AccessKey string `mapstructure:"access_key" required:"true"` AccessKey string `mapstructure:"access_key" required:"true"`
// If provided with a role ARN, Packer will attempt to assume this role
// using the supplied credentials. See
// [AssumeRoleConfig](#assume-role-configuration) below for more
// details on all of the options available, and for a usage example.
AssumeRole AssumeRoleConfig `mapstructure:"assume_role" required:"false"`
// This option is useful if you use a cloud // This option is useful if you use a cloud
// provider whose API is compatible with aws EC2. Specify another endpoint // provider whose API is compatible with aws EC2. Specify another endpoint
// like this https://ec2.custom.endpoint.com. // like this https://ec2.custom.endpoint.com.
CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2" required:"false"` CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2" required:"false"`
// Path to a credentials file to load credentials from
CredsFilename string `mapstructure:"shared_credentials_file" required:"false"`
// Enable automatic decoding of any encoded authorization (error) messages // Enable automatic decoding of any encoded authorization (error) messages
// using the `sts:DecodeAuthorizationMessage` API. Note: requires that the // using the `sts:DecodeAuthorizationMessage` API. Note: requires that the
// effective user/role have permissions to `sts:DecodeAuthorizationMessage` // effective user/role have permissions to `sts:DecodeAuthorizationMessage`
@ -86,6 +145,8 @@ type AccessConfig struct {
// validation of the ami_regions configuration option. Default false. // validation of the ami_regions configuration option. Default false.
SkipValidation bool `mapstructure:"skip_region_validation" required:"false"` SkipValidation bool `mapstructure:"skip_region_validation" required:"false"`
SkipMetadataApiCheck bool `mapstructure:"skip_metadata_api_check"` SkipMetadataApiCheck bool `mapstructure:"skip_metadata_api_check"`
// Set to true if you want to skip validating AWS credentials before runtime.
SkipCredsValidation bool `mapstructure:"skip_credential_validation"`
// The access token to use. This is different from the // The access token to use. This is different from the
// access key and secret key. If you're not sure what this is, then you // access key and secret key. If you're not sure what this is, then you
// probably don't need it. This will also be read from the AWS_SESSION_TOKEN // probably don't need it. This will also be read from the AWS_SESSION_TOKEN
@ -152,16 +213,13 @@ func (c *AccessConfig) Session() (*session.Session, error) {
return c.session, nil return c.session, nil
} }
// Create new AWS config
config := aws.NewConfig().WithCredentialsChainVerboseErrors(true) config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
if c.MaxRetries > 0 { if c.MaxRetries > 0 {
config = config.WithMaxRetries(c.MaxRetries) config = config.WithMaxRetries(c.MaxRetries)
} }
staticCreds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token) // Set AWS config defaults.
if _, err := staticCreds.Get(); err != credentials.ErrStaticCredentialsEmpty {
config.WithCredentials(staticCreds)
}
if c.RawRegion != "" { if c.RawRegion != "" {
config = config.WithRegion(c.RawRegion) config = config.WithRegion(c.RawRegion)
} }
@ -179,6 +237,16 @@ func (c *AccessConfig) Session() (*session.Session, error) {
} }
transport.Proxy = http.ProxyFromEnvironment transport.Proxy = http.ProxyFromEnvironment
// Figure out which possible credential providers are valid; test that we
// can get credentials via the selected providers, and set the providers in
// the config.
creds, err := c.GetCredentials(config)
if err != nil {
return nil, err
}
config.WithCredentials(creds)
// Create session options based on our AWS config
opts := session.Options{ opts := session.Options{
SharedConfigState: session.SharedConfigEnable, SharedConfigState: session.SharedConfigEnable,
Config: *config, Config: *config,
@ -204,9 +272,7 @@ func (c *AccessConfig) Session() (*session.Session, error) {
cp, err := c.session.Config.Credentials.Get() cp, err := c.session.Config.Credentials.Get()
if IsAWSErr(err, "NoCredentialProviders", "") { if IsAWSErr(err, "NoCredentialProviders", "") {
return nil, fmt.Errorf("No valid credential sources found for AWS Builder. " + return nil, c.NewNoValidCredentialSourcesError(err)
"Please see https://www.packer.io/docs/builders/amazon#specifying-amazon-credentials " +
"for more information on providing credentials for the AWS Builder.")
} }
if err != nil { if err != nil {
@ -237,6 +303,42 @@ func (c *AccessConfig) IsChinaCloud() bool {
return strings.HasPrefix(c.SessionRegion(), "cn-") return strings.HasPrefix(c.SessionRegion(), "cn-")
} }
// GetCredentials gets credentials from the environment, shared credentials,
// the session (which may include a credential process), or ECS/EC2 metadata
// endpoints. GetCredentials also validates the credentials and the ability to
// assume a role or will return an error if unsuccessful.
func (c *AccessConfig) GetCredentials(config *aws.Config) (*awsCredentials.Credentials, error) {
// Reload values into the config used by the Packer-Terraform shared SDK
awsbaseConfig := &awsbase.Config{
AccessKey: c.AccessKey,
AssumeRoleARN: c.AssumeRole.AssumeRoleARN,
AssumeRoleDurationSeconds: c.AssumeRole.AssumeRoleDurationSeconds,
AssumeRoleExternalID: c.AssumeRole.AssumeRoleExternalID,
AssumeRolePolicy: c.AssumeRole.AssumeRolePolicy,
AssumeRolePolicyARNs: c.AssumeRole.AssumeRolePolicyARNs,
AssumeRoleSessionName: c.AssumeRole.AssumeRoleSessionName,
AssumeRoleTags: c.AssumeRole.AssumeRoleTags,
AssumeRoleTransitiveTagKeys: c.AssumeRole.AssumeRoleTransitiveTagKeys,
CredsFilename: c.CredsFilename,
DebugLogging: false,
// TODO: implement for Packer
// IamEndpoint: c.Endpoints["iam"],
Insecure: c.InsecureSkipTLSVerify,
MaxRetries: c.MaxRetries,
Profile: c.ProfileName,
Region: c.RawRegion,
SecretKey: c.SecretKey,
SkipCredsValidation: c.SkipCredsValidation,
SkipMetadataApiCheck: c.SkipMetadataApiCheck,
// TODO: implement for Packer
// SkipRequestingAccountId: c.SkipRequestingAccountId,
// StsEndpoint: c.Endpoints["sts"],
Token: c.Token,
}
return awsbase.GetCredentials(awsbaseConfig)
}
func (c *AccessConfig) GetCredsFromVault() error { func (c *AccessConfig) GetCredsFromVault() error {
// const EnvVaultAddress = "VAULT_ADDR" // const EnvVaultAddress = "VAULT_ADDR"
// const EnvVaultToken = "VAULT_TOKEN" // const EnvVaultToken = "VAULT_TOKEN"
@ -306,6 +408,13 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
return errs return errs
} }
func (c *AccessConfig) NewNoValidCredentialSourcesError(err error) error {
return fmt.Errorf("No valid credential sources found for AWS Builder. "+
"Please see https://www.packer.io/docs/builders/amazon#authentication "+
"for more information on providing credentials for the AWS Builder. "+
"Error: %w", err)
}
func (c *AccessConfig) NewEC2Connection() (ec2iface.EC2API, error) { func (c *AccessConfig) NewEC2Connection() (ec2iface.EC2API, error) {
if c.getEC2Connection != nil { if c.getEC2Connection != nil {
return c.getEC2Connection(), nil return c.getEC2Connection(), nil

View File

@ -1,4 +1,4 @@
// Code generated by "mapstructure-to-hcl2 -type VaultAWSEngineOptions"; DO NOT EDIT. // Code generated by "mapstructure-to-hcl2 -type VaultAWSEngineOptions,AssumeRoleConfig"; DO NOT EDIT.
package common package common
import ( import (
@ -6,6 +6,43 @@ import (
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
) )
// FlatAssumeRoleConfig is an auto-generated flat version of AssumeRoleConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatAssumeRoleConfig struct {
AssumeRoleARN *string `mapstructure:"role_arn" required:"false" cty:"role_arn" hcl:"role_arn"`
AssumeRoleDurationSeconds *int `mapstructure:"duration_seconds" required:"false" cty:"duration_seconds" hcl:"duration_seconds"`
AssumeRoleExternalID *string `mapstructure:"external_id" required:"false" cty:"external_id" hcl:"external_id"`
AssumeRolePolicy *string `mapstructure:"policy" required:"false" cty:"policy" hcl:"policy"`
AssumeRolePolicyARNs []string `mapstructure:"policy_arns" required:"false" cty:"policy_arns" hcl:"policy_arns"`
AssumeRoleSessionName *string `mapstructure:"session_name" required:"false" cty:"session_name" hcl:"session_name"`
AssumeRoleTags map[string]string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
AssumeRoleTransitiveTagKeys []string `mapstructure:"transitive_tag_keys" required:"false" cty:"transitive_tag_keys" hcl:"transitive_tag_keys"`
}
// FlatMapstructure returns a new FlatAssumeRoleConfig.
// FlatAssumeRoleConfig is an auto-generated flat version of AssumeRoleConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*AssumeRoleConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatAssumeRoleConfig)
}
// HCL2Spec returns the hcl spec of a AssumeRoleConfig.
// This spec is used by HCL to read the fields of AssumeRoleConfig.
// The decoded values from this spec will then be applied to a FlatAssumeRoleConfig.
func (*FlatAssumeRoleConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"role_arn": &hcldec.AttrSpec{Name: "role_arn", Type: cty.String, Required: false},
"duration_seconds": &hcldec.AttrSpec{Name: "duration_seconds", Type: cty.Number, Required: false},
"external_id": &hcldec.AttrSpec{Name: "external_id", Type: cty.String, Required: false},
"policy": &hcldec.AttrSpec{Name: "policy", Type: cty.String, Required: false},
"policy_arns": &hcldec.AttrSpec{Name: "policy_arns", Type: cty.List(cty.String), Required: false},
"session_name": &hcldec.AttrSpec{Name: "session_name", Type: cty.String, Required: false},
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false},
"transitive_tag_keys": &hcldec.AttrSpec{Name: "transitive_tag_keys", Type: cty.List(cty.String), Required: false},
}
return s
}
// FlatVaultAWSEngineOptions is an auto-generated flat version of VaultAWSEngineOptions. // FlatVaultAWSEngineOptions is an auto-generated flat version of VaultAWSEngineOptions.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatVaultAWSEngineOptions struct { type FlatVaultAWSEngineOptions struct {

View File

@ -473,6 +473,13 @@ type RunConfig struct {
// terminating the tunnel it will automatically terminate itself after 20 minutes of inactivity. // terminating the tunnel it will automatically terminate itself after 20 minutes of inactivity.
SSHInterface string `mapstructure:"ssh_interface"` SSHInterface string `mapstructure:"ssh_interface"`
// The time to wait before establishing the Session Manager session.
// The value of this should be a duration. Examples are
// `5s` and `1m30s` which will cause Packer to wait five seconds and one
// minute 30 seconds, respectively. If no set, defaults to 10 seconds.
// This option is useful when the remote port takes longer to become available.
PauseBeforeSSM time.Duration `mapstructure:"pause_before_ssm"`
// Which port to connect the local end of the session tunnel to. If // Which port to connect the local end of the session tunnel to. If
// left blank, Packer will choose a port for you from available ports. // left blank, Packer will choose a port for you from available ports.
// This option is only used when `ssh_interface` is set `session_manager`. // This option is only used when `ssh_interface` is set `session_manager`.
@ -535,6 +542,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
msg := fmt.Errorf(`no iam_instance_profile defined; session_manager connectivity requires a valid instance profile with AmazonSSMManagedInstanceCore permissions. Alternatively a temporary_iam_instance_profile_policy_document can be used.`) msg := fmt.Errorf(`no iam_instance_profile defined; session_manager connectivity requires a valid instance profile with AmazonSSMManagedInstanceCore permissions. Alternatively a temporary_iam_instance_profile_policy_document can be used.`)
errs = append(errs, msg) errs = append(errs, msg)
} }
if c.PauseBeforeSSM == 0 {
c.PauseBeforeSSM = 10 * time.Second
}
} }
if c.Comm.SSHKeyPairName != "" { if c.Comm.SSHKeyPairName != "" {

View File

@ -46,7 +46,7 @@ func NewSSMDriver(config SSMDriverConfig) *SSMDriver {
// not wish to manage the session manually calling StopSession on a instance of this driver will terminate the active session // not wish to manage the session manually calling StopSession on a instance of this driver will terminate the active session
// created from calling StartSession. // created from calling StartSession.
func (d *SSMDriver) StartSession(ctx context.Context, input ssm.StartSessionInput) (*ssm.StartSessionOutput, error) { func (d *SSMDriver) StartSession(ctx context.Context, input ssm.StartSessionInput) (*ssm.StartSessionOutput, error) {
log.Printf("Starting PortForwarding session to instance %q with following params %v", aws.StringValue(input.Target), input.Parameters) log.Printf("Starting PortForwarding session to instance %q", aws.StringValue(input.Target))
var output *ssm.StartSessionOutput var output *ssm.StartSessionOutput
err := retry.Config{ err := retry.Config{
@ -110,15 +110,30 @@ func (d *SSMDriver) openTunnelForSession(ctx context.Context) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
case output := <-stderrCh: case output, ok := <-stderrCh:
if !ok {
stderrCh = nil
break
}
if output != "" { if output != "" {
log.Printf("[ERROR] %s: %s", prefix, output) log.Printf("[ERROR] %s: %s", prefix, output)
} }
case output := <-stdoutCh: case output, ok := <-stdoutCh:
if !ok {
stdoutCh = nil
break
}
if output != "" { if output != "" {
log.Printf("[DEBUG] %s: %s", prefix, output) log.Printf("[DEBUG] %s: %s", prefix, output)
} }
} }
if stdoutCh == nil && stderrCh == nil {
log.Printf("[DEBUG] %s: %s", prefix, "active session has been terminated; stopping all log polling processes.")
return
}
} }
}(ctx, sessionManagerPluginName) }(ctx, sessionManagerPluginName)

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"strconv" "strconv"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
@ -21,6 +22,7 @@ type StepCreateSSMTunnel struct {
RemotePortNumber int RemotePortNumber int
SSMAgentEnabled bool SSMAgentEnabled bool
instanceId string instanceId string
PauseBeforeSSM time.Duration
driver *SSMDriver driver *SSMDriver
} }
@ -32,6 +34,17 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
return multistep.ActionContinue return multistep.ActionContinue
} }
// Wait for the remote port to become available
if s.PauseBeforeSSM > 0 {
ui.Say(fmt.Sprintf("Waiting %s for establishing the SSM session...", s.PauseBeforeSSM))
select {
case <-time.After(s.PauseBeforeSSM):
break
case <-ctx.Done():
return multistep.ActionHalt
}
}
// Configure local port number // Configure local port number
if err := s.ConfigureLocalHostPort(ctx); err != nil { if err := s.ConfigureLocalHostPort(ctx); err != nil {
err := fmt.Errorf("error finding an available port to initiate a session tunnel: %s", err) err := fmt.Errorf("error finding an available port to initiate a session tunnel: %s", err)

View File

@ -269,6 +269,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&awscommon.StepCreateSSMTunnel{ &awscommon.StepCreateSSMTunnel{
AWSSession: session, AWSSession: session,
Region: *ec2conn.Config.Region, Region: *ec2conn.Config.Region,
PauseBeforeSSM: b.config.PauseBeforeSSM,
LocalPortNumber: b.config.SessionManagerPort, LocalPortNumber: b.config.SessionManagerPort,
RemotePortNumber: b.config.Comm.Port(), RemotePortNumber: b.config.Comm.Port(),
SSMAgentEnabled: b.config.SSMAgentEnabled(), SSMAgentEnabled: b.config.SSMAgentEnabled(),

View File

@ -19,7 +19,9 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
@ -29,6 +31,7 @@ type FlatConfig struct {
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"` SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
@ -132,6 +135,7 @@ type FlatConfig struct {
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"`
PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"`
SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"`
AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"` AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"`
LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"` LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"`
@ -160,7 +164,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
@ -170,6 +176,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
@ -273,6 +280,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
"pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false},
"session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false},
"ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},
"launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},

View File

@ -293,6 +293,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&awscommon.StepCreateSSMTunnel{ &awscommon.StepCreateSSMTunnel{
AWSSession: session, AWSSession: session,
Region: *ec2conn.Config.Region, Region: *ec2conn.Config.Region,
PauseBeforeSSM: b.config.PauseBeforeSSM,
LocalPortNumber: b.config.SessionManagerPort, LocalPortNumber: b.config.SessionManagerPort,
RemotePortNumber: b.config.Comm.Port(), RemotePortNumber: b.config.Comm.Port(),
SSMAgentEnabled: b.config.SSMAgentEnabled(), SSMAgentEnabled: b.config.SSMAgentEnabled(),

View File

@ -62,7 +62,9 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
@ -72,6 +74,7 @@ type FlatConfig struct {
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"` SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
@ -154,6 +157,7 @@ type FlatConfig struct {
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"`
PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"`
SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"`
AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name" hcl:"ami_name"` AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name" hcl:"ami_name"`
AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description" hcl:"ami_description"` AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description" hcl:"ami_description"`
@ -204,7 +208,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
@ -214,6 +220,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
@ -296,6 +303,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
"pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false},
"session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false},
"ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false},
"ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false},

View File

@ -263,6 +263,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&awscommon.StepCreateSSMTunnel{ &awscommon.StepCreateSSMTunnel{
AWSSession: session, AWSSession: session,
Region: *ec2conn.Config.Region, Region: *ec2conn.Config.Region,
PauseBeforeSSM: b.config.PauseBeforeSSM,
LocalPortNumber: b.config.SessionManagerPort, LocalPortNumber: b.config.SessionManagerPort,
RemotePortNumber: b.config.Comm.Port(), RemotePortNumber: b.config.Comm.Port(),
SSMAgentEnabled: b.config.SSMAgentEnabled(), SSMAgentEnabled: b.config.SSMAgentEnabled(),

View File

@ -64,7 +64,9 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
@ -74,6 +76,7 @@ type FlatConfig struct {
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"` SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
@ -156,6 +159,7 @@ type FlatConfig struct {
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"`
PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"`
SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"`
AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support" hcl:"ena_support"` AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support" hcl:"ena_support"`
AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support" hcl:"sriov_support"` AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support" hcl:"sriov_support"`
@ -184,7 +188,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
@ -194,6 +200,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
@ -276,6 +283,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
"pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false},
"session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false},
"ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false},
"sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false},

View File

@ -343,6 +343,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&awscommon.StepCreateSSMTunnel{ &awscommon.StepCreateSSMTunnel{
AWSSession: session, AWSSession: session,
Region: *ec2conn.Config.Region, Region: *ec2conn.Config.Region,
PauseBeforeSSM: b.config.PauseBeforeSSM,
LocalPortNumber: b.config.SessionManagerPort, LocalPortNumber: b.config.SessionManagerPort,
RemotePortNumber: b.config.Comm.Port(), RemotePortNumber: b.config.Comm.Port(),
SSMAgentEnabled: b.config.SSMAgentEnabled(), SSMAgentEnabled: b.config.SSMAgentEnabled(),

View File

@ -19,7 +19,9 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
@ -29,6 +31,7 @@ type FlatConfig struct {
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"` SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
@ -132,6 +135,7 @@ type FlatConfig struct {
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"` SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"`
PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"`
SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"` SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"`
AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"` AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"`
LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"` LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"`
@ -166,7 +170,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
@ -176,6 +182,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
@ -279,6 +286,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
"pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false},
"session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false}, "session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false},
"ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},
"launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},

View File

@ -390,7 +390,7 @@ func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, resou
func (b *Builder) configureStateBag(stateBag multistep.StateBag) { func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey) stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey)
stateBag.Put(constants.ArmTags, b.config.AzureTags) stateBag.Put(constants.ArmTags, packerAzureCommon.MapToAzureTags(b.config.AzureTags))
stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName)
stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName)

View File

@ -223,7 +223,11 @@ const testBuilderAccManagedDiskLinux = `
"image_sku": "16.04-LTS", "image_sku": "16.04-LTS",
"location": "South Central US", "location": "South Central US",
"vm_size": "Standard_DS2_v2" "vm_size": "Standard_DS2_v2",
"azure_tags": {
"env": "testing",
"builder": "packer"
}
}] }]
} }
` `

View File

@ -33,3 +33,35 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) {
} }
} }
} }
func TestStateBagShouldPoluateExpectedTags(t *testing.T) {
var testSubject Builder
expectedTags := map[string]string{
"env": "test",
"builder": "packer",
}
armConfig := getArmBuilderConfiguration()
armConfig["azure_tags"] = expectedTags
_, _, err := testSubject.Prepare(armConfig, getPackerConfiguration())
if err != nil {
t.Fatalf("failed to prepare: %s", err)
}
tags, ok := testSubject.stateBag.Get(constants.ArmTags).(map[string]*string)
if !ok {
t.Errorf("Expected the builder's state bag to contain tags of type %T, but didn't.", testSubject.config.AzureTags)
}
if len(tags) != len(expectedTags) {
t.Errorf("expect tags from state to be the same length as tags from config")
}
for k, v := range tags {
if expectedTags[k] != *v {
t.Errorf("expect tag value of %s to be %s, but got %s", k, expectedTags[k], *v)
}
}
}

View File

@ -288,7 +288,7 @@ type Config struct {
// Group, VM, NIC, VNET, Public IP, KeyVault, etc. The user can define up // Group, VM, NIC, VNET, Public IP, KeyVault, etc. The user can define up
// to 15 tags. Tag names cannot exceed 512 characters, and tag values // to 15 tags. Tag names cannot exceed 512 characters, and tag values
// cannot exceed 256 characters. // cannot exceed 256 characters.
AzureTags map[string]*string `mapstructure:"azure_tags" required:"false"` AzureTags map[string]string `mapstructure:"azure_tags" required:"false"`
// Same as [`azure_tags`](#azure_tags) but defined as a singular repeatable block // Same as [`azure_tags`](#azure_tags) but defined as a singular repeatable block
// containing a `name` and a `value` field. In HCL2 mode the // containing a `name` and a `value` field. In HCL2 mode the
// [`dynamic_block`](/docs/configuration/from-1.5/expressions#dynamic-blocks) // [`dynamic_block`](/docs/configuration/from-1.5/expressions#dynamic-blocks)
@ -513,7 +513,7 @@ func (c *Config) toImageParameters() *compute.Image {
}, },
}, },
Location: to.StringPtr(c.Location), Location: to.StringPtr(c.Location),
Tags: c.AzureTags, Tags: azcommon.MapToAzureTags(c.AzureTags),
} }
} }
@ -596,10 +596,7 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
} }
// copy singular blocks // copy singular blocks
for _, kv := range c.AzureTag { c.AzureTag.CopyOn(&c.AzureTags)
v := kv.Value
c.AzureTags[kv.Name] = &v
}
err = c.ClientConfig.SetDefaultValues() err = c.ClientConfig.SetDefaultValues()
if err != nil { if err != nil {
@ -807,8 +804,8 @@ func assertTagProperties(c *Config, errs *packer.MultiError) {
if len(k) > 512 { if len(k) > 512 {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 512 character limit", k, len(k))) errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 512 character limit", k, len(k)))
} }
if len(*v) > 256 { if len(v) > 256 {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", *v, len(*v))) errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", v, len(v)))
} }
} }
} }
@ -1063,13 +1060,13 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("if either plan_name, plan_product, plan_publisher, or plan_promotion_code are defined then plan_name, plan_product, and plan_publisher must be defined")) errs = packer.MultiErrorAppend(errs, fmt.Errorf("if either plan_name, plan_product, plan_publisher, or plan_promotion_code are defined then plan_name, plan_product, and plan_publisher must be defined"))
} else { } else {
if c.AzureTags == nil { if c.AzureTags == nil {
c.AzureTags = make(map[string]*string) c.AzureTags = make(map[string]string)
} }
c.AzureTags["PlanInfo"] = &c.PlanInfo.PlanName c.AzureTags["PlanInfo"] = c.PlanInfo.PlanName
c.AzureTags["PlanProduct"] = &c.PlanInfo.PlanProduct c.AzureTags["PlanProduct"] = c.PlanInfo.PlanProduct
c.AzureTags["PlanPublisher"] = &c.PlanInfo.PlanPublisher c.AzureTags["PlanPublisher"] = c.PlanInfo.PlanPublisher
c.AzureTags["PlanPromotionCode"] = &c.PlanInfo.PlanPromotionCode c.AzureTags["PlanPromotionCode"] = c.PlanInfo.PlanPromotionCode
} }
} }

View File

@ -49,7 +49,7 @@ type FlatConfig struct {
ManagedImageOSDiskSnapshotName *string `mapstructure:"managed_image_os_disk_snapshot_name" required:"false" cty:"managed_image_os_disk_snapshot_name" hcl:"managed_image_os_disk_snapshot_name"` ManagedImageOSDiskSnapshotName *string `mapstructure:"managed_image_os_disk_snapshot_name" required:"false" cty:"managed_image_os_disk_snapshot_name" hcl:"managed_image_os_disk_snapshot_name"`
ManagedImageDataDiskSnapshotPrefix *string `mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" cty:"managed_image_data_disk_snapshot_prefix" hcl:"managed_image_data_disk_snapshot_prefix"` ManagedImageDataDiskSnapshotPrefix *string `mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" cty:"managed_image_data_disk_snapshot_prefix" hcl:"managed_image_data_disk_snapshot_prefix"`
ManagedImageZoneResilient *bool `mapstructure:"managed_image_zone_resilient" required:"false" cty:"managed_image_zone_resilient" hcl:"managed_image_zone_resilient"` ManagedImageZoneResilient *bool `mapstructure:"managed_image_zone_resilient" required:"false" cty:"managed_image_zone_resilient" hcl:"managed_image_zone_resilient"`
AzureTags map[string]*string `mapstructure:"azure_tags" required:"false" cty:"azure_tags" hcl:"azure_tags"` AzureTags map[string]string `mapstructure:"azure_tags" required:"false" cty:"azure_tags" hcl:"azure_tags"`
AzureTag []hcl2template.FlatNameValue `mapstructure:"azure_tag" required:"false" cty:"azure_tag" hcl:"azure_tag"` AzureTag []hcl2template.FlatNameValue `mapstructure:"azure_tag" required:"false" cty:"azure_tag" hcl:"azure_tag"`
ResourceGroupName *string `mapstructure:"resource_group_name" cty:"resource_group_name" hcl:"resource_group_name"` ResourceGroupName *string `mapstructure:"resource_group_name" cty:"resource_group_name" hcl:"resource_group_name"`
StorageAccount *string `mapstructure:"storage_account" cty:"storage_account" hcl:"storage_account"` StorageAccount *string `mapstructure:"storage_account" cty:"storage_account" hcl:"storage_account"`

View File

@ -6,7 +6,9 @@ import (
"time" "time"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/hcl2template"
) )
// List of configuration parameters that are required by the ARM builder. // List of configuration parameters that are required by the ARM builder.
@ -910,32 +912,55 @@ func TestConfigShouldAcceptTags(t *testing.T) {
}, },
} }
var c Config c := Config{
AzureTag: hcl2template.NameValues{
{Name: "tag03", Value: "value03"},
},
}
_, err := c.Prepare(config, getPackerConfiguration()) _, err := c.Prepare(config, getPackerConfiguration())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(c.AzureTags) != 2 { if diff := cmp.Diff(c.AzureTags, map[string]string{
t.Fatalf("expected to find 2 tags, but got %d", len(c.AzureTags)) "tag01": "value01",
"tag02": "value02",
"tag03": "value03",
}); diff != "" {
t.Fatalf("unexpected azure tags: %s", diff)
}
} }
if _, ok := c.AzureTags["tag01"]; !ok { func TestConfigShouldAcceptTag(t *testing.T) {
t.Error("expected to find key=\"tag01\", but did not") config := map[string]interface{}{
} "capture_name_prefix": "ignore",
if _, ok := c.AzureTags["tag02"]; !ok { "capture_container_name": "ignore",
t.Error("expected to find key=\"tag02\", but did not") "image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"storage_account": "ignore",
"resource_group_name": "ignore",
"subscription_id": "ignore",
"communicator": "none",
// Does not matter for this test case, just pick one.
"os_type": constants.Target_Linux,
} }
value := c.AzureTags["tag01"] c := Config{
if *value != "value01" { AzureTag: hcl2template.NameValues{
t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", *value) {Name: "tag03", Value: "value03"},
},
}
_, err := c.Prepare(config, getPackerConfiguration())
if err != nil {
t.Fatal(err)
} }
value = c.AzureTags["tag02"] if diff := cmp.Diff(c.AzureTags, map[string]string{
if *value != "value02" { "tag03": "value03",
t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", *value) }); diff != "" {
t.Fatalf("unexpected azure tags: %s", diff)
} }
} }
@ -2047,8 +2072,8 @@ func TestConfig_PrepareProvidedWinRMPassword(t *testing.T) {
} }
} }
func getArmBuilderConfiguration() map[string]string { func getArmBuilderConfiguration() map[string]interface{} {
m := make(map[string]string) m := make(map[string]interface{})
for _, v := range requiredConfigValues { for _, v := range requiredConfigValues {
m[v] = "ignored00" m[v] = "ignored00"
} }

View File

@ -61,7 +61,13 @@ func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.State
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
var location = state.Get(constants.ArmLocation).(string) var location = state.Get(constants.ArmLocation).(string)
var tags = state.Get(constants.ArmTags).(map[string]*string) tags, ok := state.Get(constants.ArmTags).(map[string]*string)
if !ok {
err := fmt.Errorf("failed to extract tags from state bag")
state.Put(constants.Error, err)
s.error(err)
return multistep.ActionHalt
}
exists, err := s.exists(ctx, resourceGroupName) exists, err := s.exists(ctx, resourceGroupName)
if err != nil { if err != nil {

View File

@ -220,3 +220,32 @@ func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag {
stateBag.Put(constants.ArmTags, tags) stateBag.Put(constants.ArmTags, tags)
return stateBag return stateBag
} }
func TestStepCreateResourceGroupShouldFailIfTagsFailCast(t *testing.T) {
stateBag := new(multistep.BasicStateBag)
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
value := "Unit Test: Tags"
tags := map[string]string{
"tag01": value,
}
stateBag.Put(constants.ArmTags, tags)
var testSubject = &StepCreateResourceGroup{
create: func(context.Context, string, string, map[string]*string) error { return nil },
say: func(message string) {},
error: func(e error) {},
exists: func(context.Context, string) (bool, error) { return false, nil },
}
var result = testSubject.Run(context.Background(), stateBag)
if result != multistep.ActionHalt {
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk(constants.Error); ok == false {
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
}
}

View File

@ -0,0 +1,10 @@
package common
func MapToAzureTags(in map[string]string) map[string]*string {
res := map[string]*string{}
for k := range in {
v := in[k]
res[k] = &v
}
return res
}

View File

@ -32,7 +32,7 @@ type Resource struct {
DependsOn *[]string `json:"dependsOn,omitempty"` DependsOn *[]string `json:"dependsOn,omitempty"`
Plan *Plan `json:"plan,omitempty"` Plan *Plan `json:"plan,omitempty"`
Properties *Properties `json:"properties,omitempty"` Properties *Properties `json:"properties,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"` Tags *map[string]string `json:"tags,omitempty"`
Resources *[]Resource `json:"resources,omitempty"` Resources *[]Resource `json:"resources,omitempty"`
Identity *Identity `json:"identity,omitempty"` Identity *Identity `json:"identity,omitempty"`
} }

View File

@ -374,7 +374,7 @@ func (s *TemplateBuilder) SetNetworkSecurityGroup(ipAddresses []string, port int
return nil return nil
} }
func (s *TemplateBuilder) SetTags(tags *map[string]*string) error { func (s *TemplateBuilder) SetTags(tags *map[string]string) error {
if tags == nil || len(*tags) == 0 { if tags == nil || len(*tags) == 0 {
return nil return nil
} }

View File

@ -3,16 +3,20 @@
package openstack package openstack
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/utils/openstack/clientconfig" "github.com/gophercloud/utils/openstack/clientconfig"
"github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
@ -236,6 +240,15 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
return nil return nil
} }
func (c *AccessConfig) enableDebug(ui packer.Ui) {
c.osClient.HTTPClient = http.Client{
Transport: &DebugRoundTripper{
ui: ui,
rt: c.osClient.HTTPClient.Transport,
},
}
}
func (c *AccessConfig) computeV2Client() (*gophercloud.ServiceClient, error) { func (c *AccessConfig) computeV2Client() (*gophercloud.ServiceClient, error) {
return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{ return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{
Region: c.Region, Region: c.Region,
@ -273,3 +286,49 @@ func (c *AccessConfig) getEndpointType() gophercloud.Availability {
} }
return gophercloud.AvailabilityPublic return gophercloud.AvailabilityPublic
} }
type DebugRoundTripper struct {
ui packer.Ui
rt http.RoundTripper
numReauthAttempts int
}
// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
func (drt *DebugRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
defer func() {
if request.Body != nil {
request.Body.Close()
}
}()
var response *http.Response
var err error
response, err = drt.rt.RoundTrip(request)
if response == nil {
return nil, err
}
if response.StatusCode == http.StatusUnauthorized {
if drt.numReauthAttempts == 3 {
return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.")
}
drt.numReauthAttempts++
}
drt.DebugMessage(fmt.Sprintf("Request %s %s %d", request.Method, request.URL, response.StatusCode))
if response.StatusCode >= 400 {
buf := bytes.NewBuffer([]byte{})
body, _ := ioutil.ReadAll(io.TeeReader(response.Body, buf))
drt.DebugMessage(fmt.Sprintf("Response Error: %+v\n", string(body)))
bufWithClose := ioutil.NopCloser(buf)
response.Body = bufWithClose
}
return response, err
}
func (drt *DebugRoundTripper) DebugMessage(message string) {
drt.ui.Message(fmt.Sprintf("[DEBUG] %s", message))
}

View File

@ -71,6 +71,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
} }
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
if b.config.PackerDebug {
b.config.enableDebug(ui)
}
computeClient, err := b.config.computeV2Client() computeClient, err := b.config.computeV2Client()
if err != nil { if err != nil {
return nil, fmt.Errorf("Error initializing compute client: %s", err) return nil, fmt.Errorf("Error initializing compute client: %s", err)
@ -100,6 +104,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&StepSourceImageInfo{ &StepSourceImageInfo{
SourceImage: b.config.RunConfig.SourceImage, SourceImage: b.config.RunConfig.SourceImage,
SourceImageName: b.config.RunConfig.SourceImageName, SourceImageName: b.config.RunConfig.SourceImageName,
ExternalSourceImageURL: b.config.RunConfig.ExternalSourceImageURL,
ExternalSourceImageFormat: b.config.RunConfig.ExternalSourceImageFormat,
SourceImageOpts: b.config.RunConfig.sourceImageOpts, SourceImageOpts: b.config.RunConfig.sourceImageOpts,
SourceMostRecent: b.config.SourceImageFilters.MostRecent, SourceMostRecent: b.config.SourceImageFilters.MostRecent,
SourceProperties: b.config.SourceImageFilters.Filters.Properties, SourceProperties: b.config.SourceImageFilters.Filters.Properties,

View File

@ -95,6 +95,8 @@ type FlatConfig struct {
SSHIPVersion *string `mapstructure:"ssh_ip_version" required:"false" cty:"ssh_ip_version" hcl:"ssh_ip_version"` SSHIPVersion *string `mapstructure:"ssh_ip_version" required:"false" cty:"ssh_ip_version" hcl:"ssh_ip_version"`
SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image" hcl:"source_image"` SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image" hcl:"source_image"`
SourceImageName *string `mapstructure:"source_image_name" required:"true" cty:"source_image_name" hcl:"source_image_name"` SourceImageName *string `mapstructure:"source_image_name" required:"true" cty:"source_image_name" hcl:"source_image_name"`
ExternalSourceImageURL *string `mapstructure:"external_source_image_url" required:"true" cty:"external_source_image_url" hcl:"external_source_image_url"`
ExternalSourceImageFormat *string `mapstructure:"external_source_image_format" required:"false" cty:"external_source_image_format" hcl:"external_source_image_format"`
SourceImageFilters *FlatImageFilter `mapstructure:"source_image_filter" required:"true" cty:"source_image_filter" hcl:"source_image_filter"` SourceImageFilters *FlatImageFilter `mapstructure:"source_image_filter" required:"true" cty:"source_image_filter" hcl:"source_image_filter"`
Flavor *string `mapstructure:"flavor" required:"true" cty:"flavor" hcl:"flavor"` Flavor *string `mapstructure:"flavor" required:"true" cty:"flavor" hcl:"flavor"`
AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"` AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"`
@ -220,6 +222,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"ssh_ip_version": &hcldec.AttrSpec{Name: "ssh_ip_version", Type: cty.String, Required: false}, "ssh_ip_version": &hcldec.AttrSpec{Name: "ssh_ip_version", Type: cty.String, Required: false},
"source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false}, "source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false},
"source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false},
"external_source_image_url": &hcldec.AttrSpec{Name: "external_source_image_url", Type: cty.String, Required: false},
"external_source_image_format": &hcldec.AttrSpec{Name: "external_source_image_format", Type: cty.String, Required: false},
"source_image_filter": &hcldec.BlockSpec{TypeName: "source_image_filter", Nested: hcldec.ObjectSpec((*FlatImageFilter)(nil).HCL2Spec())}, "source_image_filter": &hcldec.BlockSpec{TypeName: "source_image_filter", Nested: hcldec.ObjectSpec((*FlatImageFilter)(nil).HCL2Spec())},
"flavor": &hcldec.AttrSpec{Name: "flavor", Type: cty.String, Required: false}, "flavor": &hcldec.AttrSpec{Name: "flavor", Type: cty.String, Required: false},
"availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false},

View File

@ -33,6 +33,11 @@ type RunConfig struct {
// The name of the base image to use. This is an alternative way of // The name of the base image to use. This is an alternative way of
// providing source_image and only either of them can be specified. // providing source_image and only either of them can be specified.
SourceImageName string `mapstructure:"source_image_name" required:"true"` SourceImageName string `mapstructure:"source_image_name" required:"true"`
// The URL of an external base image to use. This is an alternative way of
// providing source_image and only either of them can be specified.
ExternalSourceImageURL string `mapstructure:"external_source_image_url" required:"true"`
// The format of the external source image to use, e.g. qcow2, raw.
ExternalSourceImageFormat string `mapstructure:"external_source_image_format" required:"false"`
// Filters used to populate filter options. Example: // Filters used to populate filter options. Example:
// //
// ```json // ```json
@ -247,10 +252,19 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
} }
} }
if c.SourceImage == "" && c.SourceImageName == "" && c.SourceImageFilters.Filters.Empty() { hasOnlySourceImage := len(c.SourceImage) > 0 && len(c.SourceImageName) == 0 && len(c.ExternalSourceImageURL) == 0
errs = append(errs, errors.New("Either a source_image, a source_image_name, or source_image_filter must be specified")) hasOnlySourceImageName := len(c.SourceImageName) > 0 && len(c.SourceImage) == 0 && len(c.ExternalSourceImageURL) == 0
} else if len(c.SourceImage) > 0 && len(c.SourceImageName) > 0 { hasOnlyExternalSourceImageURL := len(c.ExternalSourceImageURL) > 0 && len(c.SourceImage) == 0 && len(c.SourceImageName) == 0
errs = append(errs, errors.New("Only a source_image or a source_image_name can be specified, not both."))
if c.SourceImage == "" && c.SourceImageName == "" && c.ExternalSourceImageURL == "" && c.SourceImageFilters.Filters.Empty() {
errs = append(errs, errors.New("Either a source_image, a source_image_name, an external_source_image_url or source_image_filter must be specified"))
} else if !(hasOnlySourceImage || hasOnlySourceImageName || hasOnlyExternalSourceImageURL) {
errs = append(errs, errors.New("Only a source_image, a source_image_name or an external_source_image_url can be specified, not multiple."))
}
// if external_source_image_format is not set use qcow2 as default
if c.ExternalSourceImageFormat == "" {
c.ExternalSourceImageFormat = "qcow2"
} }
if c.Flavor == "" { if c.Flavor == "" {
@ -283,9 +297,9 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
} }
} }
// if neither ID or image name is provided outside the filter, build the // if neither ID, image name or external image URL is provided outside the filter,
// filter // build the filter
if len(c.SourceImage) == 0 && len(c.SourceImageName) == 0 { if len(c.SourceImage) == 0 && len(c.SourceImageName) == 0 && len(c.ExternalSourceImageURL) == 0 {
listOpts, filterErr := c.SourceImageFilters.Filters.Build() listOpts, filterErr := c.SourceImageFilters.Filters.Build()
@ -295,6 +309,11 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
c.sourceImageOpts = *listOpts c.sourceImageOpts = *listOpts
} }
// if c.ExternalSourceImageURL is set use a generated source image name
if c.ExternalSourceImageURL != "" {
c.SourceImageName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
return errs return errs
} }

View File

@ -2,6 +2,7 @@ package openstack
import ( import (
"os" "os"
"regexp"
"testing" "testing"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
@ -132,6 +133,49 @@ func TestRunConfigPrepare_FloatingIPPoolCompat(t *testing.T) {
} }
} }
func TestRunConfigPrepare_ExternalSourceImageURL(t *testing.T) {
c := testRunConfig()
// test setting both ExternalSourceImageURL and SourceImage causes an error
c.ExternalSourceImageURL = "http://example.com/image.qcow2"
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
// test setting both ExternalSourceImageURL and SourceImageName causes an error
c.SourceImage = ""
c.SourceImageName = "abcd"
c.ExternalSourceImageURL = "http://example.com/image.qcow2"
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
// test neither setting SourceImage, SourceImageName or ExternalSourceImageURL causes an error
c.SourceImage = ""
c.SourceImageName = ""
c.ExternalSourceImageURL = ""
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
// test setting only ExternalSourceImageURL passes
c.SourceImage = ""
c.SourceImageName = ""
c.ExternalSourceImageURL = "http://example.com/image.qcow2"
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
// test default values
if c.ExternalSourceImageFormat != "qcow2" {
t.Fatalf("ExternalSourceImageFormat should have been set to default: qcow2")
}
p := `packer_[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`
if matches, _ := regexp.MatchString(p, c.SourceImageName); !matches {
t.Fatalf("invalid format for SourceImageName: %s", c.SourceImageName)
}
}
// This test case confirms that only allowed fields will be set to values // This test case confirms that only allowed fields will be set to values
// The checked values are non-nil for their target type // The checked values are non-nil for their target type
func TestBuildImageFilter(t *testing.T) { func TestBuildImageFilter(t *testing.T) {

View File

@ -4,7 +4,9 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"time"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
"github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/pagination"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
@ -14,6 +16,8 @@ import (
type StepSourceImageInfo struct { type StepSourceImageInfo struct {
SourceImage string SourceImage string
SourceImageName string SourceImageName string
ExternalSourceImageURL string
ExternalSourceImageFormat string
SourceImageOpts images.ListOpts SourceImageOpts images.ListOpts
SourceMostRecent bool SourceMostRecent bool
SourceProperties map[string]string SourceProperties map[string]string
@ -33,12 +37,6 @@ func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag)
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
if s.SourceImage != "" {
state.Put("source_image", s.SourceImage)
return multistep.ActionContinue
}
client, err := config.imageV2Client() client, err := config.imageV2Client()
if err != nil { if err != nil {
err := fmt.Errorf("error creating image client: %s", err) err := fmt.Errorf("error creating image client: %s", err)
@ -47,6 +45,70 @@ func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag)
return multistep.ActionHalt return multistep.ActionHalt
} }
if s.ExternalSourceImageURL != "" {
createOpts := images.CreateOpts{
Name: s.SourceImageName,
ContainerFormat: "bare",
DiskFormat: s.ExternalSourceImageFormat,
Properties: map[string]string{
"packer_external_source_image_url": s.ExternalSourceImageURL,
"packer_external_source_image_format": s.ExternalSourceImageFormat,
},
}
ui.Say("Creating image using external source image with name " + s.SourceImageName)
ui.Say("Using disk format " + s.ExternalSourceImageFormat)
image, err := images.Create(client, createOpts).Extract()
if err != nil {
err := fmt.Errorf("Error creating source image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Created image with ID " + image.ID)
importOpts := imageimport.CreateOpts{
Name: imageimport.WebDownloadMethod,
URI: s.ExternalSourceImageURL,
}
ui.Say("Importing External Source Image from URL " + s.ExternalSourceImageURL)
err = imageimport.Create(client, image.ID, importOpts).ExtractErr()
if err != nil {
err := fmt.Errorf("Error importing source image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
for image.Status != images.ImageStatusActive {
ui.Message("Image not Active, retrying in 10 seconds")
time.Sleep(10 * time.Second)
img, err := images.Get(client, image.ID).Extract()
if err != nil {
err := fmt.Errorf("Error querying image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
image = img
}
s.SourceImage = image.ID
}
if s.SourceImage != "" {
state.Put("source_image", s.SourceImage)
return multistep.ActionContinue
}
if s.SourceImageName != "" { if s.SourceImageName != "" {
s.SourceImageOpts = images.ListOpts{ s.SourceImageOpts = images.ListOpts{
Name: s.SourceImageName, Name: s.SourceImageName,
@ -117,5 +179,25 @@ func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag)
} }
func (s *StepSourceImageInfo) Cleanup(state multistep.StateBag) { func (s *StepSourceImageInfo) Cleanup(state multistep.StateBag) {
// No cleanup required for backout if s.ExternalSourceImageURL != "" {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
client, err := config.imageV2Client()
if err != nil {
err := fmt.Errorf("error creating image client: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return
}
ui.Say(fmt.Sprintf("Deleting temporary external source image: %s ...", s.SourceImageName))
err = images.Delete(client, s.SourceImage).ExtractErr()
if err != nil {
err := fmt.Errorf("error cleaning up external source image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return
}
}
} }

View File

@ -86,6 +86,7 @@ type diskConfig struct {
Size string `mapstructure:"disk_size"` Size string `mapstructure:"disk_size"`
CacheMode string `mapstructure:"cache_mode"` CacheMode string `mapstructure:"cache_mode"`
DiskFormat string `mapstructure:"format"` DiskFormat string `mapstructure:"format"`
IOThread bool `mapstructure:"io_thread"`
} }
type vgaConfig struct { type vgaConfig struct {
Type string `mapstructure:"type"` Type string `mapstructure:"type"`
@ -189,6 +190,17 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
log.Printf("Disk %d cache mode not set, using default 'none'", idx) log.Printf("Disk %d cache mode not set, using default 'none'", idx)
c.Disks[idx].CacheMode = "none" c.Disks[idx].CacheMode = "none"
} }
if c.Disks[idx].IOThread {
// io thread is only supported by virtio-scsi-single controller
if c.SCSIController != "virtio-scsi-single" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("io thread option requires virtio-scsi-single controller"))
} else {
// ... and only for virtio and scsi disks
if !(c.Disks[idx].Type == "scsi" || c.Disks[idx].Type == "virtio") {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("io thread option requires scsi or a virtio disk"))
}
}
}
// For any storage pool types which aren't in rxStorageTypes in proxmox-api/proxmox/config_qemu.go:890 // For any storage pool types which aren't in rxStorageTypes in proxmox-api/proxmox/config_qemu.go:890
// (currently zfspool|lvm|rbd|cephfs), the format parameter is mandatory. Make sure this is still up to date // (currently zfspool|lvm|rbd|cephfs), the format parameter is mandatory. Make sure this is still up to date
// when updating the vendored code! // when updating the vendored code!

View File

@ -230,6 +230,7 @@ type FlatdiskConfig struct {
Size *string `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"` Size *string `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
CacheMode *string `mapstructure:"cache_mode" cty:"cache_mode" hcl:"cache_mode"` CacheMode *string `mapstructure:"cache_mode" cty:"cache_mode" hcl:"cache_mode"`
DiskFormat *string `mapstructure:"format" cty:"format" hcl:"format"` DiskFormat *string `mapstructure:"format" cty:"format" hcl:"format"`
IOThread *bool `mapstructure:"io_thread" cty:"io_thread" hcl:"io_thread"`
} }
// FlatMapstructure returns a new FlatdiskConfig. // FlatMapstructure returns a new FlatdiskConfig.
@ -250,6 +251,7 @@ func (*FlatdiskConfig) HCL2Spec() map[string]hcldec.Spec {
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.String, Required: false}, "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.String, Required: false},
"cache_mode": &hcldec.AttrSpec{Name: "cache_mode", Type: cty.String, Required: false}, "cache_mode": &hcldec.AttrSpec{Name: "cache_mode", Type: cty.String, Required: false},
"format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false},
"io_thread": &hcldec.AttrSpec{Name: "io_thread", Type: cty.Bool, Required: false},
} }
return s return s
} }

View File

@ -208,3 +208,52 @@ func TestPacketQueueSupportForNetworkAdapters(t *testing.T) {
} }
} }
} }
func TestHardDiskControllerIOThreadSupport(t *testing.T) {
drivertests := []struct {
expectedToFail bool
controller string
disk_type string
}{
// io thread is only supported by virtio-scsi-single controller
// and only for virtio and scsi disks
{expectedToFail: false, controller: "virtio-scsi-single", disk_type: "scsi"},
{expectedToFail: false, controller: "virtio-scsi-single", disk_type: "virtio"},
{expectedToFail: true, controller: "virtio-scsi-single", disk_type: "sata"},
{expectedToFail: true, controller: "lsi", disk_type: "scsi"},
{expectedToFail: true, controller: "lsi53c810", disk_type: "virtio"},
}
for _, tt := range drivertests {
nic := make(map[string]interface{})
nic["bridge"] = "vmbr0"
nics := make([]map[string]interface{}, 0)
nics = append(nics, nic)
disk := make(map[string]interface{})
disk["type"] = tt.disk_type
disk["io_thread"] = true
disk["storage_pool"] = "local-lvm"
disk["storage_pool_type"] = "lvm"
disks := make([]map[string]interface{}, 0)
disks = append(disks, disk)
cfg := mandatoryConfig(t)
cfg["network_adapters"] = nics
cfg["disks"] = disks
cfg["scsi_controller"] = tt.controller
var c Config
_, err := c.Prepare(cfg)
if tt.expectedToFail == true && err == nil {
t.Error("expected config preparation to fail, but no error occured")
}
if tt.expectedToFail == false && err != nil {
t.Errorf("expected config preparation to succeed, but %s", err.Error())
}
}
}

View File

@ -142,6 +142,10 @@ func generateProxmoxDisks(disks []diskConfig) proxmox.QemuDevices {
setDeviceParamIfDefined(devs[idx], "storage_type", disks[idx].StoragePoolType) setDeviceParamIfDefined(devs[idx], "storage_type", disks[idx].StoragePoolType)
setDeviceParamIfDefined(devs[idx], "cache", disks[idx].CacheMode) setDeviceParamIfDefined(devs[idx], "cache", disks[idx].CacheMode)
setDeviceParamIfDefined(devs[idx], "format", disks[idx].DiskFormat) setDeviceParamIfDefined(devs[idx], "format", disks[idx].DiskFormat)
if devs[idx]["type"] == "scsi" || devs[idx]["type"] == "virtio" {
setDeviceParamIfDefined(devs[idx], "iothread", strconv.FormatBool(disks[idx].IOThread))
}
} }
return devs return devs
} }

View File

@ -137,6 +137,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
OutputDir: b.config.OutputDir, OutputDir: b.config.OutputDir,
SkipCompaction: b.config.SkipCompaction, SkipCompaction: b.config.SkipCompaction,
VMName: b.config.VMName, VMName: b.config.VMName,
QemuImgArgs: b.config.QemuImgArgs,
}, },
) )

View File

@ -11,6 +11,7 @@ import (
type StepPrepareTools struct { type StepPrepareTools struct {
RemoteType string RemoteType string
ToolsUploadFlavor string ToolsUploadFlavor string
ToolsSourcePath string
} }
func (c *StepPrepareTools) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (c *StepPrepareTools) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
@ -20,11 +21,15 @@ func (c *StepPrepareTools) Run(ctx context.Context, state multistep.StateBag) mu
return multistep.ActionContinue return multistep.ActionContinue
} }
if c.ToolsUploadFlavor == "" { if c.ToolsUploadFlavor == "" && c.ToolsSourcePath == "" {
return multistep.ActionContinue return multistep.ActionContinue
} }
path := driver.ToolsIsoPath(c.ToolsUploadFlavor) path := c.ToolsSourcePath
if path == "" {
path = driver.ToolsIsoPath(c.ToolsUploadFlavor)
}
if _, err := os.Stat(path); err != nil { if _, err := os.Stat(path); err != nil {
state.Put("error", fmt.Errorf( state.Put("error", fmt.Errorf(
"Couldn't find VMware tools for '%s'! VMware often downloads these\n"+ "Couldn't find VMware tools for '%s'! VMware often downloads these\n"+

View File

@ -114,3 +114,65 @@ func TestStepPrepareTools_nonExist(t *testing.T) {
t.Fatal("should NOT have tools_upload_source") t.Fatal("should NOT have tools_upload_source")
} }
} }
func TestStepPrepareTools_SourcePath(t *testing.T) {
state := testState(t)
step := &StepPrepareTools{
RemoteType: "",
ToolsSourcePath: "/path/to/tool.iso",
}
driver := state.Get("driver").(*DriverMock)
// Mock results
driver.ToolsIsoPathResult = "foo"
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("Should have failed when stat failed %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatal("should have error")
}
// Test the driver
if driver.ToolsIsoPathCalled {
t.Fatal("tools iso path should not be called when ToolsSourcePath is set")
}
// Test the resulting state
if _, ok := state.GetOk("tools_upload_source"); ok {
t.Fatal("should NOT have tools_upload_source")
}
}
func TestStepPrepareTools_SourcePath_exists(t *testing.T) {
state := testState(t)
step := &StepPrepareTools{
RemoteType: "",
ToolsSourcePath: "./step_prepare_tools.go",
}
driver := state.Get("driver").(*DriverMock)
// Mock results
driver.ToolsIsoPathResult = "foo"
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Step should succeed when stat succeeds: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
// Test the driver
if driver.ToolsIsoPathCalled {
t.Fatal("tools iso path should not be called when ToolsSourcePath is set")
}
// Test the resulting state
if _, ok := state.GetOk("tools_upload_source"); !ok {
t.Fatal("should have tools_upload_source")
}
}

View File

@ -3,6 +3,8 @@
package common package common
import ( import (
"fmt"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
@ -18,12 +20,22 @@ type ToolsConfig struct {
// the upload path is set to `{{.Flavor}}.iso`. This setting is not used // the upload path is set to `{{.Flavor}}.iso`. This setting is not used
// when `remote_type` is `esx5`. // when `remote_type` is `esx5`.
ToolsUploadPath string `mapstructure:"tools_upload_path" required:"false"` ToolsUploadPath string `mapstructure:"tools_upload_path" required:"false"`
// The path on your local machine to fetch the vmware tools from. If this
// is not set but the tools_upload_flavor is set, then Packer will try to
// load the VMWare tools from the VMWare installation directory.
ToolsSourcePath string `mapstructure:"tools_source_path" required:"false"`
} }
func (c *ToolsConfig) Prepare(ctx *interpolate.Context) []error { func (c *ToolsConfig) Prepare(ctx *interpolate.Context) []error {
errs := []error{}
if c.ToolsUploadPath == "" { if c.ToolsUploadPath == "" {
if c.ToolsSourcePath != "" && c.ToolsUploadFlavor == "" {
errs = append(errs, fmt.Errorf("If you provide a "+
"tools_source_path, you must also provide either a "+
"tools_upload_flavor or a tools_upload_path."))
}
c.ToolsUploadPath = "{{ .Flavor }}.iso" c.ToolsUploadPath = "{{ .Flavor }}.iso"
} }
return nil return errs
} }

View File

@ -0,0 +1,65 @@
package common
import (
"testing"
"github.com/hashicorp/packer/template/interpolate"
)
func TestToolsConfigPrepare_Empty(t *testing.T) {
c := &ToolsConfig{}
errs := c.Prepare(interpolate.NewContext())
if len(errs) > 0 {
t.Fatalf("err: %#v", errs)
}
if c.ToolsUploadPath != "{{ .Flavor }}.iso" {
t.Fatal("should have defaulted tools upload path")
}
}
func TestToolsConfigPrepare_SetUploadPath(t *testing.T) {
c := &ToolsConfig{
ToolsUploadPath: "path/to/tools.iso",
}
errs := c.Prepare(interpolate.NewContext())
if len(errs) > 0 {
t.Fatalf("err: %#v", errs)
}
if c.ToolsUploadPath != "path/to/tools.iso" {
t.Fatal("should have used given tools upload path")
}
}
func TestToolsConfigPrepare_ErrorIfOnlySource(t *testing.T) {
c := &ToolsConfig{
ToolsSourcePath: "path/to/tools.iso",
}
errs := c.Prepare(interpolate.NewContext())
if len(errs) != 1 {
t.Fatalf("Should have received an error because the flavor and " +
"upload path aren't set")
}
}
func TestToolsConfigPrepare_SourceSuccess(t *testing.T) {
for _, c := range []*ToolsConfig{
&ToolsConfig{
ToolsSourcePath: "path/to/tools.iso",
ToolsUploadPath: "partypath.iso",
},
&ToolsConfig{
ToolsSourcePath: "path/to/tools.iso",
ToolsUploadFlavor: "linux",
},
} {
errs := c.Prepare(interpolate.NewContext())
if len(errs) != 0 {
t.Fatalf("Should not have received an error")
}
}
}

View File

@ -119,6 +119,7 @@ type FlatConfig struct {
SSHSkipRequestPty *bool `mapstructure:"ssh_skip_request_pty" cty:"ssh_skip_request_pty" hcl:"ssh_skip_request_pty"` SSHSkipRequestPty *bool `mapstructure:"ssh_skip_request_pty" cty:"ssh_skip_request_pty" hcl:"ssh_skip_request_pty"`
ToolsUploadFlavor *string `mapstructure:"tools_upload_flavor" required:"false" cty:"tools_upload_flavor" hcl:"tools_upload_flavor"` ToolsUploadFlavor *string `mapstructure:"tools_upload_flavor" required:"false" cty:"tools_upload_flavor" hcl:"tools_upload_flavor"`
ToolsUploadPath *string `mapstructure:"tools_upload_path" required:"false" cty:"tools_upload_path" hcl:"tools_upload_path"` ToolsUploadPath *string `mapstructure:"tools_upload_path" required:"false" cty:"tools_upload_path" hcl:"tools_upload_path"`
ToolsSourcePath *string `mapstructure:"tools_source_path" required:"false" cty:"tools_source_path" hcl:"tools_source_path"`
VMXData map[string]string `mapstructure:"vmx_data" required:"false" cty:"vmx_data" hcl:"vmx_data"` VMXData map[string]string `mapstructure:"vmx_data" required:"false" cty:"vmx_data" hcl:"vmx_data"`
VMXDataPost map[string]string `mapstructure:"vmx_data_post" required:"false" cty:"vmx_data_post" hcl:"vmx_data_post"` VMXDataPost map[string]string `mapstructure:"vmx_data_post" required:"false" cty:"vmx_data_post" hcl:"vmx_data_post"`
VMXRemoveEthernet *bool `mapstructure:"vmx_remove_ethernet_interfaces" required:"false" cty:"vmx_remove_ethernet_interfaces" hcl:"vmx_remove_ethernet_interfaces"` VMXRemoveEthernet *bool `mapstructure:"vmx_remove_ethernet_interfaces" required:"false" cty:"vmx_remove_ethernet_interfaces" hcl:"vmx_remove_ethernet_interfaces"`
@ -263,6 +264,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"ssh_skip_request_pty": &hcldec.AttrSpec{Name: "ssh_skip_request_pty", Type: cty.Bool, Required: false}, "ssh_skip_request_pty": &hcldec.AttrSpec{Name: "ssh_skip_request_pty", Type: cty.Bool, Required: false},
"tools_upload_flavor": &hcldec.AttrSpec{Name: "tools_upload_flavor", Type: cty.String, Required: false}, "tools_upload_flavor": &hcldec.AttrSpec{Name: "tools_upload_flavor", Type: cty.String, Required: false},
"tools_upload_path": &hcldec.AttrSpec{Name: "tools_upload_path", Type: cty.String, Required: false}, "tools_upload_path": &hcldec.AttrSpec{Name: "tools_upload_path", Type: cty.String, Required: false},
"tools_source_path": &hcldec.AttrSpec{Name: "tools_source_path", Type: cty.String, Required: false},
"vmx_data": &hcldec.AttrSpec{Name: "vmx_data", Type: cty.Map(cty.String), Required: false}, "vmx_data": &hcldec.AttrSpec{Name: "vmx_data", Type: cty.Map(cty.String), Required: false},
"vmx_data_post": &hcldec.AttrSpec{Name: "vmx_data_post", Type: cty.Map(cty.String), Required: false}, "vmx_data_post": &hcldec.AttrSpec{Name: "vmx_data_post", Type: cty.Map(cty.String), Required: false},
"vmx_remove_ethernet_interfaces": &hcldec.AttrSpec{Name: "vmx_remove_ethernet_interfaces", Type: cty.Bool, Required: false}, "vmx_remove_ethernet_interfaces": &hcldec.AttrSpec{Name: "vmx_remove_ethernet_interfaces", Type: cty.Bool, Required: false},

View File

@ -104,6 +104,7 @@ type FlatConfig struct {
SSHSkipRequestPty *bool `mapstructure:"ssh_skip_request_pty" cty:"ssh_skip_request_pty" hcl:"ssh_skip_request_pty"` SSHSkipRequestPty *bool `mapstructure:"ssh_skip_request_pty" cty:"ssh_skip_request_pty" hcl:"ssh_skip_request_pty"`
ToolsUploadFlavor *string `mapstructure:"tools_upload_flavor" required:"false" cty:"tools_upload_flavor" hcl:"tools_upload_flavor"` ToolsUploadFlavor *string `mapstructure:"tools_upload_flavor" required:"false" cty:"tools_upload_flavor" hcl:"tools_upload_flavor"`
ToolsUploadPath *string `mapstructure:"tools_upload_path" required:"false" cty:"tools_upload_path" hcl:"tools_upload_path"` ToolsUploadPath *string `mapstructure:"tools_upload_path" required:"false" cty:"tools_upload_path" hcl:"tools_upload_path"`
ToolsSourcePath *string `mapstructure:"tools_source_path" required:"false" cty:"tools_source_path" hcl:"tools_source_path"`
VMXData map[string]string `mapstructure:"vmx_data" required:"false" cty:"vmx_data" hcl:"vmx_data"` VMXData map[string]string `mapstructure:"vmx_data" required:"false" cty:"vmx_data" hcl:"vmx_data"`
VMXDataPost map[string]string `mapstructure:"vmx_data_post" required:"false" cty:"vmx_data_post" hcl:"vmx_data_post"` VMXDataPost map[string]string `mapstructure:"vmx_data_post" required:"false" cty:"vmx_data_post" hcl:"vmx_data_post"`
VMXRemoveEthernet *bool `mapstructure:"vmx_remove_ethernet_interfaces" required:"false" cty:"vmx_remove_ethernet_interfaces" hcl:"vmx_remove_ethernet_interfaces"` VMXRemoveEthernet *bool `mapstructure:"vmx_remove_ethernet_interfaces" required:"false" cty:"vmx_remove_ethernet_interfaces" hcl:"vmx_remove_ethernet_interfaces"`
@ -229,6 +230,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"ssh_skip_request_pty": &hcldec.AttrSpec{Name: "ssh_skip_request_pty", Type: cty.Bool, Required: false}, "ssh_skip_request_pty": &hcldec.AttrSpec{Name: "ssh_skip_request_pty", Type: cty.Bool, Required: false},
"tools_upload_flavor": &hcldec.AttrSpec{Name: "tools_upload_flavor", Type: cty.String, Required: false}, "tools_upload_flavor": &hcldec.AttrSpec{Name: "tools_upload_flavor", Type: cty.String, Required: false},
"tools_upload_path": &hcldec.AttrSpec{Name: "tools_upload_path", Type: cty.String, Required: false}, "tools_upload_path": &hcldec.AttrSpec{Name: "tools_upload_path", Type: cty.String, Required: false},
"tools_source_path": &hcldec.AttrSpec{Name: "tools_source_path", Type: cty.String, Required: false},
"vmx_data": &hcldec.AttrSpec{Name: "vmx_data", Type: cty.Map(cty.String), Required: false}, "vmx_data": &hcldec.AttrSpec{Name: "vmx_data", Type: cty.Map(cty.String), Required: false},
"vmx_data_post": &hcldec.AttrSpec{Name: "vmx_data_post", Type: cty.Map(cty.String), Required: false}, "vmx_data_post": &hcldec.AttrSpec{Name: "vmx_data_post", Type: cty.Map(cty.String), Required: false},
"vmx_remove_ethernet_interfaces": &hcldec.AttrSpec{Name: "vmx_remove_ethernet_interfaces", Type: cty.Bool, Required: false}, "vmx_remove_ethernet_interfaces": &hcldec.AttrSpec{Name: "vmx_remove_ethernet_interfaces", Type: cty.Bool, Required: false},

View File

@ -41,6 +41,15 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepConnect{ &common.StepConnect{
Config: &b.config.ConnectConfig, Config: &b.config.ConnectConfig,
}, },
&packerCommon.StepCreateCD{
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&common.StepRemoteUpload{
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&StepCloneVM{ &StepCloneVM{
Config: &b.config.CloneConfig, Config: &b.config.CloneConfig,
Location: &b.config.LocationConfig, Location: &b.config.LocationConfig,
@ -49,6 +58,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepConfigureHardware{ &common.StepConfigureHardware{
Config: &b.config.HardwareConfig, Config: &b.config.HardwareConfig,
}, },
&common.StepAddCDRom{
Config: &b.config.CDRomConfig,
},
&common.StepConfigParams{ &common.StepConfigParams{
Config: &b.config.ConfigParamsConfig, Config: &b.config.ConfigParamsConfig,
}, },
@ -62,6 +74,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
if b.config.Comm.Type != "none" { if b.config.Comm.Type != "none" {
steps = append(steps, steps = append(steps,
&packerCommon.StepCreateFloppy{
Files: b.config.FloppyFiles,
Directories: b.config.FloppyDirectories,
Label: b.config.FloppyLabel,
},
&common.StepAddFloppy{
Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&common.StepHTTPIPDiscover{ &common.StepHTTPIPDiscover{
HTTPIP: b.config.BootConfig.HTTPIP, HTTPIP: b.config.BootConfig.HTTPIP,
Network: b.config.WaitIpConfig.GetIPNet(), Network: b.config.WaitIpConfig.GetIPNet(),
@ -98,10 +121,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepShutdown{ &common.StepShutdown{
Config: &b.config.ShutdownConfig, Config: &b.config.ShutdownConfig,
}, },
&common.StepRemoveFloppy{
Datastore: b.config.Datastore,
Host: b.config.Host,
},
) )
} }
steps = append(steps, steps = append(steps,
&common.StepRemoveCDRom{
Config: &b.config.RemoveCDRomConfig,
},
&common.StepCreateSnapshot{ &common.StepCreateSnapshot{
CreateSnapshot: b.config.CreateSnapshot, CreateSnapshot: b.config.CreateSnapshot,
}, },

View File

@ -15,6 +15,7 @@ import (
type Config struct { type Config struct {
packerCommon.PackerConfig `mapstructure:",squash"` packerCommon.PackerConfig `mapstructure:",squash"`
packerCommon.HTTPConfig `mapstructure:",squash"` packerCommon.HTTPConfig `mapstructure:",squash"`
packerCommon.CDConfig `mapstructure:",squash"`
common.ConnectConfig `mapstructure:",squash"` common.ConnectConfig `mapstructure:",squash"`
CloneConfig `mapstructure:",squash"` CloneConfig `mapstructure:",squash"`
@ -22,6 +23,9 @@ type Config struct {
common.HardwareConfig `mapstructure:",squash"` common.HardwareConfig `mapstructure:",squash"`
common.ConfigParamsConfig `mapstructure:",squash"` common.ConfigParamsConfig `mapstructure:",squash"`
common.CDRomConfig `mapstructure:",squash"`
common.RemoveCDRomConfig `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"` common.RunConfig `mapstructure:",squash"`
common.BootConfig `mapstructure:",squash"` common.BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"` common.WaitIpConfig `mapstructure:",squash"`
@ -67,6 +71,8 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...) errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...) errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)

View File

@ -22,6 +22,8 @@ type FlatConfig struct {
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server" hcl:"vcenter_server"` VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server" hcl:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"` Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"` Password *string `mapstructure:"password" cty:"password" hcl:"password"`
@ -58,6 +60,13 @@ type FlatConfig struct {
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters" hcl:"configuration_parameters"` ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters" hcl:"configuration_parameters"`
ToolsSyncTime *bool `mapstructure:"tools_sync_time" cty:"tools_sync_time" hcl:"tools_sync_time"` ToolsSyncTime *bool `mapstructure:"tools_sync_time" cty:"tools_sync_time" hcl:"tools_sync_time"`
ToolsUpgradePolicy *bool `mapstructure:"tools_upgrade_policy" cty:"tools_upgrade_policy" hcl:"tools_upgrade_policy"` ToolsUpgradePolicy *bool `mapstructure:"tools_upgrade_policy" cty:"tools_upgrade_policy" hcl:"tools_upgrade_policy"`
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type" hcl:"cdrom_type"`
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths" hcl:"iso_paths"`
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"`
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"` BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
@ -147,6 +156,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false}, "http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false},
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false}, "vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false}, "username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
@ -183,6 +194,13 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"configuration_parameters": &hcldec.AttrSpec{Name: "configuration_parameters", Type: cty.Map(cty.String), Required: false}, "configuration_parameters": &hcldec.AttrSpec{Name: "configuration_parameters", Type: cty.Map(cty.String), Required: false},
"tools_sync_time": &hcldec.AttrSpec{Name: "tools_sync_time", Type: cty.Bool, Required: false}, "tools_sync_time": &hcldec.AttrSpec{Name: "tools_sync_time", Type: cty.Bool, Required: false},
"tools_upgrade_policy": &hcldec.AttrSpec{Name: "tools_upgrade_policy", Type: cty.Bool, Required: false}, "tools_upgrade_policy": &hcldec.AttrSpec{Name: "tools_upgrade_policy", Type: cty.Bool, Required: false},
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
"iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false},
"remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false},
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false}, "boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false}, "boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false}, "boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},

View File

@ -1,7 +1,7 @@
//go:generate struct-markdown //go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type CDRomConfig //go:generate mapstructure-to-hcl2 -type CDRomConfig
package iso package common
import ( import (
"context" "context"

View File

@ -1,5 +1,5 @@
// Code generated by "mapstructure-to-hcl2 -type CDRomConfig"; DO NOT EDIT. // Code generated by "mapstructure-to-hcl2 -type CDRomConfig"; DO NOT EDIT.
package iso package common
import ( import (
"github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcldec"

View File

@ -1,4 +1,4 @@
package iso package common
import ( import (
"context" "context"
@ -96,7 +96,7 @@ func TestStepAddCDRom_Run(t *testing.T) {
}, },
{ {
name: "Add SATA Controller", name: "Add SATA Controller",
state: basicStateBag(), state: basicStateBag(nil),
step: &StepAddCDRom{ step: &StepAddCDRom{
Config: &CDRomConfig{ Config: &CDRomConfig{
CdromType: "sata", CdromType: "sata",
@ -116,7 +116,7 @@ func TestStepAddCDRom_Run(t *testing.T) {
}, },
{ {
name: "Fail to add SATA Controller", name: "Fail to add SATA Controller",
state: basicStateBag(), state: basicStateBag(nil),
step: &StepAddCDRom{ step: &StepAddCDRom{
Config: &CDRomConfig{ Config: &CDRomConfig{
CdromType: "sata", CdromType: "sata",
@ -136,7 +136,7 @@ func TestStepAddCDRom_Run(t *testing.T) {
}, },
{ {
name: "IDE CDRom Type and Iso Path set", name: "IDE CDRom Type and Iso Path set",
state: basicStateBag(), state: basicStateBag(nil),
step: &StepAddCDRom{ step: &StepAddCDRom{
Config: &CDRomConfig{ Config: &CDRomConfig{
CdromType: "ide", CdromType: "ide",
@ -156,7 +156,7 @@ func TestStepAddCDRom_Run(t *testing.T) {
}, },
{ {
name: "Fail to add cdrom from ISOPaths", name: "Fail to add cdrom from ISOPaths",
state: basicStateBag(), state: basicStateBag(nil),
step: &StepAddCDRom{ step: &StepAddCDRom{
Config: &CDRomConfig{ Config: &CDRomConfig{
ISOPaths: []string{"iso/path"}, ISOPaths: []string{"iso/path"},
@ -222,14 +222,14 @@ func TestStepAddCDRom_Run(t *testing.T) {
} }
func cdAndIsoRemotePathStateBag() *multistep.BasicStateBag { func cdAndIsoRemotePathStateBag() *multistep.BasicStateBag {
state := basicStateBag() state := basicStateBag(nil)
state.Put("iso_remote_path", "remote/path") state.Put("iso_remote_path", "remote/path")
state.Put("cd_path", "cd/path") state.Put("cd_path", "cd/path")
return state return state
} }
func isoRemotePathStateBag() *multistep.BasicStateBag { func isoRemotePathStateBag() *multistep.BasicStateBag {
state := basicStateBag() state := basicStateBag(nil)
state.Put("iso_remote_path", "remote/path") state.Put("iso_remote_path", "remote/path")
return state return state
} }

View File

@ -1,7 +1,7 @@
//go:generate struct-markdown //go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type FloppyConfig //go:generate mapstructure-to-hcl2 -type FloppyConfig
package iso package common
import ( import (
"context" "context"
@ -37,8 +37,8 @@ type StepAddFloppy struct {
func (s *StepAddFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepAddFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver) vm := state.Get("vm").(driver.VirtualMachine)
d := state.Get("driver").(*driver.VCenterDriver) d := state.Get("driver").(driver.Driver)
if floppyPath, ok := state.GetOk("floppy_path"); ok { if floppyPath, ok := state.GetOk("floppy_path"); ok {
ui.Say("Uploading created floppy image") ui.Say("Uploading created floppy image")
@ -90,7 +90,7 @@ func (s *StepAddFloppy) Cleanup(state multistep.StateBag) {
} }
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
d := state.Get("driver").(*driver.VCenterDriver) d := state.Get("driver").(driver.Driver)
if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok { if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok {
ui.Say("Deleting Floppy image ...") ui.Say("Deleting Floppy image ...")

View File

@ -1,5 +1,5 @@
// Code generated by "mapstructure-to-hcl2 -type FloppyConfig"; DO NOT EDIT. // Code generated by "mapstructure-to-hcl2 -type FloppyConfig"; DO NOT EDIT.
package iso package common
import ( import (
"github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcldec"

View File

@ -0,0 +1,453 @@
package common
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepAddFloppy_Run(t *testing.T) {
tc := []struct {
name string
floppyPath string
uploadedPath string
step *StepAddFloppy
expectedAction multistep.StepAction
vmMock *driver.VirtualMachineMock
expectedVmMock *driver.VirtualMachineMock
driverMock *driver.DriverMock
expectedDriverMock *driver.DriverMock
dsMock *driver.DatastoreMock
expectedDsMock *driver.DatastoreMock
fail bool
errMessage string
}{
{
name: "Add floppy from state floppy path",
floppyPath: "floppy/path",
uploadedPath: "vm/dir/packer-tmp-created-floppy.flp",
step: &StepAddFloppy{
Config: new(FloppyConfig),
Datastore: "datastore",
Host: "host",
SetHostForDatastoreUploads: true,
},
expectedAction: multistep.ActionContinue,
vmMock: &driver.VirtualMachineMock{
GetDirResponse: "vm/dir",
},
expectedVmMock: &driver.VirtualMachineMock{
GetDirResponse: "vm/dir",
GetDirCalled: true,
AddFloppyCalled: true,
AddFloppyImagePath: "resolved/path",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
ResolvePathReturn: "resolved/path",
},
expectedDsMock: &driver.DatastoreMock{
UploadFileCalled: true,
UploadFileSrc: "floppy/path",
UploadFileDst: "vm/dir/packer-tmp-created-floppy.flp",
UploadFileHost: "host",
UploadFileSetHost: true,
ResolvePathCalled: true,
ResolvePathReturn: "resolved/path",
},
fail: false,
},
{
name: "State floppy path - find datastore fail",
floppyPath: "floppy/path",
step: &StepAddFloppy{
Config: new(FloppyConfig),
Datastore: "datastore",
Host: "host",
SetHostForDatastoreUploads: true,
},
expectedAction: multistep.ActionHalt,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: new(driver.VirtualMachineMock),
driverMock: &driver.DriverMock{
FindDatastoreErr: fmt.Errorf("error finding datastore"),
},
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "error finding datastore",
},
{
name: "State floppy path - vm get dir fail",
floppyPath: "floppy/path",
step: &StepAddFloppy{
Config: new(FloppyConfig),
Datastore: "datastore",
Host: "host",
SetHostForDatastoreUploads: true,
},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
GetDirErr: fmt.Errorf("fail to get vm dir"),
},
expectedVmMock: &driver.VirtualMachineMock{
GetDirCalled: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "fail to get vm dir",
},
{
name: "State floppy path - datastore upload file fail",
floppyPath: "floppy/path",
step: &StepAddFloppy{
Config: new(FloppyConfig),
Datastore: "datastore",
Host: "host",
SetHostForDatastoreUploads: true,
},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
GetDirResponse: "vm/dir",
},
expectedVmMock: &driver.VirtualMachineMock{
GetDirResponse: "vm/dir",
GetDirCalled: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
UploadFileErr: fmt.Errorf("failed to upload file"),
},
expectedDsMock: &driver.DatastoreMock{
UploadFileCalled: true,
UploadFileSrc: "floppy/path",
UploadFileDst: "vm/dir/packer-tmp-created-floppy.flp",
UploadFileHost: "host",
UploadFileSetHost: true,
},
fail: true,
errMessage: "failed to upload file",
},
{
name: "State floppy path - vm fail to add floppy",
floppyPath: "floppy/path",
uploadedPath: "vm/dir/packer-tmp-created-floppy.flp",
step: &StepAddFloppy{
Config: new(FloppyConfig),
Datastore: "datastore",
Host: "host",
SetHostForDatastoreUploads: true,
},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
GetDirResponse: "vm/dir",
AddFloppyErr: fmt.Errorf("failed to add floppy"),
},
expectedVmMock: &driver.VirtualMachineMock{
GetDirResponse: "vm/dir",
GetDirCalled: true,
AddFloppyCalled: true,
AddFloppyImagePath: "resolved/path",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
ResolvePathReturn: "resolved/path",
},
expectedDsMock: &driver.DatastoreMock{
UploadFileCalled: true,
UploadFileSrc: "floppy/path",
UploadFileDst: "vm/dir/packer-tmp-created-floppy.flp",
UploadFileHost: "host",
UploadFileSetHost: true,
ResolvePathCalled: true,
ResolvePathReturn: "resolved/path",
},
fail: true,
errMessage: "failed to add floppy",
},
{
name: "Add floppy from FloppyIMGPath config",
step: &StepAddFloppy{
Config: &FloppyConfig{
FloppyIMGPath: "floppy/image/path",
},
},
expectedAction: multistep.ActionContinue,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
AddFloppyCalled: true,
AddFloppyImagePath: "floppy/image/path",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: false,
},
{
name: "Fail to add floppy from FloppyIMGPath config",
step: &StepAddFloppy{
Config: &FloppyConfig{
FloppyIMGPath: "floppy/image/path",
},
},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
AddFloppyErr: fmt.Errorf("fail to add floppy"),
},
expectedVmMock: &driver.VirtualMachineMock{
AddFloppyCalled: true,
AddFloppyImagePath: "floppy/image/path",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "fail to add floppy",
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
state := basicStateBag(nil)
state.Put("vm", c.vmMock)
c.driverMock.DatastoreMock = c.dsMock
state.Put("driver", c.driverMock)
if c.floppyPath != "" {
state.Put("floppy_path", c.floppyPath)
}
if action := c.step.Run(context.TODO(), state); action != c.expectedAction {
t.Fatalf("unexpected action %v", action)
}
err, ok := state.Get("error").(error)
if ok {
if err.Error() != c.errMessage {
t.Fatalf("unexpected error %s", err.Error())
}
} else {
if c.fail {
t.Fatalf("expected to fail but it didn't")
}
}
uploadedPath, _ := state.Get("uploaded_floppy_path").(string)
if uploadedPath != c.uploadedPath {
t.Fatalf("Unexpected uploaded path state %s", uploadedPath)
}
if diff := cmp.Diff(c.vmMock, c.expectedVmMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected VirtualMachine calls: %s", diff)
}
c.expectedDriverMock.DatastoreMock = c.expectedDsMock
if diff := cmp.Diff(c.driverMock, c.expectedDriverMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected Driver calls: %s", diff)
}
if diff := cmp.Diff(c.dsMock, c.expectedDsMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected Datastore calls: %s", diff)
}
})
}
}
func TestStepAddFloppy_Cleanup(t *testing.T) {
tc := []struct {
name string
uploadedPath string
multistepState string
step *StepAddFloppy
driverMock *driver.DriverMock
expectedDriverMock *driver.DriverMock
dsMock *driver.DatastoreMock
expectedDsMock *driver.DatastoreMock
fail bool
errMessage string
}{
{
name: "State cancelled clean up",
uploadedPath: "uploaded/path",
multistepState: multistep.StateCancelled,
step: &StepAddFloppy{
Datastore: "datastore",
Host: "host",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
DeleteCalled: true,
},
expectedDsMock: &driver.DatastoreMock{
DeleteCalled: true,
DeletePath: "uploaded/path",
},
fail: false,
},
{
name: "State halted clean up",
uploadedPath: "uploaded/path",
multistepState: multistep.StateHalted,
step: &StepAddFloppy{
Datastore: "datastore",
Host: "host",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
DeleteCalled: true,
},
expectedDsMock: &driver.DatastoreMock{
DeleteCalled: true,
DeletePath: "uploaded/path",
},
fail: false,
},
{
name: "Don't clean up without uploaded path",
multistepState: multistep.StateHalted,
step: new(StepAddFloppy),
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: false,
},
{
name: "Don't clean up if state is not halted or canceled",
multistepState: "",
step: new(StepAddFloppy),
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: false,
},
{
name: "Fail because datastore is not found",
uploadedPath: "uploaded/path",
multistepState: multistep.StateHalted,
step: &StepAddFloppy{
Datastore: "datastore",
Host: "host",
},
driverMock: &driver.DriverMock{
FindDatastoreErr: fmt.Errorf("fail to find datastore"),
},
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "fail to find datastore",
},
{
name: "Fail to delete floppy",
uploadedPath: "uploaded/path",
multistepState: multistep.StateHalted,
step: &StepAddFloppy{
Datastore: "datastore",
Host: "host",
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
DeleteCalled: true,
DeleteErr: fmt.Errorf("failed to delete floppy"),
},
expectedDsMock: &driver.DatastoreMock{
DeleteCalled: true,
DeletePath: "uploaded/path",
},
fail: true,
errMessage: "failed to delete floppy",
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
state := basicStateBag(nil)
c.driverMock.DatastoreMock = c.dsMock
state.Put("driver", c.driverMock)
if c.uploadedPath != "" {
state.Put("uploaded_floppy_path", c.uploadedPath)
}
if c.multistepState != "" {
state.Put(c.multistepState, true)
}
c.step.Cleanup(state)
err, ok := state.Get("error").(error)
if ok {
if err.Error() != c.errMessage {
t.Fatalf("unexpected error %s", err.Error())
}
} else {
if c.fail {
t.Fatalf("expected to fail but it didn't")
}
}
c.expectedDriverMock.DatastoreMock = c.expectedDsMock
if diff := cmp.Diff(c.driverMock, c.expectedDriverMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected Driver calls: %s", diff)
}
if diff := cmp.Diff(c.dsMock, c.expectedDsMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected Datastore calls: %s", diff)
}
})
}
}

View File

@ -1,4 +1,4 @@
package iso package common
import ( import (
"context" "context"

View File

@ -1,4 +1,4 @@
package iso package common
import ( import (
"context" "context"
@ -10,7 +10,7 @@ import (
) )
func TestStepRemoteUpload_Run(t *testing.T) { func TestStepRemoteUpload_Run(t *testing.T) {
state := basicStateBag() state := basicStateBag(nil)
driverMock := driver.NewDriverMock() driverMock := driver.NewDriverMock()
state.Put("driver", driverMock) state.Put("driver", driverMock)
state.Put("iso_path", "[datastore] iso/path") state.Put("iso_path", "[datastore] iso/path")
@ -48,7 +48,7 @@ func TestStepRemoteUpload_Run(t *testing.T) {
} }
func TestStepRemoteUpload_SkipRun(t *testing.T) { func TestStepRemoteUpload_SkipRun(t *testing.T) {
state := basicStateBag() state := basicStateBag(nil)
driverMock := driver.NewDriverMock() driverMock := driver.NewDriverMock()
state.Put("driver", driverMock) state.Put("driver", driverMock)

View File

@ -1,7 +1,7 @@
//go:generate struct-markdown //go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type RemoveCDRomConfig //go:generate mapstructure-to-hcl2 -type RemoveCDRomConfig
package iso package common
import ( import (
"context" "context"
@ -22,7 +22,7 @@ type StepRemoveCDRom struct {
func (s *StepRemoveCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepRemoveCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver) vm := state.Get("vm").(driver.VirtualMachine)
ui.Say("Eject CD-ROM drives...") ui.Say("Eject CD-ROM drives...")
err := vm.EjectCdroms() err := vm.EjectCdroms()

View File

@ -1,5 +1,5 @@
// Code generated by "mapstructure-to-hcl2 -type RemoveCDRomConfig"; DO NOT EDIT. // Code generated by "mapstructure-to-hcl2 -type RemoveCDRomConfig"; DO NOT EDIT.
package iso package common
import ( import (
"github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcldec"

View File

@ -0,0 +1,111 @@
package common
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepRemoveCDRom_Run(t *testing.T) {
tc := []struct {
name string
step *StepRemoveCDRom
expectedAction multistep.StepAction
vmMock *driver.VirtualMachineMock
expectedVmMock *driver.VirtualMachineMock
fail bool
errMessage string
}{
{
name: "Eject CD-ROM drives",
step: &StepRemoveCDRom{
Config: &RemoveCDRomConfig{},
},
expectedAction: multistep.ActionContinue,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
EjectCdromsCalled: true,
},
fail: false,
},
{
name: "Failed to eject CD-ROM drives",
step: &StepRemoveCDRom{
Config: &RemoveCDRomConfig{},
},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
EjectCdromsErr: fmt.Errorf("failed to eject cd-rom drives"),
},
expectedVmMock: &driver.VirtualMachineMock{
EjectCdromsCalled: true,
},
fail: true,
errMessage: "failed to eject cd-rom drives",
},
{
name: "Eject and delete CD-ROM drives",
step: &StepRemoveCDRom{
Config: &RemoveCDRomConfig{
RemoveCdrom: true,
},
},
expectedAction: multistep.ActionContinue,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
EjectCdromsCalled: true,
RemoveCdromsCalled: true,
},
fail: false,
},
{
name: "Fail to delete CD-ROM drives",
step: &StepRemoveCDRom{
Config: &RemoveCDRomConfig{
RemoveCdrom: true,
},
},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
RemoveCdromsErr: fmt.Errorf("failed to delete cd-rom devices"),
},
expectedVmMock: &driver.VirtualMachineMock{
EjectCdromsCalled: true,
RemoveCdromsCalled: true,
},
fail: true,
errMessage: "failed to delete cd-rom devices",
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
state := basicStateBag(nil)
state.Put("vm", c.vmMock)
if action := c.step.Run(context.TODO(), state); action != c.expectedAction {
t.Fatalf("unexpected action %v", action)
}
err, ok := state.Get("error").(error)
if ok {
if err.Error() != c.errMessage {
t.Fatalf("unexpected error %s", err.Error())
}
} else {
if c.fail {
t.Fatalf("expected to fail but it didn't")
}
}
if diff := cmp.Diff(c.vmMock, c.expectedVmMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected VirtualMachine calls: %s", diff)
}
})
}
}

View File

@ -1,4 +1,4 @@
package iso package common
import ( import (
"context" "context"
@ -6,7 +6,6 @@ import (
"github.com/hashicorp/packer/builder/vsphere/driver" "github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/vim25/types"
) )
type StepRemoveFloppy struct { type StepRemoveFloppy struct {
@ -16,16 +15,15 @@ type StepRemoveFloppy struct {
func (s *StepRemoveFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepRemoveFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver) vm := state.Get("vm").(driver.VirtualMachine)
d := state.Get("driver").(*driver.VCenterDriver) d := state.Get("driver").(driver.Driver)
ui.Say("Deleting Floppy drives...") ui.Say("Deleting Floppy drives...")
devices, err := vm.Devices() floppies, err := vm.FloppyDevices()
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
return multistep.ActionHalt return multistep.ActionHalt
} }
floppies := devices.SelectByType((*types.VirtualFloppy)(nil))
if err = vm.RemoveDevice(true, floppies...); err != nil { if err = vm.RemoveDevice(true, floppies...); err != nil {
state.Put("error", err) state.Put("error", err)
return multistep.ActionHalt return multistep.ActionHalt

View File

@ -0,0 +1,213 @@
package common
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepRemoveFloppy_Run(t *testing.T) {
tc := []struct {
name string
uploadedPath string
step *StepRemoveFloppy
expectedAction multistep.StepAction
vmMock *driver.VirtualMachineMock
expectedVmMock *driver.VirtualMachineMock
driverMock *driver.DriverMock
expectedDriverMock *driver.DriverMock
dsMock *driver.DatastoreMock
expectedDsMock *driver.DatastoreMock
fail bool
errMessage string
}{
{
name: "Remove floppy drives and images",
uploadedPath: "vm/dir/packer-tmp-created-floppy.flp",
step: &StepRemoveFloppy{
Datastore: "datastore",
Host: "host",
},
expectedAction: multistep.ActionContinue,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
FloppyDevicesCalled: true,
RemoveDeviceCalled: true,
RemoveDeviceKeepFiles: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: new(driver.DatastoreMock),
expectedDsMock: &driver.DatastoreMock{
DeleteCalled: true,
DeletePath: "vm/dir/packer-tmp-created-floppy.flp",
},
fail: false,
},
{
name: "No floppy image to remove",
step: &StepRemoveFloppy{},
expectedAction: multistep.ActionContinue,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
FloppyDevicesCalled: true,
RemoveDeviceCalled: true,
RemoveDeviceKeepFiles: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: false,
},
{
name: "Fail to find floppy devices",
step: &StepRemoveFloppy{},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
FloppyDevicesErr: fmt.Errorf("failed to find floppy devices"),
},
expectedVmMock: &driver.VirtualMachineMock{
FloppyDevicesCalled: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "failed to find floppy devices",
},
{
name: "Fail to remove floppy devices",
step: &StepRemoveFloppy{},
expectedAction: multistep.ActionHalt,
vmMock: &driver.VirtualMachineMock{
RemoveDeviceErr: fmt.Errorf("failed to remove device"),
},
expectedVmMock: &driver.VirtualMachineMock{
FloppyDevicesCalled: true,
RemoveDeviceCalled: true,
RemoveDeviceKeepFiles: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: new(driver.DriverMock),
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "failed to remove device",
},
{
name: "Fail to find datastore",
uploadedPath: "vm/dir/packer-tmp-created-floppy.flp",
step: &StepRemoveFloppy{
Datastore: "datastore",
Host: "host",
},
expectedAction: multistep.ActionHalt,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
FloppyDevicesCalled: true,
RemoveDeviceCalled: true,
RemoveDeviceKeepFiles: true,
},
driverMock: &driver.DriverMock{
FindDatastoreErr: fmt.Errorf("failed to find datastore"),
},
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: new(driver.DatastoreMock),
expectedDsMock: new(driver.DatastoreMock),
fail: true,
errMessage: "failed to find datastore",
},
{
name: "Fail to delete floppy image",
uploadedPath: "vm/dir/packer-tmp-created-floppy.flp",
step: &StepRemoveFloppy{
Datastore: "datastore",
Host: "host",
},
expectedAction: multistep.ActionHalt,
vmMock: new(driver.VirtualMachineMock),
expectedVmMock: &driver.VirtualMachineMock{
FloppyDevicesCalled: true,
RemoveDeviceCalled: true,
RemoveDeviceKeepFiles: true,
},
driverMock: new(driver.DriverMock),
expectedDriverMock: &driver.DriverMock{
FindDatastoreCalled: true,
FindDatastoreName: "datastore",
FindDatastoreHost: "host",
},
dsMock: &driver.DatastoreMock{
DeleteErr: fmt.Errorf("failed to delete floppy"),
},
expectedDsMock: &driver.DatastoreMock{
DeleteCalled: true,
DeletePath: "vm/dir/packer-tmp-created-floppy.flp",
},
fail: true,
errMessage: "failed to delete floppy",
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
state := basicStateBag(nil)
state.Put("vm", c.vmMock)
c.driverMock.DatastoreMock = c.dsMock
state.Put("driver", c.driverMock)
if c.uploadedPath != "" {
state.Put("uploaded_floppy_path", c.uploadedPath)
}
if action := c.step.Run(context.TODO(), state); action != c.expectedAction {
t.Fatalf("unexpected action %v", action)
}
err, ok := state.Get("error").(error)
if ok {
if err.Error() != c.errMessage {
t.Fatalf("unexpected error %s", err.Error())
}
} else {
if c.fail {
t.Fatalf("expected to fail but it didn't")
}
}
if !c.fail {
if _, ok := state.GetOk("uploaded_floppy_path"); ok {
t.Fatalf("uploaded_floppy_path should not be in state")
}
}
if diff := cmp.Diff(c.vmMock, c.expectedVmMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected VirtualMachine calls: %s", diff)
}
c.expectedDriverMock.DatastoreMock = c.expectedDsMock
if diff := cmp.Diff(c.driverMock, c.expectedDriverMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected Driver calls: %s", diff)
}
if diff := cmp.Diff(c.dsMock, c.expectedDsMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected Datastore calls: %s", diff)
}
})
}
}

View File

@ -8,7 +8,20 @@ import (
type DatastoreMock struct { type DatastoreMock struct {
FileExistsCalled bool FileExistsCalled bool
MakeDirectoryCalled bool MakeDirectoryCalled bool
ResolvePathCalled bool
ResolvePathReturn string
DeleteCalled bool
DeletePath string
DeleteErr error
UploadFileCalled bool UploadFileCalled bool
UploadFileSrc string
UploadFileDst string
UploadFileHost string
UploadFileSetHost bool
UploadFileErr error
} }
func (ds *DatastoreMock) Info(params ...string) (*mo.Datastore, error) { func (ds *DatastoreMock) Info(params ...string) (*mo.Datastore, error) {
@ -29,16 +42,23 @@ func (ds *DatastoreMock) Reference() types.ManagedObjectReference {
} }
func (ds *DatastoreMock) ResolvePath(path string) string { func (ds *DatastoreMock) ResolvePath(path string) string {
return "" ds.ResolvePathCalled = true
return ds.ResolvePathReturn
} }
func (ds *DatastoreMock) UploadFile(src, dst, host string, setHost bool) error { func (ds *DatastoreMock) UploadFile(src, dst, host string, setHost bool) error {
ds.UploadFileCalled = true ds.UploadFileCalled = true
return nil ds.UploadFileSrc = src
ds.UploadFileDst = dst
ds.UploadFileHost = host
ds.UploadFileSetHost = setHost
return ds.UploadFileErr
} }
func (ds *DatastoreMock) Delete(path string) error { func (ds *DatastoreMock) Delete(path string) error {
return nil ds.DeleteCalled = true
ds.DeletePath = path
return ds.DeleteErr
} }
func (ds *DatastoreMock) MakeDirectory(path string) error { func (ds *DatastoreMock) MakeDirectory(path string) error {

View File

@ -1,6 +1,10 @@
package driver package driver
import "testing" import (
"testing"
"github.com/vmware/govmomi/simulator"
)
func TestDatastoreIsoPath(t *testing.T) { func TestDatastoreIsoPath(t *testing.T) {
tc := []struct { tc := []struct {
@ -87,3 +91,84 @@ func TestDatastoreIsoPath(t *testing.T) {
} }
} }
} }
func TestVCenterDriver_FindDatastore(t *testing.T) {
sim, err := NewVCenterSimulator()
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
defer sim.Close()
_, datastore := sim.ChooseSimulatorPreCreatedDatastore()
_, host := sim.ChooseSimulatorPreCreatedHost()
tc := []struct {
name string
datastore string
host string
fail bool
errMessage string
}{
{
name: "should find datastore when name is provided",
datastore: datastore.Name,
fail: false,
},
{
name: "should find datastore when only host is provided",
host: host.Name,
fail: false,
},
{
name: "should not find invalid datastore",
datastore: "invalid",
fail: true,
},
{
name: "should not find invalid host",
host: "invalid",
fail: true,
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
ds, err := sim.driver.FindDatastore(c.datastore, c.host)
if c.fail {
if err == nil {
t.Fatalf("expected to fail")
}
if c.errMessage != "" && err.Error() != c.errMessage {
t.Fatalf("unexpected error message %s", err.Error())
}
} else {
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
if ds == nil {
t.Fatalf("expected to find datastore")
}
}
})
}
}
func TestVCenterDriver_MultipleDatastoreError(t *testing.T) {
model := simulator.ESX()
model.Datastore = 2
sim, err := NewCustomVCenterSimulator(model)
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
defer sim.Close()
_, host := sim.ChooseSimulatorPreCreatedHost()
_, err = sim.driver.FindDatastore("", host.Name)
if err == nil {
t.Fatalf("expected to fail")
}
if err.Error() != "Host has multiple datastores. Specify it explicitly" {
t.Fatalf("unexpected error message %s", err.Error())
}
}

View File

@ -11,6 +11,9 @@ import (
type DriverMock struct { type DriverMock struct {
FindDatastoreCalled bool FindDatastoreCalled bool
DatastoreMock *DatastoreMock DatastoreMock *DatastoreMock
FindDatastoreName string
FindDatastoreHost string
FindDatastoreErr error
PreCleanShouldFail bool PreCleanShouldFail bool
PreCleanVMCalled bool PreCleanVMCalled bool
@ -32,7 +35,9 @@ func (d *DriverMock) FindDatastore(name string, host string) (Datastore, error)
if d.DatastoreMock == nil { if d.DatastoreMock == nil {
d.DatastoreMock = new(DatastoreMock) d.DatastoreMock = new(DatastoreMock)
} }
return d.DatastoreMock, nil d.FindDatastoreName = name
d.FindDatastoreHost = host
return d.DatastoreMock, d.FindDatastoreErr
} }
func (d *DriverMock) NewVM(ref *types.ManagedObjectReference) VirtualMachine { func (d *DriverMock) NewVM(ref *types.ManagedObjectReference) VirtualMachine {

View File

@ -50,24 +50,89 @@ func newVMName() string {
return fmt.Sprintf("test-%v", rand.Intn(1000)) return fmt.Sprintf("test-%v", rand.Intn(1000))
} }
func NewSimulatorServer(model *simulator.Model) (*simulator.Server, error) { type VCenterSimulator struct {
err := model.Create() model *simulator.Model
server *simulator.Server
driver *VCenterDriver
}
func NewCustomVCenterSimulator(model *simulator.Model) (*VCenterSimulator, error) {
sim := new(VCenterSimulator)
sim.model = model
server, err := sim.NewSimulatorServer()
if err != nil {
sim.Close()
return nil, err
}
sim.server = server
driver, err := sim.NewSimulatorDriver()
if err != nil {
sim.Close()
return nil, err
}
sim.driver = driver
return sim, nil
}
func NewVCenterSimulator() (*VCenterSimulator, error) {
model := simulator.VPX()
model.Machine = 1
return NewCustomVCenterSimulator(model)
}
func (s *VCenterSimulator) Close() {
if s.model != nil {
s.model.Remove()
}
if s.server != nil {
s.server.Close()
}
}
//Simulator shortcut to choose any pre created VM.
func (s *VCenterSimulator) ChooseSimulatorPreCreatedVM() (VirtualMachine, *simulator.VirtualMachine) {
machine := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
ref := machine.Reference()
vm := s.driver.NewVM(&ref)
return vm, machine
}
//Simulator shortcut to choose any pre created Datastore.
func (s *VCenterSimulator) ChooseSimulatorPreCreatedDatastore() (Datastore, *simulator.Datastore) {
ds := simulator.Map.Any("Datastore").(*simulator.Datastore)
ref := ds.Reference()
datastore := s.driver.NewDatastore(&ref)
return datastore, ds
}
//Simulator shortcut to choose any pre created Host.
func (s *VCenterSimulator) ChooseSimulatorPreCreatedHost() (*Host, *simulator.HostSystem) {
h := simulator.Map.Any("HostSystem").(*simulator.HostSystem)
ref := h.Reference()
host := s.driver.NewHost(&ref)
return host, h
}
func (s *VCenterSimulator) NewSimulatorServer() (*simulator.Server, error) {
err := s.model.Create()
if err != nil { if err != nil {
return nil, err return nil, err
} }
model.Service.RegisterEndpoints = true s.model.Service.RegisterEndpoints = true
model.Service.TLS = new(tls.Config) s.model.Service.TLS = new(tls.Config)
model.Service.ServeMux = http.NewServeMux() s.model.Service.ServeMux = http.NewServeMux()
return model.Service.NewServer(), nil return s.model.Service.NewServer(), nil
} }
func NewSimulatorDriver(s *simulator.Server) (*VCenterDriver, error) { func (s *VCenterSimulator) NewSimulatorDriver() (*VCenterDriver, error) {
ctx := context.TODO() ctx := context.TODO()
user := &url.Userinfo{} user := &url.Userinfo{}
s.URL.User = user s.server.URL.User = user
soapClient := soap.NewClient(s.URL, true) soapClient := soap.NewClient(s.server.URL, true)
vimClient, err := vim25.NewClient(ctx, soapClient) vimClient, err := vim25.NewClient(ctx, soapClient)
if err != nil { if err != nil {
return nil, err return nil, err
@ -104,11 +169,3 @@ func NewSimulatorDriver(s *simulator.Server) (*VCenterDriver, error) {
} }
return d, nil return d, nil
} }
//Simulator shortcut to choose any pre created VM.
func ChooseSimulatorPreCreatedVM(driverSim *VCenterDriver) VirtualMachine {
machine := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
ref := machine.Reference()
vm := driverSim.NewVM(&ref)
return vm
}

View File

@ -7,21 +7,13 @@ import (
) )
func TestVCenterDriver_FindResourcePool(t *testing.T) { func TestVCenterDriver_FindResourcePool(t *testing.T) {
model := simulator.VPX() sim, err := NewVCenterSimulator()
defer model.Remove()
s, err := NewSimulatorServer(model)
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
defer s.Close() defer sim.Close()
driverSim, err := NewSimulatorDriver(s) res, err := sim.driver.FindResourcePool("", "DC0_H0", "")
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
res, err := driverSim.FindResourcePool("", "DC0_H0", "")
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
@ -47,19 +39,13 @@ func TestVCenterDriver_FindResourcePoolStandaloneESX(t *testing.T) {
model.DelayConfig.MethodDelay = opts.DelayConfig.MethodDelay model.DelayConfig.MethodDelay = opts.DelayConfig.MethodDelay
model.DelayConfig.DelayJitter = opts.DelayConfig.DelayJitter model.DelayConfig.DelayJitter = opts.DelayConfig.DelayJitter
s, err := NewSimulatorServer(model) sim, err := NewCustomVCenterSimulator(model)
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
defer s.Close() defer sim.Close()
driverSim, err := NewSimulatorDriver(s) res, err := sim.driver.FindResourcePool("", "localhost.localdomain", "")
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
//
res, err := driverSim.FindResourcePool("", "localhost.localdomain", "")
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
@ -72,7 +58,7 @@ func TestVCenterDriver_FindResourcePoolStandaloneESX(t *testing.T) {
} }
// Invalid resource name should look for default resource pool // Invalid resource name should look for default resource pool
res, err = driverSim.FindResourcePool("", "localhost.localdomain", "invalid") res, err = sim.driver.FindResourcePool("", "localhost.localdomain", "invalid")
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }

View File

@ -24,6 +24,7 @@ import (
type VirtualMachine interface { type VirtualMachine interface {
Info(params ...string) (*mo.VirtualMachine, error) Info(params ...string) (*mo.VirtualMachine, error)
Devices() (object.VirtualDeviceList, error) Devices() (object.VirtualDeviceList, error)
FloppyDevices() (object.VirtualDeviceList, error)
Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error) Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error)
updateVAppConfig(ctx context.Context, newProps map[string]string) (*types.VmConfigSpec, error) updateVAppConfig(ctx context.Context, newProps map[string]string) (*types.VmConfigSpec, error)
AddPublicKeys(ctx context.Context, publicKeys string) error AddPublicKeys(ctx context.Context, publicKeys string) error
@ -284,6 +285,15 @@ func (vm *VirtualMachineDriver) Devices() (object.VirtualDeviceList, error) {
return vmInfo.Config.Hardware.Device, nil return vmInfo.Config.Hardware.Device, nil
} }
func (vm *VirtualMachineDriver) FloppyDevices() (object.VirtualDeviceList, error) {
device, err := vm.Devices()
if err != nil {
return device, err
}
floppies := device.SelectByType((*types.VirtualFloppy)(nil))
return floppies, nil
}
func (vm *VirtualMachineDriver) Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error) { func (vm *VirtualMachineDriver) Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error) {
folder, err := vm.driver.FindFolder(config.Folder) folder, err := vm.driver.FindFolder(config.Folder)
if err != nil { if err != nil {

View File

@ -5,27 +5,17 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
) )
func TestVirtualMachineDriver_FindAndAddSATAController(t *testing.T) { func TestVirtualMachineDriver_FindAndAddSATAController(t *testing.T) {
model := simulator.VPX() sim, err := NewVCenterSimulator()
model.Machine = 1
defer model.Remove()
s, err := NewSimulatorServer(model)
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
defer s.Close() defer sim.Close()
driverSim, err := NewSimulatorDriver(s) vm, _ := sim.ChooseSimulatorPreCreatedVM()
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
vm := ChooseSimulatorPreCreatedVM(driverSim)
_, err = vm.FindSATAController() _, err = vm.FindSATAController()
if err != nil && !strings.Contains(err.Error(), "no available SATA controller") { if err != nil && !strings.Contains(err.Error(), "no available SATA controller") {
@ -49,23 +39,13 @@ func TestVirtualMachineDriver_FindAndAddSATAController(t *testing.T) {
} }
func TestVirtualMachineDriver_CreateAndRemoveCdrom(t *testing.T) { func TestVirtualMachineDriver_CreateAndRemoveCdrom(t *testing.T) {
model := simulator.VPX() sim, err := NewVCenterSimulator()
model.Machine = 1
defer model.Remove()
s, err := NewSimulatorServer(model)
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
defer s.Close() defer sim.Close()
driverSim, err := NewSimulatorDriver(s) vm, _ := sim.ChooseSimulatorPreCreatedVM()
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
//Simulator shortcut to choose any pre created VM.
vm := ChooseSimulatorPreCreatedVM(driverSim)
// Add SATA Controller // Add SATA Controller
if err := vm.AddSATAController(); err != nil { if err := vm.AddSATAController(); err != nil {
@ -118,23 +98,13 @@ func TestVirtualMachineDriver_CreateAndRemoveCdrom(t *testing.T) {
} }
func TestVirtualMachineDriver_EjectCdrom(t *testing.T) { func TestVirtualMachineDriver_EjectCdrom(t *testing.T) {
model := simulator.VPX() sim, err := NewVCenterSimulator()
model.Machine = 1
defer model.Remove()
s, err := NewSimulatorServer(model)
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
defer s.Close() defer sim.Close()
driverSim, err := NewSimulatorDriver(s) vm, _ := sim.ChooseSimulatorPreCreatedVM()
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
//Simulator shortcut to choose any pre created VM.
vm := ChooseSimulatorPreCreatedVM(driverSim)
// Add SATA Controller // Add SATA Controller
if err := vm.AddSATAController(); err != nil { if err := vm.AddSATAController(); err != nil {

View File

@ -32,6 +32,29 @@ type VirtualMachineMock struct {
AddCdromErr error AddCdromErr error
AddCdromTypes []string AddCdromTypes []string
AddCdromPaths []string AddCdromPaths []string
GetDirCalled bool
GetDirResponse string
GetDirErr error
AddFloppyCalled bool
AddFloppyImagePath string
AddFloppyErr error
FloppyDevicesErr error
FloppyDevicesReturn object.VirtualDeviceList
FloppyDevicesCalled bool
RemoveDeviceErr error
RemoveDeviceCalled bool
RemoveDeviceKeepFiles bool
RemoveDeviceDevices []types.BaseVirtualDevice
EjectCdromsCalled bool
EjectCdromsErr error
RemoveCdromsCalled bool
RemoveCdromsErr error
} }
func (vm *VirtualMachineMock) Info(params ...string) (*mo.VirtualMachine, error) { func (vm *VirtualMachineMock) Info(params ...string) (*mo.VirtualMachine, error) {
@ -42,6 +65,11 @@ func (vm *VirtualMachineMock) Devices() (object.VirtualDeviceList, error) {
return object.VirtualDeviceList{}, nil return object.VirtualDeviceList{}, nil
} }
func (vm *VirtualMachineMock) FloppyDevices() (object.VirtualDeviceList, error) {
vm.FloppyDevicesCalled = true
return vm.FloppyDevicesReturn, vm.FloppyDevicesErr
}
func (vm *VirtualMachineMock) Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error) { func (vm *VirtualMachineMock) Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error) {
return nil, nil return nil, nil
} }
@ -124,7 +152,8 @@ func (vm *VirtualMachineMock) ImportToContentLibrary(template vcenter.Template)
} }
func (vm *VirtualMachineMock) GetDir() (string, error) { func (vm *VirtualMachineMock) GetDir() (string, error) {
return "", nil vm.GetDirCalled = true
return vm.GetDirResponse, vm.GetDirErr
} }
func (vm *VirtualMachineMock) AddCdrom(cdromType string, isoPath string) error { func (vm *VirtualMachineMock) AddCdrom(cdromType string, isoPath string) error {
@ -136,7 +165,9 @@ func (vm *VirtualMachineMock) AddCdrom(cdromType string, isoPath string) error {
} }
func (vm *VirtualMachineMock) AddFloppy(imgPath string) error { func (vm *VirtualMachineMock) AddFloppy(imgPath string) error {
return nil vm.AddFloppyCalled = true
vm.AddFloppyImagePath = imgPath
return vm.AddFloppyErr
} }
func (vm *VirtualMachineMock) SetBootOrder(order []string) error { func (vm *VirtualMachineMock) SetBootOrder(order []string) error {
@ -144,7 +175,10 @@ func (vm *VirtualMachineMock) SetBootOrder(order []string) error {
} }
func (vm *VirtualMachineMock) RemoveDevice(keepFiles bool, device ...types.BaseVirtualDevice) error { func (vm *VirtualMachineMock) RemoveDevice(keepFiles bool, device ...types.BaseVirtualDevice) error {
return nil vm.RemoveDeviceCalled = true
vm.RemoveDeviceKeepFiles = keepFiles
vm.RemoveDeviceDevices = device
return vm.RemoveDeviceErr
} }
func (vm *VirtualMachineMock) addDevice(device types.BaseVirtualDevice) error { func (vm *VirtualMachineMock) addDevice(device types.BaseVirtualDevice) error {
@ -186,9 +220,11 @@ func (vm *VirtualMachineMock) CreateCdrom(c *types.VirtualController) (*types.Vi
} }
func (vm *VirtualMachineMock) RemoveCdroms() error { func (vm *VirtualMachineMock) RemoveCdroms() error {
return nil vm.RemoveCdromsCalled = true
return vm.RemoveCdromsErr
} }
func (vm *VirtualMachineMock) EjectCdroms() error { func (vm *VirtualMachineMock) EjectCdroms() error {
return nil vm.EjectCdromsCalled = true
return vm.EjectCdromsErr
} }

View File

@ -28,25 +28,13 @@ func (vm *ReconfigureFail) ReconfigVMTask(req *types.ReconfigVM_Task) soap.HasFa
} }
func TestVirtualMachineDriver_Configure(t *testing.T) { func TestVirtualMachineDriver_Configure(t *testing.T) {
model := simulator.VPX() sim, err := NewVCenterSimulator()
model.Machine = 1
defer model.Remove()
s, err := NewSimulatorServer(model)
if err != nil { if err != nil {
t.Fatalf("should not fail: %s", err.Error()) t.Fatalf("should not fail: %s", err.Error())
} }
defer s.Close() defer sim.Close()
driverSim, err := NewSimulatorDriver(s) vm, machine := sim.ChooseSimulatorPreCreatedVM()
if err != nil {
t.Fatalf("should not fail: %s", err.Error())
}
//Simulator shortcut to choose any pre created VM.
machine := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
ref := machine.Reference()
vm := driverSim.NewVM(&ref)
// Happy test // Happy test
hardwareConfig := &HardwareConfig{ hardwareConfig := &HardwareConfig{

View File

@ -52,7 +52,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Files: b.config.CDConfig.CDFiles, Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel, Label: b.config.CDConfig.CDLabel,
}, },
&StepRemoteUpload{ &common.StepRemoteUpload{
Datastore: b.config.Datastore, Datastore: b.config.Datastore,
Host: b.config.Host, Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads, SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
@ -65,7 +65,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepConfigureHardware{ &common.StepConfigureHardware{
Config: &b.config.HardwareConfig, Config: &b.config.HardwareConfig,
}, },
&StepAddCDRom{ &common.StepAddCDRom{
Config: &b.config.CDRomConfig, Config: &b.config.CDRomConfig,
}, },
&common.StepConfigParams{ &common.StepConfigParams{
@ -80,7 +80,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Directories: b.config.FloppyDirectories, Directories: b.config.FloppyDirectories,
Label: b.config.FloppyLabel, Label: b.config.FloppyLabel,
}, },
&StepAddFloppy{ &common.StepAddFloppy{
Config: &b.config.FloppyConfig, Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore, Datastore: b.config.Datastore,
Host: b.config.Host, Host: b.config.Host,
@ -117,7 +117,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepShutdown{ &common.StepShutdown{
Config: &b.config.ShutdownConfig, Config: &b.config.ShutdownConfig,
}, },
&StepRemoveFloppy{ &common.StepRemoveFloppy{
Datastore: b.config.Datastore, Datastore: b.config.Datastore,
Host: b.config.Host, Host: b.config.Host,
}, },
@ -125,7 +125,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
} }
steps = append(steps, steps = append(steps,
&StepRemoveCDRom{ &common.StepRemoveCDRom{
Config: &b.config.RemoveCDRomConfig, Config: &b.config.RemoveCDRomConfig,
}, },
&common.StepCreateSnapshot{ &common.StepCreateSnapshot{

View File

@ -25,9 +25,9 @@ type Config struct {
packerCommon.ISOConfig `mapstructure:",squash"` packerCommon.ISOConfig `mapstructure:",squash"`
CDRomConfig `mapstructure:",squash"` common.CDRomConfig `mapstructure:",squash"`
RemoveCDRomConfig `mapstructure:",squash"` common.RemoveCDRomConfig `mapstructure:",squash"`
FloppyConfig `mapstructure:",squash"` common.FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"` common.RunConfig `mapstructure:",squash"`
common.BootConfig `mapstructure:",squash"` common.BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"` common.WaitIpConfig `mapstructure:",squash"`

View File

@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"github.com/hashicorp/packer/helper/builder/localexec" "github.com/hashicorp/packer/helper/builder/localexec"
@ -167,12 +168,34 @@ var supportedCDISOCreationCommands []cdISOCreationCommand = []cdISOCreationComma
}, },
} }
func isCygwinExecutable(path string) bool {
return runtime.GOOS == "windows" && strings.Contains(path, "\\usr\\bin\\")
}
func toCygwinPath(path string) (string, error) {
c := exec.Command("cygpath", path)
cygwinPath, err := c.Output()
return strings.TrimSpace(string(cygwinPath)), err
}
func retrieveCDISOCreationCommand(label string, source string, dest string) (*exec.Cmd, error) { func retrieveCDISOCreationCommand(label string, source string, dest string) (*exec.Cmd, error) {
for _, c := range supportedCDISOCreationCommands { for _, c := range supportedCDISOCreationCommands {
path, err := exec.LookPath(c.Name) path, err := exec.LookPath(c.Name)
if err != nil { if err != nil {
continue continue
} }
// if we are running a cygwin/msys2 executable we must convert the
// native win32 path to a cygwin/msys2/unix style path.
if isCygwinExecutable(path) {
source, err = toCygwinPath(source)
if err != nil {
return nil, err
}
dest, err = toCygwinPath(dest)
if err != nil {
return nil, err
}
}
return c.Command(path, label, source, dest), nil return c.Command(path, label, source, dest), nil
} }
var commands = make([]string, 0, len(supportedCDISOCreationCommands)) var commands = make([]string, 0, len(supportedCDISOCreationCommands))

View File

@ -245,6 +245,7 @@ func TestStepDownload_download(t *testing.T) {
ui := &packer.BasicUi{ ui := &packer.BasicUi{
Reader: new(bytes.Buffer), Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer), Writer: new(bytes.Buffer),
PB: &packer.NoopProgressTracker{},
} }
dir := createTempDir(t) dir := createTempDir(t)

View File

@ -16,6 +16,7 @@ func testState(t *testing.T) multistep.StateBag {
state.Put("ui", &packer.BasicUi{ state.Put("ui", &packer.BasicUi{
Reader: new(bytes.Buffer), Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer), Writer: new(bytes.Buffer),
PB: &packer.NoopProgressTracker{},
}) })
return state return state
} }

9
go.mod
View File

@ -22,7 +22,7 @@ require (
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 // indirect github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 // indirect
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect
github.com/aws/aws-sdk-go v1.30.8 github.com/aws/aws-sdk-go v1.34.26
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee
github.com/cheggaaa/pb v1.0.27 github.com/cheggaaa/pb v1.0.27
@ -40,9 +40,6 @@ require (
github.com/go-ole/go-ole v1.2.4 // indirect github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-resty/resty/v2 v2.3.0 github.com/go-resty/resty/v2 v2.3.0
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.0.4 // indirect
github.com/gofrs/flock v0.7.3 github.com/gofrs/flock v0.7.3
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/protobuf v1.4.2 // indirect github.com/golang/protobuf v1.4.2 // indirect
@ -53,14 +50,14 @@ require (
github.com/gophercloud/gophercloud v0.12.0 github.com/gophercloud/gophercloud v0.12.0
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
github.com/hashicorp/aws-sdk-go-base v0.6.0
github.com/hashicorp/consul/api v1.4.0 github.com/hashicorp/consul/api v1.4.0
github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-cty-funcs v0.0.0-20200520133146-0d04eb807361 github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad
github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad
github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad

52
go.sum
View File

@ -129,6 +129,9 @@ github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3A
github.com/aws/aws-sdk-go v1.16.22/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.16.22/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.30.8 h1:4BHbh8K3qKmcnAgToZ2LShldRF9inoqIBccpCLNCy3I= github.com/aws/aws-sdk-go v1.30.8 h1:4BHbh8K3qKmcnAgToZ2LShldRF9inoqIBccpCLNCy3I=
github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.34.26 h1:tw4nsSfGvCDnXt2xPe8NkxIrDui+asAWinMknPLEf80=
github.com/aws/aws-sdk-go v1.34.26/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
@ -172,8 +175,6 @@ github.com/digitalocean/godo v1.11.1 h1:OsTh37YFKk+g6DnAOrkXJ9oDArTkRx5UTkBJ2EWA
github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/diskfs/go-diskfs v1.1.1 h1:rMjLpaydtXGVZb7mdkRGK1+//30i76nKAit89zUzeaI=
github.com/diskfs/go-diskfs v1.1.1/go.mod h1:afUPxxu+x1snp4aCY2bKR0CoZ/YFJewV3X2UEr2nPZE=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI= github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
@ -217,12 +218,6 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ= github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ=
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
@ -288,6 +283,7 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -313,12 +309,12 @@ github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c h1:iawx2ojEQA7c+
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w= github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 h1:JIM+OacoOJRU30xpjMf8sulYqjr0ViA3WDrTX6j/yDI=
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ= github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ=
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE= github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
github.com/hashicorp/aws-sdk-go-base v0.6.0 h1:qmUbzM36msbBF59YctwuO5w0M2oNXjlilgKpnEhx1uw=
github.com/hashicorp/aws-sdk-go-base v0.6.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY=
github.com/hashicorp/consul/api v1.4.0 h1:jfESivXnO5uLdH650JU/6AnjRoHrLhULq0FnC3Kp9EY= github.com/hashicorp/consul/api v1.4.0 h1:jfESivXnO5uLdH650JU/6AnjRoHrLhULq0FnC3Kp9EY=
github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU=
github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30= github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30=
@ -331,8 +327,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6K
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cty-funcs v0.0.0-20200520133146-0d04eb807361 h1:qFuR7ZaMDC5xelTZeNwsJ90I4+4km7ACDdAP+2w84xI= github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 h1:kgvybwEeu0SXktbB2y3uLHX9lklLo+nzUwh59A3jzQc=
github.com/hashicorp/go-cty-funcs v0.0.0-20200520133146-0d04eb807361/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA= github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA= github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad h1:QPLyAkuTS5Uf9uqJQxCTDDFgmD+gVAzSvqkGb3h+7oQ= github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad h1:QPLyAkuTS5Uf9uqJQxCTDDFgmD+gVAzSvqkGb3h+7oQ=
@ -380,7 +376,6 @@ github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
@ -517,8 +512,6 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@ -619,14 +612,6 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/sylviamoss/go-vnc v0.0.0-20200908143403-27ce26f2bed9 h1:uEIT4Mi8C8O+S44150F3lug+WaZ01aJ0sLHvN4npMsc=
github.com/sylviamoss/go-vnc v0.0.0-20200908143403-27ce26f2bed9/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/sylviamoss/go-vnc v0.0.0-20200908143844-452d6786ec66 h1:4pQ2Ex/3Fxn7WDvpa85XqHLkc+cSoceIefX/8mD518o=
github.com/sylviamoss/go-vnc v0.0.0-20200908143844-452d6786ec66/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/sylviamoss/go-vnc v0.0.0-20200908144818-694f171108bf h1:HUHmcnxuPAJktJM1acw5AckmPvOWz/BoenBLB+IMuo0=
github.com/sylviamoss/go-vnc v0.0.0-20200908144818-694f171108bf/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/sylviamoss/go-vnc v0.0.0-20200908145922-2061e37ffa00 h1:mURYI59JxRHmjEKY4ZZING6qm6e/R91tUqdwFmHoAjY=
github.com/sylviamoss/go-vnc v0.0.0-20200908145922-2061e37ffa00/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible h1:bs+0lcG4RELNbE8PsBC9oaPP0/qExr0DuEGnZyocm84= github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible h1:bs+0lcG4RELNbE8PsBC9oaPP0/qExr0DuEGnZyocm84=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
@ -764,8 +749,6 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
@ -829,11 +812,8 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff h1:1CPUrky56AcgSpxz/KfgzQWzfG09u5YOL8MvPYBlrL8= golang.org/x/sys v0.0.0-20200918174421-af09f7315aff h1:1CPUrky56AcgSpxz/KfgzQWzfG09u5YOL8MvPYBlrL8=
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -880,7 +860,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2 h1:L/G4KZvrQn7FWLN/LlulBtBzrLUhqjiGfTWWDmrh+IQ=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
@ -894,16 +873,12 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200918201133-e94ab7288189 h1:7E/geNtekOV4N/07EhKz7zyXs0hZhoZZ19R2O2mMHoI=
golang.org/x/tools v0.0.0-20200918201133-e94ab7288189/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266 h1:k7tVuG0g1JwmD3Jh8oAl1vQ1C3jb4Hi/dUl1wWDBJpQ= golang.org/x/tools v0.0.0-20200918232735-d647fc253266 h1:k7tVuG0g1JwmD3Jh8oAl1vQ1C3jb4Hi/dUl1wWDBJpQ=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -918,7 +893,6 @@ google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.21.0 h1:zS+Q/CJJnVlXpXQVIz+lH0ZT2lBuT2ac7XD8Y/3w6hY=
google.golang.org/api v0.21.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.21.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
@ -932,7 +906,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@ -963,14 +936,11 @@ google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200617032506-f1bdc9086088 h1:XXo4PvhJkaWYIkwn7bX7mcdB8RdcOvn12HbaUUAwX3E=
google.golang.org/genproto v0.0.0-20200617032506-f1bdc9086088/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200918140846-d0d605568037 h1:ujwz1DPMeHwCvo36rK5shXhAzc4GMRecrqQFaMZJBKQ= google.golang.org/genproto v0.0.0-20200918140846-d0d605568037 h1:ujwz1DPMeHwCvo36rK5shXhAzc4GMRecrqQFaMZJBKQ=
@ -986,11 +956,9 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
@ -1002,7 +970,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
@ -1013,8 +980,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo= gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/djherbis/times.v1 v1.2.0 h1:UCvDKl1L/fmBygl2Y7hubXCnY7t4Yj46ZrBFNUipFbM=
gopkg.in/djherbis/times.v1 v1.2.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -1034,7 +999,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/hashicorp/go-cty-funcs/cidr" "github.com/hashicorp/go-cty-funcs/cidr"
"github.com/hashicorp/go-cty-funcs/collection"
"github.com/hashicorp/go-cty-funcs/crypto" "github.com/hashicorp/go-cty-funcs/crypto"
"github.com/hashicorp/go-cty-funcs/encoding" "github.com/hashicorp/go-cty-funcs/encoding"
"github.com/hashicorp/go-cty-funcs/filesystem" "github.com/hashicorp/go-cty-funcs/filesystem"
@ -41,7 +42,7 @@ func Functions(basedir string) map[string]function.Function {
"cidrnetmask": cidr.NetmaskFunc, "cidrnetmask": cidr.NetmaskFunc,
"cidrsubnet": cidr.SubnetFunc, "cidrsubnet": cidr.SubnetFunc,
"cidrsubnets": cidr.SubnetsFunc, "cidrsubnets": cidr.SubnetsFunc,
"coalesce": stdlib.CoalesceFunc, "coalesce": collection.CoalesceFunc,
"coalescelist": stdlib.CoalesceListFunc, "coalescelist": stdlib.CoalesceListFunc,
"compact": stdlib.CompactFunc, "compact": stdlib.CompactFunc,
"concat": stdlib.ConcatFunc, "concat": stdlib.ConcatFunc,

View File

@ -7,7 +7,7 @@ build {
] ]
provisioner "shell" { provisioner "shell" {
string = "string" string = coalesce(null, "", "string")
int = "${41 + 1}" int = "${41 + 1}"
int64 = "${42 + 1}" int64 = "${42 + 1}"
bool = "true" bool = "true"

View File

@ -196,6 +196,7 @@ func wrappedMain() int {
Reader: os.Stdin, Reader: os.Stdin,
Writer: os.Stdout, Writer: os.Stdout,
ErrorWriter: os.Stdout, ErrorWriter: os.Stdout,
PB: &packer.NoopProgressTracker{},
} }
ui = basicUi ui = basicUi
if !inPlugin { if !inPlugin {
@ -211,6 +212,7 @@ func wrappedMain() int {
fmt.Fprintf(os.Stderr, "No tty available: %s\n", err) fmt.Fprintf(os.Stderr, "No tty available: %s\n", err)
} else { } else {
basicUi.TTY = TTY basicUi.TTY = TTY
basicUi.PB = &packer.UiProgressBar{}
defer TTY.Close() defer TTY.Close()
} }
} }

View File

@ -15,25 +15,17 @@ func ProgressBarConfig(bar *pb.ProgressBar, prefix string) {
bar.Prefix(prefix) bar.Prefix(prefix)
} }
var defaultUiProgressBar = &uiProgressBar{} // UiProgressBar is a progress bar compatible with go-getter used in our
// UI structs.
// uiProgressBar is a self managed progress bar singleton. type UiProgressBar struct {
// decorate your struct with a *uiProgressBar to
// give it TrackProgress capabilities.
// In TrackProgress if uiProgressBar is nil
// defaultUiProgressBar will be used as
// the progress bar.
type uiProgressBar struct {
lock sync.Mutex lock sync.Mutex
pool *pb.Pool pool *pb.Pool
pbs int pbs int
} }
func (p *uiProgressBar) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) io.ReadCloser { func (p *UiProgressBar) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) io.ReadCloser {
if p == nil { if p == nil {
return defaultUiProgressBar.TrackProgress(src, currentSize, totalSize, stream) return stream
} }
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()

View File

@ -1,3 +1,3 @@
package packer package packer
type uiProgressBar = NoopProgressTracker type UiProgressBar = NoopProgressTracker

View File

@ -11,7 +11,7 @@ import (
// The following tests rarelly just happen. So we run them 100 times. // The following tests rarelly just happen. So we run them 100 times.
func TestProgressTracking_open_close(t *testing.T) { func TestProgressTracking_open_close(t *testing.T) {
var bar *uiProgressBar var bar *UiProgressBar
tracker := bar.TrackProgress("1,", 1, 42, ioutil.NopCloser(nil)) tracker := bar.TrackProgress("1,", 1, 42, ioutil.NopCloser(nil))
tracker.Close() tracker.Close()
@ -21,7 +21,7 @@ func TestProgressTracking_open_close(t *testing.T) {
} }
func TestProgressTracking_multi_open_close(t *testing.T) { func TestProgressTracking_multi_open_close(t *testing.T) {
var bar *uiProgressBar var bar *UiProgressBar
g := errgroup.Group{} g := errgroup.Group{}
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
@ -36,7 +36,7 @@ func TestProgressTracking_multi_open_close(t *testing.T) {
} }
func TestProgressTracking_races(t *testing.T) { func TestProgressTracking_races(t *testing.T) {
var bar *uiProgressBar var bar *UiProgressBar
g := errgroup.Group{} g := errgroup.Group{}
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {

View File

@ -40,11 +40,12 @@ type Ui interface {
Message(string) Message(string)
Error(string) Error(string)
Machine(string, ...string) Machine(string, ...string)
// TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser)
getter.ProgressTracker getter.ProgressTracker
} }
type NoopUi struct { type NoopUi struct {
NoopProgressTracker PB NoopProgressTracker
} }
var _ Ui = new(NoopUi) var _ Ui = new(NoopUi)
@ -54,13 +55,16 @@ func (*NoopUi) Say(string) { return }
func (*NoopUi) Message(string) { return } func (*NoopUi) Message(string) { return }
func (*NoopUi) Error(string) { return } func (*NoopUi) Error(string) { return }
func (*NoopUi) Machine(string, ...string) { return } func (*NoopUi) Machine(string, ...string) { return }
func (u *NoopUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) io.ReadCloser {
return u.PB.TrackProgress(src, currentSize, totalSize, stream)
}
// ColoredUi is a UI that is colored using terminal colors. // ColoredUi is a UI that is colored using terminal colors.
type ColoredUi struct { type ColoredUi struct {
Color UiColor Color UiColor
ErrorColor UiColor ErrorColor UiColor
Ui Ui Ui Ui
*uiProgressBar PB getter.ProgressTracker
} }
var _ Ui = new(ColoredUi) var _ Ui = new(ColoredUi)
@ -189,7 +193,7 @@ type BasicUi struct {
l sync.Mutex l sync.Mutex
interrupted bool interrupted bool
TTY TTY TTY TTY
*uiProgressBar PB getter.ProgressTracker
} }
var _ Ui = new(BasicUi) var _ Ui = new(BasicUi)
@ -304,11 +308,15 @@ func (rw *BasicUi) Machine(t string, args ...string) {
log.Printf("machine readable: %s %#v", t, args) log.Printf("machine readable: %s %#v", t, args)
} }
func (rw *BasicUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
return rw.PB.TrackProgress(src, currentSize, totalSize, stream)
}
// MachineReadableUi is a UI that only outputs machine-readable output // MachineReadableUi is a UI that only outputs machine-readable output
// to the given Writer. // to the given Writer.
type MachineReadableUi struct { type MachineReadableUi struct {
Writer io.Writer Writer io.Writer
NoopProgressTracker PB NoopProgressTracker
} }
var _ Ui = new(MachineReadableUi) var _ Ui = new(MachineReadableUi)
@ -366,11 +374,15 @@ func (u *MachineReadableUi) Machine(category string, args ...string) {
log.Printf("%d,%s,%s,%s\n", now.Unix(), target, category, argsString) log.Printf("%d,%s,%s,%s\n", now.Unix(), target, category, argsString)
} }
func (u *MachineReadableUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
return u.PB.TrackProgress(src, currentSize, totalSize, stream)
}
// TimestampedUi is a UI that wraps another UI implementation and // TimestampedUi is a UI that wraps another UI implementation and
// prefixes each message with an RFC3339 timestamp // prefixes each message with an RFC3339 timestamp
type TimestampedUi struct { type TimestampedUi struct {
Ui Ui Ui Ui
*uiProgressBar PB getter.ProgressTracker
} }
var _ Ui = new(TimestampedUi) var _ Ui = new(TimestampedUi)
@ -395,6 +407,10 @@ func (u *TimestampedUi) Machine(message string, args ...string) {
u.Ui.Machine(message, args...) u.Ui.Machine(message, args...)
} }
func (u *TimestampedUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
return u.Ui.TrackProgress(src, currentSize, totalSize, stream)
}
func (u *TimestampedUi) timestampLine(string string) string { func (u *TimestampedUi) timestampLine(string string) string {
return fmt.Sprintf("%v: %v", time.Now().Format(time.RFC3339), string) return fmt.Sprintf("%v: %v", time.Now().Format(time.RFC3339), string)
} }
@ -404,7 +420,7 @@ func (u *TimestampedUi) timestampLine(string string) string {
type SafeUi struct { type SafeUi struct {
Sem chan int Sem chan int
Ui Ui Ui Ui
*uiProgressBar PB getter.ProgressTracker
} }
var _ Ui = new(SafeUi) var _ Ui = new(SafeUi)
@ -440,3 +456,11 @@ func (u *SafeUi) Machine(t string, args ...string) {
u.Ui.Machine(t, args...) u.Ui.Machine(t, args...)
<-u.Sem <-u.Sem
} }
func (u *SafeUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
u.Sem <- 1
ret := u.Ui.TrackProgress(src, currentSize, totalSize, stream)
<-u.Sem
return ret
}

View File

@ -49,7 +49,7 @@ func (tty *testTTY) ReadString() (string, error) {
func TestColoredUi(t *testing.T) { func TestColoredUi(t *testing.T) {
bufferUi := testUi() bufferUi := testUi()
ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi, defaultUiProgressBar} ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi, &UiProgressBar{}}
if !ui.supportsColors() { if !ui.supportsColors() {
t.Skip("skipping for ui without color support") t.Skip("skipping for ui without color support")
@ -81,7 +81,7 @@ func TestColoredUi(t *testing.T) {
func TestColoredUi_noColorEnv(t *testing.T) { func TestColoredUi_noColorEnv(t *testing.T) {
bufferUi := testUi() bufferUi := testUi()
ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi, defaultUiProgressBar} ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi, &UiProgressBar{}}
// Set the env var to get rid of the color // Set the env var to get rid of the color
oldenv := os.Getenv("PACKER_NO_COLOR") oldenv := os.Getenv("PACKER_NO_COLOR")

View File

@ -18,7 +18,9 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
@ -28,6 +30,7 @@ type FlatConfig struct {
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"` SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
@ -68,7 +71,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
@ -78,6 +83,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},

View File

@ -375,7 +375,7 @@ func (p *Provisioner) setupAdapter(ui packer.Ui, comm packer.Communicator) (stri
keyChecker := ssh.CertChecker{ keyChecker := ssh.CertChecker{
UserKeyFallback: func(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { UserKeyFallback: func(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
if user := conn.User(); user != p.config.User { if user := conn.User(); user != p.config.User {
return nil, errors.New(fmt.Sprintf("authentication failed: %s is not a valid user", user)) return nil, fmt.Errorf("authentication failed: %s is not a valid user", user)
} }
if !bytes.Equal(k.Marshal(), pubKey.Marshal()) { if !bytes.Equal(k.Marshal(), pubKey.Marshal()) {
@ -648,16 +648,30 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro
collectionArgs = append(collectionArgs, "-p", filepath.ToSlash(p.config.CollectionsPath)) collectionArgs = append(collectionArgs, "-p", filepath.ToSlash(p.config.CollectionsPath))
} }
roleInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm) // Search galaxy_file for roles and collections keywords
// Return the error if the role installation failed before attempting the collection install f, err := ioutil.ReadFile(galaxyFile)
if roleInstallError != nil { if err != nil {
return err
}
hasRoles, _ := regexp.Match(`(?m)^roles:`, f)
hasCollections, _ := regexp.Match(`(?m)^collections:`, f)
// If if roles keyword present (v2 format), or no collections keywork present (v1), install roles
if hasRoles || !hasCollections {
if roleInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm); roleInstallError != nil {
return roleInstallError return roleInstallError
} }
// If all is well, proceed with collection install }
// This variable isn't strictly necessary but including for readability to match the role installation
collectionInstallError := p.invokeGalaxyCommand(collectionArgs, ui, comm) // If collections keyword present (v2 format), install collections
if hasCollections {
if collectionInstallError := p.invokeGalaxyCommand(collectionArgs, ui, comm); collectionInstallError != nil {
return collectionInstallError return collectionInstallError
} }
}
return nil
}
// Intended to be invoked from p.executeGalaxy depending on the Ansible Galaxy parameters passed to Packer // Intended to be invoked from p.executeGalaxy depending on the Ansible Galaxy parameters passed to Packer
func (p *Provisioner) invokeGalaxyCommand(args []string, ui packer.Ui, comm packer.Communicator) error { func (p *Provisioner) invokeGalaxyCommand(args []string, ui packer.Ui, comm packer.Communicator) error {

View File

@ -70,7 +70,7 @@ $xml = [xml]@'
<Hidden>false</Hidden> <Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle> <RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun> <WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT24H</ExecutionTimeLimit> <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>4</Priority> <Priority>4</Priority>
</Settings> </Settings>
<Actions Context="Author"> <Actions Context="Author">

View File

@ -126,6 +126,7 @@ func TestProvisionerProvision_SendsFile(t *testing.T) {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
ui := &packer.BasicUi{ ui := &packer.BasicUi{
Writer: b, Writer: b,
PB: &packer.NoopProgressTracker{},
} }
comm := &packer.MockCommunicator{} comm := &packer.MockCommunicator{}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
@ -184,6 +185,7 @@ func TestProvisionerProvision_SendsFileMultipleFiles(t *testing.T) {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
ui := &packer.BasicUi{ ui := &packer.BasicUi{
Writer: b, Writer: b,
PB: &packer.NoopProgressTracker{},
} }
comm := &packer.MockCommunicator{} comm := &packer.MockCommunicator{}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
@ -253,6 +255,7 @@ func TestProvisionerProvision_SendsFileMultipleDirs(t *testing.T) {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
ui := &packer.BasicUi{ ui := &packer.BasicUi{
Writer: b, Writer: b,
PB: &packer.NoopProgressTracker{},
} }
comm := &packer.MockCommunicator{} comm := &packer.MockCommunicator{}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
@ -304,6 +307,7 @@ func TestProvisionerProvision_SendsFileMultipleFilesToFolder(t *testing.T) {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
ui := &packer.BasicUi{ ui := &packer.BasicUi{
Writer: b, Writer: b,
PB: &packer.NoopProgressTracker{},
} }
comm := &packer.MockCommunicator{} comm := &packer.MockCommunicator{}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
@ -361,6 +365,7 @@ func TestProvisionDownloadMkdirAll(t *testing.T) {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
ui := &packer.BasicUi{ ui := &packer.BasicUi{
Writer: b, Writer: b,
PB: &packer.NoopProgressTracker{},
} }
comm := &packer.MockCommunicator{} comm := &packer.MockCommunicator{}
err = p.ProvisionDownload(ui, comm) err = p.ProvisionDownload(ui, comm)

View File

@ -43,7 +43,7 @@ type Config struct {
// An optional endpoint URL (hostname only or fully qualified URI) // An optional endpoint URL (hostname only or fully qualified URI)
// that overrides the default generated endpoint for a client. Set this // that overrides the default generated endpoint for a client. Set this
// to `""` to use the default generated endpoint. // to `nil` or the value to `""` to use the default generated endpoint.
// //
// Note: You must still provide a `Region` value when specifying an // Note: You must still provide a `Region` value when specifying an
// endpoint for a client. // endpoint for a client.
@ -138,7 +138,7 @@ type Config struct {
// `ExpectContinueTimeout` for information on adjusting the continue wait // `ExpectContinueTimeout` for information on adjusting the continue wait
// timeout. https://golang.org/pkg/net/http/#Transport // timeout. https://golang.org/pkg/net/http/#Transport
// //
// You should use this flag to disble 100-Continue if you experience issues // You should use this flag to disable 100-Continue if you experience issues
// with proxies or third party S3 compatible services. // with proxies or third party S3 compatible services.
S3Disable100Continue *bool S3Disable100Continue *bool
@ -183,7 +183,7 @@ type Config struct {
// //
// Example: // Example:
// sess := session.Must(session.NewSession(aws.NewConfig() // sess := session.Must(session.NewSession(aws.NewConfig()
// .WithEC2MetadataDiableTimeoutOverride(true))) // .WithEC2MetadataDisableTimeoutOverride(true)))
// //
// svc := s3.New(sess) // svc := s3.New(sess)
// //
@ -194,7 +194,7 @@ type Config struct {
// both IPv4 and IPv6 addressing. // both IPv4 and IPv6 addressing.
// //
// Setting this for a service which does not support dual stack will fail // Setting this for a service which does not support dual stack will fail
// to make requets. It is not recommended to set this value on the session // to make requests. It is not recommended to set this value on the session
// as it will apply to all service clients created with the session. Even // as it will apply to all service clients created with the session. Even
// services which don't support dual stack endpoints. // services which don't support dual stack endpoints.
// //
@ -238,6 +238,7 @@ type Config struct {
// EnableEndpointDiscovery will allow for endpoint discovery on operations that // EnableEndpointDiscovery will allow for endpoint discovery on operations that
// have the definition in its model. By default, endpoint discovery is off. // have the definition in its model. By default, endpoint discovery is off.
// To use EndpointDiscovery, Endpoint should be unset or set to an empty string.
// //
// Example: // Example:
// sess := session.Must(session.NewSession(&aws.Config{ // sess := session.Must(session.NewSession(&aws.Config{

View File

@ -225,6 +225,8 @@ var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointH
if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" { if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
r.Error = aws.ErrMissingRegion r.Error = aws.ErrMissingRegion
} else if r.ClientInfo.Endpoint == "" { } else if r.ClientInfo.Endpoint == "" {
// Was any endpoint provided by the user, or one was derived by the
// SDK's endpoint resolver?
r.Error = aws.ErrMissingEndpoint r.Error = aws.ErrMissingEndpoint
} }
}} }}

View File

@ -17,8 +17,9 @@ var (
ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil) ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil)
) )
// A SharedCredentialsProvider retrieves credentials from the current user's home // A SharedCredentialsProvider retrieves access key pair (access key ID,
// directory, and keeps track if those credentials are expired. // secret access key, and session token if present) credentials from the current
// user's home directory, and keeps track if those credentials are expired.
// //
// Profile ini file example: $HOME/.aws/credentials // Profile ini file example: $HOME/.aws/credentials
type SharedCredentialsProvider struct { type SharedCredentialsProvider struct {

View File

@ -169,6 +169,29 @@ type AssumeRoleProvider struct {
// size. // size.
Policy *string Policy *string
// The ARNs of IAM managed policies you want to use as managed session policies.
// The policies must exist in the same account as the role.
//
// This parameter is optional. You can provide up to 10 managed policy ARNs.
// However, the plain text that you use for both inline and managed session
// policies can't exceed 2,048 characters.
//
// An AWS conversion compresses the passed session policies and session tags
// into a packed binary format that has a separate limit. Your request can fail
// for this limit even if your plain text meets the other requirements. The
// PackedPolicySize response element indicates by percentage how close the policies
// and tags for your request are to the upper size limit.
//
// Passing policies to this operation returns new temporary credentials. The
// resulting session's permissions are the intersection of the role's identity-based
// policy and the session policies. You can use the role's temporary credentials
// in subsequent AWS API calls to access resources in the account that owns
// the role. You cannot use session policies to grant more permissions than
// those allowed by the identity-based policy of the role that is being assumed.
// For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session)
// in the IAM User Guide.
PolicyArns []*sts.PolicyDescriptorType
// The identification number of the MFA device that is associated with the user // The identification number of the MFA device that is associated with the user
// who is making the AssumeRole call. Specify this value if the trust policy // who is making the AssumeRole call. Specify this value if the trust policy
// of the role being assumed includes a condition that requires MFA authentication. // of the role being assumed includes a condition that requires MFA authentication.
@ -291,6 +314,7 @@ func (p *AssumeRoleProvider) RetrieveWithContext(ctx credentials.Context) (crede
RoleSessionName: aws.String(p.RoleSessionName), RoleSessionName: aws.String(p.RoleSessionName),
ExternalId: p.ExternalID, ExternalId: p.ExternalID,
Tags: p.Tags, Tags: p.Tags,
PolicyArns: p.PolicyArns,
TransitiveTagKeys: p.TransitiveTagKeys, TransitiveTagKeys: p.TransitiveTagKeys,
} }
if p.Policy != nil { if p.Policy != nil {

View File

@ -28,15 +28,46 @@ const (
// compare test values. // compare test values.
var now = time.Now var now = time.Now
// TokenFetcher shuold return WebIdentity token bytes or an error
type TokenFetcher interface {
FetchToken(credentials.Context) ([]byte, error)
}
// FetchTokenPath is a path to a WebIdentity token file
type FetchTokenPath string
// FetchToken returns a token by reading from the filesystem
func (f FetchTokenPath) FetchToken(ctx credentials.Context) ([]byte, error) {
data, err := ioutil.ReadFile(string(f))
if err != nil {
errMsg := fmt.Sprintf("unable to read file at %s", f)
return nil, awserr.New(ErrCodeWebIdentity, errMsg, err)
}
return data, nil
}
// WebIdentityRoleProvider is used to retrieve credentials using // WebIdentityRoleProvider is used to retrieve credentials using
// an OIDC token. // an OIDC token.
type WebIdentityRoleProvider struct { type WebIdentityRoleProvider struct {
credentials.Expiry credentials.Expiry
PolicyArns []*sts.PolicyDescriptorType
client stsiface.STSAPI // Duration the STS credentials will be valid for. Truncated to seconds.
// If unset, the assumed role will use AssumeRoleWithWebIdentity's default
// expiry duration. See
// https://docs.aws.amazon.com/sdk-for-go/api/service/sts/#STS.AssumeRoleWithWebIdentity
// for more information.
Duration time.Duration
// The amount of time the credentials will be refreshed before they expire.
// This is useful refresh credentials before they expire to reduce risk of
// using credentials as they expire. If unset, will default to no expiry
// window.
ExpiryWindow time.Duration ExpiryWindow time.Duration
tokenFilePath string client stsiface.STSAPI
tokenFetcher TokenFetcher
roleARN string roleARN string
roleSessionName string roleSessionName string
} }
@ -52,9 +83,15 @@ func NewWebIdentityCredentials(c client.ConfigProvider, roleARN, roleSessionName
// NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the // NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the
// provided stsiface.STSAPI // provided stsiface.STSAPI
func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, path string) *WebIdentityRoleProvider { func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, path string) *WebIdentityRoleProvider {
return NewWebIdentityRoleProviderWithToken(svc, roleARN, roleSessionName, FetchTokenPath(path))
}
// NewWebIdentityRoleProviderWithToken will return a new WebIdentityRoleProvider with the
// provided stsiface.STSAPI and a TokenFetcher
func NewWebIdentityRoleProviderWithToken(svc stsiface.STSAPI, roleARN, roleSessionName string, tokenFetcher TokenFetcher) *WebIdentityRoleProvider {
return &WebIdentityRoleProvider{ return &WebIdentityRoleProvider{
client: svc, client: svc,
tokenFilePath: path, tokenFetcher: tokenFetcher,
roleARN: roleARN, roleARN: roleARN,
roleSessionName: roleSessionName, roleSessionName: roleSessionName,
} }
@ -71,10 +108,9 @@ func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) {
// 'WebIdentityTokenFilePath' specified destination and if that is empty an // 'WebIdentityTokenFilePath' specified destination and if that is empty an
// error will be returned. // error will be returned.
func (p *WebIdentityRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) { func (p *WebIdentityRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
b, err := ioutil.ReadFile(p.tokenFilePath) b, err := p.tokenFetcher.FetchToken(ctx)
if err != nil { if err != nil {
errMsg := fmt.Sprintf("unable to read file at %s", p.tokenFilePath) return credentials.Value{}, awserr.New(ErrCodeWebIdentity, "failed fetching WebIdentity token: ", err)
return credentials.Value{}, awserr.New(ErrCodeWebIdentity, errMsg, err)
} }
sessionName := p.roleSessionName sessionName := p.roleSessionName
@ -83,10 +119,18 @@ func (p *WebIdentityRoleProvider) RetrieveWithContext(ctx credentials.Context) (
// uses unix time in nanoseconds to uniquely identify sessions. // uses unix time in nanoseconds to uniquely identify sessions.
sessionName = strconv.FormatInt(now().UnixNano(), 10) sessionName = strconv.FormatInt(now().UnixNano(), 10)
} }
var duration *int64
if p.Duration != 0 {
duration = aws.Int64(int64(p.Duration / time.Second))
}
req, resp := p.client.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{ req, resp := p.client.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{
PolicyArns: p.PolicyArns,
RoleArn: &p.roleARN, RoleArn: &p.roleARN,
RoleSessionName: &sessionName, RoleSessionName: &sessionName,
WebIdentityToken: aws.String(string(b)), WebIdentityToken: aws.String(string(b)),
DurationSeconds: duration,
}) })
req.SetContext(ctx) req.SetContext(ctx)

View File

@ -20,7 +20,7 @@ func (c *EC2Metadata) getToken(ctx aws.Context, duration time.Duration) (tokenOu
op := &request.Operation{ op := &request.Operation{
Name: "GetToken", Name: "GetToken",
HTTPMethod: "PUT", HTTPMethod: "PUT",
HTTPPath: "/api/token", HTTPPath: "/latest/api/token",
} }
var output tokenOutput var output tokenOutput
@ -62,7 +62,7 @@ func (c *EC2Metadata) GetMetadataWithContext(ctx aws.Context, p string) (string,
op := &request.Operation{ op := &request.Operation{
Name: "GetMetadata", Name: "GetMetadata",
HTTPMethod: "GET", HTTPMethod: "GET",
HTTPPath: sdkuri.PathJoin("/meta-data", p), HTTPPath: sdkuri.PathJoin("/latest/meta-data", p),
} }
output := &metadataOutput{} output := &metadataOutput{}
@ -88,7 +88,7 @@ func (c *EC2Metadata) GetUserDataWithContext(ctx aws.Context) (string, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetUserData", Name: "GetUserData",
HTTPMethod: "GET", HTTPMethod: "GET",
HTTPPath: "/user-data", HTTPPath: "/latest/user-data",
} }
output := &metadataOutput{} output := &metadataOutput{}
@ -113,7 +113,7 @@ func (c *EC2Metadata) GetDynamicDataWithContext(ctx aws.Context, p string) (stri
op := &request.Operation{ op := &request.Operation{
Name: "GetDynamicData", Name: "GetDynamicData",
HTTPMethod: "GET", HTTPMethod: "GET",
HTTPPath: sdkuri.PathJoin("/dynamic", p), HTTPPath: sdkuri.PathJoin("/latest/dynamic", p),
} }
output := &metadataOutput{} output := &metadataOutput{}

View File

@ -5,6 +5,10 @@
// variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to // variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
// true instructs the SDK to disable the EC2 Metadata client. The client cannot // true instructs the SDK to disable the EC2 Metadata client. The client cannot
// be used while the environment variable is set to true, (case insensitive). // be used while the environment variable is set to true, (case insensitive).
//
// The endpoint of the EC2 IMDS client can be configured via the environment
// variable, AWS_EC2_METADATA_SERVICE_ENDPOINT when creating the client with a
// Session. See aws/session#Options.EC2IMDSEndpoint for more details.
package ec2metadata package ec2metadata
import ( import (
@ -12,6 +16,7 @@ import (
"errors" "errors"
"io" "io"
"net/http" "net/http"
"net/url"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -69,6 +74,9 @@ func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
// a client when not using a session. Generally using just New with a session // a client when not using a session. Generally using just New with a session
// is preferred. // is preferred.
// //
// Will remove the URL path from the endpoint provided to ensure the EC2 IMDS
// client is able to communicate with the EC2 IMDS API.
//
// If an unmodified HTTP client is provided from the stdlib default, or no client // If an unmodified HTTP client is provided from the stdlib default, or no client
// the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened. // the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened.
// To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default. // To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default.
@ -86,6 +94,15 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
cfg.MaxRetries = aws.Int(2) cfg.MaxRetries = aws.Int(2)
} }
if u, err := url.Parse(endpoint); err == nil {
// Remove path from the endpoint since it will be added by requests.
// This is an artifact of the SDK adding `/latest` to the endpoint for
// EC2 IMDS, but this is now moved to the operation definition.
u.Path = ""
u.RawPath = ""
endpoint = u.String()
}
svc := &EC2Metadata{ svc := &EC2Metadata{
Client: client.New( Client: client.New(
cfg, cfg,

View File

@ -93,7 +93,7 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
} }
func custAddS3DualStack(p *partition) { func custAddS3DualStack(p *partition) {
if p.ID != "aws" { if !(p.ID == "aws" || p.ID == "aws-cn" || p.ID == "aws-us-gov") {
return return
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,8 @@ import (
"strings" "strings"
) )
var regionValidationRegex = regexp.MustCompile(`^[[:alnum:]]([[:alnum:]\-]*[[:alnum:]])?$`)
type partitions []partition type partitions []partition
func (ps partitions) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) { func (ps partitions) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
@ -124,7 +126,7 @@ func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (
defs := []endpoint{p.Defaults, s.Defaults} defs := []endpoint{p.Defaults, s.Defaults}
return e.resolve(service, p.ID, region, p.DNSSuffix, defs, opt), nil return e.resolve(service, p.ID, region, p.DNSSuffix, defs, opt)
} }
func serviceList(ss services) []string { func serviceList(ss services) []string {
@ -233,7 +235,7 @@ func getByPriority(s []string, p []string, def string) string {
return s[0] return s[0]
} }
func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs []endpoint, opts Options) ResolvedEndpoint { func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs []endpoint, opts Options) (ResolvedEndpoint, error) {
var merged endpoint var merged endpoint
for _, def := range defs { for _, def := range defs {
merged.mergeIn(def) merged.mergeIn(def)
@ -260,6 +262,10 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [
region = signingRegion region = signingRegion
} }
if !validateInputRegion(region) {
return ResolvedEndpoint{}, fmt.Errorf("invalid region identifier format provided")
}
u := strings.Replace(hostname, "{service}", service, 1) u := strings.Replace(hostname, "{service}", service, 1)
u = strings.Replace(u, "{region}", region, 1) u = strings.Replace(u, "{region}", region, 1)
u = strings.Replace(u, "{dnsSuffix}", dnsSuffix, 1) u = strings.Replace(u, "{dnsSuffix}", dnsSuffix, 1)
@ -274,7 +280,7 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [
SigningName: signingName, SigningName: signingName,
SigningNameDerived: signingNameDerived, SigningNameDerived: signingNameDerived,
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner), SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
} }, nil
} }
func getEndpointScheme(protocols []string, disableSSL bool) string { func getEndpointScheme(protocols []string, disableSSL bool) string {
@ -339,3 +345,7 @@ const (
boxedFalse boxedFalse
boxedTrue boxedTrue
) )
func validateInputRegion(region string) bool {
return regionValidationRegex.MatchString(region)
}

View File

@ -9,7 +9,8 @@ func isErrConnectionReset(err error) bool {
return false return false
} }
if strings.Contains(err.Error(), "connection reset") || if strings.Contains(err.Error(), "use of closed network connection") ||
strings.Contains(err.Error(), "connection reset") ||
strings.Contains(err.Error(), "broken pipe") { strings.Contains(err.Error(), "broken pipe") {
return true return true
} }

Some files were not shown because too many files have changed in this diff Show More