Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44eda339d3 |
@@ -7,11 +7,11 @@ version: 2.1
|
|||||||
executors:
|
executors:
|
||||||
golang:
|
golang:
|
||||||
docker:
|
docker:
|
||||||
- image: docker.mirror.hashicorp.services/circleci/golang:1.15
|
- image: circleci/golang:1.13
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
darwin:
|
darwin:
|
||||||
macos:
|
macos:
|
||||||
xcode: "12.0.0"
|
xcode: "9.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.15"
|
GOVERSION: "1.13"
|
||||||
- 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.15"
|
GOVERSION: "1.13"
|
||||||
- codecov/upload:
|
- codecov/upload:
|
||||||
file: coverage.txt
|
file: coverage.txt
|
||||||
check-lint:
|
check-lint:
|
||||||
@@ -153,7 +153,7 @@ jobs:
|
|||||||
destination: /
|
destination: /
|
||||||
build-website-docker-image:
|
build-website-docker-image:
|
||||||
docker:
|
docker:
|
||||||
- image: docker.mirror.hashicorp.services/circleci/buildpack-deps
|
- image: circleci/buildpack-deps
|
||||||
shell: /usr/bin/env bash -euo pipefail -c
|
shell: /usr/bin/env bash -euo pipefail -c
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -167,14 +167,14 @@ jobs:
|
|||||||
echo "Dependencies have not changed, not building a new website docker image."
|
echo "Dependencies have not changed, not building a new website docker image."
|
||||||
else
|
else
|
||||||
cd website/
|
cd website/
|
||||||
docker login -u $WEBSITE_DOCKER_USER -p $WEBSITE_DOCKER_PASS
|
|
||||||
docker build -t hashicorp/packer-website:$IMAGE_TAG .
|
docker build -t hashicorp/packer-website:$IMAGE_TAG .
|
||||||
docker tag hashicorp/packer-website:$IMAGE_TAG hashicorp/packer-website:latest
|
docker tag hashicorp/packer-website:$IMAGE_TAG hashicorp/packer-website:latest
|
||||||
|
docker login -u $WEBSITE_DOCKER_USER -p $WEBSITE_DOCKER_PASS
|
||||||
docker push hashicorp/packer-website
|
docker push hashicorp/packer-website
|
||||||
fi
|
fi
|
||||||
algolia-index:
|
algolia-index:
|
||||||
docker:
|
docker:
|
||||||
- image: docker.mirror.hashicorp.services/node:12
|
- image: node:12
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
|
|||||||
+1
-2
@@ -13,6 +13,5 @@ coverage:
|
|||||||
project: off
|
project: off
|
||||||
patch: off
|
patch: off
|
||||||
|
|
||||||
ignore: # ignore hcl2spec generated code for coverage and mocks
|
ignore: # ignore hcl2spec generated code for coverage
|
||||||
- "**/*.hcl2spec.go"
|
- "**/*.hcl2spec.go"
|
||||||
- "**/*_mock.go"
|
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
*.mdx text eol=lf
|
*.mdx text eol=lf
|
||||||
*.ps1 text eol=lf
|
*.ps1 text eol=lf
|
||||||
*.hcl text eol=lf
|
*.hcl text eol=lf
|
||||||
*.txt text eol=lf
|
|
||||||
go.mod text eol=lf
|
go.mod text eol=lf
|
||||||
go.sum text eol=lf
|
go.sum text eol=lf
|
||||||
common/test-fixtures/root/* eol=lf
|
common/test-fixtures/root/* eol=lf
|
||||||
|
|||||||
@@ -27,4 +27,3 @@ Thumbs.db
|
|||||||
/packer.exe
|
/packer.exe
|
||||||
.project
|
.project
|
||||||
cache
|
cache
|
||||||
/.vscode/
|
|
||||||
|
|||||||
+1
-227
@@ -1,211 +1,4 @@
|
|||||||
## 1.6.6 (Upcoming)
|
## 1.6.2 (Upcoming)
|
||||||
|
|
||||||
### FEATURES:
|
|
||||||
* **New command** `fmt` allows users to format existing HCL2 configuration
|
|
||||||
files into a canonical style. Please see [fmt command
|
|
||||||
docs](https://packer.io/docs/commands/fmt) for more details. [GH-10225]
|
|
||||||
* **New function** `env` allows users to set the default value of a variable to
|
|
||||||
the value of an environment variable. Please see [env function
|
|
||||||
docs](https://www.packer.io/docs/from-1.5/functions/contextual/env") for
|
|
||||||
more details. [GH-10240]
|
|
||||||
* builder/azure-arm: Create keyvaults with SoftDelete enabled [GH-10210]
|
|
||||||
* builder/outscale: Add x509 certificate support [GH-10161]
|
|
||||||
* post-processor/yandex-export: Verify the access to a specific bucket
|
|
||||||
[GH-10188]
|
|
||||||
|
|
||||||
### IMPROVEMENTS
|
|
||||||
* builder/amazon-ebs: Add tags to launch templates. [GH-10203]
|
|
||||||
* builder/azure-arm: Add Azure CLI authentication support to builder.
|
|
||||||
[GH-10157]
|
|
||||||
* core/hcl: Update to `hcl2_upgrade` command to support complex variable
|
|
||||||
values and packer version blocks. [GH-10221]
|
|
||||||
* post-processor/vagrant-cloud: Add support for uploading directly to storage
|
|
||||||
on Vagrant Cloud. [GH-10193]
|
|
||||||
|
|
||||||
### BUG FIXES
|
|
||||||
* builder/amazon: Fix single `tag` interpolation to allow for templating engine
|
|
||||||
usage. [GH-10224]
|
|
||||||
|
|
||||||
## 1.6.5 (October 30, 2020)
|
|
||||||
|
|
||||||
### FEATURES:
|
|
||||||
* New Builder(s): Proxmox builder has been split into two new builders
|
|
||||||
`proxmox-iso` and `promox-clone`. See [Proxmox
|
|
||||||
Builder](https://packer.io/docs/builders/proxmox) for more information on
|
|
||||||
the builder. For users of the previous `proxmox` builder please use `packer
|
|
||||||
fix` to migrate your templates to the new `promox-iso` builder. [GH-9262]
|
|
||||||
|
|
||||||
### BUG FIXES:
|
|
||||||
|
|
||||||
* builder/amazon: SSM connection now recovers from reboots. [GH-10003]
|
|
||||||
* builder/azure-arm: Fix build failures due to the deletion of additional
|
|
||||||
managed disks defined in "disk_additional_size". [GH-10163]
|
|
||||||
* builder/azure-chroot: Fix typo in option `exlude_from_latest` to
|
|
||||||
`exclude_from_latest`. Old name will still be respected. [GH-10034]
|
|
||||||
* builder/googlecompute: Fix HCL image_encryption_key fields and use the same
|
|
||||||
casing in JSON and HCL2 [GH-10173]
|
|
||||||
* builder/openstack: Fix source image validation regression when using filters.
|
|
||||||
[GH-10065]
|
|
||||||
* builder/proxmox: Fix unhandled buildvar type for HCL2 enabled build
|
|
||||||
templates. [GH-10154]
|
|
||||||
* builder/qemu: Fix a regression where Packer would not wait properly in
|
|
||||||
step_shutdown when a null communicator was used. [GH-10178]
|
|
||||||
* builder/qemu: Fix crash in step_run of qemu when loading commhostport form
|
|
||||||
the statebag in a situation where the communicator is none. [GH-10145]
|
|
||||||
* builder/vsphere-clone: Packer was not respecting the "destroy" flag set in
|
|
||||||
the content library config, and always keeping the source vm. This has been
|
|
||||||
fixed. [GH-10165]
|
|
||||||
* builder/vsphere: Ensure builds are able to continue when no communicator has
|
|
||||||
been specified `"communicator": "none"`. [GH-9964]
|
|
||||||
* builder/vsphere: Fix CD uploads so that Packer does not try to delete a CD
|
|
||||||
that was not successfully uploaded. [GH-10155]
|
|
||||||
* core/hcl: Hide sensitive variables from output. [GH-10031]
|
|
||||||
* core/hcl: Packer HCL's "Coalesce" function now behaves same way as
|
|
||||||
Terraform's. [GH-10016]
|
|
||||||
* core: Fix artifact handling so that input artifacts are properly preserved in
|
|
||||||
postprocessors that don't modify artifacts. [GH-9996]
|
|
||||||
* core: Fix pathing in cd_files to copy proper directory tree when user
|
|
||||||
provided absolute paths. [GH-10022]
|
|
||||||
* provisioner/ansible: Ansible galaxy no longer forces use of collections in v1
|
|
||||||
files. [GH-10010]
|
|
||||||
|
|
||||||
### IMPROVEMENTS:
|
|
||||||
|
|
||||||
* builder/amazon-ebssurrogate: Apply snapshot tags at snapshot creation time.
|
|
||||||
[GH-10150]
|
|
||||||
* builder/amazon: Add `io2` as a supported volume type. [GH-10102]
|
|
||||||
* builder/amazon: Add support for source instance tenancy [GH-10085]
|
|
||||||
* builder/google: Add service account impersonation. [GH-9968] [GH-10054]
|
|
||||||
* builder/googlecompute: Add `skip_create_image` option. [GH-10115]
|
|
||||||
* builder/googlecompute: Allow users to select the algorithm to use when
|
|
||||||
generating temporary SSH keypair [GH-10111]
|
|
||||||
* builder/linode: Add `state_timeout` attribute to Linode builder. [GH-10128]
|
|
||||||
* builder/oracle-oci: New option to specify image compartment separate from
|
|
||||||
build compartment. [GH-10040]
|
|
||||||
* builder/oracle-oci: New option to specify boot volume size. [GH-10017]
|
|
||||||
* builder/oracle: Add `base_image_filter` option as alternative to
|
|
||||||
`base_image_ocid` [GH-10116]
|
|
||||||
* builder/outscale: Migrate to new Outscale SDK. [GH-10056]
|
|
||||||
* builder/proxmox: split Proxmox into proxmox-iso and proxmox-clone. [GH-9626]
|
|
||||||
[GH-10166]
|
|
||||||
* builder/scaleway: Allow the user to use an image label (eg ubuntu_focal)
|
|
||||||
instead of a hardcoded UUID on the Scaleway builder. [GH-10061]
|
|
||||||
* builder/vsphere: Skip iso download if hashed file is already present on
|
|
||||||
remote datastore. [GH-10143]
|
|
||||||
* builder/yandex: Add support for IAM credentials in the token field and
|
|
||||||
YC_TOKEN environment variable. [GH-10158]
|
|
||||||
* core/hcl: Add ability to set version restrictions [GH-10149]
|
|
||||||
* core/hcl: Add build.name variable so users can access build name in addition
|
|
||||||
to source name. [GH-10114]
|
|
||||||
* core/hcl: Add consul_key function to HCL templates. [GH-10119]
|
|
||||||
* core/hcl: Add HCL2 aws_secretsmanager function [GH-10124]
|
|
||||||
* core/hcl: Add packer.version variable to hcl configs so users can access the
|
|
||||||
Packer release version. [GH-10117]
|
|
||||||
* core: Let user provide type of generated ssh key instead of always doing ssh-
|
|
||||||
rsa [GH-10101]
|
|
||||||
|
|
||||||
## 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:
|
|
||||||
* 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
|
|
||||||
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.
|
|
||||||
[GH-9856]
|
|
||||||
* builder/oracle-oci: Allow freeform and defined tags to be added to an instance.
|
|
||||||
[GH-9802]
|
|
||||||
* 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]
|
|
||||||
* builder/proxmox: Allow the mounting of multiple ISOs via the `cd_drive`
|
|
||||||
option. [GH-9653]
|
|
||||||
* builder/proxmox: Fix boot command special keys. [GH-9885]
|
|
||||||
* builder/qemu: Add `qemu_img_args` option to set special cli flags for calls
|
|
||||||
to qemu-img [GH-9956]
|
|
||||||
* builder/qemu: Add `skip_resize_disk` option to skip the resizing of QCOW2
|
|
||||||
images. [GH-9896] [GH-9860]
|
|
||||||
* builder/qemu: Skip qemu-img convert on MacOS to prevent the creation of
|
|
||||||
corrupt images [QEMU
|
|
||||||
#1776920](https://bugs.launchpad.net/qemu/+bug/1776920) [GH-9949]
|
|
||||||
* builder/scaleway: Change default boottype to local. [GH-9853]
|
|
||||||
* builder/scaleway: Update scaleway to use non-deprecated sdk. [GH-9902]
|
|
||||||
* builder/vmware: Add `vnc_over_websocket` to allow the sending of a
|
|
||||||
`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 floppy_files, cd_files, and iso_paths options.
|
|
||||||
[GH-9963]
|
|
||||||
* builder/vsphere-iso: Add NVMe controller support. [GH-9880]
|
|
||||||
* builder/vsphere: Look for a default resource pool when root resource pool is
|
|
||||||
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
|
|
||||||
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
|
|
||||||
string instead of a variable name. [GH-9863]
|
|
||||||
* post-processor/vagrant: Support the use of template variables within
|
|
||||||
Vagrantfile templates. [GH-9923]
|
|
||||||
* post-processor/yandex-import: Allow custom API endpoint. [GH-9850]
|
|
||||||
* provisioner/ansible: Add support for Ansible Galaxy Collections. [GH-9903]
|
|
||||||
|
|
||||||
### BUG FIXES:
|
|
||||||
* builder/amazon-ebs: Fix issue where retrying on invalid IAM instance profile
|
|
||||||
error was creating multiple spot instances. [GH-9946]
|
|
||||||
* builder/amazon-ebssurrogate: Fix issue where builder defaults to AWS managed
|
|
||||||
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: Skip compaction when backing file is used. [GH-9918]
|
|
||||||
* builder/scaleway: Add pre validate step to prevent the creation of multiple
|
|
||||||
images with the same name. [GH-9840]
|
|
||||||
* builder/vmware-iso: Prevent the use of reserved SCSI ID 0:7 when attaching
|
|
||||||
multiple disks. [GH-9940]
|
|
||||||
* builder/vsphere: Fix overly strict iso_path validation regex. [GH-9855]
|
|
||||||
* command/console: Prevent failure when there are unknown vars. [GH-9864]
|
|
||||||
* command/inspect: Allow unset variables in HCL2 and JSON. [GH-9832]
|
|
||||||
* 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]
|
|
||||||
* post-processor/digitalocean-import: Fix crash caused by empty artifact.Files
|
|
||||||
slice. [GH-9857]
|
|
||||||
* post-processor/yandex-export: Check for error after runner completes.
|
|
||||||
[GH-9925]
|
|
||||||
* post-processor/yandex-export: Set metadata key to expected value on error.
|
|
||||||
[GH-9849]
|
|
||||||
* post-processor/yandex-import: Fix S3 URL construct process. [GH-9931]
|
|
||||||
|
|
||||||
## 1.6.2 (August 28, 2020)
|
|
||||||
|
|
||||||
### FEATURES:
|
|
||||||
* **New command** `hcl2_upgrade` is a JSON to HCL2 transpiler that allows users
|
|
||||||
to transform an existing JSON configuration template into its HCL2 template
|
|
||||||
equivalent. Please see [hcl2_upgrade command
|
|
||||||
docs](https://packer.io/docs/commands/hcl2_upgrade) for more details.
|
|
||||||
[GH-9659]
|
|
||||||
|
|
||||||
### IMPROVEMENTS:
|
### IMPROVEMENTS:
|
||||||
* builder/amazon: Add all of the custom AWS template engines to `build`
|
* builder/amazon: Add all of the custom AWS template engines to `build`
|
||||||
@@ -215,22 +8,14 @@
|
|||||||
* builder/azure: Add FreeBSD support to azure/chroot builder. [GH-9697]
|
* builder/azure: Add FreeBSD support to azure/chroot builder. [GH-9697]
|
||||||
* builder/vmware-esx: Add `network_name` option to vmware so that users can set
|
* builder/vmware-esx: Add `network_name` option to vmware so that users can set
|
||||||
a network without using vmx data. [GH-9718]
|
a network without using vmx data. [GH-9718]
|
||||||
* builder/vmware-vmx: Add additional disk configuration option. Previously
|
|
||||||
only implemented for vmware-iso builder [GH-9815]
|
|
||||||
* builder/vmware: Add a `remote_output_directory option` so users can tell
|
* builder/vmware: Add a `remote_output_directory option` so users can tell
|
||||||
Packer where on a datastore to create a vm. [GH-9784]
|
Packer where on a datastore to create a vm. [GH-9784]
|
||||||
* builder/vmware: Add option to export to ovf or ova from a local vmware build
|
|
||||||
[GH-9825]
|
|
||||||
* builder/vmware: Add progress tracker to vmware-esx5 iso upload. [GH-9779]
|
* builder/vmware: Add progress tracker to vmware-esx5 iso upload. [GH-9779]
|
||||||
* builder/vsphere-iso: Add support for building on a single ESXi host
|
|
||||||
[GH-9793]
|
|
||||||
* builder/vsphere: Add new `directory_permission` config export option.
|
* builder/vsphere: Add new `directory_permission` config export option.
|
||||||
[GH-9704]
|
[GH-9704]
|
||||||
* builder/vsphere: Add option to import OVF templates to the Content Library
|
* builder/vsphere: Add option to import OVF templates to the Content Library
|
||||||
[GH-9755]
|
[GH-9755]
|
||||||
* builder/vsphere: Add step and options to customize cloned VMs. [GH-9665]
|
* builder/vsphere: Add step and options to customize cloned VMs. [GH-9665]
|
||||||
* builder/vsphere: Update `iso_paths` to support reading ISOs from Content
|
|
||||||
Library paths [GH-9801]
|
|
||||||
* core/hcl: Add provisioner "override" option to HCL2 templates. [GH-9764]
|
* core/hcl: Add provisioner "override" option to HCL2 templates. [GH-9764]
|
||||||
* core/hcl: Add vault integration as an HCL2 function function. [GH-9746]
|
* core/hcl: Add vault integration as an HCL2 function function. [GH-9746]
|
||||||
* core: Add colored prefix to progress bar so it's clearer what build each
|
* core: Add colored prefix to progress bar so it's clearer what build each
|
||||||
@@ -242,8 +27,6 @@
|
|||||||
[GH-9773]
|
[GH-9773]
|
||||||
* post-processor/vsphere: Improve UI to catch bad credentials and print errors.
|
* post-processor/vsphere: Improve UI to catch bad credentials and print errors.
|
||||||
[GH-9649]
|
[GH-9649]
|
||||||
* provisioner/ansible-remote: Add `ansible_ssh_extra_args` so users can specify
|
|
||||||
extra arguments to pass to ssh [GH-9821]
|
|
||||||
* provisioner/file: Clean up, bugfix, and document previously-hidden `sources`
|
* provisioner/file: Clean up, bugfix, and document previously-hidden `sources`
|
||||||
option. [GH-9725] [GH-9735]
|
option. [GH-9725] [GH-9735]
|
||||||
* provisioner/salt-masterless: Add option to option to download community
|
* provisioner/salt-masterless: Add option to option to download community
|
||||||
@@ -256,11 +39,6 @@
|
|||||||
binaries. [GH-9706]
|
binaries. [GH-9706]
|
||||||
* builder/amazon-ebssurrogate: Make skip_save_build_region option work in the
|
* builder/amazon-ebssurrogate: Make skip_save_build_region option work in the
|
||||||
ebssurrogate builder, not just the ebs builder. [GH-9666]
|
ebssurrogate builder, not just the ebs builder. [GH-9666]
|
||||||
* builder/amazon: Add retry logic to the spot instance creation step to handle
|
|
||||||
"Invalid IAM Instance Profile name" errors [GH-9810]
|
|
||||||
* builder/amazon: Update the `aws_secretsmanager` function to read from the AWS
|
|
||||||
credentials file for obtaining default region information; fixes the
|
|
||||||
'MissingRegion' error when AWS_REGION is not set [GH-9781]
|
|
||||||
* builder/file: Make sure that UploadDir receives the interpolated destination.
|
* builder/file: Make sure that UploadDir receives the interpolated destination.
|
||||||
[GH-9698]
|
[GH-9698]
|
||||||
* builder/googlecompute: Fix bug where startup script hang would cause export
|
* builder/googlecompute: Fix bug where startup script hang would cause export
|
||||||
@@ -283,10 +61,6 @@
|
|||||||
interpolation. [GH-9673]
|
interpolation. [GH-9673]
|
||||||
* post-processor/vsphere-template: Fix ReregisterVM to default to true instead
|
* post-processor/vsphere-template: Fix ReregisterVM to default to true instead
|
||||||
of false. [GH-9736]
|
of false. [GH-9736]
|
||||||
* post-processor/yandex-export: Fix issue when validating region_name [GH-9814]
|
|
||||||
* provisioner/inspec: Fix the 'Unsupported argument; An argument named
|
|
||||||
"command"' error when using the inspec provisioner in an HCL2 configuration
|
|
||||||
[GH-9800]
|
|
||||||
|
|
||||||
## 1.6.1 (July 30, 2020)
|
## 1.6.1 (July 30, 2020)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -49,8 +49,8 @@
|
|||||||
/builder/proxmox/ @carlpett
|
/builder/proxmox/ @carlpett
|
||||||
/website/pages/docs/builders/proxmox* @carlpett
|
/website/pages/docs/builders/proxmox* @carlpett
|
||||||
|
|
||||||
/builder/scaleway/ @scaleway/devtools
|
/builder/scaleway/ @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
||||||
/website/pages/docs/builders/scaleway* @scaleway/devtools
|
/website/pages/docs/builders/scaleway* @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
||||||
|
|
||||||
/builder/hcloud/ @LKaemmerling
|
/builder/hcloud/ @LKaemmerling
|
||||||
/website/pages/docs/builders/hcloud* @LKaemmerling
|
/website/pages/docs/builders/hcloud* @LKaemmerling
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM docker.mirror.hashicorp.services/ubuntu:16.04
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
TEST?=$(shell go list ./...)
|
TEST?=$(shell go list ./...)
|
||||||
COUNT?=1
|
|
||||||
VET?=$(shell go list ./...)
|
VET?=$(shell go list ./...)
|
||||||
ACC_TEST_BUILDERS?=all
|
ACC_TEST_BUILDERS?=all
|
||||||
ACC_TEST_PROVISIONERS?=all
|
ACC_TEST_PROVISIONERS?=all
|
||||||
@@ -26,7 +25,7 @@ export GOLDFLAGS
|
|||||||
.PHONY: bin checkversion ci ci-lint default install-build-deps install-gen-deps fmt fmt-docs fmt-examples generate install-lint-deps lint \
|
.PHONY: bin checkversion ci ci-lint default install-build-deps install-gen-deps fmt fmt-docs fmt-examples generate install-lint-deps lint \
|
||||||
releasebin test testacc testrace
|
releasebin test testacc testrace
|
||||||
|
|
||||||
default: install-build-deps install-gen-deps generate dev
|
default: install-build-deps install-gen-deps generate bin
|
||||||
|
|
||||||
ci: testrace ## Test in continuous integration
|
ci: testrace ## Test in continuous integration
|
||||||
|
|
||||||
@@ -137,7 +136,7 @@ generate-check: generate ## Check go code generation is on par
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
test: mode-check vet ## Run unit tests
|
test: mode-check vet ## Run unit tests
|
||||||
@go test -count $(COUNT) $(TEST) $(TESTARGS) -timeout=3m
|
@go test $(TEST) $(TESTARGS) -timeout=3m
|
||||||
|
|
||||||
# acctest runs provisioners acceptance tests
|
# acctest runs provisioners acceptance tests
|
||||||
provisioners-acctest: install-build-deps generate
|
provisioners-acctest: install-build-deps generate
|
||||||
@@ -146,14 +145,14 @@ provisioners-acctest: install-build-deps generate
|
|||||||
# testacc runs acceptance tests
|
# testacc runs acceptance tests
|
||||||
testacc: install-build-deps generate ## Run acceptance tests
|
testacc: install-build-deps generate ## Run acceptance tests
|
||||||
@echo "WARN: Acceptance tests will take a long time to run and may cost money. Ctrl-C if you want to cancel."
|
@echo "WARN: Acceptance tests will take a long time to run and may cost money. Ctrl-C if you want to cancel."
|
||||||
PACKER_ACC=1 go test -count $(COUNT) -v $(TEST) $(TESTARGS) -timeout=120m
|
PACKER_ACC=1 go test -v $(TEST) $(TESTARGS) -timeout=45m
|
||||||
|
|
||||||
testrace: mode-check vet ## Test with race detection enabled
|
testrace: mode-check vet ## Test with race detection enabled
|
||||||
@GO111MODULE=off go test -count $(COUNT) -race $(TEST) $(TESTARGS) -timeout=3m -p=8
|
@GO111MODULE=off go test -race $(TEST) $(TESTARGS) -timeout=3m -p=8
|
||||||
|
|
||||||
# Runs code coverage and open a html page with report
|
# Runs code coverage and open a html page with report
|
||||||
cover:
|
cover:
|
||||||
go test -count $(COUNT) $(TEST) $(TESTARGS) -timeout=3m -coverprofile=coverage.out
|
go test $(TEST) $(TESTARGS) -timeout=3m -coverprofile=coverage.out
|
||||||
go tool cover -html=coverage.out
|
go tool cover -html=coverage.out
|
||||||
rm coverage.out
|
rm coverage.out
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstruct
|
|||||||
|
|
||||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
|||||||
@@ -107,8 +107,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -223,8 +221,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
|
|||||||
@@ -422,6 +422,7 @@ func TestBuilderAcc_ECSImageSharing(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// share with catsby
|
||||||
const testBuilderAccSharing = `
|
const testBuilderAccSharing = `
|
||||||
{
|
{
|
||||||
"builders": [{
|
"builders": [{
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import (
|
|||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The "AlicloudDiskDevice" object us used for the `ECSSystemDiskMapping` and
|
|
||||||
// `ECSImagesDiskMappings` options, and contains the following fields:
|
|
||||||
type AlicloudDiskDevice struct {
|
type AlicloudDiskDevice struct {
|
||||||
// The value of disk name is blank by default. [2,
|
// The value of disk name is blank by default. [2,
|
||||||
// 128] English or Chinese characters, must begin with an
|
// 128] English or Chinese characters, must begin with an
|
||||||
@@ -21,7 +19,8 @@ type AlicloudDiskDevice struct {
|
|||||||
// ., _ and -. The disk name will appear on the console. It cannot
|
// ., _ and -. The disk name will appear on the console. It cannot
|
||||||
// begin with `http://` or `https://`.
|
// begin with `http://` or `https://`.
|
||||||
DiskName string `mapstructure:"disk_name" required:"false"`
|
DiskName string `mapstructure:"disk_name" required:"false"`
|
||||||
// Category of the system disk. Optional values are:
|
// Category of the system disk. Optional values
|
||||||
|
// are:
|
||||||
// - cloud - general cloud disk
|
// - cloud - general cloud disk
|
||||||
// - cloud_efficiency - efficiency cloud disk
|
// - cloud_efficiency - efficiency cloud disk
|
||||||
// - cloud_ssd - cloud SSD
|
// - cloud_ssd - cloud SSD
|
||||||
@@ -33,8 +32,6 @@ type AlicloudDiskDevice struct {
|
|||||||
// Snapshots are used to create the data
|
// Snapshots are used to create the data
|
||||||
// disk After this parameter is specified, Size is ignored. The actual
|
// disk After this parameter is specified, Size is ignored. The actual
|
||||||
// size of the created disk is the size of the specified snapshot.
|
// size of the created disk is the size of the specified snapshot.
|
||||||
// This field is only used in the ECSImagesDiskMappings option, not
|
|
||||||
// the ECSSystemDiskMapping option.
|
|
||||||
SnapshotId string `mapstructure:"disk_snapshot_id" required:"false"`
|
SnapshotId string `mapstructure:"disk_snapshot_id" required:"false"`
|
||||||
// The value of disk description is blank by
|
// The value of disk description is blank by
|
||||||
// default. [2, 256] characters. The disk description will appear on the
|
// default. [2, 256] characters. The disk description will appear on the
|
||||||
@@ -47,51 +44,94 @@ type AlicloudDiskDevice struct {
|
|||||||
// such as /dev/xvdb It is null unless the Status is In_use.
|
// such as /dev/xvdb It is null unless the Status is In_use.
|
||||||
Device string `mapstructure:"disk_device" required:"false"`
|
Device string `mapstructure:"disk_device" required:"false"`
|
||||||
// Whether or not to encrypt the data disk.
|
// Whether or not to encrypt the data disk.
|
||||||
// If this option is set to true, the data disk will be encryped and
|
// If this option is set to true, the data disk will be encryped and corresponding snapshot in the target image will also be encrypted. By
|
||||||
// corresponding snapshot in the target image will also be encrypted. By
|
|
||||||
// default, if this is an extra data disk, Packer will not encrypt the
|
// default, if this is an extra data disk, Packer will not encrypt the
|
||||||
// data disk. Otherwise, Packer will keep the encryption setting to what
|
// data disk. Otherwise, Packer will keep the encryption setting to what
|
||||||
// it was in the source image. Please refer to Introduction of ECS disk
|
// it was in the source image. Please refer to Introduction of ECS disk encryption
|
||||||
// encryption for more details.
|
// for more details.
|
||||||
Encrypted config.Trilean `mapstructure:"disk_encrypted" required:"false"`
|
Encrypted config.Trilean `mapstructure:"disk_encrypted" required:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// The "AlicloudDiskDevices" object is used to define disk mappings for your
|
|
||||||
// instance.
|
|
||||||
type AlicloudDiskDevices struct {
|
type AlicloudDiskDevices struct {
|
||||||
// Image disk mapping for the system disk.
|
// Image disk mapping for system
|
||||||
// See the [disk device configuration](#disk-devices-configuration) section
|
// disk.
|
||||||
// for more information on options.
|
// - `disk_category` (string) - Category of the system disk. Optional values
|
||||||
// Usage example:
|
// are:
|
||||||
|
// - `cloud` - general cloud disk
|
||||||
|
// - `cloud_efficiency` - efficiency cloud disk
|
||||||
|
// - `cloud_ssd` - cloud SSD
|
||||||
|
//
|
||||||
|
// For phased-out instance types and non-I/O optimized instances, the
|
||||||
|
// default value is cloud. Otherwise, the default value is
|
||||||
|
// cloud\_efficiency.
|
||||||
|
//
|
||||||
|
// - `disk_description` (string) - The value of disk description is blank by
|
||||||
|
// default. \[2, 256\] characters. The disk description will appear on the
|
||||||
|
// console. It cannot begin with `http://` or `https://`.
|
||||||
|
//
|
||||||
|
// - `disk_name` (string) - The value of disk name is blank by default. \[2,
|
||||||
|
// 128\] English or Chinese characters, must begin with an
|
||||||
|
// uppercase/lowercase letter or Chinese character. Can contain numbers,
|
||||||
|
// `.`, `_` and `-`. The disk name will appear on the console. It cannot
|
||||||
|
// begin with `http://` or `https://`.
|
||||||
|
//
|
||||||
|
// - `disk_size` (number) - Size of the system disk, measured in GiB. Value
|
||||||
|
// range: \[20, 500\]. The specified value must be equal to or greater
|
||||||
|
// than max{20, ImageSize}. Default value: max{40, ImageSize}.
|
||||||
//
|
//
|
||||||
// ```json
|
|
||||||
// "builders": [{
|
|
||||||
// "type":"alicloud-ecs",
|
|
||||||
// "system_disk_mapping": {
|
|
||||||
// "disk_size": 50,
|
|
||||||
// "disk_name": "mydisk"
|
|
||||||
// },
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
ECSSystemDiskMapping AlicloudDiskDevice `mapstructure:"system_disk_mapping" required:"false"`
|
ECSSystemDiskMapping AlicloudDiskDevice `mapstructure:"system_disk_mapping" required:"false"`
|
||||||
// Add one or more data disks to the image.
|
// Add one or more data
|
||||||
// See the [disk device configuration](#disk-devices-configuration) section
|
// disks to the image.
|
||||||
// for more information on options.
|
//
|
||||||
// Usage example:
|
// - `disk_category` (string) - Category of the data disk. Optional values
|
||||||
|
// are:
|
||||||
|
// - `cloud` - general cloud disk
|
||||||
|
// - `cloud_efficiency` - efficiency cloud disk
|
||||||
|
// - `cloud_ssd` - cloud SSD
|
||||||
|
//
|
||||||
|
// Default value: cloud.
|
||||||
|
//
|
||||||
|
// - `disk_delete_with_instance` (boolean) - Whether or not the disk is
|
||||||
|
// released along with the instance:
|
||||||
|
// - True indicates that when the instance is released, this disk will
|
||||||
|
// be released with it
|
||||||
|
// - False indicates that when the instance is released, this disk will
|
||||||
|
// be retained.
|
||||||
|
// - `disk_description` (string) - The value of disk description is blank by
|
||||||
|
// default. \[2, 256\] characters. The disk description will appear on the
|
||||||
|
// console. It cannot begin with `http://` or `https://`.
|
||||||
|
//
|
||||||
|
// - `disk_device` (string) - Device information of the related instance:
|
||||||
|
// such as `/dev/xvdb` It is null unless the Status is In\_use.
|
||||||
|
//
|
||||||
|
// - `disk_name` (string) - The value of disk name is blank by default. \[2,
|
||||||
|
// 128\] English or Chinese characters, must begin with an
|
||||||
|
// uppercase/lowercase letter or Chinese character. Can contain numbers,
|
||||||
|
// `.`, `_` and `-`. The disk name will appear on the console. It cannot
|
||||||
|
// begin with `http://` or `https://`.
|
||||||
|
//
|
||||||
|
// - `disk_size` (number) - Size of the data disk, in GB, values range:
|
||||||
|
// - `cloud` - 5 \~ 2000
|
||||||
|
// - `cloud_efficiency` - 20 \~ 2048
|
||||||
|
// - `cloud_ssd` - 20 \~ 2048
|
||||||
|
//
|
||||||
|
// The value should be equal to or greater than the size of the specific
|
||||||
|
// SnapshotId.
|
||||||
|
//
|
||||||
|
// - `disk_snapshot_id` (string) - Snapshots are used to create the data
|
||||||
|
// disk After this parameter is specified, Size is ignored. The actual
|
||||||
|
// size of the created disk is the size of the specified snapshot.
|
||||||
|
//
|
||||||
|
// Snapshots from on or before July 15, 2013 cannot be used to create a
|
||||||
|
// disk.
|
||||||
|
//
|
||||||
|
// - `disk_encrypted` (boolean) - Whether or not to encrypt the data disk.
|
||||||
|
// If this option is set to true, the data disk will be encryped and corresponding snapshot in the target image will also be encrypted. By
|
||||||
|
// default, if this is an extra data disk, Packer will not encrypt the
|
||||||
|
// data disk. Otherwise, Packer will keep the encryption setting to what
|
||||||
|
// it was in the source image. Please refer to Introduction of [ECS disk encryption](https://www.alibabacloud.com/help/doc-detail/59643.htm)
|
||||||
|
// for more details.
|
||||||
//
|
//
|
||||||
// ```json
|
|
||||||
// "builders": [{
|
|
||||||
// "type":"alicloud-ecs",
|
|
||||||
// "image_disk_mappings": [
|
|
||||||
// {
|
|
||||||
// "disk_snapshot_id": "someid",
|
|
||||||
// "disk_device": "dev/xvdb"
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
ECSImagesDiskMappings []AlicloudDiskDevice `mapstructure:"image_disk_mappings" required:"false"`
|
ECSImagesDiskMappings []AlicloudDiskDevice `mapstructure:"image_disk_mappings" required:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/chroot"
|
"github.com/hashicorp/packer/common/chroot"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/hcl2template"
|
"github.com/hashicorp/packer/hcl2template"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
@@ -194,18 +194,14 @@ func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstruct
|
|||||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
Exclude: []string{
|
Exclude: []string{
|
||||||
"ami_description",
|
"ami_description",
|
||||||
"snapshot_tags",
|
"snapshot_tags",
|
||||||
"snapshot_tag",
|
|
||||||
"tags",
|
"tags",
|
||||||
"tag",
|
|
||||||
"root_volume_tags",
|
"root_volume_tags",
|
||||||
"root_volume_tag",
|
|
||||||
"command_wrapper",
|
"command_wrapper",
|
||||||
"post_mount_commands",
|
"post_mount_commands",
|
||||||
"pre_mount_commands",
|
"pre_mount_commands",
|
||||||
@@ -372,7 +368,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand))
|
state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand))
|
||||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
generatedData := &builder.GeneratedData{State: state}
|
||||||
|
|
||||||
// Build the steps
|
// Build the steps
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ 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"`
|
||||||
@@ -52,7 +50,6 @@ 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"`
|
||||||
@@ -120,9 +117,7 @@ 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},
|
||||||
@@ -131,7 +126,6 @@ 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())},
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
@@ -31,7 +31,7 @@ type StepMountDevice struct {
|
|||||||
MountPartition string
|
MountPartition string
|
||||||
|
|
||||||
mountPath string
|
mountPath string
|
||||||
GeneratedData *packerbuilderdata.GeneratedData
|
GeneratedData *builder.GeneratedData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StepPrepareDevice finds an available device and sets it.
|
// StepPrepareDevice finds an available device and sets it.
|
||||||
type StepPrepareDevice struct {
|
type StepPrepareDevice struct {
|
||||||
GeneratedData *packerbuilderdata.GeneratedData
|
GeneratedData *builder.GeneratedData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
//go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions,AssumeRoleConfig
|
//go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
@@ -11,68 +11,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
|
"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/builder/amazon/common/awserrors"
|
|
||||||
"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"`
|
||||||
@@ -101,17 +48,10 @@ 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`
|
||||||
@@ -146,8 +86,6 @@ 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
|
||||||
@@ -214,13 +152,16 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set AWS config defaults.
|
staticCreds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
@@ -238,16 +179,6 @@ 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,
|
||||||
@@ -272,8 +203,10 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
|||||||
|
|
||||||
cp, err := c.session.Config.Credentials.Get()
|
cp, err := c.session.Config.Credentials.Get()
|
||||||
|
|
||||||
if awserrors.Matches(err, "NoCredentialProviders", "") {
|
if IsAWSErr(err, "NoCredentialProviders", "") {
|
||||||
return nil, c.NewNoValidCredentialSourcesError(err)
|
return nil, fmt.Errorf("No valid credential sources found for AWS Builder. " +
|
||||||
|
"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 {
|
||||||
@@ -304,42 +237,6 @@ 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"
|
||||||
@@ -409,13 +306,6 @@ 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by "mapstructure-to-hcl2 -type VaultAWSEngineOptions,AssumeRoleConfig"; DO NOT EDIT.
|
// Code generated by "mapstructure-to-hcl2 -type VaultAWSEngineOptions"; DO NOT EDIT.
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,43 +6,6 @@ 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 {
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package awserrors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Returns true if the err matches all these conditions:
|
|
||||||
// * err is of type awserr.Error
|
|
||||||
// * Error.Code() matches code
|
|
||||||
// * Error.Message() contains message
|
|
||||||
func Matches(err error, code string, message string) bool {
|
|
||||||
if err, ok := err.(awserr.Error); ok {
|
|
||||||
return err.Code() == code && strings.Contains(err.Message(), message)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -134,8 +134,8 @@ func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapp
|
|||||||
ebsBlockDevice.VolumeSize = aws.Int64(blockDevice.VolumeSize)
|
ebsBlockDevice.VolumeSize = aws.Int64(blockDevice.VolumeSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOPS is only valid for io1 and io2 types
|
// IOPS is only valid for io1 type
|
||||||
if blockDevice.VolumeType == "io1" || blockDevice.VolumeType == "io2" {
|
if blockDevice.VolumeType == "io1" {
|
||||||
ebsBlockDevice.Iops = aws.Int64(blockDevice.IOPS)
|
ebsBlockDevice.Iops = aws.Int64(blockDevice.IOPS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,25 +66,6 @@ func TestBlockDevice(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
IOPS: 1000,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("io2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Iops: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Config: &BlockDevice{
|
Config: &BlockDevice{
|
||||||
DeviceName: "/dev/sdb",
|
DeviceName: "/dev/sdb",
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
|||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
return awserrors.Matches(err, "UnauthorizedOperation", "")
|
return IsAWSErr(err, "UnauthorizedOperation", "")
|
||||||
},
|
},
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
@@ -53,7 +54,7 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
|||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
return awserrors.Matches(err, "UnauthorizedOperation", "")
|
return IsAWSErr(err, "UnauthorizedOperation", "")
|
||||||
},
|
},
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
@@ -73,3 +74,14 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if the error matches all these conditions:
|
||||||
|
// * err is of type awserr.Error
|
||||||
|
// * Error.Code() matches code
|
||||||
|
// * Error.Message() contains message
|
||||||
|
func IsAWSErr(err error, code string, message string) bool {
|
||||||
|
if err, ok := err.(awserr.Error); ok {
|
||||||
|
return err.Code() == code && strings.Contains(err.Message(), message)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package common
|
|||||||
import (
|
import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ type BuildInfoTemplate struct {
|
|||||||
SourceAMITags map[string]string
|
SourceAMITags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractBuildInfo(region string, state multistep.StateBag, generatedData *packerbuilderdata.GeneratedData) *BuildInfoTemplate {
|
func extractBuildInfo(region string, state multistep.StateBag, generatedData *builder.GeneratedData) *BuildInfoTemplate {
|
||||||
rawSourceAMI, hasSourceAMI := state.GetOk("source_image")
|
rawSourceAMI, hasSourceAMI := state.GetOk("source_image")
|
||||||
if !hasSourceAMI {
|
if !hasSourceAMI {
|
||||||
return &BuildInfoTemplate{
|
return &BuildInfoTemplate{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +17,6 @@ func testImage() *ec2.Image {
|
|||||||
Name: aws.String("ami_test_name"),
|
Name: aws.String("ami_test_name"),
|
||||||
OwnerId: aws.String("ami_test_owner_id"),
|
OwnerId: aws.String("ami_test_owner_id"),
|
||||||
ImageOwnerAlias: aws.String("ami_test_owner_alias"),
|
ImageOwnerAlias: aws.String("ami_test_owner_alias"),
|
||||||
RootDeviceType: aws.String("ebs"),
|
|
||||||
Tags: []*ec2.Tag{
|
Tags: []*ec2.Tag{
|
||||||
{
|
{
|
||||||
Key: aws.String("key-1"),
|
Key: aws.String("key-1"),
|
||||||
@@ -36,8 +35,8 @@ func testState() multistep.StateBag {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGeneratedData(state multistep.StateBag) packerbuilderdata.GeneratedData {
|
func testGeneratedData(state multistep.StateBag) builder.GeneratedData {
|
||||||
generatedData := packerbuilderdata.GeneratedData{State: state}
|
generatedData := builder.GeneratedData{State: state}
|
||||||
return generatedData
|
return generatedData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ type VpcFilterOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Statement struct {
|
type Statement struct {
|
||||||
Effect string `mapstructure:"Effect" required:"false"`
|
Effect string
|
||||||
Action []string `mapstructure:"Action" required:"false"`
|
Action []string
|
||||||
Resource []string `mapstructure:"Resource" required:"false"`
|
Resource []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PolicyDocument struct {
|
type PolicyDocument struct {
|
||||||
Version string `mapstructure:"Version" required:"false"`
|
Version string
|
||||||
Statement []Statement `mapstructure:"Statement" required:"false"`
|
Statement []Statement
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecurityGroupFilterOptions struct {
|
type SecurityGroupFilterOptions struct {
|
||||||
@@ -376,12 +376,6 @@ type RunConfig struct {
|
|||||||
// subnet-12345def, where Packer will launch the EC2 instance. This field is
|
// subnet-12345def, where Packer will launch the EC2 instance. This field is
|
||||||
// required if you are using an non-default VPC.
|
// required if you are using an non-default VPC.
|
||||||
SubnetId string `mapstructure:"subnet_id" required:"false"`
|
SubnetId string `mapstructure:"subnet_id" required:"false"`
|
||||||
// [Tenancy](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-instance.html) used
|
|
||||||
// when Packer launches the EC2 instance, allowing it to be launched on dedicated hardware.
|
|
||||||
//
|
|
||||||
// The default is "default", meaning shared tenancy. Allowed values are "default",
|
|
||||||
// "dedicated" and "host".
|
|
||||||
Tenancy string `mapstructure:"tenancy" required:"false"`
|
|
||||||
// The name of the temporary key pair to
|
// The name of the temporary key pair to
|
||||||
// generate. By default, Packer generates a name that looks like
|
// generate. By default, Packer generates a name that looks like
|
||||||
// `packer_<UUID>`, where <UUID> is a 36 character unique identifier.
|
// `packer_<UUID>`, where <UUID> is a 36 character unique identifier.
|
||||||
@@ -479,13 +473,6 @@ 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`.
|
||||||
@@ -633,13 +620,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Tenancy != "" &&
|
|
||||||
c.Tenancy != "default" &&
|
|
||||||
c.Tenancy != "dedicated" &&
|
|
||||||
c.Tenancy != "host" {
|
|
||||||
errs = append(errs, fmt.Errorf("Error: Unknown tenancy type %s", c.Tenancy))
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
// FlatPolicyDocument is an auto-generated flat version of PolicyDocument.
|
// FlatPolicyDocument is an auto-generated flat version of PolicyDocument.
|
||||||
// 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 FlatPolicyDocument struct {
|
type FlatPolicyDocument struct {
|
||||||
Version *string `mapstructure:"Version" required:"false" cty:"Version" hcl:"Version"`
|
Version *string `cty:"version" hcl:"version"`
|
||||||
Statement []FlatStatement `mapstructure:"Statement" required:"false" cty:"Statement" hcl:"Statement"`
|
Statement []FlatStatement `cty:"statement" hcl:"statement"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatPolicyDocument.
|
// FlatMapstructure returns a new FlatPolicyDocument.
|
||||||
@@ -55,8 +55,8 @@ func (*PolicyDocument) FlatMapstructure() interface{ HCL2Spec() map[string]hclde
|
|||||||
// The decoded values from this spec will then be applied to a FlatPolicyDocument.
|
// The decoded values from this spec will then be applied to a FlatPolicyDocument.
|
||||||
func (*FlatPolicyDocument) HCL2Spec() map[string]hcldec.Spec {
|
func (*FlatPolicyDocument) HCL2Spec() map[string]hcldec.Spec {
|
||||||
s := map[string]hcldec.Spec{
|
s := map[string]hcldec.Spec{
|
||||||
"Version": &hcldec.AttrSpec{Name: "Version", Type: cty.String, Required: false},
|
"version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false},
|
||||||
"Statement": &hcldec.BlockListSpec{TypeName: "Statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())},
|
"statement": &hcldec.BlockListSpec{TypeName: "statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())},
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@@ -89,9 +89,9 @@ func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
// FlatStatement is an auto-generated flat version of Statement.
|
// FlatStatement is an auto-generated flat version of Statement.
|
||||||
// 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 FlatStatement struct {
|
type FlatStatement struct {
|
||||||
Effect *string `mapstructure:"Effect" required:"false" cty:"Effect" hcl:"Effect"`
|
Effect *string `cty:"effect" hcl:"effect"`
|
||||||
Action []string `mapstructure:"Action" required:"false" cty:"Action" hcl:"Action"`
|
Action []string `cty:"action" hcl:"action"`
|
||||||
Resource []string `mapstructure:"Resource" required:"false" cty:"Resource" hcl:"Resource"`
|
Resource []string `cty:"resource" hcl:"resource"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatStatement.
|
// FlatMapstructure returns a new FlatStatement.
|
||||||
@@ -106,9 +106,9 @@ func (*Statement) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spe
|
|||||||
// The decoded values from this spec will then be applied to a FlatStatement.
|
// The decoded values from this spec will then be applied to a FlatStatement.
|
||||||
func (*FlatStatement) HCL2Spec() map[string]hcldec.Spec {
|
func (*FlatStatement) HCL2Spec() map[string]hcldec.Spec {
|
||||||
s := map[string]hcldec.Spec{
|
s := map[string]hcldec.Spec{
|
||||||
"Effect": &hcldec.AttrSpec{Name: "Effect", Type: cty.String, Required: false},
|
"effect": &hcldec.AttrSpec{Name: "effect", Type: cty.String, Required: false},
|
||||||
"Action": &hcldec.AttrSpec{Name: "Action", Type: cty.List(cty.String), Required: false},
|
"action": &hcldec.AttrSpec{Name: "action", Type: cty.List(cty.String), Required: false},
|
||||||
"Resource": &hcldec.AttrSpec{Name: "Resource", Type: cty.List(cty.String), Required: false},
|
"resource": &hcldec.AttrSpec{Name: "resource", Type: cty.List(cty.String), Required: false},
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,23 +232,3 @@ func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
|
|||||||
t.Fatal("keypair name does not match")
|
t.Fatal("keypair name does not match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunConfigPrepare_TenancyBad(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Tenancy = "not_real"
|
|
||||||
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatal("Should error if tenancy is set to an invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TenancyGood(t *testing.T) {
|
|
||||||
validTenancy := []string{"", "default", "dedicated", "host"}
|
|
||||||
for _, vt := range validTenancy {
|
|
||||||
c := testConfig()
|
|
||||||
c.Tenancy = vt
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("Should not error if tenancy is set to %s", vt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
package ssm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ssm"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
|
||||||
"github.com/hashicorp/packer/common/shell-local/localexec"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Session struct {
|
|
||||||
SvcClient ssmiface.SSMAPI
|
|
||||||
Region string
|
|
||||||
InstanceID string
|
|
||||||
LocalPort, RemotePort int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Session) buildTunnelInput() *ssm.StartSessionInput {
|
|
||||||
portNumber, localPortNumber := strconv.Itoa(s.RemotePort), strconv.Itoa(s.LocalPort)
|
|
||||||
params := map[string][]*string{
|
|
||||||
"portNumber": []*string{aws.String(portNumber)},
|
|
||||||
"localPortNumber": []*string{aws.String(localPortNumber)},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ssm.StartSessionInput{
|
|
||||||
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
|
||||||
Parameters: params,
|
|
||||||
Target: aws.String(s.InstanceID),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCommand return a valid ordered set of arguments to pass to the driver command.
|
|
||||||
func (s Session) getCommand(ctx context.Context) ([]string, string, error) {
|
|
||||||
input := s.buildTunnelInput()
|
|
||||||
|
|
||||||
var session *ssm.StartSessionOutput
|
|
||||||
err := retry.Config{
|
|
||||||
ShouldRetry: func(err error) bool { return awserrors.Matches(err, "TargetNotConnected", "") },
|
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
|
|
||||||
}.Run(ctx, func(ctx context.Context) (err error) {
|
|
||||||
session, err = s.SvcClient.StartSessionWithContext(ctx, input)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if session == nil {
|
|
||||||
return nil, "", fmt.Errorf("an active Amazon SSM Session is required before trying to open a session tunnel")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AWS session-manager-plugin requires a valid session be passed in JSON.
|
|
||||||
sessionDetails, err := json.Marshal(session)
|
|
||||||
if err != nil {
|
|
||||||
return nil, *session.SessionId, fmt.Errorf("error encountered in reading session details %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AWS session-manager-plugin requires the parameters used in the session to be passed in JSON as well.
|
|
||||||
sessionParameters, err := json.Marshal(input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", fmt.Errorf("error encountered in reading session parameter details %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args must be in this order
|
|
||||||
args := []string{
|
|
||||||
string(sessionDetails),
|
|
||||||
s.Region,
|
|
||||||
"StartSession",
|
|
||||||
"", // ProfileName
|
|
||||||
string(sessionParameters),
|
|
||||||
*session.StreamUrl,
|
|
||||||
}
|
|
||||||
return args, *session.SessionId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start an interactive Systems Manager session with a remote instance via the
|
|
||||||
// AWS session-manager-plugin. To terminate the session you must cancell the
|
|
||||||
// context. If you do not wish to terminate the session manually: calling
|
|
||||||
// StopSession on a instance of this driver will terminate the active session
|
|
||||||
// created from calling StartSession.
|
|
||||||
func (s Session) Start(ctx context.Context, ui packer.Ui) error {
|
|
||||||
for ctx.Err() == nil {
|
|
||||||
log.Printf("ssm: Starting PortForwarding session to instance %s", s.InstanceID)
|
|
||||||
args, sessionID, err := s.getCommand(ctx)
|
|
||||||
if sessionID != "" {
|
|
||||||
defer func() {
|
|
||||||
_, err := s.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: aws.String(sessionID)})
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("Error terminating SSM Session %q. Please terminate the session manually: %s", sessionID, err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, "session-manager-plugin", args...)
|
|
||||||
|
|
||||||
ui.Message(fmt.Sprintf("Starting portForwarding session %q.", sessionID))
|
|
||||||
err = localexec.RunAndStream(cmd, ui, nil)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
||||||
|
"github.com/hashicorp/packer/common/retry"
|
||||||
|
"github.com/mitchellh/iochan"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sessionManagerPluginName string = "session-manager-plugin"
|
||||||
|
|
||||||
|
//sessionCommand is the AWS-SDK equivalent to the command you would specify to `aws ssm ...`
|
||||||
|
sessionCommand string = "StartSession"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SSMDriverConfig struct {
|
||||||
|
SvcClient ssmiface.SSMAPI
|
||||||
|
Region string
|
||||||
|
ProfileName string
|
||||||
|
SvcEndpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSMDriver struct {
|
||||||
|
SSMDriverConfig
|
||||||
|
session *ssm.StartSessionOutput
|
||||||
|
sessionParams ssm.StartSessionInput
|
||||||
|
pluginCmdFunc func(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSSMDriver(config SSMDriverConfig) *SSMDriver {
|
||||||
|
d := SSMDriver{SSMDriverConfig: config}
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartSession starts an interactive Systems Manager session with a remote instance via the AWS session-manager-plugin
|
||||||
|
// This ssm.StartSessionOutput returned by this function can be used for terminating the session manually. If you do
|
||||||
|
// not wish to manage the session manually calling StopSession on a instance of this driver will terminate the active session
|
||||||
|
// created from calling StartSession.
|
||||||
|
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)
|
||||||
|
|
||||||
|
var output *ssm.StartSessionOutput
|
||||||
|
err := retry.Config{
|
||||||
|
ShouldRetry: func(err error) bool { return IsAWSErr(err, "TargetNotConnected", "") },
|
||||||
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
|
||||||
|
}.Run(ctx, func(ctx context.Context) (err error) {
|
||||||
|
output, err = d.SvcClient.StartSessionWithContext(ctx, &input)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error encountered in starting session for instance %q: %s", aws.StringValue(input.Target), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.session = output
|
||||||
|
d.sessionParams = input
|
||||||
|
|
||||||
|
if d.pluginCmdFunc == nil {
|
||||||
|
d.pluginCmdFunc = d.openTunnelForSession
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.pluginCmdFunc(ctx); err != nil {
|
||||||
|
return nil, fmt.Errorf("error encountered in starting session for instance %q: %s", aws.StringValue(input.Target), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SSMDriver) openTunnelForSession(ctx context.Context) error {
|
||||||
|
args, err := d.Args()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error encountered validating session details: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, sessionManagerPluginName, args...)
|
||||||
|
|
||||||
|
// Let's build up our logging
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stderr, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the channels we'll use for data
|
||||||
|
stdoutCh := iochan.DelimReader(stdout, '\n')
|
||||||
|
stderrCh := iochan.DelimReader(stderr, '\n')
|
||||||
|
|
||||||
|
/* Loop and get all our output
|
||||||
|
This particular logger will continue to run through an entire Packer run.
|
||||||
|
The decision to continue logging is due to the fact that session-manager-plugin
|
||||||
|
doesn't give a good way of knowing if the command failed or was successful other
|
||||||
|
than looking at the logs. Seeing as the plugin is updated frequently and that the
|
||||||
|
log information is a bit sparse this logger will indefinitely relying on other
|
||||||
|
steps to fail if the tunnel is unable to be created. If successful then the user
|
||||||
|
will get more information on the tunnel connection when running in a debug mode.
|
||||||
|
*/
|
||||||
|
go func(ctx context.Context, prefix string) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case output := <-stderrCh:
|
||||||
|
if output != "" {
|
||||||
|
log.Printf("[ERROR] %s: %s", prefix, output)
|
||||||
|
}
|
||||||
|
case output := <-stdoutCh:
|
||||||
|
if output != "" {
|
||||||
|
log.Printf("[DEBUG] %s: %s", prefix, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(ctx, sessionManagerPluginName)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG %s] opening session tunnel to instance %q for session %q", sessionManagerPluginName,
|
||||||
|
aws.StringValue(d.sessionParams.Target),
|
||||||
|
aws.StringValue(d.session.SessionId),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
err = fmt.Errorf("error encountered when calling %s: %s\n", sessionManagerPluginName, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopSession terminates an active Session Manager session
|
||||||
|
func (d *SSMDriver) StopSession() error {
|
||||||
|
|
||||||
|
if d.session == nil || d.session.SessionId == nil {
|
||||||
|
return fmt.Errorf("Unable to find a valid session to instance %q; skipping the termination step",
|
||||||
|
aws.StringValue(d.sessionParams.Target))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := d.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: d.session.SessionId})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error terminating SSM Session %q. Please terminate the session manually: %s", aws.StringValue(d.session.SessionId), err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args validates the driver inputs before returning an ordered set of arguments to pass to the driver command.
|
||||||
|
func (d *SSMDriver) Args() ([]string, error) {
|
||||||
|
if d.session == nil {
|
||||||
|
return nil, fmt.Errorf("an active Amazon SSM Session is required before trying to open a session tunnel")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWS session-manager-plugin requires a valid session be passed in JSON.
|
||||||
|
sessionDetails, err := json.Marshal(d.session)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error encountered in reading session details %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWS session-manager-plugin requires the parameters used in the session to be passed in JSON as well.
|
||||||
|
sessionParameters, err := json.Marshal(d.sessionParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error encountered in reading session parameter details %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args must be in this order
|
||||||
|
args := []string{
|
||||||
|
string(sessionDetails),
|
||||||
|
d.Region,
|
||||||
|
sessionCommand,
|
||||||
|
d.ProfileName,
|
||||||
|
string(sessionParameters),
|
||||||
|
d.SvcEndpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewSSMDriverWithMockSvc(svc *MockSSMSvc) *SSMDriver {
|
||||||
|
config := SSMDriverConfig{
|
||||||
|
SvcClient: svc,
|
||||||
|
Region: "east",
|
||||||
|
ProfileName: "default",
|
||||||
|
SvcEndpoint: "example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
driver := SSMDriver{
|
||||||
|
SSMDriverConfig: config,
|
||||||
|
pluginCmdFunc: func(ctx context.Context) error { return nil },
|
||||||
|
}
|
||||||
|
|
||||||
|
return &driver
|
||||||
|
}
|
||||||
|
func TestSSMDriver_StartSession(t *testing.T) {
|
||||||
|
mockSvc := MockSSMSvc{}
|
||||||
|
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||||
|
|
||||||
|
if driver.SvcClient == nil {
|
||||||
|
t.Fatalf("SvcClient for driver should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("calling StartSession should not error but got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mockSvc.StartSessionCalled {
|
||||||
|
t.Fatalf("expected test to call ssm mocks but didn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
if session == nil {
|
||||||
|
t.Errorf("expected session to be set after a successful call to StartSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(session, MockStartSessionOutput()) {
|
||||||
|
t.Errorf("expected session to be %v but got %v", MockStartSessionOutput(), session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSSMDriver_StartSessionWithError(t *testing.T) {
|
||||||
|
mockSvc := MockSSMSvc{StartSessionError: fmt.Errorf("bogus error")}
|
||||||
|
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||||
|
|
||||||
|
if driver.SvcClient == nil {
|
||||||
|
t.Fatalf("SvcClient for driver should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("StartSession should have thrown an error but didn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mockSvc.StartSessionCalled {
|
||||||
|
t.Errorf("expected test to call StartSession mock but didn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
if session != nil {
|
||||||
|
t.Errorf("expected session to be nil after a bad StartSession call, but got %v", session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSSMDriver_StopSession(t *testing.T) {
|
||||||
|
mockSvc := MockSSMSvc{}
|
||||||
|
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||||
|
|
||||||
|
if driver.SvcClient == nil {
|
||||||
|
t.Fatalf("SvcClient for driver should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calling StopSession before StartSession should fail
|
||||||
|
err := driver.StopSession()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("calling StopSession() on a driver that has no started session should fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
if driver.session != nil {
|
||||||
|
t.Errorf("expected session to be default to nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mockSvc.TerminateSessionCalled {
|
||||||
|
t.Fatalf("a call to TerminateSession should not occur when there is no valid SSM session")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lets try calling start session, then stopping to see what happens.
|
||||||
|
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("calling StartSession should not error but got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mockSvc.StartSessionCalled {
|
||||||
|
t.Fatalf("expected test to call StartSession mock but didn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
if session == nil || driver.session != session {
|
||||||
|
t.Errorf("expected session to be set after a successful call to StartSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(session, MockStartSessionOutput()) {
|
||||||
|
t.Errorf("expected session to be %v but got %v", MockStartSessionOutput(), session)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = driver.StopSession()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("calling StopSession() on a driver on a started session should not fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mockSvc.TerminateSessionCalled {
|
||||||
|
t.Fatalf("expected test to call StopSession mock but didn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSSMDriver_Args(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
Name string
|
||||||
|
ProfileName string
|
||||||
|
SkipStartSession bool
|
||||||
|
ErrorExpected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "NilSession",
|
||||||
|
SkipStartSession: true,
|
||||||
|
ErrorExpected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "NonNilSession",
|
||||||
|
ErrorExpected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "SessionWithProfileName",
|
||||||
|
ProfileName: "default",
|
||||||
|
ErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
mockSvc := MockSSMSvc{}
|
||||||
|
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||||
|
driver.ProfileName = tc.ProfileName
|
||||||
|
|
||||||
|
if driver.SvcClient == nil {
|
||||||
|
t.Fatalf("svcclient for driver should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.SkipStartSession {
|
||||||
|
_, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got an error when calling StartSession %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := driver.Args()
|
||||||
|
if tc.ErrorExpected && err == nil {
|
||||||
|
t.Fatalf("Driver.Args with a %q should have failed but instead no error was returned", tc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.ErrorExpected {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got an error when it should've worked %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate launch script
|
||||||
|
expectedArgString := fmt.Sprintf(`{"SessionId":"packerid","StreamUrl":"http://packer.io","TokenValue":"packer-token"} east StartSession %s {"DocumentName":"AWS-StartPortForwardingSession","Parameters":{"localPortNumber":["8001"],"portNumber":["22"]},"Target":"fakeinstance"} example.com`, tc.ProfileName)
|
||||||
|
argString := strings.Join(args, " ")
|
||||||
|
if argString != expectedArgString {
|
||||||
|
t.Errorf("Expected launch script to be %q but got %q", expectedArgString, argString)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockSSMSvc struct {
|
||||||
|
ssmiface.SSMAPI
|
||||||
|
StartSessionError error
|
||||||
|
TerminateSessionError error
|
||||||
|
StartSessionCalled bool
|
||||||
|
TerminateSessionCalled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *MockSSMSvc) StartSessionWithContext(ctx aws.Context, input *ssm.StartSessionInput, options ...request.Option) (*ssm.StartSessionOutput, error) {
|
||||||
|
svc.StartSessionCalled = true
|
||||||
|
return MockStartSessionOutput(), svc.StartSessionError
|
||||||
|
}
|
||||||
|
func (svc *MockSSMSvc) TerminateSession(input *ssm.TerminateSessionInput) (*ssm.TerminateSessionOutput, error) {
|
||||||
|
svc.TerminateSessionCalled = true
|
||||||
|
return new(ssm.TerminateSessionOutput), svc.TerminateSessionError
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockPluginCmdFunc(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockStartSessionOutput() *ssm.StartSessionOutput {
|
||||||
|
id, url, token := "packerid", "http://packer.io", "packer-token"
|
||||||
|
output := ssm.StartSessionOutput{
|
||||||
|
SessionId: &id,
|
||||||
|
StreamUrl: &url,
|
||||||
|
TokenValue: &token,
|
||||||
|
}
|
||||||
|
return &output
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockStartSessionInput(instance string) ssm.StartSessionInput {
|
||||||
|
params := map[string][]*string{
|
||||||
|
"portNumber": []*string{aws.String("22")},
|
||||||
|
"localPortNumber": []*string{aws.String("8001")},
|
||||||
|
}
|
||||||
|
|
||||||
|
input := ssm.StartSessionInput{
|
||||||
|
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
||||||
|
Parameters: params,
|
||||||
|
Target: aws.String(instance),
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
}
|
||||||
@@ -3,13 +3,12 @@ package common
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"strconv"
|
||||||
|
|
||||||
"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"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ssm"
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
pssm "github.com/hashicorp/packer/builder/amazon/common/ssm"
|
|
||||||
"github.com/hashicorp/packer/common/net"
|
"github.com/hashicorp/packer/common/net"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -21,8 +20,8 @@ type StepCreateSSMTunnel struct {
|
|||||||
LocalPortNumber int
|
LocalPortNumber int
|
||||||
RemotePortNumber int
|
RemotePortNumber int
|
||||||
SSMAgentEnabled bool
|
SSMAgentEnabled bool
|
||||||
PauseBeforeSSM time.Duration
|
instanceId string
|
||||||
stopSSMCommand func()
|
driver *SSMDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run executes the Packer build step that creates a session tunnel.
|
// Run executes the Packer build step that creates a session tunnel.
|
||||||
@@ -33,17 +32,6 @@ 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 before 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)
|
||||||
@@ -60,38 +48,42 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
s.instanceId = aws.StringValue(instance.InstanceId)
|
||||||
|
|
||||||
state.Put("sessionPort", s.LocalPortNumber)
|
if s.driver == nil {
|
||||||
|
|
||||||
ssmCtx, ssmCancel := context.WithCancel(ctx)
|
|
||||||
s.stopSSMCommand = ssmCancel
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
ssmconn := ssm.New(s.AWSSession)
|
ssmconn := ssm.New(s.AWSSession)
|
||||||
err := pssm.Session{
|
cfg := SSMDriverConfig{
|
||||||
SvcClient: ssmconn,
|
SvcClient: ssmconn,
|
||||||
InstanceID: aws.StringValue(instance.InstanceId),
|
Region: s.Region,
|
||||||
RemotePort: s.RemotePortNumber,
|
SvcEndpoint: ssmconn.Endpoint,
|
||||||
LocalPort: s.LocalPortNumber,
|
|
||||||
Region: s.Region,
|
|
||||||
}.Start(ssmCtx, ui)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("ssm error: %s", err))
|
|
||||||
}
|
}
|
||||||
}()
|
driver := SSMDriver{SSMDriverConfig: cfg}
|
||||||
|
s.driver = &driver
|
||||||
|
}
|
||||||
|
|
||||||
|
input := s.BuildTunnelInputForInstance(s.instanceId)
|
||||||
|
_, err := s.driver.StartSession(ctx, input)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error encountered in establishing a tunnel %s", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("PortForwarding session %q has been started", s.instanceId))
|
||||||
|
state.Put("sessionPort", s.LocalPortNumber)
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup terminates an active session on AWS, which in turn terminates the associated tunnel process running on the local machine.
|
// Cleanup terminates an active session on AWS, which in turn terminates the associated tunnel process running on the local machine.
|
||||||
func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) {
|
func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
if !s.SSMAgentEnabled {
|
if !s.SSMAgentEnabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.stopSSMCommand != nil {
|
if err := s.driver.StopSession(); err != nil {
|
||||||
s.stopSSMCommand()
|
ui.Error(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,3 +116,19 @@ func (s *StepCreateSSMTunnel) ConfigureLocalHostPort(ctx context.Context) error
|
|||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateSSMTunnel) BuildTunnelInputForInstance(instance string) ssm.StartSessionInput {
|
||||||
|
dst, src := strconv.Itoa(s.RemotePortNumber), strconv.Itoa(s.LocalPortNumber)
|
||||||
|
params := map[string][]*string{
|
||||||
|
"portNumber": []*string{aws.String(dst)},
|
||||||
|
"localPortNumber": []*string{aws.String(src)},
|
||||||
|
}
|
||||||
|
|
||||||
|
input := ssm.StartSessionInput{
|
||||||
|
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
||||||
|
Parameters: params,
|
||||||
|
Target: aws.String(instance),
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStepCreateSSMTunnel_Run(t *testing.T) {
|
||||||
|
mockSvc := MockSSMSvc{}
|
||||||
|
config := SSMDriverConfig{
|
||||||
|
SvcClient: &mockSvc,
|
||||||
|
SvcEndpoint: "example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDriver := NewSSMDriver(config)
|
||||||
|
mockDriver.pluginCmdFunc = MockPluginCmdFunc
|
||||||
|
|
||||||
|
state := testState()
|
||||||
|
state.Put("ui", &packer.NoopUi{})
|
||||||
|
state.Put("instance", &ec2.Instance{InstanceId: aws.String("i-something")})
|
||||||
|
|
||||||
|
step := StepCreateSSMTunnel{
|
||||||
|
driver: mockDriver,
|
||||||
|
}
|
||||||
|
|
||||||
|
step.Run(context.Background(), state)
|
||||||
|
|
||||||
|
err := state.Get("error")
|
||||||
|
if err != nil {
|
||||||
|
err = err.(error)
|
||||||
|
t.Fatalf("the call to Run failed with an error when it should've executed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mockSvc.StartSessionCalled {
|
||||||
|
t.Errorf("StartSession should not be called when SSMAgentEnabled is false")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run when SSMAgentEnabled is true
|
||||||
|
step.SSMAgentEnabled = true
|
||||||
|
step.Run(context.Background(), state)
|
||||||
|
|
||||||
|
err = state.Get("error")
|
||||||
|
if err != nil {
|
||||||
|
err = err.(error)
|
||||||
|
t.Fatalf("the call to Run failed with an error when it should've executed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mockSvc.StartSessionCalled {
|
||||||
|
t.Errorf("calling run with the correct inputs should call StartSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
step.Cleanup(state)
|
||||||
|
if !mockSvc.TerminateSessionCalled {
|
||||||
|
t.Errorf("calling cleanup on a successful run should call TerminateSession")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateSSMTunnel_Cleanup(t *testing.T) {
|
||||||
|
mockSvc := MockSSMSvc{}
|
||||||
|
config := SSMDriverConfig{
|
||||||
|
SvcClient: &mockSvc,
|
||||||
|
SvcEndpoint: "example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDriver := NewSSMDriver(config)
|
||||||
|
mockDriver.pluginCmdFunc = MockPluginCmdFunc
|
||||||
|
|
||||||
|
step := StepCreateSSMTunnel{
|
||||||
|
SSMAgentEnabled: true,
|
||||||
|
driver: mockDriver,
|
||||||
|
}
|
||||||
|
|
||||||
|
state := testState()
|
||||||
|
state.Put("ui", &packer.NoopUi{})
|
||||||
|
state.Put("instance", &ec2.Instance{InstanceId: aws.String("i-something")})
|
||||||
|
|
||||||
|
step.Cleanup(state)
|
||||||
|
|
||||||
|
if mockSvc.TerminateSessionCalled {
|
||||||
|
t.Fatalf("calling cleanup on a non started session should not call TerminateSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateSSMTunnel_BuildTunnelInputForInstance(t *testing.T) {
|
||||||
|
step := StepCreateSSMTunnel{
|
||||||
|
Region: "region",
|
||||||
|
LocalPortNumber: 8001,
|
||||||
|
RemotePortNumber: 22,
|
||||||
|
SSMAgentEnabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
input := step.BuildTunnelInputForInstance("i-something")
|
||||||
|
|
||||||
|
target := aws.StringValue(input.Target)
|
||||||
|
if target != "i-something" {
|
||||||
|
t.Errorf("input should contain instance id as target but it got %q", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string][]*string{
|
||||||
|
"portNumber": []*string{aws.String("22")},
|
||||||
|
"localPortNumber": []*string{aws.String("8001")},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(input.Parameters, params) {
|
||||||
|
t.Errorf("input should contain the expected port parameters but it got %v", input.Parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateSSMTunnel_ConfigureLocalHostPort(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
Name string
|
||||||
|
Step StepCreateSSMTunnel
|
||||||
|
PortCheck func(int) bool
|
||||||
|
}{
|
||||||
|
{"WithLocalPortNumber", StepCreateSSMTunnel{LocalPortNumber: 9001}, func(port int) bool { return port == 9001 }},
|
||||||
|
{"WithNoLocalPortNumber", StepCreateSSMTunnel{}, func(port int) bool { return port >= 8000 && port <= 9000 }},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
step := tc.Step
|
||||||
|
if err := step.ConfigureLocalHostPort(context.TODO()); err != nil {
|
||||||
|
t.Errorf("failed to configure a port on localhost")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.PortCheck(step.LocalPortNumber) {
|
||||||
|
t.Errorf("failed to configure a port on localhost")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"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"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -92,7 +91,7 @@ func (s *StepCreateTags) Run(ctx context.Context, state multistep.StateBag) mult
|
|||||||
|
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||||
if awserrors.Matches(err, "InvalidAMIID.NotFound", "") || awserrors.Matches(err, "InvalidSnapshot.NotFound", "") {
|
if IsAWSErr(err, "InvalidAMIID.NotFound", "") || IsAWSErr(err, "InvalidSnapshot.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"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"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
@@ -22,7 +22,7 @@ type StepModifyAMIAttributes struct {
|
|||||||
Description string
|
Description string
|
||||||
Ctx interpolate.Context
|
Ctx interpolate.Context
|
||||||
|
|
||||||
GeneratedData *packerbuilderdata.GeneratedData
|
GeneratedData *builder.GeneratedData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepModifyAMIAttributes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepModifyAMIAttributes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"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"
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -40,7 +39,7 @@ func (s *StepPreValidate) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
err := retry.Config{
|
err := retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
if awserrors.Matches(err, "AuthFailure", "") {
|
if IsAWSErr(err, "AuthFailure", "") {
|
||||||
log.Printf("Waiting for Vault-generated AWS credentials" +
|
log.Printf("Waiting for Vault-generated AWS credentials" +
|
||||||
" to pass authentication... trying again.")
|
" to pass authentication... trying again.")
|
||||||
return true
|
return true
|
||||||
@@ -132,7 +131,7 @@ func (s *StepPreValidate) checkVpc(conn ec2iface.EC2API) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res, err := conn.DescribeVpcs(&ec2.DescribeVpcsInput{VpcIds: []*string{aws.String(s.VpcId)}})
|
res, err := conn.DescribeVpcs(&ec2.DescribeVpcsInput{VpcIds: []*string{aws.String(s.VpcId)}})
|
||||||
if awserrors.Matches(err, "InvalidVpcID.NotFound", "") || err != nil {
|
if IsAWSErr(err, "InvalidVpcID.NotFound", "") || err != nil {
|
||||||
return fmt.Errorf("Error retrieving VPC information for vpc_id %s: %s", s.VpcId, err)
|
return fmt.Errorf("Error retrieving VPC information for vpc_id %s: %s", s.VpcId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
@@ -34,7 +33,6 @@ type StepRunSourceInstance struct {
|
|||||||
IsRestricted bool
|
IsRestricted bool
|
||||||
SourceAMI string
|
SourceAMI string
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
Tenancy string
|
|
||||||
UserData string
|
UserData string
|
||||||
UserDataFile string
|
UserDataFile string
|
||||||
VolumeTags map[string]string
|
VolumeTags map[string]string
|
||||||
@@ -197,15 +195,11 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||||||
runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior
|
runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Tenancy != "" {
|
|
||||||
runOpts.Placement.Tenancy = aws.String(s.Tenancy)
|
|
||||||
}
|
|
||||||
|
|
||||||
var runResp *ec2.Reservation
|
var runResp *ec2.Reservation
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
if awserrors.Matches(err, "InvalidParameterValue", "iamInstanceProfile") {
|
if IsAWSErr(err, "InvalidParameterValue", "iamInstanceProfile") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -216,7 +210,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
if awserrors.Matches(err, "VPCIdNotSpecified", "No default VPC for this user") && subnetId == "" {
|
if IsAWSErr(err, "VPCIdNotSpecified", "No default VPC for this user") && subnetId == "" {
|
||||||
err := fmt.Errorf("Error launching source instance: a valid Subnet Id was not specified")
|
err := fmt.Errorf("Error launching source instance: a valid Subnet Id was not specified")
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
@@ -254,7 +248,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||||||
|
|
||||||
var r *ec2.DescribeInstancesOutput
|
var r *ec2.DescribeInstancesOutput
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(err error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(err error) bool {
|
||||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -299,7 +293,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||||||
ec2Tags.Report(ui)
|
ec2Tags.Report(ui)
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"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/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/random"
|
"github.com/hashicorp/packer/common/random"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
@@ -36,7 +33,6 @@ type StepRunSpotInstance struct {
|
|||||||
ExpectedRootDevice string
|
ExpectedRootDevice string
|
||||||
InstanceInitiatedShutdownBehavior string
|
InstanceInitiatedShutdownBehavior string
|
||||||
InstanceType string
|
InstanceType string
|
||||||
Region string
|
|
||||||
SourceAMI string
|
SourceAMI string
|
||||||
SpotPrice string
|
SpotPrice string
|
||||||
SpotTags map[string]string
|
SpotTags map[string]string
|
||||||
@@ -160,7 +156,7 @@ func (s *StepRunSpotInstance) LoadUserData() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
ec2conn := state.Get("ec2").(ec2iface.EC2API)
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say("Launching a spot AWS instance...")
|
ui.Say("Launching a spot AWS instance...")
|
||||||
@@ -199,7 +195,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert tags from the tag map provided by the user into *ec2.Tag s
|
// Convert tags from the tag map provided by the user into *ec2.Tag s
|
||||||
ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, s.Region, state)
|
ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error generating tags for source instance: %s", err)
|
err := fmt.Errorf("Error generating tags for source instance: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
@@ -223,14 +219,6 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
}
|
}
|
||||||
marketOptions.SetMarketType(ec2.MarketTypeSpot)
|
marketOptions.SetMarketType(ec2.MarketTypeSpot)
|
||||||
|
|
||||||
spotTags, err := TagMap(s.SpotTags).EC2Tags(s.Ctx, s.Region, state)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error generating tags for spot request: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a launch template for the instance
|
// Create a launch template for the instance
|
||||||
ui.Message("Loading User Data File...")
|
ui.Message("Loading User Data File...")
|
||||||
|
|
||||||
@@ -253,14 +241,6 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
LaunchTemplateName: aws.String(launchTemplateName),
|
LaunchTemplateName: aws.String(launchTemplateName),
|
||||||
VersionDescription: aws.String("template generated by packer for launching spot instances"),
|
VersionDescription: aws.String("template generated by packer for launching spot instances"),
|
||||||
}
|
}
|
||||||
if len(spotTags) > 0 {
|
|
||||||
launchTemplate.TagSpecifications = []*ec2.TagSpecification{
|
|
||||||
{
|
|
||||||
ResourceType: aws.String("launch-template"),
|
|
||||||
Tags: spotTags,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell EC2 to create the template
|
// Tell EC2 to create the template
|
||||||
_, err = ec2conn.CreateLaunchTemplate(launchTemplate)
|
_, err = ec2conn.CreateLaunchTemplate(launchTemplate)
|
||||||
@@ -298,53 +278,36 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
Type: aws.String("instant"),
|
Type: aws.String("instant"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var createOutput *ec2.CreateFleetOutput
|
// Create the request for the spot instance.
|
||||||
err = retry.Config{
|
req, createOutput := ec2conn.CreateFleetRequest(createFleetInput)
|
||||||
Tries: 11,
|
ui.Message(fmt.Sprintf("Sending spot request (%s)...", req.RequestID))
|
||||||
ShouldRetry: func(err error) bool {
|
// Actually send the spot connection request.
|
||||||
if strings.Contains(err.Error(), "Invalid IAM Instance Profile name") {
|
err = req.Send()
|
||||||
// eventual consistency of the profile. PutRolePolicy &
|
|
||||||
// AddRoleToInstanceProfile are eventually consistent and once
|
|
||||||
// we can wait on those operations, this can be removed.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 500 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
|
||||||
createOutput, err = ec2conn.CreateFleet(createFleetInput)
|
|
||||||
if err == nil && createOutput.Errors != nil {
|
|
||||||
err = fmt.Errorf("errors: %v", createOutput.Errors)
|
|
||||||
}
|
|
||||||
// We can end up with errors because one of the allowed availability
|
|
||||||
// zones doesn't have one of the allowed instance types; as long as
|
|
||||||
// an instance is launched, these errors aren't important.
|
|
||||||
if len(createOutput.Instances) > 0 {
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("create request failed for some instances %v", err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("create request failed %v", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if createOutput.FleetId != nil {
|
if createOutput.FleetId != nil {
|
||||||
err = fmt.Errorf("Error waiting for fleet request (%s): %s", *createOutput.FleetId, err)
|
err = fmt.Errorf("Error waiting for fleet request (%s): %s", *createOutput.FleetId, err)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("Error waiting for fleet request: %s", err)
|
||||||
}
|
}
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(createOutput.Instances) == 0 {
|
||||||
|
// We can end up with errors because one of the allowed availability
|
||||||
|
// zones doesn't have one of the allowed instance types; as long as
|
||||||
|
// an instance is launched, these errors aren't important.
|
||||||
if len(createOutput.Errors) > 0 {
|
if len(createOutput.Errors) > 0 {
|
||||||
errString := fmt.Sprintf("Error waiting for fleet request (%s) to become ready:", *createOutput.FleetId)
|
errString := fmt.Sprintf("Error waiting for fleet request (%s) to become ready:", *createOutput.FleetId)
|
||||||
for _, outErr := range createOutput.Errors {
|
for _, outErr := range createOutput.Errors {
|
||||||
errString = errString + aws.StringValue(outErr.ErrorMessage)
|
errString = errString + aws.StringValue(outErr.ErrorMessage)
|
||||||
}
|
}
|
||||||
err = fmt.Errorf(errString)
|
err = fmt.Errorf(errString)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceId = *createOutput.Instances[0].InstanceIds[0]
|
instanceId = *createOutput.Instances[0].InstanceIds[0]
|
||||||
@@ -379,6 +342,14 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
instance := describeOutput.Reservations[0].Instances[0]
|
instance := describeOutput.Reservations[0].Instances[0]
|
||||||
|
|
||||||
// Tag the spot instance request (not the eventual spot instance)
|
// Tag the spot instance request (not the eventual spot instance)
|
||||||
|
spotTags, err := TagMap(s.SpotTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error generating tags for spot request: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
if len(spotTags) > 0 && len(s.SpotTags) > 0 {
|
if len(spotTags) > 0 && len(s.SpotTags) > 0 {
|
||||||
spotTags.Report(ui)
|
spotTags.Report(ui)
|
||||||
// Use the instance ID to find out the SIR, so that we can tag the spot
|
// Use the instance ID to find out the SIR, so that we can tag the spot
|
||||||
@@ -407,7 +378,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
|
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -438,7 +409,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
if len(volumeIds) > 0 && len(s.VolumeTags) > 0 {
|
if len(volumeIds) > 0 && len(s.VolumeTags) > 0 {
|
||||||
ui.Say("Adding tags to source EBS Volumes")
|
ui.Say("Adding tags to source EBS Volumes")
|
||||||
|
|
||||||
volumeTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, s.Region, state)
|
volumeTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"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/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -45,7 +43,6 @@ func getBasicStep() *StepRunSpotInstance {
|
|||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
InstanceInitiatedShutdownBehavior: "stop",
|
InstanceInitiatedShutdownBehavior: "stop",
|
||||||
InstanceType: "t2.micro",
|
InstanceType: "t2.micro",
|
||||||
Region: "us-east-1",
|
|
||||||
SourceAMI: "",
|
SourceAMI: "",
|
||||||
SpotPrice: "auto",
|
SpotPrice: "auto",
|
||||||
SpotTags: nil,
|
SpotTags: nil,
|
||||||
@@ -137,226 +134,3 @@ func TestCreateTemplateData_NoEphemeral(t *testing.T) {
|
|||||||
// t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
// t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
type runSpotEC2ConnMock struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
|
|
||||||
CreateLaunchTemplateParams []*ec2.CreateLaunchTemplateInput
|
|
||||||
CreateLaunchTemplateFn func(*ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error)
|
|
||||||
|
|
||||||
CreateFleetParams []*ec2.CreateFleetInput
|
|
||||||
CreateFleetFn func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error)
|
|
||||||
|
|
||||||
CreateTagsParams []*ec2.CreateTagsInput
|
|
||||||
CreateTagsFn func(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error)
|
|
||||||
|
|
||||||
DescribeInstancesParams []*ec2.DescribeInstancesInput
|
|
||||||
DescribeInstancesFn func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateLaunchTemplate(req *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
|
||||||
m.CreateLaunchTemplateParams = append(m.CreateLaunchTemplateParams, req)
|
|
||||||
resp, err := m.CreateLaunchTemplateFn(req)
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateFleet(req *ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
|
||||||
m.CreateFleetParams = append(m.CreateFleetParams, req)
|
|
||||||
if m.CreateFleetFn != nil {
|
|
||||||
resp, err := m.CreateFleetFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) DescribeInstances(req *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
m.DescribeInstancesParams = append(m.DescribeInstancesParams, req)
|
|
||||||
if m.DescribeInstancesFn != nil {
|
|
||||||
resp, err := m.DescribeInstancesFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateTags(req *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
|
|
||||||
m.CreateTagsParams = append(m.CreateTagsParams, req)
|
|
||||||
if m.CreateTagsFn != nil {
|
|
||||||
resp, err := m.CreateTagsFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultEc2Mock(instanceId, spotRequestId, volumeId *string) *runSpotEC2ConnMock {
|
|
||||||
instance := &ec2.Instance{
|
|
||||||
InstanceId: instanceId,
|
|
||||||
SpotInstanceRequestId: spotRequestId,
|
|
||||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: volumeId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &runSpotEC2ConnMock{
|
|
||||||
CreateLaunchTemplateFn: func(in *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
|
||||||
return &ec2.CreateLaunchTemplateOutput{
|
|
||||||
LaunchTemplate: nil,
|
|
||||||
Warning: nil,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
CreateFleetFn: func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
|
||||||
return &ec2.CreateFleetOutput{
|
|
||||||
Errors: nil,
|
|
||||||
FleetId: nil,
|
|
||||||
Instances: []*ec2.CreateFleetInstance{
|
|
||||||
{
|
|
||||||
InstanceIds: []*string{instanceId},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
DescribeInstancesFn: func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
return &ec2.DescribeInstancesOutput{
|
|
||||||
NextToken: nil,
|
|
||||||
Reservations: []*ec2.Reservation{
|
|
||||||
{
|
|
||||||
Instances: []*ec2.Instance{instance},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
|
||||||
instanceId := aws.String("test-instance-id")
|
|
||||||
spotRequestId := aws.String("spot-id")
|
|
||||||
volumeId := aws.String("volume-id")
|
|
||||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId)
|
|
||||||
|
|
||||||
uiMock := packer.TestUi(t)
|
|
||||||
|
|
||||||
state := tStateSpot()
|
|
||||||
state.Put("ec2", ec2Mock)
|
|
||||||
state.Put("ui", uiMock)
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
|
||||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
|
||||||
stepRunSpotInstance.SpotTags = map[string]string{
|
|
||||||
"spot-tag": "spot-tag-value",
|
|
||||||
}
|
|
||||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
|
||||||
"volume-tag": "volume-tag-value",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
action := stepRunSpotInstance.Run(ctx, state)
|
|
||||||
|
|
||||||
if err := state.Get("error"); err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("shoul continue, but: %v", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams) != 1 {
|
|
||||||
t.Fatalf("createLaunchTemplate should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
|
||||||
}
|
|
||||||
launchTemplateName := ec2Mock.CreateLaunchTemplateParams[0].LaunchTemplateName
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 1 {
|
|
||||||
t.Fatalf("exactly one launch template tag specification expected")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].ResourceType != "launch-template" {
|
|
||||||
t.Fatalf("resource type 'launch-template' expected")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags) != 1 {
|
|
||||||
t.Fatalf("1 launch template tag expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameTag := ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags[0]
|
|
||||||
if *nameTag.Key != "spot-tag" || *nameTag.Value != "spot-tag-value" {
|
|
||||||
t.Fatalf("expected spot-tag: spot-tag-value")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateFleetParams) != 1 {
|
|
||||||
t.Fatalf("createFleet should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.DefaultTargetCapacityType != "spot" {
|
|
||||||
t.Fatalf("capacity type should be spot")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.TotalTargetCapacity != 1 {
|
|
||||||
t.Fatalf("target capacity should be 1")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs) != 1 {
|
|
||||||
t.Fatalf("exactly one launch config template expected")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs[0].LaunchTemplateSpecification.LaunchTemplateName != *launchTemplateName {
|
|
||||||
t.Fatalf("launchTemplateName should match in createLaunchTemplate and createFleet requests")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.DescribeInstancesParams) != 1 {
|
|
||||||
t.Fatalf("describeInstancesParams should be invoked once, but invoked %v", len(ec2Mock.DescribeInstancesParams))
|
|
||||||
}
|
|
||||||
if *ec2Mock.DescribeInstancesParams[0].InstanceIds[0] != *instanceId {
|
|
||||||
t.Fatalf("instanceId should match from createFleet response")
|
|
||||||
}
|
|
||||||
|
|
||||||
uiMock.Say(fmt.Sprintf("%v", ec2Mock.CreateTagsParams))
|
|
||||||
if len(ec2Mock.CreateTagsParams) != 3 {
|
|
||||||
t.Fatalf("createTags should be invoked 3 times")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[0].Resources) != 1 || *ec2Mock.CreateTagsParams[0].Resources[0] != *spotRequestId {
|
|
||||||
t.Fatalf("should create tags for spot request")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[1].Resources) != 1 || *ec2Mock.CreateTagsParams[1].Resources[0] != *instanceId {
|
|
||||||
t.Fatalf("should create tags for instance")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[2].Resources) != 1 || ec2Mock.CreateTagsParams[2].Resources[0] != volumeId {
|
|
||||||
t.Fatalf("should create tags for volume")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRun_NoSpotTags(t *testing.T) {
|
|
||||||
instanceId := aws.String("test-instance-id")
|
|
||||||
spotRequestId := aws.String("spot-id")
|
|
||||||
volumeId := aws.String("volume-id")
|
|
||||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId)
|
|
||||||
|
|
||||||
uiMock := packer.TestUi(t)
|
|
||||||
|
|
||||||
state := tStateSpot()
|
|
||||||
state.Put("ec2", ec2Mock)
|
|
||||||
state.Put("ui", uiMock)
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
|
||||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
|
||||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
|
||||||
"volume-tag": "volume-tag-value",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
action := stepRunSpotInstance.Run(ctx, state)
|
|
||||||
|
|
||||||
if err := state.Get("error"); err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("shoul continue, but: %v", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 0 {
|
|
||||||
t.Fatalf("0 launch template tags expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
// },
|
// },
|
||||||
|
|
||||||
type StepSetGeneratedData struct {
|
type StepSetGeneratedData struct {
|
||||||
GeneratedData *packerbuilderdata.GeneratedData
|
GeneratedData *builder.GeneratedData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepSetGeneratedData) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepSetGeneratedData) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -43,7 +42,7 @@ func (s *StepStopEBSBackedInstance) Run(ctx context.Context, state multistep.Sta
|
|||||||
|
|
||||||
// Work around this by retrying a few times, up to about 5 minutes.
|
// Work around this by retrying a few times, up to about 5 minutes.
|
||||||
err := retry.Config{Tries: 6, ShouldRetry: func(error) bool {
|
err := retry.Config{Tries: 6, ShouldRetry: func(error) bool {
|
||||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
"github.com/hashicorp/packer/builder"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
@@ -23,7 +23,7 @@ func (t EC2Tags) Report(ui packer.Ui) {
|
|||||||
|
|
||||||
func (t TagMap) EC2Tags(ictx interpolate.Context, region string, state multistep.StateBag) (EC2Tags, error) {
|
func (t TagMap) EC2Tags(ictx interpolate.Context, region string, state multistep.StateBag) (EC2Tags, error) {
|
||||||
var ec2Tags []*ec2.Tag
|
var ec2Tags []*ec2.Tag
|
||||||
generatedData := packerbuilderdata.GeneratedData{State: state}
|
generatedData := builder.GeneratedData{State: state}
|
||||||
ictx.Data = extractBuildInfo(region, state, &generatedData)
|
ictx.Data = extractBuildInfo(region, state, &generatedData)
|
||||||
|
|
||||||
for key, value := range t {
|
for key, value := range t {
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/hcl2template"
|
"github.com/hashicorp/packer/hcl2template"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
@@ -85,22 +85,16 @@ func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstruct
|
|||||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
Exclude: []string{
|
Exclude: []string{
|
||||||
"ami_description",
|
"ami_description",
|
||||||
"run_tags",
|
"run_tags",
|
||||||
"run_tag",
|
|
||||||
"run_volume_tags",
|
"run_volume_tags",
|
||||||
"run_volume_tag",
|
|
||||||
"spot_tags",
|
"spot_tags",
|
||||||
"spot_tag",
|
|
||||||
"snapshot_tags",
|
"snapshot_tags",
|
||||||
"snapshot_tag",
|
|
||||||
"tags",
|
"tags",
|
||||||
"tag",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, raws...)
|
}, raws...)
|
||||||
@@ -169,7 +163,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("awsSession", session)
|
state.Put("awsSession", session)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
generatedData := &builder.GeneratedData{State: state}
|
||||||
|
|
||||||
var instanceStep multistep.Step
|
var instanceStep multistep.Step
|
||||||
|
|
||||||
@@ -186,7 +180,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
Region: *ec2conn.Config.Region,
|
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
SpotTags: b.config.SpotTags,
|
SpotTags: b.config.SpotTags,
|
||||||
@@ -213,7 +206,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
Tenancy: b.config.Tenancy,
|
|
||||||
UserData: b.config.UserData,
|
UserData: b.config.UserData,
|
||||||
UserDataFile: b.config.UserDataFile,
|
UserDataFile: b.config.UserDataFile,
|
||||||
VolumeTags: b.config.VolumeRunTags,
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
@@ -277,7 +269,6 @@ 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(),
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ 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"`
|
||||||
@@ -31,7 +29,6 @@ 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"`
|
||||||
@@ -81,7 +78,6 @@ type FlatConfig struct {
|
|||||||
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
||||||
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
||||||
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
||||||
Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"`
|
|
||||||
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
||||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||||
@@ -96,8 +92,6 @@ type FlatConfig struct {
|
|||||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -138,7 +132,6 @@ 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"`
|
||||||
@@ -167,9 +160,7 @@ 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},
|
||||||
@@ -179,7 +170,6 @@ 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())},
|
||||||
@@ -229,7 +219,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
||||||
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
||||||
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
||||||
"tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
||||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
@@ -244,8 +233,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
@@ -286,7 +273,6 @@ 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())},
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package ebs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
@@ -105,10 +104,10 @@ func checkSnapshotsDeleted(snapshotIds []*string) builderT.TestCheckFunc {
|
|||||||
|
|
||||||
func TestBuilderAcc_amiSharing(t *testing.T) {
|
func TestBuilderAcc_amiSharing(t *testing.T) {
|
||||||
builderT.Test(t, builderT.TestCase{
|
builderT.Test(t, builderT.TestCase{
|
||||||
PreCheck: func() { testAccSharingPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Builder: &Builder{},
|
Builder: &Builder{},
|
||||||
Template: buildSharingConfig(os.Getenv("TESTACC_AWS_ACCOUNT_ID")),
|
Template: testBuilderAccSharing,
|
||||||
Check: checkAMISharing(2, os.Getenv("TESTACC_AWS_ACCOUNT_ID"), "all"),
|
Check: checkAMISharing(2, "932021504756", "all"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,23 +245,9 @@ func checkBootEncrypted() builderT.TestCheckFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilderAcc_SessionManagerInterface(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccSessionManagerInterface,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccPreCheck(t *testing.T) {
|
func testAccPreCheck(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccSharingPreCheck(t *testing.T) {
|
|
||||||
if v := os.Getenv("TESTACC_AWS_ACCOUNT_ID"); v == "" {
|
|
||||||
t.Fatal(fmt.Sprintf("TESTACC_AWS_ACCOUNT_ID must be set for acceptance tests"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testEC2Conn() (*ec2.EC2, error) {
|
func testEC2Conn() (*ec2.EC2, error) {
|
||||||
access := &common.AccessConfig{RawRegion: "us-east-1"}
|
access := &common.AccessConfig{RawRegion: "us-east-1"}
|
||||||
session, err := access.Session()
|
session, err := access.Session()
|
||||||
@@ -329,6 +314,7 @@ const testBuilderAccForceDeleteSnapshot = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// share with catsby
|
||||||
const testBuilderAccSharing = `
|
const testBuilderAccSharing = `
|
||||||
{
|
{
|
||||||
"builders": [{
|
"builders": [{
|
||||||
@@ -338,7 +324,7 @@ const testBuilderAccSharing = `
|
|||||||
"source_ami": "ami-76b2a71e",
|
"source_ami": "ami-76b2a71e",
|
||||||
"ssh_username": "ubuntu",
|
"ssh_username": "ubuntu",
|
||||||
"ami_name": "packer-test {{timestamp}}",
|
"ami_name": "packer-test {{timestamp}}",
|
||||||
"ami_users":["%s"],
|
"ami_users":["932021504756"],
|
||||||
"ami_groups":["all"]
|
"ami_groups":["all"]
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -358,31 +344,6 @@ const testBuilderAccEncrypted = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testBuilderAccSessionManagerInterface = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami_filter": {
|
|
||||||
"filters": {
|
|
||||||
"virtualization-type": "hvm",
|
|
||||||
"name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
|
|
||||||
"root-device-type": "ebs"
|
|
||||||
},
|
|
||||||
"owners": [
|
|
||||||
"099720109477"
|
|
||||||
],
|
|
||||||
"most_recent": true
|
|
||||||
},
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ssh_interface": "session_manager",
|
|
||||||
"iam_instance_profile": "SSMInstanceProfile",
|
|
||||||
"ami_name": "packer-ssm-test-{{timestamp}}"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
func buildForceDeregisterConfig(val, name string) string {
|
func buildForceDeregisterConfig(val, name string) string {
|
||||||
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
||||||
}
|
}
|
||||||
@@ -390,7 +351,3 @@ func buildForceDeregisterConfig(val, name string) string {
|
|||||||
func buildForceDeleteSnapshotConfig(val, name string) string {
|
func buildForceDeleteSnapshotConfig(val, name string) string {
|
||||||
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
|
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSharingConfig(val string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccSharing, val)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
"github.com/hashicorp/packer/common/random"
|
"github.com/hashicorp/packer/common/random"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
@@ -62,7 +61,7 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
|
|||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 0,
|
Tries: 0,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
if awserrors.Matches(err, "InvalidParameterValue", "Instance is not in state") {
|
if awscommon.IsAWSErr(err, "InvalidParameterValue", "Instance is not in state") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
package ebssurrogate
|
package ebssurrogate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
@@ -38,6 +41,50 @@ func (bds BlockDevices) BuildEC2BlockDeviceMappings() []*ec2.BlockDeviceMapping
|
|||||||
return blockDevices
|
return blockDevices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapping {
|
||||||
|
|
||||||
|
mapping := &ec2.BlockDeviceMapping{
|
||||||
|
DeviceName: aws.String(blockDevice.DeviceName),
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockDevice.NoDevice {
|
||||||
|
mapping.NoDevice = aws.String("")
|
||||||
|
return mapping
|
||||||
|
} else if blockDevice.VirtualName != "" {
|
||||||
|
if strings.HasPrefix(blockDevice.VirtualName, "ephemeral") {
|
||||||
|
mapping.VirtualName = aws.String(blockDevice.VirtualName)
|
||||||
|
}
|
||||||
|
return mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
ebsBlockDevice := &ec2.EbsBlockDevice{
|
||||||
|
DeleteOnTermination: aws.Bool(blockDevice.DeleteOnTermination),
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockDevice.VolumeType != "" {
|
||||||
|
ebsBlockDevice.VolumeType = aws.String(blockDevice.VolumeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockDevice.VolumeSize > 0 {
|
||||||
|
ebsBlockDevice.VolumeSize = aws.Int64(blockDevice.VolumeSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IOPS is only valid for io1 type
|
||||||
|
if blockDevice.VolumeType == "io1" {
|
||||||
|
ebsBlockDevice.Iops = aws.Int64(blockDevice.IOPS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// You cannot specify Encrypted if you specify a Snapshot ID
|
||||||
|
if blockDevice.SnapshotId != "" {
|
||||||
|
ebsBlockDevice.SnapshotId = aws.String(blockDevice.SnapshotId)
|
||||||
|
}
|
||||||
|
ebsBlockDevice.Encrypted = blockDevice.Encrypted.ToBoolPointer()
|
||||||
|
|
||||||
|
mapping.Ebs = ebsBlockDevice
|
||||||
|
|
||||||
|
return mapping
|
||||||
|
}
|
||||||
|
|
||||||
func (bds BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) {
|
func (bds BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||||
for _, block := range bds {
|
for _, block := range bds {
|
||||||
if err := block.Prepare(ctx); err != nil {
|
if err := block.Prepare(ctx); err != nil {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/hcl2template"
|
"github.com/hashicorp/packer/hcl2template"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
@@ -84,22 +84,16 @@ func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstruct
|
|||||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
Exclude: []string{
|
Exclude: []string{
|
||||||
"ami_description",
|
"ami_description",
|
||||||
"run_tags",
|
"run_tags",
|
||||||
"run_tag",
|
|
||||||
"run_volume_tags",
|
"run_volume_tags",
|
||||||
"run_volume_tag",
|
|
||||||
"snapshot_tags",
|
"snapshot_tags",
|
||||||
"snapshot_tag",
|
|
||||||
"spot_tags",
|
"spot_tags",
|
||||||
"spot_tag",
|
|
||||||
"tags",
|
"tags",
|
||||||
"tag",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, raws...)
|
}, raws...)
|
||||||
@@ -192,7 +186,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("awsSession", session)
|
state.Put("awsSession", session)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
generatedData := &builder.GeneratedData{State: state}
|
||||||
|
|
||||||
var instanceStep multistep.Step
|
var instanceStep multistep.Step
|
||||||
|
|
||||||
@@ -209,7 +203,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
Region: *ec2conn.Config.Region,
|
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||||
@@ -235,7 +228,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
Tenancy: b.config.Tenancy,
|
|
||||||
UserData: b.config.UserData,
|
UserData: b.config.UserData,
|
||||||
UserDataFile: b.config.UserDataFile,
|
UserDataFile: b.config.UserDataFile,
|
||||||
VolumeTags: b.config.VolumeRunTags,
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
@@ -301,7 +293,6 @@ 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(),
|
||||||
@@ -340,8 +331,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
PollingConfig: b.config.PollingConfig,
|
PollingConfig: b.config.PollingConfig,
|
||||||
LaunchDevices: launchDevices,
|
LaunchDevices: launchDevices,
|
||||||
SnapshotOmitMap: b.config.LaunchMappings.GetOmissions(),
|
SnapshotOmitMap: b.config.LaunchMappings.GetOmissions(),
|
||||||
SnapshotTags: b.config.SnapshotTags,
|
|
||||||
Ctx: b.config.ctx,
|
|
||||||
},
|
},
|
||||||
&awscommon.StepDeregisterAMI{
|
&awscommon.StepDeregisterAMI{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
|
|||||||
@@ -62,9 +62,7 @@ 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,7 +72,6 @@ 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"`
|
||||||
@@ -103,7 +100,6 @@ type FlatConfig struct {
|
|||||||
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
||||||
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
||||||
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
||||||
Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"`
|
|
||||||
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
||||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||||
@@ -118,8 +114,6 @@ type FlatConfig struct {
|
|||||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -160,7 +154,6 @@ 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"`
|
||||||
@@ -211,9 +204,7 @@ 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},
|
||||||
@@ -223,7 +214,6 @@ 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())},
|
||||||
@@ -252,7 +242,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
||||||
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
||||||
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
||||||
"tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
||||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
@@ -267,8 +256,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
@@ -309,7 +296,6 @@ 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},
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StepSnapshotVolumes creates snapshots of the created volumes.
|
// StepSnapshotVolumes creates snapshots of the created volumes.
|
||||||
@@ -25,8 +23,6 @@ type StepSnapshotVolumes struct {
|
|||||||
snapshotIds map[string]string
|
snapshotIds map[string]string
|
||||||
snapshotMutex sync.Mutex
|
snapshotMutex sync.Mutex
|
||||||
SnapshotOmitMap map[string]bool
|
SnapshotOmitMap map[string]bool
|
||||||
SnapshotTags map[string]string
|
|
||||||
Ctx interpolate.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName string, state multistep.StateBag) error {
|
func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName string, state multistep.StateBag) error {
|
||||||
@@ -44,34 +40,12 @@ func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName str
|
|||||||
return fmt.Errorf("Volume ID for device %s not found", deviceName)
|
return fmt.Errorf("Volume ID for device %s not found", deviceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Say("Creating snapshot tags")
|
|
||||||
snapshotTags, err := awscommon.TagMap(s.SnapshotTags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
|
||||||
if err != nil {
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
snapshotTags.Report(ui)
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Creating snapshot of EBS Volume %s...", volumeId))
|
ui.Say(fmt.Sprintf("Creating snapshot of EBS Volume %s...", volumeId))
|
||||||
description := fmt.Sprintf("Packer: %s", time.Now().String())
|
description := fmt.Sprintf("Packer: %s", time.Now().String())
|
||||||
|
|
||||||
// Collect tags for tagging on resource creation
|
|
||||||
var tagSpecs []*ec2.TagSpecification
|
|
||||||
|
|
||||||
if len(snapshotTags) > 0 {
|
|
||||||
snapTags := &ec2.TagSpecification{
|
|
||||||
ResourceType: aws.String("snapshot"),
|
|
||||||
Tags: snapshotTags,
|
|
||||||
}
|
|
||||||
|
|
||||||
tagSpecs = append(tagSpecs, snapTags)
|
|
||||||
}
|
|
||||||
|
|
||||||
createSnapResp, err := ec2conn.CreateSnapshot(&ec2.CreateSnapshotInput{
|
createSnapResp, err := ec2conn.CreateSnapshot(&ec2.CreateSnapshotInput{
|
||||||
VolumeId: &volumeId,
|
VolumeId: &volumeId,
|
||||||
Description: &description,
|
Description: &description,
|
||||||
TagSpecifications: tagSpecs,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/hcl2template"
|
"github.com/hashicorp/packer/hcl2template"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
@@ -100,21 +100,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||||||
SourceAMI: `{{ .SourceAMI }} `,
|
SourceAMI: `{{ .SourceAMI }} `,
|
||||||
}
|
}
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
|
||||||
Exclude: []string{
|
|
||||||
"run_tags",
|
|
||||||
"run_tag",
|
|
||||||
"run_volume_tags",
|
|
||||||
"run_volume_tag",
|
|
||||||
"spot_tags",
|
|
||||||
"spot_tag",
|
|
||||||
"tags",
|
|
||||||
"tag",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, raws...)
|
}, raws...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -180,7 +167,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("iam", iam)
|
state.Put("iam", iam)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
generatedData := &builder.GeneratedData{State: state}
|
||||||
|
|
||||||
var instanceStep multistep.Step
|
var instanceStep multistep.Step
|
||||||
|
|
||||||
@@ -197,7 +184,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
Region: *ec2conn.Config.Region,
|
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
@@ -223,7 +209,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
Tenancy: b.config.Tenancy,
|
|
||||||
UserData: b.config.UserData,
|
UserData: b.config.UserData,
|
||||||
UserDataFile: b.config.UserDataFile,
|
UserDataFile: b.config.UserDataFile,
|
||||||
VolumeTags: b.config.VolumeRunTags,
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
@@ -278,7 +263,6 @@ 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(),
|
||||||
|
|||||||
@@ -64,9 +64,7 @@ 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"`
|
||||||
@@ -76,7 +74,6 @@ 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"`
|
||||||
@@ -105,7 +102,6 @@ type FlatConfig struct {
|
|||||||
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
||||||
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
||||||
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
||||||
Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"`
|
|
||||||
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
||||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||||
@@ -120,8 +116,6 @@ type FlatConfig struct {
|
|||||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -162,7 +156,6 @@ 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"`
|
||||||
@@ -191,9 +184,7 @@ 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},
|
||||||
@@ -203,7 +194,6 @@ 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())},
|
||||||
@@ -232,7 +222,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
||||||
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
||||||
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
||||||
"tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
||||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
@@ -247,8 +236,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
@@ -289,7 +276,6 @@ 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},
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
@@ -107,7 +107,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||||||
|
|
||||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
@@ -250,7 +249,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("awsSession", session)
|
state.Put("awsSession", session)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
generatedData := &builder.GeneratedData{State: state}
|
||||||
|
|
||||||
var instanceStep multistep.Step
|
var instanceStep multistep.Step
|
||||||
|
|
||||||
@@ -265,7 +264,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
Region: *ec2conn.Config.Region,
|
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||||
@@ -288,7 +286,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
Tenancy: b.config.Tenancy,
|
|
||||||
UserData: b.config.UserData,
|
UserData: b.config.UserData,
|
||||||
UserDataFile: b.config.UserDataFile,
|
UserDataFile: b.config.UserDataFile,
|
||||||
}
|
}
|
||||||
@@ -346,7 +343,6 @@ 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(),
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ 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"`
|
||||||
@@ -31,7 +29,6 @@ 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"`
|
||||||
@@ -81,7 +78,6 @@ type FlatConfig struct {
|
|||||||
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
SpotTag []hcl2template.FlatKeyValue `mapstructure:"spot_tag" required:"false" cty:"spot_tag" hcl:"spot_tag"`
|
||||||
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter" hcl:"subnet_filter"`
|
||||||
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
||||||
Tenancy *string `mapstructure:"tenancy" required:"false" cty:"tenancy" hcl:"tenancy"`
|
|
||||||
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs" hcl:"temporary_security_group_source_cidrs"`
|
||||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||||
@@ -96,8 +92,6 @@ type FlatConfig struct {
|
|||||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -138,7 +132,6 @@ 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"`
|
||||||
@@ -173,9 +166,7 @@ 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},
|
||||||
@@ -185,7 +176,6 @@ 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())},
|
||||||
@@ -235,7 +225,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
"spot_tag": &hcldec.BlockListSpec{TypeName: "spot_tag", Nested: hcldec.ObjectSpec((*hcl2template.FlatKeyValue)(nil).HCL2Spec())},
|
||||||
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
||||||
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
||||||
"tenancy": &hcldec.AttrSpec{Name: "tenancy", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
||||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
@@ -250,8 +239,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
@@ -292,7 +279,6 @@ 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())},
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storageAccountName string,
|
func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string,
|
||||||
cloud *azure.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration,
|
cloud *azure.Environment, SharedGalleryTimeout time.Duration, PollingDuration time.Duration,
|
||||||
servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
|
servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
|
||||||
|
|
||||||
var azureClient = &AzureClient{}
|
var azureClient = &AzureClient{}
|
||||||
@@ -141,56 +141,56 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
|||||||
azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
|
azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentsClient.UserAgent)
|
azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentsClient.UserAgent)
|
||||||
azureClient.DeploymentsClient.Client.PollingDuration = pollingDuration
|
azureClient.DeploymentsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen)
|
azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentOperationsClient.UserAgent)
|
azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentOperationsClient.UserAgent)
|
||||||
azureClient.DeploymentOperationsClient.Client.PollingDuration = pollingDuration
|
azureClient.DeploymentOperationsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.DisksClient.RequestInspector = withInspection(maxlen)
|
azureClient.DisksClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DisksClient.UserAgent)
|
azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DisksClient.UserAgent)
|
||||||
azureClient.DisksClient.Client.PollingDuration = pollingDuration
|
azureClient.DisksClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
|
azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GroupsClient.UserAgent)
|
azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GroupsClient.UserAgent)
|
||||||
azureClient.GroupsClient.Client.PollingDuration = pollingDuration
|
azureClient.GroupsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
|
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.ImagesClient.UserAgent)
|
azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.ImagesClient.UserAgent)
|
||||||
azureClient.ImagesClient.Client.PollingDuration = pollingDuration
|
azureClient.ImagesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
|
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.InterfacesClient.UserAgent)
|
azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.InterfacesClient.UserAgent)
|
||||||
azureClient.InterfacesClient.Client.PollingDuration = pollingDuration
|
azureClient.InterfacesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
|
azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SubnetsClient.UserAgent)
|
azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SubnetsClient.UserAgent)
|
||||||
azureClient.SubnetsClient.Client.PollingDuration = pollingDuration
|
azureClient.SubnetsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
|
azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualNetworksClient.UserAgent)
|
azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualNetworksClient.UserAgent)
|
||||||
azureClient.VirtualNetworksClient.Client.PollingDuration = pollingDuration
|
azureClient.VirtualNetworksClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.SecurityGroupsClient = network.NewSecurityGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.SecurityGroupsClient = network.NewSecurityGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.SecurityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.SecurityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
@@ -203,44 +203,42 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
|||||||
azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
|
azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.PublicIPAddressesClient.UserAgent)
|
azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.PublicIPAddressesClient.UserAgent)
|
||||||
azureClient.PublicIPAddressesClient.Client.PollingDuration = pollingDuration
|
azureClient.PublicIPAddressesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
|
azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
|
azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
|
||||||
azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualMachinesClient.UserAgent)
|
azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualMachinesClient.UserAgent)
|
||||||
azureClient.VirtualMachinesClient.Client.PollingDuration = pollingDuration
|
azureClient.VirtualMachinesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.SnapshotsClient = compute.NewSnapshotsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.SnapshotsClient = compute.NewSnapshotsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.SnapshotsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.SnapshotsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen)
|
azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.SnapshotsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SnapshotsClient.UserAgent)
|
azureClient.SnapshotsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SnapshotsClient.UserAgent)
|
||||||
azureClient.SnapshotsClient.Client.PollingDuration = pollingDuration
|
azureClient.SnapshotsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
|
azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.AccountsClient.UserAgent)
|
azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.AccountsClient.UserAgent)
|
||||||
azureClient.AccountsClient.Client.PollingDuration = pollingDuration
|
azureClient.AccountsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.GalleryImageVersionsClient.RequestInspector = withInspection(maxlen)
|
azureClient.GalleryImageVersionsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImageVersionsClient.UserAgent)
|
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImageVersionsClient.UserAgent)
|
||||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
|
azureClient.GalleryImageVersionsClient.Client.PollingDuration = SharedGalleryTimeout
|
||||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
|
||||||
|
|
||||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen)
|
azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImagesClient.UserAgent)
|
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImagesClient.UserAgent)
|
||||||
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
|
azureClient.GalleryImagesClient.Client.PollingDuration = PollingDuration
|
||||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
|
||||||
|
|
||||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -252,7 +250,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
|||||||
azureClient.VaultClient.RequestInspector = withInspection(maxlen)
|
azureClient.VaultClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClient.UserAgent)
|
azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClient.UserAgent)
|
||||||
azureClient.VaultClient.Client.PollingDuration = pollingDuration
|
azureClient.VaultClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
// This client is different than the above because it manages the vault
|
// This client is different than the above because it manages the vault
|
||||||
// itself rather than the contents of the vault.
|
// itself rather than the contents of the vault.
|
||||||
@@ -261,7 +259,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
|||||||
azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen)
|
azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClientDelete.UserAgent)
|
azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClientDelete.UserAgent)
|
||||||
azureClient.VaultClientDelete.Client.PollingDuration = pollingDuration
|
azureClient.VaultClientDelete.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
// If this is a managed disk build, this should be ignored.
|
// If this is a managed disk build, this should be ignored.
|
||||||
if resourceGroupName != "" && storageAccountName != "" {
|
if resourceGroupName != "" && storageAccountName != "" {
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
ui.Message("Creating Azure Resource Manager (ARM) client ...")
|
ui.Message("Creating Azure Resource Manager (ARM) client ...")
|
||||||
azureClient, err := NewAzureClient(
|
azureClient, err := NewAzureClient(
|
||||||
b.config.ClientConfig.SubscriptionID,
|
b.config.ClientConfig.SubscriptionID,
|
||||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
|
||||||
b.config.ResourceGroupName,
|
b.config.ResourceGroupName,
|
||||||
b.config.StorageAccount,
|
b.config.StorageAccount,
|
||||||
b.config.ClientConfig.CloudEnvironment(),
|
b.config.ClientConfig.CloudEnvironment(),
|
||||||
@@ -222,6 +221,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
||||||
NewStepCaptureImage(azureClient, ui),
|
NewStepCaptureImage(azureClient, ui),
|
||||||
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
||||||
|
NewStepDeleteAdditionalDisks(azureClient, ui),
|
||||||
}
|
}
|
||||||
} else if b.config.OSType == constants.Target_Windows {
|
} else if b.config.OSType == constants.Target_Windows {
|
||||||
steps = []multistep.Step{
|
steps = []multistep.Step{
|
||||||
@@ -262,6 +262,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
||||||
NewStepCaptureImage(azureClient, ui),
|
NewStepCaptureImage(azureClient, ui),
|
||||||
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
||||||
|
NewStepDeleteAdditionalDisks(azureClient, ui),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType)
|
return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType)
|
||||||
@@ -388,7 +389,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, packerAzureCommon.MapToAzureTags(b.config.AzureTags))
|
stateBag.Put(constants.ArmTags, 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)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package arm
|
|||||||
// * ARM_CLIENT_ID
|
// * ARM_CLIENT_ID
|
||||||
// * ARM_CLIENT_SECRET
|
// * ARM_CLIENT_SECRET
|
||||||
// * ARM_SUBSCRIPTION_ID
|
// * ARM_SUBSCRIPTION_ID
|
||||||
|
// * ARM_OBJECT_ID
|
||||||
// * ARM_STORAGE_ACCOUNT
|
// * ARM_STORAGE_ACCOUNT
|
||||||
//
|
//
|
||||||
// The subscription in question should have a resource group
|
// The subscription in question should have a resource group
|
||||||
@@ -46,14 +47,6 @@ func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group_Additional_Disk(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Windows_DeviceLogin(t *testing.T) {
|
func TestBuilderAcc_ManagedDisk_Windows_DeviceLogin(t *testing.T) {
|
||||||
if os.Getenv(DeviceLoginAcceptanceTest) == "" {
|
if os.Getenv(DeviceLoginAcceptanceTest) == "" {
|
||||||
t.Skip(fmt.Sprintf(
|
t.Skip(fmt.Sprintf(
|
||||||
@@ -90,19 +83,6 @@ func TestBuilderAcc_ManagedDisk_Linux_DeviceLogin(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) {
|
|
||||||
if os.Getenv("AZURE_CLI_AUTH") == "" {
|
|
||||||
t.Skip("Azure CLI Acceptance tests skipped unless env 'AZURE_CLI_AUTH' is set, and an active `az login` session has been established")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskLinuxAzureCLI,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_Blob_Windows(t *testing.T) {
|
func TestBuilderAcc_Blob_Windows(t *testing.T) {
|
||||||
builderT.Test(t, builderT.TestCase{
|
builderT.Test(t, builderT.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
@@ -171,7 +151,7 @@ const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
|||||||
|
|
||||||
"build_resource_group_name" : "packer-acceptance-test",
|
"build_resource_group_name" : "packer-acceptance-test",
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
"managed_image_resource_group_name": "packer-acceptance-test",
|
||||||
"managed_image_name": "testBuilderAccManagedDiskWindowsBuildResourceGroup-{{timestamp}}",
|
"managed_image_name": "testBuilderAccManagedDiskWindows-{{timestamp}}",
|
||||||
|
|
||||||
"os_type": "Windows",
|
"os_type": "Windows",
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
"image_publisher": "MicrosoftWindowsServer",
|
||||||
@@ -190,42 +170,6 @@ const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"build_resource_group_name" : "packer-acceptance-test",
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionDisk-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Windows",
|
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
|
||||||
"image_offer": "WindowsServer",
|
|
||||||
"image_sku": "2012-R2-Datacenter",
|
|
||||||
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_use_ssl": "true",
|
|
||||||
"winrm_insecure": "true",
|
|
||||||
"winrm_timeout": "3m",
|
|
||||||
"winrm_username": "packer",
|
|
||||||
"async_resourcegroup_delete": "true",
|
|
||||||
|
|
||||||
"vm_size": "Standard_DS2_v2",
|
|
||||||
"disk_additional_size": [10,15]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindowsDeviceLogin = `
|
const testBuilderAccManagedDiskWindowsDeviceLogin = `
|
||||||
{
|
{
|
||||||
"variables": {
|
"variables": {
|
||||||
@@ -279,11 +223,7 @@ 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"
|
|
||||||
}
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@@ -318,6 +258,7 @@ const testBuilderAccBlobWindows = `
|
|||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}",
|
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}",
|
||||||
|
"object_id": "{{env ` + "`ARM_OBJECT_ID`" + `}}",
|
||||||
"storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}"
|
"storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}"
|
||||||
},
|
},
|
||||||
"builders": [{
|
"builders": [{
|
||||||
@@ -326,6 +267,7 @@ const testBuilderAccBlobWindows = `
|
|||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
"client_id": "{{user ` + "`client_id`" + `}}",
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
||||||
|
"object_id": "{{user ` + "`object_id`" + `}}",
|
||||||
|
|
||||||
"storage_account": "{{user ` + "`storage_account`" + `}}",
|
"storage_account": "{{user ` + "`storage_account`" + `}}",
|
||||||
"resource_group_name": "packer-acceptance-test",
|
"resource_group_name": "packer-acceptance-test",
|
||||||
@@ -379,27 +321,3 @@ const testBuilderAccBlobLinux = `
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const testBuilderAccManagedDiskLinuxAzureCLI = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"use_azure_cli_auth": true,
|
|
||||||
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Linux",
|
|
||||||
"image_publisher": "Canonical",
|
|
||||||
"image_offer": "UbuntuServer",
|
|
||||||
"image_sku": "16.04-LTS",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2",
|
|
||||||
"azure_tags": {
|
|
||||||
"env": "testing",
|
|
||||||
"builder": "packer"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|||||||
@@ -33,35 +33,3 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
+32
-67
@@ -90,7 +90,6 @@ type SharedImageGallery struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SharedImageGalleryDestination struct {
|
type SharedImageGalleryDestination struct {
|
||||||
SigDestinationSubscription string `mapstructure:"subscription"`
|
|
||||||
SigDestinationResourceGroup string `mapstructure:"resource_group"`
|
SigDestinationResourceGroup string `mapstructure:"resource_group"`
|
||||||
SigDestinationGalleryName string `mapstructure:"gallery_name"`
|
SigDestinationGalleryName string `mapstructure:"gallery_name"`
|
||||||
SigDestinationImageName string `mapstructure:"image_name"`
|
SigDestinationImageName string `mapstructure:"image_name"`
|
||||||
@@ -119,64 +118,31 @@ type Config struct {
|
|||||||
// Use a [Shared Gallery
|
// Use a [Shared Gallery
|
||||||
// image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/)
|
// image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/)
|
||||||
// as the source for this build. *VHD targets are incompatible with this
|
// as the source for this build. *VHD targets are incompatible with this
|
||||||
// build type* - the target must be a *Managed Image*. When using shared_image_gallery as a source, image_publisher,
|
// build type* - the target must be a *Managed Image*.
|
||||||
// image_offer, image_sku, image_version, and custom_managed_image_name should not be set.
|
|
||||||
//
|
//
|
||||||
// In JSON
|
// "shared_image_gallery": {
|
||||||
// ```json
|
// "subscription": "00000000-0000-0000-0000-00000000000",
|
||||||
// "shared_image_gallery": {
|
// "resource_group": "ResourceGroup",
|
||||||
// "subscription": "00000000-0000-0000-0000-00000000000",
|
// "gallery_name": "GalleryName",
|
||||||
// "resource_group": "ResourceGroup",
|
// "image_name": "ImageName",
|
||||||
// "gallery_name": "GalleryName",
|
// "image_version": "1.0.0"
|
||||||
// "image_name": "ImageName",
|
// }
|
||||||
// "image_version": "1.0.0"
|
// "managed_image_name": "TargetImageName",
|
||||||
// }
|
// "managed_image_resource_group_name": "TargetResourceGroup"
|
||||||
// "managed_image_name": "TargetImageName",
|
|
||||||
// "managed_image_resource_group_name": "TargetResourceGroup"
|
|
||||||
// ```
|
|
||||||
// In HCL2
|
|
||||||
// ```hcl
|
|
||||||
// shared_image_gallery {
|
|
||||||
// subscription = "00000000-0000-0000-0000-00000000000"
|
|
||||||
// resource_group = "ResourceGroup"
|
|
||||||
// gallery_name = "GalleryName"
|
|
||||||
// image_name = "ImageName"
|
|
||||||
// image_version = "1.0.0"
|
|
||||||
// }
|
|
||||||
// managed_image_name = "TargetImageName"
|
|
||||||
// managed_image_resource_group_name = "TargetResourceGroup"
|
|
||||||
// ```
|
|
||||||
SharedGallery SharedImageGallery `mapstructure:"shared_image_gallery" required:"false"`
|
SharedGallery SharedImageGallery `mapstructure:"shared_image_gallery" required:"false"`
|
||||||
// The name of the Shared Image Gallery under which the managed image will be published as Shared Gallery Image version.
|
// The name of the Shared Image Gallery under which the managed image will be published as Shared Gallery Image version.
|
||||||
//
|
//
|
||||||
// Following is an example.
|
// Following is an example.
|
||||||
//
|
//
|
||||||
// In JSON
|
// "shared_image_gallery_destination": {
|
||||||
// ```json
|
// "resource_group": "ResourceGroup",
|
||||||
// "shared_image_gallery_destination": {
|
// "gallery_name": "GalleryName",
|
||||||
// "subscription": "00000000-0000-0000-0000-00000000000",
|
// "image_name": "ImageName",
|
||||||
// "resource_group": "ResourceGroup",
|
// "image_version": "1.0.0",
|
||||||
// "gallery_name": "GalleryName",
|
// "replication_regions": ["regionA", "regionB", "regionC"]
|
||||||
// "image_name": "ImageName",
|
// }
|
||||||
// "image_version": "1.0.0",
|
// "managed_image_name": "TargetImageName",
|
||||||
// "replication_regions": ["regionA", "regionB", "regionC"]
|
// "managed_image_resource_group_name": "TargetResourceGroup"
|
||||||
// }
|
|
||||||
// "managed_image_name": "TargetImageName",
|
|
||||||
// "managed_image_resource_group_name": "TargetResourceGroup"
|
|
||||||
// ```
|
|
||||||
// In HCL2
|
|
||||||
// ```hcl
|
|
||||||
// shared_image_gallery_destination {
|
|
||||||
// subscription = "00000000-0000-0000-0000-00000000000"
|
|
||||||
// resource_group = "ResourceGroup"
|
|
||||||
// gallery_name = "GalleryName"
|
|
||||||
// image_name = "ImageName"
|
|
||||||
// image_version = "1.0.0"
|
|
||||||
// replication_regions = ["regionA", "regionB", "regionC"]
|
|
||||||
// }
|
|
||||||
// managed_image_name = "TargetImageName"
|
|
||||||
// managed_image_resource_group_name = "TargetResourceGroup"
|
|
||||||
// ```
|
|
||||||
SharedGalleryDestination SharedImageGalleryDestination `mapstructure:"shared_image_gallery_destination"`
|
SharedGalleryDestination SharedImageGalleryDestination `mapstructure:"shared_image_gallery_destination"`
|
||||||
// How long to wait for an image to be published to the shared image
|
// How long to wait for an image to be published to the shared image
|
||||||
// gallery before timing out. If your Packer build is failing on the
|
// gallery before timing out. If your Packer build is failing on the
|
||||||
@@ -288,7 +254,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 +479,7 @@ func (c *Config) toImageParameters() *compute.Image {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Location: to.StringPtr(c.Location),
|
Location: to.StringPtr(c.Location),
|
||||||
Tags: azcommon.MapToAzureTags(c.AzureTags),
|
Tags: c.AzureTags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,7 +546,6 @@ func (c *Config) createCertificate() (string, error) {
|
|||||||
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
c.ctx.Funcs = azcommon.TemplateFuncs
|
c.ctx.Funcs = azcommon.TemplateFuncs
|
||||||
err := config.Decode(c, &config.DecodeOpts{
|
err := config.Decode(c, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &c.ctx,
|
InterpolateContext: &c.ctx,
|
||||||
}, raws...)
|
}, raws...)
|
||||||
@@ -597,7 +562,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy singular blocks
|
// copy singular blocks
|
||||||
c.AzureTag.CopyOn(&c.AzureTags)
|
for _, kv := range c.AzureTag {
|
||||||
|
v := kv.Value
|
||||||
|
c.AzureTags[kv.Name] = &v
|
||||||
|
}
|
||||||
|
|
||||||
err = c.ClientConfig.SetDefaultValues()
|
err = c.ClientConfig.SetDefaultValues()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -805,8 +773,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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1010,9 +978,6 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
|||||||
if len(c.SharedGalleryDestination.SigDestinationReplicationRegions) == 0 {
|
if len(c.SharedGalleryDestination.SigDestinationReplicationRegions) == 0 {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A list of replication_regions must be specified for shared_image_gallery_destination"))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A list of replication_regions must be specified for shared_image_gallery_destination"))
|
||||||
}
|
}
|
||||||
if c.SharedGalleryDestination.SigDestinationSubscription == "" {
|
|
||||||
c.SharedGalleryDestination.SigDestinationSubscription = c.ClientConfig.SubscriptionID
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if c.SharedGalleryTimeout == 0 {
|
if c.SharedGalleryTimeout == 0 {
|
||||||
// default to a one-hour timeout. In the sdk, the default is 15 m.
|
// default to a one-hour timeout. In the sdk, the default is 15 m.
|
||||||
@@ -1061,13 +1026,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ type FlatConfig struct {
|
|||||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
||||||
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
||||||
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
||||||
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
|
|
||||||
UserAssignedManagedIdentities []string `mapstructure:"user_assigned_managed_identities" required:"false" cty:"user_assigned_managed_identities" hcl:"user_assigned_managed_identities"`
|
UserAssignedManagedIdentities []string `mapstructure:"user_assigned_managed_identities" required:"false" cty:"user_assigned_managed_identities" hcl:"user_assigned_managed_identities"`
|
||||||
CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"`
|
CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"`
|
||||||
CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"`
|
CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"`
|
||||||
@@ -50,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"`
|
||||||
@@ -81,8 +80,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -152,7 +149,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||||
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
||||||
"use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false},
|
|
||||||
"user_assigned_managed_identities": &hcldec.AttrSpec{Name: "user_assigned_managed_identities", Type: cty.List(cty.String), Required: false},
|
"user_assigned_managed_identities": &hcldec.AttrSpec{Name: "user_assigned_managed_identities", Type: cty.List(cty.String), Required: false},
|
||||||
"capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false},
|
"capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false},
|
||||||
"capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false},
|
"capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false},
|
||||||
@@ -208,8 +204,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
@@ -317,7 +311,6 @@ func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
||||||
// 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 FlatSharedImageGalleryDestination struct {
|
type FlatSharedImageGalleryDestination struct {
|
||||||
SigDestinationSubscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"`
|
|
||||||
SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"`
|
SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"`
|
||||||
SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"`
|
SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"`
|
||||||
SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
||||||
@@ -337,7 +330,6 @@ func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() m
|
|||||||
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
||||||
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
||||||
s := map[string]hcldec.Spec{
|
s := map[string]hcldec.Spec{
|
||||||
"subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false},
|
|
||||||
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
||||||
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
||||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ 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.
|
||||||
@@ -912,55 +910,32 @@ func TestConfigShouldAcceptTags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c := Config{
|
var 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 diff := cmp.Diff(c.AzureTags, map[string]string{
|
if len(c.AzureTags) != 2 {
|
||||||
"tag01": "value01",
|
t.Fatalf("expected to find 2 tags, but got %d", len(c.AzureTags))
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03",
|
|
||||||
}); diff != "" {
|
|
||||||
t.Fatalf("unexpected azure tags: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigShouldAcceptTag(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"capture_name_prefix": "ignore",
|
|
||||||
"capture_container_name": "ignore",
|
|
||||||
"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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c := Config{
|
if _, ok := c.AzureTags["tag01"]; !ok {
|
||||||
AzureTag: hcl2template.NameValues{
|
t.Error("expected to find key=\"tag01\", but did not")
|
||||||
{Name: "tag03", Value: "value03"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
if _, ok := c.AzureTags["tag02"]; !ok {
|
||||||
if err != nil {
|
t.Error("expected to find key=\"tag02\", but did not")
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if diff := cmp.Diff(c.AzureTags, map[string]string{
|
value := c.AzureTags["tag01"]
|
||||||
"tag03": "value03",
|
if *value != "value01" {
|
||||||
}); diff != "" {
|
t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", *value)
|
||||||
t.Fatalf("unexpected azure tags: %s", diff)
|
}
|
||||||
|
|
||||||
|
value = c.AzureTags["tag02"]
|
||||||
|
if *value != "value02" {
|
||||||
|
t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", *value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2072,8 +2047,8 @@ func TestConfig_PrepareProvidedWinRMPassword(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getArmBuilderConfiguration() map[string]interface{} {
|
func getArmBuilderConfiguration() map[string]string {
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]string)
|
||||||
for _, v := range requiredConfigValues {
|
for _, v := range requiredConfigValues {
|
||||||
m[v] = "ignored00"
|
m[v] = "ignored00"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,13 +61,7 @@ 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)
|
||||||
tags, ok := state.Get(constants.ArmTags).(map[string]*string)
|
var tags = 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 {
|
||||||
|
|||||||
@@ -220,32 +220,3 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -61,11 +61,7 @@ func (s *StepDeleteAdditionalDisk) deleteManagedDisk(ctx context.Context, resour
|
|||||||
func (s *StepDeleteAdditionalDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepDeleteAdditionalDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
s.say("Deleting the temporary Additional disk ...")
|
s.say("Deleting the temporary Additional disk ...")
|
||||||
|
|
||||||
var dataDisks []string
|
var dataDisks = state.Get(constants.ArmAdditionalDiskVhds).([]string)
|
||||||
|
|
||||||
if disks := state.Get(constants.ArmAdditionalDiskVhds); disks != nil {
|
|
||||||
dataDisks = disks.([]string)
|
|
||||||
}
|
|
||||||
var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool)
|
var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool)
|
||||||
var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
@@ -17,7 +15,7 @@ import (
|
|||||||
type StepDeployTemplate struct {
|
type StepDeployTemplate struct {
|
||||||
client *AzureClient
|
client *AzureClient
|
||||||
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
||||||
delete func(ctx context.Context, deploymentName, resourceGroupName string) error
|
delete func(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error
|
||||||
disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error)
|
disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error)
|
||||||
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
|
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
|
||||||
say func(message string)
|
say func(message string)
|
||||||
@@ -38,7 +36,7 @@ func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, de
|
|||||||
}
|
}
|
||||||
|
|
||||||
step.deploy = step.deployTemplate
|
step.deploy = step.deployTemplate
|
||||||
step.delete = step.deleteDeploymentResources
|
step.delete = deleteResource
|
||||||
step.disk = step.getImageDetails
|
step.disk = step.getImageDetails
|
||||||
step.deleteDisk = step.deleteImage
|
step.deleteDisk = step.deleteImage
|
||||||
return step
|
return step
|
||||||
@@ -64,9 +62,8 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Only clean up if this is an existing resource group that has been verified to exist.
|
//Only clean up if this was an existing resource group and the resource group
|
||||||
// ArmIsResourceGroupCreated is set in step_create_resource_group to true, when Packer has verified that the resource group exists.
|
//is marked as created
|
||||||
// ArmIsExistingResourceGroup is set to true when build_resource_group is set in the Packer configuration.
|
|
||||||
existingResourceGroup := state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
existingResourceGroup := state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||||
resourceGroupCreated := state.Get(constants.ArmIsResourceGroupCreated).(bool)
|
resourceGroupCreated := state.Get(constants.ArmIsResourceGroupCreated).(bool)
|
||||||
if !existingResourceGroup || !resourceGroupCreated {
|
if !existingResourceGroup || !resourceGroupCreated {
|
||||||
@@ -78,12 +75,74 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
|||||||
|
|
||||||
deploymentName := s.name
|
deploymentName := s.name
|
||||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||||
err := s.deleteDeploymentResources(context.TODO(), deploymentName, resourceGroupName)
|
|
||||||
if err != nil {
|
// Get image disk details before deleting the image; otherwise we won't be able to
|
||||||
s.reportIfError(err, resourceGroupName)
|
// delete the disk as the image request will return a 404
|
||||||
|
computeName := state.Get(constants.ArmComputeName).(string)
|
||||||
|
imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName)
|
||||||
|
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "ResourceNotFound") {
|
||||||
|
ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
NewStepDeleteAdditionalDisks(s.client, ui).Run(context.TODO(), state)
|
ui.Say(" -> Deployment Resources within: " + deploymentName)
|
||||||
|
if deploymentName != "" {
|
||||||
|
maxResources := int32(50)
|
||||||
|
deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(context.TODO(), resourceGroupName, deploymentName, &maxResources)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+
|
||||||
|
"Name: %s\n"+
|
||||||
|
"Error: %s", resourceGroupName, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for deploymentOperations.NotDone() {
|
||||||
|
deploymentOperation := deploymentOperations.Value()
|
||||||
|
// Sometimes an empty operation is added to the list by Azure
|
||||||
|
if deploymentOperation.Properties.TargetResource == nil {
|
||||||
|
if err := deploymentOperations.Next(); err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error moving to to next deployment operation ...\n\n"+
|
||||||
|
"Name: %s\n"+
|
||||||
|
"Error: %s", resourceGroupName, err))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf(" -> %s : '%s'",
|
||||||
|
*deploymentOperation.Properties.TargetResource.ResourceType,
|
||||||
|
*deploymentOperation.Properties.TargetResource.ResourceName))
|
||||||
|
|
||||||
|
err = s.delete(context.TODO(), s.client,
|
||||||
|
*deploymentOperation.Properties.TargetResource.ResourceType,
|
||||||
|
*deploymentOperation.Properties.TargetResource.ResourceName,
|
||||||
|
resourceGroupName)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||||
|
"Name: %s\n"+
|
||||||
|
"Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = deploymentOperations.Next(); err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+
|
||||||
|
"Name: %s\n"+
|
||||||
|
"Error: %s", resourceGroupName, err))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The disk is not defined as an operation in the template so it has to be deleted separately
|
||||||
|
if imageType == "" && imageName == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf(" -> %s : '%s'", imageType, imageName))
|
||||||
|
err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||||
|
"Name: %s\n"+
|
||||||
|
"Error: %s", imageName, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
||||||
@@ -93,31 +152,27 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa
|
|||||||
}
|
}
|
||||||
|
|
||||||
f, err := s.client.DeploymentsClient.CreateOrUpdate(ctx, resourceGroupName, deploymentName, *deployment)
|
f, err := s.client.DeploymentsClient.CreateOrUpdate(ctx, resourceGroupName, deploymentName, *deployment)
|
||||||
|
if err == nil {
|
||||||
|
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.say(s.client.LastError.Error())
|
s.say(s.client.LastError.Error())
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
|
||||||
if err == nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep.StateBag) error {
|
func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep.StateBag) error {
|
||||||
deploymentName := s.name
|
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
var deploymentName = s.name
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName))
|
ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName))
|
||||||
f, err := s.client.DeploymentsClient.Delete(ctx, resourceGroupName, deploymentName)
|
f, err := s.client.DeploymentsClient.Delete(ctx, resourceGroupName, deploymentName)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) {
|
func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) {
|
||||||
@@ -131,12 +186,11 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupN
|
|||||||
if vm.StorageProfile.OsDisk.Vhd != nil {
|
if vm.StorageProfile.OsDisk.Vhd != nil {
|
||||||
imageType = "image"
|
imageType = "image"
|
||||||
imageName = *vm.StorageProfile.OsDisk.Vhd.URI
|
imageName = *vm.StorageProfile.OsDisk.Vhd.URI
|
||||||
return imageType, imageName, nil
|
} else {
|
||||||
|
imageType = "Microsoft.Compute/disks"
|
||||||
|
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
imageType = "Microsoft.Compute/disks"
|
|
||||||
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
|
||||||
|
|
||||||
return imageType, imageName, nil
|
return imageType, imageName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,58 +267,3 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string,
|
|||||||
|
|
||||||
return blob.Delete(nil)
|
return blob.Delete(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, deploymentName, resourceGroupName string) error {
|
|
||||||
var maxResources int32 = 50
|
|
||||||
deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(ctx, resourceGroupName, deploymentName, &maxResources)
|
|
||||||
if err != nil {
|
|
||||||
s.reportIfError(err, resourceGroupName)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for deploymentOperations.NotDone() {
|
|
||||||
deploymentOperation := deploymentOperations.Value()
|
|
||||||
// Sometimes an empty operation is added to the list by Azure
|
|
||||||
if deploymentOperation.Properties.TargetResource == nil {
|
|
||||||
_ = deploymentOperations.Next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceName := *deploymentOperation.Properties.TargetResource.ResourceName
|
|
||||||
resourceType := *deploymentOperation.Properties.TargetResource.ResourceType
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> %s : '%s'", resourceType, resourceName))
|
|
||||||
|
|
||||||
err = retry.Config{
|
|
||||||
Tries: 10,
|
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
|
||||||
err := deleteResource(ctx, s.client,
|
|
||||||
resourceType,
|
|
||||||
resourceName,
|
|
||||||
resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
s.reportIfError(err, resourceName)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
s.reportIfError(err, resourceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = deploymentOperations.Next(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) reportIfError(err error, resourceName string) {
|
|
||||||
if err != nil {
|
|
||||||
s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
|
||||||
"Name: %s\n"+
|
|
||||||
"Error: %s", resourceName, err.Error()))
|
|
||||||
s.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
"tenantId": "[parameters('tenantId')]"
|
"tenantId": "[parameters('tenantId')]"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"enableSoftDelete": "true",
|
|
||||||
"enabledForDeployment": "true",
|
"enabledForDeployment": "true",
|
||||||
"enabledForTemplateDeployment": "true",
|
"enabledForTemplateDeployment": "true",
|
||||||
"sku": {
|
"sku": {
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||||||
b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc()
|
b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc()
|
||||||
md := &mapstructure.Metadata{}
|
md := &mapstructure.Metadata{}
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
PluginType: BuilderID,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &b.config.ctx,
|
InterpolateContext: &b.config.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ type FlatConfig struct {
|
|||||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
||||||
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
||||||
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
||||||
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
|
|
||||||
FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch" hcl:"from_scratch"`
|
FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch" hcl:"from_scratch"`
|
||||||
Source *string `mapstructure:"source" required:"true" cty:"source" hcl:"source"`
|
Source *string `mapstructure:"source" required:"true" cty:"source" hcl:"source"`
|
||||||
CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper" hcl:"command_wrapper"`
|
CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper" hcl:"command_wrapper"`
|
||||||
@@ -77,7 +76,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||||
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
||||||
"use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false},
|
|
||||||
"from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false},
|
"from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false},
|
||||||
"source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false},
|
"source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false},
|
||||||
"command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false},
|
"command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false},
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ type SharedImageGalleryDestination struct {
|
|||||||
ImageName string `mapstructure:"image_name" required:"true"`
|
ImageName string `mapstructure:"image_name" required:"true"`
|
||||||
ImageVersion string `mapstructure:"image_version" required:"true"`
|
ImageVersion string `mapstructure:"image_version" required:"true"`
|
||||||
|
|
||||||
TargetRegions []TargetRegion `mapstructure:"target_regions"`
|
TargetRegions []TargetRegion `mapstructure:"target_regions"`
|
||||||
ExcludeFromLatest bool `mapstructure:"exclude_from_latest"`
|
ExcludeFromLatest bool `mapstructure:"exlude_from_latest"`
|
||||||
ExcludeFromLatestTypo bool `mapstructure:"exlude_from_latest" undocumented:"true"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetRegion describes a region where the shared image should be replicated
|
// TargetRegion describes a region where the shared image should be replicated
|
||||||
@@ -62,10 +61,5 @@ func (sigd *SharedImageGalleryDestination) Validate(prefix string) (errs []error
|
|||||||
warns = append(warns,
|
warns = append(warns,
|
||||||
fmt.Sprintf("%s.target_regions is empty; image will only be available in the region of the gallery", prefix))
|
fmt.Sprintf("%s.target_regions is empty; image will only be available in the region of the gallery", prefix))
|
||||||
}
|
}
|
||||||
if sigd.ExcludeFromLatestTypo == true && sigd.ExcludeFromLatest == false {
|
|
||||||
warns = append(warns,
|
|
||||||
fmt.Sprintf("%s.exlude_from_latest is being deprecated, please use exclude_from_latest", prefix))
|
|
||||||
sigd.ExcludeFromLatest = sigd.ExcludeFromLatestTypo
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ import (
|
|||||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
||||||
// 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 FlatSharedImageGalleryDestination struct {
|
type FlatSharedImageGalleryDestination struct {
|
||||||
ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"`
|
ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"`
|
||||||
GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"`
|
GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"`
|
||||||
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
|
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
|
||||||
ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"`
|
ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"`
|
||||||
TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"`
|
TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"`
|
||||||
ExcludeFromLatest *bool `mapstructure:"exclude_from_latest" cty:"exclude_from_latest" hcl:"exclude_from_latest"`
|
ExcludeFromLatest *bool `mapstructure:"exlude_from_latest" cty:"exlude_from_latest" hcl:"exlude_from_latest"`
|
||||||
ExcludeFromLatestTypo *bool `mapstructure:"exlude_from_latest" undocumented:"true" cty:"exlude_from_latest" hcl:"exlude_from_latest"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatSharedImageGalleryDestination.
|
// FlatMapstructure returns a new FlatSharedImageGalleryDestination.
|
||||||
@@ -30,13 +29,12 @@ func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() m
|
|||||||
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
||||||
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
||||||
s := map[string]hcldec.Spec{
|
s := map[string]hcldec.Spec{
|
||||||
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
||||||
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
||||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
||||||
"target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())},
|
"target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())},
|
||||||
"exclude_from_latest": &hcldec.AttrSpec{Name: "exclude_from_latest", Type: cty.Bool, Required: false},
|
"exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false},
|
||||||
"exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false},
|
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,12 @@ func TestSharedImageGalleryDestination_ResourceID(t *testing.T) {
|
|||||||
|
|
||||||
func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ResourceGroup string
|
ResourceGroup string
|
||||||
GalleryName string
|
GalleryName string
|
||||||
ImageName string
|
ImageName string
|
||||||
ImageVersion string
|
ImageVersion string
|
||||||
TargetRegions []TargetRegion
|
TargetRegions []TargetRegion
|
||||||
ExcludeFromLatest bool
|
ExcludeFromLatest bool
|
||||||
ExcludeFromLatestTypo bool
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -67,29 +66,6 @@ func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
|
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "warn if using exlude_from_latest",
|
|
||||||
fields: fields{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "0.1.2",
|
|
||||||
TargetRegions: []TargetRegion{
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region1",
|
|
||||||
ReplicaCount: 5,
|
|
||||||
StorageAccountType: "Standard_ZRS",
|
|
||||||
},
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region2",
|
|
||||||
ReplicaCount: 3,
|
|
||||||
StorageAccountType: "Standard_LRS",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExcludeFromLatestTypo: true,
|
|
||||||
},
|
|
||||||
wantWarns: []string{"sigdest.exlude_from_latest is being deprecated, please use exclude_from_latest"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "version format",
|
name: "version format",
|
||||||
wantErrs: []string{
|
wantErrs: []string{
|
||||||
@@ -129,13 +105,12 @@ func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
sigd := &SharedImageGalleryDestination{
|
sigd := &SharedImageGalleryDestination{
|
||||||
ResourceGroup: tt.fields.ResourceGroup,
|
ResourceGroup: tt.fields.ResourceGroup,
|
||||||
GalleryName: tt.fields.GalleryName,
|
GalleryName: tt.fields.GalleryName,
|
||||||
ImageName: tt.fields.ImageName,
|
ImageName: tt.fields.ImageName,
|
||||||
ImageVersion: tt.fields.ImageVersion,
|
ImageVersion: tt.fields.ImageVersion,
|
||||||
TargetRegions: tt.fields.TargetRegions,
|
TargetRegions: tt.fields.TargetRegions,
|
||||||
ExcludeFromLatest: tt.fields.ExcludeFromLatest,
|
ExcludeFromLatest: tt.fields.ExcludeFromLatest,
|
||||||
ExcludeFromLatestTypo: tt.fields.ExcludeFromLatestTypo,
|
|
||||||
}
|
}
|
||||||
gotErrs, gotWarns := sigd.Validate("sigdest")
|
gotErrs, gotWarns := sigd.Validate("sigdest")
|
||||||
|
|
||||||
|
|||||||
@@ -54,14 +54,6 @@ type Config struct {
|
|||||||
SubscriptionID string `mapstructure:"subscription_id"`
|
SubscriptionID string `mapstructure:"subscription_id"`
|
||||||
|
|
||||||
authType string
|
authType string
|
||||||
|
|
||||||
// Flag to use Azure CLI authentication. Defaults to false.
|
|
||||||
// CLI auth will use the information from an active `az login` session to connect to Azure and set the subscription id and tenant id associated to the signed in account.
|
|
||||||
// If enabled, it will use the authentication provided by the `az` CLI.
|
|
||||||
// Azure CLI authentication will use the credential marked as `isDefault` and can be verified using `az account show`.
|
|
||||||
// Works with normal authentication (`az login`) and service principals (`az login --service-principal --username APP_ID --password PASSWORD --tenant TENANT_ID`).
|
|
||||||
// Ignores all other configurations if enabled.
|
|
||||||
UseAzureCLIAuth bool `mapstructure:"use_azure_cli_auth" required:"false"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -70,7 +62,6 @@ const (
|
|||||||
authTypeClientSecret = "ClientSecret"
|
authTypeClientSecret = "ClientSecret"
|
||||||
authTypeClientCert = "ClientCertificate"
|
authTypeClientCert = "ClientCertificate"
|
||||||
authTypeClientBearerJWT = "ClientBearerJWT"
|
authTypeClientBearerJWT = "ClientBearerJWT"
|
||||||
authTypeAzureCLI = "AzureCLI"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultCloudEnvironmentName = "Public"
|
const DefaultCloudEnvironmentName = "Public"
|
||||||
@@ -133,10 +124,6 @@ func (c Config) Validate(errs *packer.MultiError) {
|
|||||||
// readable by the ObjectID of the App. There may be another way to handle
|
// readable by the ObjectID of the App. There may be another way to handle
|
||||||
// this case, but I am not currently aware of it - send feedback.
|
// this case, but I am not currently aware of it - send feedback.
|
||||||
|
|
||||||
if c.UseCLI() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.UseMSI() {
|
if c.UseMSI() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -206,10 +193,6 @@ func (c Config) useDeviceLogin() bool {
|
|||||||
c.ClientCertPath == ""
|
c.ClientCertPath == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) UseCLI() bool {
|
|
||||||
return c.UseAzureCLIAuth == true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) UseMSI() bool {
|
func (c Config) UseMSI() bool {
|
||||||
return c.SubscriptionID == "" &&
|
return c.SubscriptionID == "" &&
|
||||||
c.ClientID == "" &&
|
c.ClientID == "" &&
|
||||||
@@ -247,9 +230,6 @@ func (c Config) GetServicePrincipalToken(
|
|||||||
case authTypeDeviceLogin:
|
case authTypeDeviceLogin:
|
||||||
say("Getting tokens using device flow")
|
say("Getting tokens using device flow")
|
||||||
auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID)
|
auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID)
|
||||||
case authTypeAzureCLI:
|
|
||||||
say("Getting tokens using Azure CLI")
|
|
||||||
auth = NewCliOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID)
|
|
||||||
case authTypeMSI:
|
case authTypeMSI:
|
||||||
say("Getting tokens using Managed Identity for Azure")
|
say("Getting tokens using Managed Identity for Azure")
|
||||||
auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment)
|
auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment)
|
||||||
@@ -288,8 +268,6 @@ func (c *Config) FillParameters() error {
|
|||||||
if c.authType == "" {
|
if c.authType == "" {
|
||||||
if c.useDeviceLogin() {
|
if c.useDeviceLogin() {
|
||||||
c.authType = authTypeDeviceLogin
|
c.authType = authTypeDeviceLogin
|
||||||
} else if c.UseCLI() {
|
|
||||||
c.authType = authTypeAzureCLI
|
|
||||||
} else if c.UseMSI() {
|
} else if c.UseMSI() {
|
||||||
c.authType = authTypeMSI
|
c.authType = authTypeMSI
|
||||||
} else if c.ClientSecret != "" {
|
} else if c.ClientSecret != "" {
|
||||||
@@ -317,16 +295,6 @@ func (c *Config) FillParameters() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.authType == authTypeAzureCLI {
|
|
||||||
tenantID, subscriptionID, err := getIDsFromAzureCLI()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error fetching tenantID and subscriptionID from Azure CLI (are you logged on using `az login`?): %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.TenantID = tenantID
|
|
||||||
c.SubscriptionID = subscriptionID
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.TenantID == "" {
|
if c.TenantID == "" {
|
||||||
tenantID, err := findTenantID(*c.cloudEnvironment, c.SubscriptionID)
|
tenantID, err := findTenantID(*c.cloudEnvironment, c.SubscriptionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -29,13 +29,6 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) {
|
|||||||
config: Config{},
|
config: Config{},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "use_azure_cli_auth will trigger Azure CLI auth",
|
|
||||||
config: Config{
|
|
||||||
UseAzureCLIAuth: true,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "subscription_id is set will trigger device flow",
|
name: "subscription_id is set will trigger device flow",
|
||||||
config: Config{
|
config: Config{
|
||||||
@@ -165,26 +158,6 @@ func Test_ClientConfig_DeviceLogin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_ClientConfig_AzureCli(t *testing.T) {
|
|
||||||
// Azure CLI tests skipped unless env 'AZURE_CLI_AUTH' is set, and an active `az login` session has been established
|
|
||||||
getEnvOrSkip(t, "AZURE_CLI_AUTH")
|
|
||||||
|
|
||||||
cfg := Config{
|
|
||||||
UseAzureCLIAuth: true,
|
|
||||||
cloudEnvironment: getCloud(),
|
|
||||||
}
|
|
||||||
assertValid(t, cfg)
|
|
||||||
|
|
||||||
err := cfg.FillParameters()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected nil err, but got: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.authType != authTypeAzureCLI {
|
|
||||||
t.Fatalf("Expected authType to be %q, but got: %q", authTypeAzureCLI, cfg.authType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ClientConfig_ClientPassword(t *testing.T) {
|
func Test_ClientConfig_ClientPassword(t *testing.T) {
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
|
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/go-autorest/autorest/adal"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// for managed identity auth
|
|
||||||
type cliOAuthTokenProvider struct {
|
|
||||||
env azure.Environment
|
|
||||||
say func(string)
|
|
||||||
tenantID string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCliOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider {
|
|
||||||
return &cliOAuthTokenProvider{
|
|
||||||
env: env,
|
|
||||||
say: say,
|
|
||||||
tenantID: tenantID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tp *cliOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
|
|
||||||
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tp *cliOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
|
|
||||||
token, err := cli.GetTokenFromCLI(resource)
|
|
||||||
if err != nil {
|
|
||||||
tp.say(fmt.Sprintf("unable to get token from azure cli: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuthConfig, err := adal.NewOAuthConfig(resource, tp.tenantID)
|
|
||||||
if err != nil {
|
|
||||||
tp.say(fmt.Sprintf("unable to generate OAuth Config: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
adalToken, err := token.ToADALToken()
|
|
||||||
if err != nil {
|
|
||||||
tp.say(fmt.Sprintf("unable to get ADAL Token from azure cli token: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(*oAuthConfig, clientIDs[tp.env.Name], resource, adalToken)
|
|
||||||
if err != nil {
|
|
||||||
tp.say(fmt.Sprintf("unable to get service principal token from adal token: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom refresh function to make it possible to use Azure CLI to refresh tokens.
|
|
||||||
// Inspired by HashiCorps go-azure-helpers: https://github.com/hashicorp/go-azure-helpers/blob/373622ce2effb0cf299051ea019cb657f357a4d8/authentication/auth_method_azure_cli_token.go#L96-L109
|
|
||||||
var customRefreshFunc adal.TokenRefresh = func(ctx context.Context, resource string) (*adal.Token, error) {
|
|
||||||
token, err := cli.GetTokenFromCLI(resource)
|
|
||||||
if err != nil {
|
|
||||||
tp.say(fmt.Sprintf("token refresh - unable to get token from azure cli: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
adalToken, err := token.ToADALToken()
|
|
||||||
if err != nil {
|
|
||||||
tp.say(fmt.Sprintf("token refresh - unable to get ADAL Token from azure cli token: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &adalToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
spt.SetCustomRefreshFunc(customRefreshFunc)
|
|
||||||
|
|
||||||
return spt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIDsFromAzureCLI returns the TenantID and SubscriptionID from an active Azure CLI login session
|
|
||||||
func getIDsFromAzureCLI() (string, string, error) {
|
|
||||||
profilePath, err := cli.ProfilePath()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
profile, err := cli.LoadProfile(profilePath)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range profile.Subscriptions {
|
|
||||||
if p.IsDefault {
|
|
||||||
return p.TenantID, p.ID, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", "", errors.New("Unable to find default subscription")
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -25,16 +25,16 @@ type Parameters struct {
|
|||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
// Template > Resource
|
// Template > Resource
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
ApiVersion *string `json:"apiVersion"`
|
ApiVersion *string `json:"apiVersion"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Type *string `json:"type"`
|
Type *string `json:"type"`
|
||||||
Location *string `json:"location,omitempty"`
|
Location *string `json:"location,omitempty"`
|
||||||
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Plan struct {
|
type Plan struct {
|
||||||
@@ -85,7 +85,6 @@ type Properties struct {
|
|||||||
DNSSettings *network.PublicIPAddressDNSSettings `json:"dnsSettings,omitempty"`
|
DNSSettings *network.PublicIPAddressDNSSettings `json:"dnsSettings,omitempty"`
|
||||||
EnabledForDeployment *string `json:"enabledForDeployment,omitempty"`
|
EnabledForDeployment *string `json:"enabledForDeployment,omitempty"`
|
||||||
EnabledForTemplateDeployment *string `json:"enabledForTemplateDeployment,omitempty"`
|
EnabledForTemplateDeployment *string `json:"enabledForTemplateDeployment,omitempty"`
|
||||||
EnableSoftDelete *string `json:"enableSoftDelete,omitempty"`
|
|
||||||
HardwareProfile *compute.HardwareProfile `json:"hardwareProfile,omitempty"`
|
HardwareProfile *compute.HardwareProfile `json:"hardwareProfile,omitempty"`
|
||||||
IPConfigurations *[]network.IPConfiguration `json:"ipConfigurations,omitempty"`
|
IPConfigurations *[]network.IPConfiguration `json:"ipConfigurations,omitempty"`
|
||||||
NetworkProfile *compute.NetworkProfile `json:"networkProfile,omitempty"`
|
NetworkProfile *compute.NetworkProfile `json:"networkProfile,omitempty"`
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -554,7 +554,6 @@ const KeyVault = `{
|
|||||||
"properties": {
|
"properties": {
|
||||||
"enabledForDeployment": "true",
|
"enabledForDeployment": "true",
|
||||||
"enabledForTemplateDeployment": "true",
|
"enabledForTemplateDeployment": "true",
|
||||||
"enableSoftDelete": "true",
|
|
||||||
"tenantId": "[parameters('tenantId')]",
|
"tenantId": "[parameters('tenantId')]",
|
||||||
"accessPolicies": [
|
"accessPolicies": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -389,7 +389,6 @@ func newConfig(raws ...interface{}) (*Config, []string, error) {
|
|||||||
var c Config
|
var c Config
|
||||||
c.ctx.Funcs = TemplateFuncs
|
c.ctx.Funcs = TemplateFuncs
|
||||||
err := config.Decode(&c, &config.DecodeOpts{
|
err := config.Decode(&c, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &c.ctx,
|
InterpolateContext: &c.ctx,
|
||||||
}, raws...)
|
}, raws...)
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ type FlatConfig struct {
|
|||||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
||||||
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
||||||
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
||||||
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
|
|
||||||
CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"`
|
CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"`
|
||||||
CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"`
|
CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"`
|
||||||
SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" cty:"shared_image_gallery" hcl:"shared_image_gallery"`
|
SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" cty:"shared_image_gallery" hcl:"shared_image_gallery"`
|
||||||
@@ -94,8 +93,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -164,7 +161,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||||
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
||||||
"use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false},
|
|
||||||
"capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false},
|
"capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false},
|
||||||
"capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false},
|
"capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false},
|
||||||
"shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())},
|
"shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())},
|
||||||
@@ -207,8 +203,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env `ARM_CLIENT_ID`}}",
|
|
||||||
"client_secret": "{{env `ARM_CLIENT_SECRET`}}",
|
|
||||||
"subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
|
|
||||||
"resource_group": "{{env `ARM_IMAGE_RESOURCEGROUP_ID`}}",
|
|
||||||
"gallery_name": "{{env `ARM_GALLERY_NAME`}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "azure-chroot",
|
|
||||||
|
|
||||||
"client_id": "{{user `client_id`}}",
|
|
||||||
"client_secret": "{{user `client_secret`}}",
|
|
||||||
"subscription_id": "{{user `subscription_id`}}",
|
|
||||||
|
|
||||||
"source": "Canonical:UbuntuServer:20.04-LTS:latest",
|
|
||||||
|
|
||||||
"shared_image_destination": {
|
|
||||||
"resource_group": "{{user `resource_group`}}",
|
|
||||||
"gallery_name": "{{user `gallery_name`}}",
|
|
||||||
"image_name": "MyUbuntuOSImage",
|
|
||||||
"image_version": "1.0.0",
|
|
||||||
"exclude_from_latest": false,
|
|
||||||
"target_regions": [
|
|
||||||
{
|
|
||||||
"name": "eastus",
|
|
||||||
"replicas": "1",
|
|
||||||
"storage_account_type": "standard_zrs"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
"provisioners": [{
|
|
||||||
"inline": [
|
|
||||||
"apt update",
|
|
||||||
"apt upgrade -y"
|
|
||||||
],
|
|
||||||
"inline_shebang": "/bin/sh -x",
|
|
||||||
"type": "shell"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,6 @@ type FlatConfig struct {
|
|||||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||||
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"`
|
|
||||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||||
@@ -29,8 +28,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -135,7 +132,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||||
"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},
|
|
||||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||||
@@ -144,8 +140,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -110,8 +108,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
|
|||||||
@@ -30,22 +30,19 @@ func (a *ImportArtifact) Id() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ImportArtifact) String() string {
|
func (a *ImportArtifact) String() string {
|
||||||
var tags []string
|
tags := a.StateData["docker_tags"]
|
||||||
switch t := a.StateData["docker_tags"].(type) {
|
if tags == nil {
|
||||||
case []string:
|
return fmt.Sprintf("Imported Docker image: %s", a.Id())
|
||||||
tags = t
|
}
|
||||||
case []interface{}:
|
cast := tags.([]interface{})
|
||||||
for _, name := range t {
|
names := []string{}
|
||||||
if n, ok := name.(string); ok {
|
for _, name := range cast {
|
||||||
tags = append(tags, n)
|
if n, ok := name.(string); ok {
|
||||||
}
|
names = append(names, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(tags) > 0 {
|
return fmt.Sprintf("Imported Docker image: %s with tags %s",
|
||||||
return fmt.Sprintf("Imported Docker image: %s with tags %s",
|
a.Id(), strings.Join(names, " "))
|
||||||
a.Id(), strings.Join(tags, " "))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Imported Docker image: %s", a.Id())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ImportArtifact) State(name string) interface{} {
|
func (a *ImportArtifact) State(name string) interface{} {
|
||||||
|
|||||||
+11
-19
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -30,9 +29,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||||||
return nil, warnings, errs
|
return nil, warnings, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{
|
return nil, warnings, nil
|
||||||
"ImageSha256",
|
|
||||||
}, warnings, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -47,16 +44,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
}
|
}
|
||||||
log.Printf("[DEBUG] Docker version: %s", version.String())
|
log.Printf("[DEBUG] Docker version: %s", version.String())
|
||||||
|
|
||||||
// Setup the state bag and initial state for the steps
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("config", &b.config)
|
|
||||||
state.Put("hook", hook)
|
|
||||||
state.Put("ui", ui)
|
|
||||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
|
||||||
|
|
||||||
// Setup the driver that will talk to Docker
|
|
||||||
state.Put("driver", driver)
|
|
||||||
|
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
&StepTempDir{},
|
&StepTempDir{},
|
||||||
&StepPull{},
|
&StepPull{},
|
||||||
@@ -80,11 +67,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
log.Print("[DEBUG] Container will be discarded")
|
log.Print("[DEBUG] Container will be discarded")
|
||||||
} else if b.config.Commit {
|
} else if b.config.Commit {
|
||||||
log.Print("[DEBUG] Container will be committed")
|
log.Print("[DEBUG] Container will be committed")
|
||||||
steps = append(steps,
|
steps = append(steps, new(StepCommit))
|
||||||
new(StepCommit),
|
|
||||||
&StepSetGeneratedData{ // Adds ImageSha256 variable available after StepCommit
|
|
||||||
GeneratedData: generatedData,
|
|
||||||
})
|
|
||||||
} else if b.config.ExportPath != "" {
|
} else if b.config.ExportPath != "" {
|
||||||
log.Printf("[DEBUG] Container will be exported to %s", b.config.ExportPath)
|
log.Printf("[DEBUG] Container will be exported to %s", b.config.ExportPath)
|
||||||
steps = append(steps, new(StepExport))
|
steps = append(steps, new(StepExport))
|
||||||
@@ -92,6 +75,15 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
return nil, errArtifactNotUsed
|
return nil, errArtifactNotUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup the state bag and initial state for the steps
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("config", &b.config)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
// Setup the driver that will talk to Docker
|
||||||
|
state.Put("driver", driver)
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner.Run(ctx, state)
|
b.runner.Run(ctx, state)
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||||||
var md mapstructure.Metadata
|
var md mapstructure.Metadata
|
||||||
err := config.Decode(c, &config.DecodeOpts{
|
err := config.Decode(c, &config.DecodeOpts{
|
||||||
Metadata: &md,
|
Metadata: &md,
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &c.ctx,
|
InterpolateContext: &c.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -123,8 +121,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ type Driver interface {
|
|||||||
// for external access.
|
// for external access.
|
||||||
IPAddress(id string) (string, error)
|
IPAddress(id string) (string, error)
|
||||||
|
|
||||||
// Sha256 returns the sha256 id of the image
|
|
||||||
Sha256(id string) (string, error)
|
|
||||||
|
|
||||||
// Login. This will lock the driver from performing another Login
|
// Login. This will lock the driver from performing another Login
|
||||||
// until Logout is called. Therefore, any users MUST call Logout.
|
// until Logout is called. Therefore, any users MUST call Logout.
|
||||||
Login(repo, username, password string) error
|
Login(repo, username, password string) error
|
||||||
|
|||||||
@@ -159,23 +159,6 @@ func (d *DockerDriver) IPAddress(id string) (string, error) {
|
|||||||
return strings.TrimSpace(stdout.String()), nil
|
return strings.TrimSpace(stdout.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DockerDriver) Sha256(id string) (string, error) {
|
|
||||||
var stderr, stdout bytes.Buffer
|
|
||||||
cmd := exec.Command(
|
|
||||||
"docker",
|
|
||||||
"inspect",
|
|
||||||
"--format",
|
|
||||||
"{{ .Id }}",
|
|
||||||
id)
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
cmd.Stderr = &stderr
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return "", fmt.Errorf("Error: %s\n\nStderr: %s", err, stderr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(stdout.String()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DockerDriver) Login(repo, user, pass string) error {
|
func (d *DockerDriver) Login(repo, user, pass string) error {
|
||||||
d.l.Lock()
|
d.l.Lock()
|
||||||
|
|
||||||
|
|||||||
@@ -28,11 +28,6 @@ type MockDriver struct {
|
|||||||
IPAddressResult string
|
IPAddressResult string
|
||||||
IPAddressErr error
|
IPAddressErr error
|
||||||
|
|
||||||
Sha256Called bool
|
|
||||||
Sha256Id string
|
|
||||||
Sha256Result string
|
|
||||||
Sha256Err error
|
|
||||||
|
|
||||||
KillCalled bool
|
KillCalled bool
|
||||||
KillID string
|
KillID string
|
||||||
KillError error
|
KillError error
|
||||||
@@ -123,12 +118,6 @@ func (d *MockDriver) IPAddress(id string) (string, error) {
|
|||||||
return d.IPAddressResult, d.IPAddressErr
|
return d.IPAddressResult, d.IPAddressErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MockDriver) Sha256(id string) (string, error) {
|
|
||||||
d.Sha256Called = true
|
|
||||||
d.Sha256Id = id
|
|
||||||
return d.Sha256Result, d.Sha256Err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *MockDriver) Login(r, u, p string) error {
|
func (d *MockDriver) Login(r, u, p string) error {
|
||||||
d.LoginCalled = true
|
d.LoginCalled = true
|
||||||
d.LoginRepo = r
|
d.LoginRepo = r
|
||||||
|
|||||||
+93
-5
@@ -1,26 +1,114 @@
|
|||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/common/shell-local/localexec"
|
"github.com/hashicorp/packer/common/iochan"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runAndStream(cmd *exec.Cmd, ui packer.Ui) error {
|
func runAndStream(cmd *exec.Cmd, ui packer.Ui) error {
|
||||||
|
stdout_r, stdout_w := io.Pipe()
|
||||||
|
stderr_r, stderr_w := io.Pipe()
|
||||||
|
defer stdout_w.Close()
|
||||||
|
defer stderr_w.Close()
|
||||||
|
|
||||||
args := make([]string, len(cmd.Args)-1)
|
args := make([]string, len(cmd.Args)-1)
|
||||||
copy(args, cmd.Args[1:])
|
copy(args, cmd.Args[1:])
|
||||||
|
|
||||||
// Scrub password from the log output.
|
// Scrub password from the log output.
|
||||||
capturedPassword := ""
|
|
||||||
for i, v := range args {
|
for i, v := range args {
|
||||||
if v == "-p" || v == "--password" {
|
if v == "-p" || v == "--password" {
|
||||||
capturedPassword = args[i+1]
|
args[i+1] = "<Filtered>"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run local command and stream output to UI.
|
log.Printf("Executing: %s %v", cmd.Path, args)
|
||||||
return localexec.RunAndStream(cmd, ui, []string{capturedPassword})
|
cmd.Stdout = stdout_w
|
||||||
|
cmd.Stderr = stderr_w
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the channels we'll use for data
|
||||||
|
exitCh := make(chan int, 1)
|
||||||
|
stdoutCh := iochan.LineReader(stdout_r)
|
||||||
|
stderrCh := iochan.LineReader(stderr_r)
|
||||||
|
|
||||||
|
// Start the goroutine to watch for the exit
|
||||||
|
go func() {
|
||||||
|
defer stdout_w.Close()
|
||||||
|
defer stderr_w.Close()
|
||||||
|
exitStatus := 0
|
||||||
|
|
||||||
|
err := cmd.Wait()
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
exitStatus = 1
|
||||||
|
|
||||||
|
// There is no process-independent way to get the REAL
|
||||||
|
// exit status so we just try to go deeper.
|
||||||
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
exitStatus = status.ExitStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exitCh <- exitStatus
|
||||||
|
}()
|
||||||
|
|
||||||
|
// This waitgroup waits for the streaming to end
|
||||||
|
var streamWg sync.WaitGroup
|
||||||
|
streamWg.Add(2)
|
||||||
|
|
||||||
|
streamFunc := func(ch <-chan string) {
|
||||||
|
defer streamWg.Done()
|
||||||
|
|
||||||
|
for data := range ch {
|
||||||
|
data = cleanOutputLine(data)
|
||||||
|
if data != "" {
|
||||||
|
ui.Message(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream stderr/stdout
|
||||||
|
go streamFunc(stderrCh)
|
||||||
|
go streamFunc(stdoutCh)
|
||||||
|
|
||||||
|
// Wait for the process to end and then wait for the streaming to end
|
||||||
|
exitStatus := <-exitCh
|
||||||
|
streamWg.Wait()
|
||||||
|
|
||||||
|
if exitStatus != 0 {
|
||||||
|
return fmt.Errorf("Bad exit status: %d", exitStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanOutputLine cleans up a line so that '\r' don't muck up the
|
||||||
|
// UI output when we're reading from a remote command.
|
||||||
|
func cleanOutputLine(line string) string {
|
||||||
|
// Build a regular expression that will get rid of shell codes
|
||||||
|
re := regexp.MustCompile("(?i)\x1b\\[([0-9]{1,2}(;[0-9]{1,2})?)?[a|b|m|k]")
|
||||||
|
line = re.ReplaceAllString(line, "")
|
||||||
|
|
||||||
|
// Trim surrounding whitespace
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// Trim up to the first carriage return, since that text would be
|
||||||
|
// lost anyways.
|
||||||
|
idx := strings.LastIndex(line, "\r")
|
||||||
|
if idx > -1 {
|
||||||
|
line = line[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return line
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package localexec
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepSetGeneratedData struct {
|
|
||||||
GeneratedData *packerbuilderdata.GeneratedData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSetGeneratedData) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
driver := state.Get("driver").(Driver)
|
|
||||||
|
|
||||||
sha256 := "ERR_IMAGE_SHA256_NOT_FOUND"
|
|
||||||
if imageId, ok := state.GetOk("image_id"); ok {
|
|
||||||
s256, err := driver.Sha256(imageId.(string))
|
|
||||||
if err == nil {
|
|
||||||
sha256 = s256
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.GeneratedData.Put("ImageSha256", sha256)
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSetGeneratedData) Cleanup(_ multistep.StateBag) {
|
|
||||||
// No cleanup...
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSetGeneratedData_Run(t *testing.T) {
|
|
||||||
state := testState(t)
|
|
||||||
step := new(StepSetGeneratedData)
|
|
||||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
|
||||||
driver := state.Get("driver").(*MockDriver)
|
|
||||||
driver.Sha256Result = "80B3BB1B1696E73A9B19DEEF92F664F8979F948DF348088B61F9A3477655AF64"
|
|
||||||
state.Put("image_id", "12345")
|
|
||||||
|
|
||||||
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Should not halt")
|
|
||||||
}
|
|
||||||
if !driver.Sha256Called {
|
|
||||||
t.Fatalf("driver.SHA256 should be called")
|
|
||||||
}
|
|
||||||
if driver.Sha256Id != "12345" {
|
|
||||||
t.Fatalf("driver.SHA256 got wrong image it: %s", driver.Sha256Id)
|
|
||||||
}
|
|
||||||
genData := state.Get("generated_data").(map[string]interface{})
|
|
||||||
imgSha256 := genData["ImageSha256"].(string)
|
|
||||||
if imgSha256 != driver.Sha256Result {
|
|
||||||
t.Fatalf("Expected ImageSha256 to be %s but was %s", driver.Sha256Result, imgSha256)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image ID not implement
|
|
||||||
state = testState(t)
|
|
||||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
|
||||||
driver = state.Get("driver").(*MockDriver)
|
|
||||||
notImplementedMsg := "ERR_IMAGE_SHA256_NOT_FOUND"
|
|
||||||
|
|
||||||
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Should not halt")
|
|
||||||
}
|
|
||||||
if driver.Sha256Called {
|
|
||||||
t.Fatalf("driver.SHA256 should not be called")
|
|
||||||
}
|
|
||||||
genData = state.Get("generated_data").(map[string]interface{})
|
|
||||||
imgSha256 = genData["ImageSha256"].(string)
|
|
||||||
if imgSha256 != notImplementedMsg {
|
|
||||||
t.Fatalf("Expected ImageSha256 to be %s but was %s", notImplementedMsg, imgSha256)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
package packerbuilderdata
|
package builder
|
||||||
|
|
||||||
import "github.com/hashicorp/packer/helper/multistep"
|
import "github.com/hashicorp/packer/helper/multistep"
|
||||||
|
|
||||||
// This is used in the BasicPlaceholderData() func in the packer/provisioner.go
|
|
||||||
// To force users to access generated data via the "generated" func.
|
|
||||||
const PlaceholderMsg = "To set this dynamically in the Packer template, " +
|
|
||||||
"you must use the `build` function"
|
|
||||||
|
|
||||||
// GeneratedData manages variables exported by a builder after
|
// GeneratedData manages variables exported by a builder after
|
||||||
// it started. It uses the builder's multistep.StateBag internally, make sure it
|
// it started. It uses the builder's multistep.StateBag internally, make sure it
|
||||||
// is not nil before calling any functions.
|
// is not nil before calling any functions.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package packerbuilderdata
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -9,39 +9,26 @@ import (
|
|||||||
"golang.org/x/oauth2/jwt"
|
"golang.org/x/oauth2/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceAccount struct {
|
func ProcessAccountFile(text string) (*jwt.Config, error) {
|
||||||
jsonKey []byte
|
|
||||||
jwt *jwt.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessAccountFile will return a ServiceAccount for the JSON account file stored in text.
|
|
||||||
// Otherwise it will return an error if text does not look or reference a valid account file.
|
|
||||||
func ProcessAccountFile(text string) (*ServiceAccount, error) {
|
|
||||||
// Assume text is a JSON string
|
// Assume text is a JSON string
|
||||||
if conf, err := google.JWTConfigFromJSON([]byte(text), DriverScopes...); err == nil {
|
conf, err := google.JWTConfigFromJSON([]byte(text), DriverScopes...)
|
||||||
return &ServiceAccount{
|
|
||||||
jsonKey: []byte(text),
|
|
||||||
jwt: conf,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If text was not JSON, assume it is a file path instead
|
|
||||||
if _, err := os.Stat(text); os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("account_file path does not exist: %s", text)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(text)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error reading account_file from path '%s': %s", text, err)
|
// If text was not JSON, assume it is a file path instead
|
||||||
|
if _, err := os.Stat(text); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"account_file path does not exist: %s",
|
||||||
|
text)
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(text)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Error reading account_file from path '%s': %s",
|
||||||
|
text, err)
|
||||||
|
}
|
||||||
|
conf, err = google.JWTConfigFromJSON(data, DriverScopes...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing account_file: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return conf, nil
|
||||||
conf, err := google.JWTConfigFromJSON(data, DriverScopes...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing account_file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ServiceAccount{
|
|
||||||
jsonKey: data,
|
|
||||||
jwt: conf,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||||||
// Run executes a googlecompute Packer build and returns a packer.Artifact
|
// Run executes a googlecompute Packer build and returns a packer.Artifact
|
||||||
// representing a GCE machine image.
|
// representing a GCE machine image.
|
||||||
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) {
|
||||||
cfg := GCEDriverConfig{
|
driver, err := NewDriverGCE(
|
||||||
Ui: ui,
|
ui, b.config.ProjectId, b.config.account, b.config.VaultGCPOauthEngine)
|
||||||
ProjectId: b.config.ProjectId,
|
|
||||||
Account: b.config.account,
|
|
||||||
ImpersonateServiceAccountName: b.config.ImpersonateServiceAccount,
|
|
||||||
VaultOauthEngineName: b.config.VaultGCPOauthEngine,
|
|
||||||
}
|
|
||||||
|
|
||||||
driver, err := NewDriverGCE(cfg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -59,16 +52,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
// Build the steps.
|
// Build the steps.
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
new(StepCheckExistingImage),
|
new(StepCheckExistingImage),
|
||||||
&communicator.StepSSHKeyGen{
|
&StepCreateSSHKey{
|
||||||
CommConf: &b.config.Comm,
|
Debug: b.config.PackerDebug,
|
||||||
SSHTemporaryKeyPair: b.config.Comm.SSH.SSHTemporaryKeyPair,
|
DebugKeyPath: fmt.Sprintf("gce_%s.pem", b.config.PackerBuildName),
|
||||||
},
|
},
|
||||||
multistep.If(b.config.PackerDebug && b.config.Comm.SSHPrivateKeyFile == "",
|
|
||||||
&communicator.StepDumpSSHKey{
|
|
||||||
Path: fmt.Sprintf("gce_%s.pem", b.config.PackerBuildName),
|
|
||||||
SSH: &b.config.Comm.SSH,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
&StepImportOSLoginSSHKey{
|
&StepImportOSLoginSSHKey{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
},
|
||||||
@@ -83,11 +70,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
},
|
||||||
&StepStartTunnel{
|
&StepStartTunnel{
|
||||||
IAPConf: &b.config.IAPConfig,
|
IAPConf: &b.config.IAPConfig,
|
||||||
CommConf: &b.config.Comm,
|
CommConf: &b.config.Comm,
|
||||||
AccountFile: b.config.AccountFile,
|
AccountFile: b.config.AccountFile,
|
||||||
ImpersonateAccount: b.config.ImpersonateServiceAccount,
|
ProjectId: b.config.ProjectId,
|
||||||
ProjectId: b.config.ProjectId,
|
|
||||||
},
|
},
|
||||||
&communicator.StepConnect{
|
&communicator.StepConnect{
|
||||||
Config: &b.config.Comm,
|
Config: &b.config.Comm,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"golang.org/x/oauth2/jwt"
|
||||||
compute "google.golang.org/api/compute/v1"
|
compute "google.golang.org/api/compute/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,8 +35,6 @@ type Config struct {
|
|||||||
// run Packer on a GCE instance with a service account. Instructions for
|
// run Packer on a GCE instance with a service account. Instructions for
|
||||||
// creating the file or using service accounts are above.
|
// creating the file or using service accounts are above.
|
||||||
AccountFile string `mapstructure:"account_file" required:"false"`
|
AccountFile string `mapstructure:"account_file" required:"false"`
|
||||||
// This allows service account impersonation as per the [docs](https://cloud.google.com/iam/docs/impersonating-service-accounts).
|
|
||||||
ImpersonateServiceAccount string `mapstructure:"impersonate_service_account" required:"false"`
|
|
||||||
// The project ID that will be used to launch instances and store images.
|
// The project ID that will be used to launch instances and store images.
|
||||||
ProjectId string `mapstructure:"project_id" required:"true"`
|
ProjectId string `mapstructure:"project_id" required:"true"`
|
||||||
// Full or partial URL of the guest accelerator type. GPU accelerators can
|
// Full or partial URL of the guest accelerator type. GPU accelerators can
|
||||||
@@ -76,8 +75,6 @@ type Config struct {
|
|||||||
EnableIntegrityMonitoring bool `mapstructure:"enable_integrity_monitoring" required:"false"`
|
EnableIntegrityMonitoring bool `mapstructure:"enable_integrity_monitoring" required:"false"`
|
||||||
// Whether to use an IAP proxy.
|
// Whether to use an IAP proxy.
|
||||||
IAPConfig `mapstructure:",squash"`
|
IAPConfig `mapstructure:",squash"`
|
||||||
// Skip creating the image. Useful for setting to `true` during a build test stage. Defaults to `false`.
|
|
||||||
SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"`
|
|
||||||
// The unique name of the resulting image. Defaults to
|
// The unique name of the resulting image. Defaults to
|
||||||
// `packer-{{timestamp}}`.
|
// `packer-{{timestamp}}`.
|
||||||
ImageName string `mapstructure:"image_name" required:"false"`
|
ImageName string `mapstructure:"image_name" required:"false"`
|
||||||
@@ -87,19 +84,13 @@ type Config struct {
|
|||||||
// * kmsKeyName - The name of the encryption key that is stored in Google Cloud KMS.
|
// * kmsKeyName - The name of the encryption key that is stored in Google Cloud KMS.
|
||||||
// * RawKey: - A 256-bit customer-supplied encryption key, encodes in RFC 4648 base64.
|
// * RawKey: - A 256-bit customer-supplied encryption key, encodes in RFC 4648 base64.
|
||||||
//
|
//
|
||||||
// examples:
|
// example:
|
||||||
//
|
//
|
||||||
// ```json
|
// ```json
|
||||||
// {
|
// {
|
||||||
// "kmsKeyName": "projects/${project}/locations/${region}/keyRings/computeEngine/cryptoKeys/computeEngine/cryptoKeyVersions/4"
|
// "kmsKeyName": "projects/${project}/locations/${region}/keyRings/computeEngine/cryptoKeys/computeEngine/cryptoKeyVersions/4"
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
//
|
|
||||||
// ```hcl
|
|
||||||
// image_encryption_key {
|
|
||||||
// kmsKeyName = "projects/${var.project}/locations/${var.region}/keyRings/computeEngine/cryptoKeys/computeEngine/cryptoKeyVersions/4"
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
ImageEncryptionKey *CustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false"`
|
ImageEncryptionKey *CustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false"`
|
||||||
// The name of the image family to which the resulting image belongs. You
|
// The name of the image family to which the resulting image belongs. You
|
||||||
// can create disks by specifying an image family instead of a specific
|
// can create disks by specifying an image family instead of a specific
|
||||||
@@ -275,7 +266,7 @@ type Config struct {
|
|||||||
UseOSLogin bool `mapstructure:"use_os_login" required:"false"`
|
UseOSLogin bool `mapstructure:"use_os_login" required:"false"`
|
||||||
// Can be set instead of account_file. If set, this builder will use
|
// Can be set instead of account_file. If set, this builder will use
|
||||||
// HashiCorp Vault to generate an Oauth token for authenticating against
|
// HashiCorp Vault to generate an Oauth token for authenticating against
|
||||||
// Google Cloud. The value should be the path of the token generator
|
// Google's cloud. The value should be the path of the token generator
|
||||||
// within vault.
|
// within vault.
|
||||||
// For information on how to configure your Vault + GCP engine to produce
|
// For information on how to configure your Vault + GCP engine to produce
|
||||||
// Oauth tokens, see https://www.vaultproject.io/docs/auth/gcp
|
// Oauth tokens, see https://www.vaultproject.io/docs/auth/gcp
|
||||||
@@ -289,7 +280,7 @@ type Config struct {
|
|||||||
// Example: "us-central1-a"
|
// Example: "us-central1-a"
|
||||||
Zone string `mapstructure:"zone" required:"true"`
|
Zone string `mapstructure:"zone" required:"true"`
|
||||||
|
|
||||||
account *ServiceAccount
|
account *jwt.Config
|
||||||
imageAlreadyExists bool
|
imageAlreadyExists bool
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
@@ -297,7 +288,6 @@ type Config struct {
|
|||||||
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
c.ctx.Funcs = TemplateFuncs
|
c.ctx.Funcs = TemplateFuncs
|
||||||
err := config.Decode(c, &config.DecodeOpts{
|
err := config.Decode(c, &config.DecodeOpts{
|
||||||
PluginType: BuilderId,
|
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &c.ctx,
|
InterpolateContext: &c.ctx,
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
@@ -491,9 +481,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||||||
|
|
||||||
// Authenticating via an account file
|
// Authenticating via an account file
|
||||||
if c.AccountFile != "" {
|
if c.AccountFile != "" {
|
||||||
if c.VaultGCPOauthEngine != "" && c.ImpersonateServiceAccount != "" {
|
if c.VaultGCPOauthEngine != "" {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("You cannot "+
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("You cannot "+
|
||||||
"specify impersonate_service_account, account_file and vault_gcp_oauth_engine at the same time"))
|
"specify both account_file and vault_gcp_oauth_engine."))
|
||||||
}
|
}
|
||||||
cfg, err := ProcessAccountFile(c.AccountFile)
|
cfg, err := ProcessAccountFile(c.AccountFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -545,11 +535,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||||||
type CustomerEncryptionKey struct {
|
type CustomerEncryptionKey struct {
|
||||||
// KmsKeyName: The name of the encryption key that is stored in Google
|
// KmsKeyName: The name of the encryption key that is stored in Google
|
||||||
// Cloud KMS.
|
// Cloud KMS.
|
||||||
KmsKeyName string `mapstructure:"kmsKeyName" json:"kmsKeyName,omitempty"`
|
KmsKeyName string `json:"kmsKeyName,omitempty"`
|
||||||
|
|
||||||
// RawKey: Specifies a 256-bit customer-supplied encryption key, encoded
|
// RawKey: Specifies a 256-bit customer-supplied encryption key, encoded
|
||||||
// in RFC 4648 base64 to either encrypt or decrypt this resource.
|
// in RFC 4648 base64 to either encrypt or decrypt this resource.
|
||||||
RawKey string `mapstructure:"rawKey" json:"rawKey,omitempty"`
|
RawKey string `json:"rawKey,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *CustomerEncryptionKey) ComputeType() *compute.CustomerEncryptionKey {
|
func (k *CustomerEncryptionKey) ComputeType() *compute.CustomerEncryptionKey {
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
|||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||||
@@ -66,7 +64,6 @@ 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"`
|
||||||
AccountFile *string `mapstructure:"account_file" required:"false" cty:"account_file" hcl:"account_file"`
|
AccountFile *string `mapstructure:"account_file" required:"false" cty:"account_file" hcl:"account_file"`
|
||||||
ImpersonateServiceAccount *string `mapstructure:"impersonate_service_account" required:"false" cty:"impersonate_service_account" hcl:"impersonate_service_account"`
|
|
||||||
ProjectId *string `mapstructure:"project_id" required:"true" cty:"project_id" hcl:"project_id"`
|
ProjectId *string `mapstructure:"project_id" required:"true" cty:"project_id" hcl:"project_id"`
|
||||||
AcceleratorType *string `mapstructure:"accelerator_type" required:"false" cty:"accelerator_type" hcl:"accelerator_type"`
|
AcceleratorType *string `mapstructure:"accelerator_type" required:"false" cty:"accelerator_type" hcl:"accelerator_type"`
|
||||||
AcceleratorCount *int64 `mapstructure:"accelerator_count" required:"false" cty:"accelerator_count" hcl:"accelerator_count"`
|
AcceleratorCount *int64 `mapstructure:"accelerator_count" required:"false" cty:"accelerator_count" hcl:"accelerator_count"`
|
||||||
@@ -83,7 +80,6 @@ type FlatConfig struct {
|
|||||||
IAPHashBang *string `mapstructure:"iap_hashbang" required:"false" cty:"iap_hashbang" hcl:"iap_hashbang"`
|
IAPHashBang *string `mapstructure:"iap_hashbang" required:"false" cty:"iap_hashbang" hcl:"iap_hashbang"`
|
||||||
IAPExt *string `mapstructure:"iap_ext" required:"false" cty:"iap_ext" hcl:"iap_ext"`
|
IAPExt *string `mapstructure:"iap_ext" required:"false" cty:"iap_ext" hcl:"iap_ext"`
|
||||||
IAPTunnelLaunchWait *int `mapstructure:"iap_tunnel_launch_wait" required:"false" cty:"iap_tunnel_launch_wait" hcl:"iap_tunnel_launch_wait"`
|
IAPTunnelLaunchWait *int `mapstructure:"iap_tunnel_launch_wait" required:"false" cty:"iap_tunnel_launch_wait" hcl:"iap_tunnel_launch_wait"`
|
||||||
SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"`
|
|
||||||
ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"`
|
ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"`
|
||||||
ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"`
|
ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"`
|
||||||
ImageEncryptionKey *FlatCustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false" cty:"image_encryption_key" hcl:"image_encryption_key"`
|
ImageEncryptionKey *FlatCustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false" cty:"image_encryption_key" hcl:"image_encryption_key"`
|
||||||
@@ -146,8 +142,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||||
@@ -188,7 +182,6 @@ 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},
|
||||||
"account_file": &hcldec.AttrSpec{Name: "account_file", Type: cty.String, Required: false},
|
"account_file": &hcldec.AttrSpec{Name: "account_file", Type: cty.String, Required: false},
|
||||||
"impersonate_service_account": &hcldec.AttrSpec{Name: "impersonate_service_account", Type: cty.String, Required: false},
|
|
||||||
"project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false},
|
"project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false},
|
||||||
"accelerator_type": &hcldec.AttrSpec{Name: "accelerator_type", Type: cty.String, Required: false},
|
"accelerator_type": &hcldec.AttrSpec{Name: "accelerator_type", Type: cty.String, Required: false},
|
||||||
"accelerator_count": &hcldec.AttrSpec{Name: "accelerator_count", Type: cty.Number, Required: false},
|
"accelerator_count": &hcldec.AttrSpec{Name: "accelerator_count", Type: cty.Number, Required: false},
|
||||||
@@ -205,7 +198,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"iap_hashbang": &hcldec.AttrSpec{Name: "iap_hashbang", Type: cty.String, Required: false},
|
"iap_hashbang": &hcldec.AttrSpec{Name: "iap_hashbang", Type: cty.String, Required: false},
|
||||||
"iap_ext": &hcldec.AttrSpec{Name: "iap_ext", Type: cty.String, Required: false},
|
"iap_ext": &hcldec.AttrSpec{Name: "iap_ext", Type: cty.String, Required: false},
|
||||||
"iap_tunnel_launch_wait": &hcldec.AttrSpec{Name: "iap_tunnel_launch_wait", Type: cty.Number, Required: false},
|
"iap_tunnel_launch_wait": &hcldec.AttrSpec{Name: "iap_tunnel_launch_wait", Type: cty.Number, Required: false},
|
||||||
"skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false},
|
|
||||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||||
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
|
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
|
||||||
"image_encryption_key": &hcldec.BlockSpec{TypeName: "image_encryption_key", Nested: hcldec.ObjectSpec((*FlatCustomerEncryptionKey)(nil).HCL2Spec())},
|
"image_encryption_key": &hcldec.BlockSpec{TypeName: "image_encryption_key", Nested: hcldec.ObjectSpec((*FlatCustomerEncryptionKey)(nil).HCL2Spec())},
|
||||||
@@ -246,8 +238,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
// FlatCustomerEncryptionKey is an auto-generated flat version of CustomerEncryptionKey.
|
// FlatCustomerEncryptionKey is an auto-generated flat version of CustomerEncryptionKey.
|
||||||
// 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 FlatCustomerEncryptionKey struct {
|
type FlatCustomerEncryptionKey struct {
|
||||||
KmsKeyName *string `mapstructure:"kmsKeyName" json:"kmsKeyName,omitempty" cty:"kmsKeyName" hcl:"kmsKeyName"`
|
KmsKeyName *string `json:"kmsKeyName,omitempty" cty:"kms_key_name" hcl:"kms_key_name"`
|
||||||
RawKey *string `mapstructure:"rawKey" json:"rawKey,omitempty" cty:"rawKey" hcl:"rawKey"`
|
RawKey *string `json:"rawKey,omitempty" cty:"raw_key" hcl:"raw_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatCustomerEncryptionKey.
|
// FlatMapstructure returns a new FlatCustomerEncryptionKey.
|
||||||
@@ -262,8 +254,8 @@ func (*CustomerEncryptionKey) FlatMapstructure() interface{ HCL2Spec() map[strin
|
|||||||
// The decoded values from this spec will then be applied to a FlatCustomerEncryptionKey.
|
// The decoded values from this spec will then be applied to a FlatCustomerEncryptionKey.
|
||||||
func (*FlatCustomerEncryptionKey) HCL2Spec() map[string]hcldec.Spec {
|
func (*FlatCustomerEncryptionKey) HCL2Spec() map[string]hcldec.Spec {
|
||||||
s := map[string]hcldec.Spec{
|
s := map[string]hcldec.Spec{
|
||||||
"kmsKeyName": &hcldec.AttrSpec{Name: "kmsKeyName", Type: cty.String, Required: false},
|
"kms_key_name": &hcldec.AttrSpec{Name: "kms_key_name", Type: cty.String, Required: false},
|
||||||
"rawKey": &hcldec.AttrSpec{Name: "rawKey", Type: cty.String, Required: false},
|
"raw_key": &hcldec.AttrSpec{Name: "raw_key", Type: cty.String, Required: false},
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
compute "google.golang.org/api/compute/v1"
|
compute "google.golang.org/api/compute/v1"
|
||||||
"google.golang.org/api/option"
|
|
||||||
oslogin "google.golang.org/api/oslogin/v1"
|
oslogin "google.golang.org/api/oslogin/v1"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
|
"golang.org/x/oauth2/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// driverGCE is a Driver implementation that actually talks to GCE.
|
// driverGCE is a Driver implementation that actually talks to GCE.
|
||||||
@@ -35,14 +36,6 @@ type driverGCE struct {
|
|||||||
ui packer.Ui
|
ui packer.Ui
|
||||||
}
|
}
|
||||||
|
|
||||||
type GCEDriverConfig struct {
|
|
||||||
Ui packer.Ui
|
|
||||||
ProjectId string
|
|
||||||
Account *ServiceAccount
|
|
||||||
ImpersonateServiceAccountName string
|
|
||||||
VaultOauthEngineName string
|
|
||||||
}
|
|
||||||
|
|
||||||
var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}
|
var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}
|
||||||
|
|
||||||
// Define a TokenSource that gets tokens from Vault
|
// Define a TokenSource that gets tokens from Vault
|
||||||
@@ -78,34 +71,31 @@ func (ots OauthTokenSource) Token() (*oauth2.Token, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientOptionGoogle(account *ServiceAccount, vaultOauth string, impersonatesa string) (option.ClientOption, error) {
|
func NewClientGCE(conf *jwt.Config, vaultOauth string) (*http.Client, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var opts option.ClientOption
|
var client *http.Client
|
||||||
|
|
||||||
if vaultOauth != "" {
|
if vaultOauth != "" {
|
||||||
// Auth with Vault Oauth
|
// Auth with Vault Oauth
|
||||||
log.Printf("Using Vault to generate Oauth token.")
|
log.Printf("Using Vault to generate Oauth token.")
|
||||||
ts := OauthTokenSource{vaultOauth}
|
ts := OauthTokenSource{vaultOauth}
|
||||||
opts = option.WithTokenSource(ts)
|
return oauth2.NewClient(context.TODO(), ts), nil
|
||||||
|
|
||||||
} else if impersonatesa != "" {
|
} else if conf != nil && len(conf.PrivateKey) > 0 {
|
||||||
opts = option.ImpersonateCredentials(impersonatesa)
|
|
||||||
} else if account != nil && account.jwt != nil && len(account.jwt.PrivateKey) > 0 {
|
|
||||||
// Auth with AccountFile if provided
|
// Auth with AccountFile if provided
|
||||||
log.Printf("[INFO] Requesting Google token via account_file...")
|
log.Printf("[INFO] Requesting Google token via account_file...")
|
||||||
log.Printf("[INFO] -- Email: %s", account.jwt.Email)
|
log.Printf("[INFO] -- Email: %s", conf.Email)
|
||||||
log.Printf("[INFO] -- Scopes: %s", DriverScopes)
|
log.Printf("[INFO] -- Scopes: %s", DriverScopes)
|
||||||
log.Printf("[INFO] -- Private Key Length: %d", len(account.jwt.PrivateKey))
|
log.Printf("[INFO] -- Private Key Length: %d", len(conf.PrivateKey))
|
||||||
|
|
||||||
opts = option.WithCredentialsJSON(account.jsonKey)
|
// Initiate an http.Client. The following GET request will be
|
||||||
|
// authorized and authenticated on the behalf of
|
||||||
|
// your service account.
|
||||||
|
client = conf.Client(context.TODO())
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[INFO] Requesting Google token via GCE API Default Client Token Source...")
|
log.Printf("[INFO] Requesting Google token via GCE API Default Client Token Source...")
|
||||||
ts, err := google.DefaultTokenSource(context.TODO(), "https://www.googleapis.com/auth/cloud-platform")
|
client, err = google.DefaultClient(context.TODO(), DriverScopes...)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
opts = option.WithTokenSource(ts)
|
|
||||||
// The DefaultClient uses the DefaultTokenSource of the google lib.
|
// The DefaultClient uses the DefaultTokenSource of the google lib.
|
||||||
// The DefaultTokenSource uses the "Application Default Credentials"
|
// The DefaultTokenSource uses the "Application Default Credentials"
|
||||||
// It looks for credentials in the following places, preferring the first location found:
|
// It looks for credentials in the following places, preferring the first location found:
|
||||||
@@ -124,23 +114,22 @@ func NewClientOptionGoogle(account *ServiceAccount, vaultOauth string, impersona
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return opts, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
|
func NewDriverGCE(ui packer.Ui, p string, conf *jwt.Config, vaultOauth string) (Driver, error) {
|
||||||
opts, err := NewClientOptionGoogle(config.Account, config.VaultOauthEngineName, config.ImpersonateServiceAccountName)
|
client, err := NewClientGCE(conf, vaultOauth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Instantiating GCE client...")
|
log.Printf("[INFO] Instantiating GCE client...")
|
||||||
service, err := compute.NewService(context.TODO(), opts)
|
service, err := compute.New(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Instantiating OS Login client...")
|
osLoginService, err := oslogin.New(client)
|
||||||
osLoginService, err := oslogin.NewService(context.TODO(), opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -149,10 +138,10 @@ func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
|
|||||||
service.UserAgent = useragent.String()
|
service.UserAgent = useragent.String()
|
||||||
|
|
||||||
return &driverGCE{
|
return &driverGCE{
|
||||||
projectId: config.ProjectId,
|
projectId: p,
|
||||||
service: service,
|
service: service,
|
||||||
osLoginService: osLoginService,
|
osLoginService: osLoginService,
|
||||||
ui: config.Ui,
|
ui: ui,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user