Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fe1008323 |
+7
-28
@@ -7,11 +7,10 @@ version: 2.1
|
||||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/circleci/golang:1.15
|
||||
resource_class: medium+
|
||||
- image: circleci/golang:1.13
|
||||
darwin:
|
||||
macos:
|
||||
xcode: "12.0.0"
|
||||
xcode: "9.0"
|
||||
|
||||
commands:
|
||||
install-go-run-tests-unix:
|
||||
@@ -67,7 +66,7 @@ jobs:
|
||||
steps:
|
||||
- install-go-run-tests-unix:
|
||||
GOOS: darwin
|
||||
GOVERSION: "1.15"
|
||||
GOVERSION: "1.13"
|
||||
- codecov/upload:
|
||||
file: coverage.txt
|
||||
test-windows:
|
||||
@@ -76,7 +75,7 @@ jobs:
|
||||
shell: bash.exe
|
||||
steps:
|
||||
- install-go-run-tests-windows:
|
||||
GOVERSION: "1.15"
|
||||
GOVERSION: "1.13"
|
||||
- codecov/upload:
|
||||
file: coverage.txt
|
||||
check-lint:
|
||||
@@ -153,7 +152,7 @@ jobs:
|
||||
destination: /
|
||||
build-website-docker-image:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/circleci/buildpack-deps
|
||||
- image: circleci/buildpack-deps
|
||||
shell: /usr/bin/env bash -euo pipefail -c
|
||||
steps:
|
||||
- checkout
|
||||
@@ -167,26 +166,11 @@ jobs:
|
||||
echo "Dependencies have not changed, not building a new website docker image."
|
||||
else
|
||||
cd website/
|
||||
docker login -u $WEBSITE_DOCKER_USER -p $WEBSITE_DOCKER_PASS
|
||||
docker build -t hashicorp/packer-website:$IMAGE_TAG .
|
||||
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
|
||||
fi
|
||||
algolia-index:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Push content to Algolia Index
|
||||
command: |
|
||||
if [ "$CIRCLE_REPOSITORY_URL" != "git@github.com:hashicorp/packer.git" ]; then
|
||||
echo "Not Packer OSS Repo, not indexing Algolia"
|
||||
exit 0
|
||||
fi
|
||||
cd website/
|
||||
npm install
|
||||
node scripts/index_search_content.js
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
@@ -217,15 +201,10 @@ workflows:
|
||||
- build_freebsd
|
||||
- build_openbsd
|
||||
- build_solaris
|
||||
website:
|
||||
build_website_docker_image:
|
||||
jobs:
|
||||
- build-website-docker-image:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- algolia-index:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- stable-website
|
||||
|
||||
+2
-3
@@ -13,6 +13,5 @@ coverage:
|
||||
project: off
|
||||
patch: off
|
||||
|
||||
ignore: # ignore hcl2spec generated code for coverage and mocks
|
||||
- "**/*.hcl2spec.go"
|
||||
- "**/*_mock.go"
|
||||
ignore: # ignore hcl2spec generated code for coverage
|
||||
- "**/*.hcl2spec.go"
|
||||
@@ -6,7 +6,6 @@
|
||||
*.mdx text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.hcl text eol=lf
|
||||
*.txt text eol=lf
|
||||
go.mod text eol=lf
|
||||
go.sum text eol=lf
|
||||
common/test-fixtures/root/* eol=lf
|
||||
|
||||
@@ -9,23 +9,10 @@ reaction feature
|
||||
(https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/)
|
||||
to add upvotes to pre-existing requests.
|
||||
|
||||
#### Community Note
|
||||
|
||||
Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request.
|
||||
Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request.
|
||||
If you are interested in working on this issue or have submitted a pull request, please leave a comment.
|
||||
|
||||
#### Description
|
||||
#### Feature Description
|
||||
|
||||
A written overview of the feature.
|
||||
|
||||
#### Use Case(s)
|
||||
|
||||
Any relevant use-cases that you see.
|
||||
|
||||
#### Potential configuration
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
#### Potential References
|
||||
|
||||
@@ -8,7 +8,6 @@ jobs:
|
||||
steps:
|
||||
- name: Add track-internal
|
||||
uses: andymckay/labeler@1.0.2
|
||||
if: github.event.issue.pull_request == null
|
||||
with:
|
||||
repo-token: ${{ secrets.Github_Token }}
|
||||
add-labels: "track-internal"
|
||||
|
||||
@@ -27,4 +27,3 @@ Thumbs.db
|
||||
/packer.exe
|
||||
.project
|
||||
cache
|
||||
/.vscode/
|
||||
|
||||
+9
-438
@@ -1,445 +1,23 @@
|
||||
## 1.6.6 (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]
|
||||
## 1.6.1 (Upcoming)
|
||||
|
||||
### 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:
|
||||
* builder/amazon: Add all of the custom AWS template engines to `build`
|
||||
template function for use by provisioners. [GH-9751]
|
||||
* builder/amazon: Add aws_polling config option to override env variables.
|
||||
[GH-9777]
|
||||
* 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
|
||||
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
|
||||
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/vsphere-iso: Add support for building on a single ESXi host
|
||||
[GH-9793]
|
||||
* builder/vsphere: Add new `directory_permission` config export option.
|
||||
[GH-9704]
|
||||
* builder/vsphere: Add option to import OVF templates to the Content Library
|
||||
[GH-9755]
|
||||
* 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 vault integration as an HCL2 function function. [GH-9746]
|
||||
* core: Add colored prefix to progress bar so it's clearer what build each
|
||||
progress bar belongs to. [GH-9780]
|
||||
* core: Ui now pretty prints build durations. [GH-9749]
|
||||
* core: When a build is cancelled, Packer will skip postprocessors gracefully
|
||||
rather than failing them. [GH-9720]
|
||||
* integrations/secretsmanager: Add support for plaintext non-key/pair secrets.
|
||||
[GH-9773]
|
||||
* post-processor/vsphere: Improve UI to catch bad credentials and print errors.
|
||||
[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`
|
||||
option. [GH-9725] [GH-9735]
|
||||
* provisioner/salt-masterless: Add option to option to download community
|
||||
SaltStack formulas. [GH-9726]
|
||||
|
||||
### BUG FIXES:
|
||||
* build: Fix bug in code generator that caused generation to fail in nested
|
||||
packer/packer dirs [GH-9728]
|
||||
* build: Fix Makefile so that default target doesn't crash and creates dev
|
||||
binaries. [GH-9706]
|
||||
* builder/amazon-ebssurrogate: Make skip_save_build_region option work in the
|
||||
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.
|
||||
[GH-9698]
|
||||
* builder/googlecompute: Fix bug where startup script hang would cause export
|
||||
to hang. [GH-9708]
|
||||
* builder/hyperv: Send boot command in small chunks to make it more stable.
|
||||
[GH-9765]
|
||||
* builder/scaleway: Fix config issue that made scaleway builder fail when used
|
||||
with HCL2 config. [GH-9677]
|
||||
* builder/vmware: Fully destroy vm if it was cancelled or errored. This will
|
||||
make orphaned vms easier to destroy through vCenter. [GH-9782]
|
||||
* builder/vsphere: Fix `alt`, `ctrl`, and `shift` keypresses in the
|
||||
boot_command. [GH-9702] [GH-9739]
|
||||
* builder/vsphere: Fix bug where Packer timed out if two interfaces were
|
||||
defined but only one had an available IP. [GH-9748]
|
||||
* builder/vsphere: Fix the configuration_parameters option so that it is always
|
||||
applied, not just when the tool sync policy is set. [GH-9713]
|
||||
* communicator: Fix `pause_before_connect` option to force a reconnect after
|
||||
the pause. [GH-9772]
|
||||
* core: Make `max_retries` provisioner option a string to allow variable
|
||||
interpolation. [GH-9673]
|
||||
* post-processor/vsphere-template: Fix ReregisterVM to default to true instead
|
||||
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)
|
||||
|
||||
### BACKWARDS INCOMPATABILITIES:
|
||||
|
||||
* HCL: builder/vsphere: Add option to add a xhci/usb3 controller; changes
|
||||
controller value to an array of strings. [GH-9574]
|
||||
* HCL: New HCL-only `post-processors` block to run chained post-processors
|
||||
after a build [GH-9638]. Before this, defining multiple `post-processor`
|
||||
blocks after provisioning steps would run them sequentially, now doing this
|
||||
makes them start from the build artifact. To queue post-processors you now
|
||||
have to define them in a `post-processors` block. [GH-9638]
|
||||
* post-processor/vSphere: We have fixed a bug in the ovftool URL encoding. This
|
||||
may mean that you have performed an encoding workaround that is no longer
|
||||
necessary. [GH-9589]
|
||||
|
||||
### FEATURES:
|
||||
* **New post-processor** Yandex Import [GH-9553]
|
||||
|
||||
### IMPROVEMENTS:
|
||||
* builder/amazon-ebs: Wrap CreateImage call in a retry to account for eventual
|
||||
consistency [GH-9579]
|
||||
* builder/azure: Disable SSH password authentication unless password is
|
||||
explicitly specified. [GH-9603]
|
||||
* builder/docker: Add options for --cap-add, --cap-drop, --tmpfs, --device
|
||||
[GH-9565]
|
||||
* builder/file: Create parent directories of target file, if they don't exist.
|
||||
[GH-9452]
|
||||
* builder/googlecompute: Add `wrap_startup_script` configuration option to
|
||||
allow the disabling of Packer's startup script wrapper [GH-9505]
|
||||
* builder/googlecompute: Add support for oslogin via the `use_os_login`
|
||||
configuration option [GH-9339]
|
||||
* builder/googlecompute: Make IAP tunnel timeout configurable. [GH-9545]
|
||||
* builder/googlecompute: Support using WinRM over an IAP tunnel [GH-9610]
|
||||
* builder/hyper-v: Include secure boot template in box.xml [GH-9552]
|
||||
* builder/hyperone: Add support for custom username in vm creation. [GH-9497]
|
||||
* builder/hyperone: Skip chroot device discovery. [GH-9489]
|
||||
* builder/openstack: Bump gophercloud to latest version [GH-9573]
|
||||
* builder/proxmox: Add option to disable KVM hardware virtualization in Proxmox
|
||||
builder [GH-9587]
|
||||
* builder/proxmox: Add support for multiple NIC packet queues [GH-9597]
|
||||
* builder/proxmox: Enable Proxmox builder to toggle the firewall parameter for
|
||||
network interfaces. [GH-9487]
|
||||
* builder/proxmox: Update Proxmox storagePoolTypes [GH-9418]
|
||||
* builder/qemu: Add 'cdrom_interface' option to QEMU builder [GH-9483]
|
||||
* builder/tencentcloud: Add `source_image_name` to support getting source image
|
||||
by name [GH-9619]
|
||||
* builder/tencentcloud: Update cvm root disk type to `CLOUD_PREMIUM` [GH-9663]
|
||||
* builder/ucloud: New access config options and run config options. [GH-9466]
|
||||
* builder/vsphere-clone: Add `boot_command` support to vsphere-clone builder,
|
||||
including support for starting an HTTP server
|
||||
* builder/vsphere-clone: Add `vapp` configuration option [GH-9507]
|
||||
* builder/vsphere: Add ability to define multiple disk controllers [GH-9519]
|
||||
* builder/vsphere: Add `boot_command` support to vsphere-clone builder. [GH-9397]
|
||||
* builder/vsphere: Add `content_library_destination` to import VM template to a
|
||||
Content Library [GH-9551]
|
||||
* builder/vsphere: Add `force_bios_setup` configuration option [GH-9563]
|
||||
* builder/vsphere: Add option to add a xhci/usb3 controller [GH-9574]
|
||||
* builder/proxmox: Update Proxmox storagePoolTypes [GH-9418]
|
||||
* builder/vsphere: Create vm output folders if they don't exist [GH-9402]
|
||||
* builder/vsphere: Fix file size descriptor when exporting OFV [GH-9568]
|
||||
* builder/vsphere: Look at all available IPs in the waiting for IP loop.
|
||||
[GH-9450]
|
||||
* builder/vsphere: Match network to host when multiple networks are found
|
||||
[GH-9556]
|
||||
* builder/vsphere: Update vsphere `boot_command` to bring it in line with other
|
||||
* builder/vsphere: Update vsphere boot_command to bring it in line with other
|
||||
builders' boot_command functionality. [GH-9406]
|
||||
* builder/vsphere: Use datacenter inventory path for find folder [GH-9390]
|
||||
* builder/vsphere: Use value from "ip_wait_address" option to determine the
|
||||
default for the http server IP [GH-9441]
|
||||
* builder/yandex: Allow set `min_disk_size` for an image.
|
||||
* builder/yandex: Support authentication by Service Account on instance
|
||||
[GH-9383]
|
||||
* builder/yandex: yandex: Add new property `min_disk_size` of built image
|
||||
[GH-9594]
|
||||
* communicator/ssh: Add support for OpenSSH certificate signing [GH-9521]
|
||||
* communicator/ssh: Allow users to provide a list of ciphers that they want
|
||||
Packer to support. [GH-9453]
|
||||
* core/hcl2: Add possibility to name singular build.source blocks to
|
||||
differentiate their output and to filter on them [GH-9490]
|
||||
* core/hcl2: Add the "inspect" command for hcl2 configs. [GH-9468]
|
||||
* core/hcl2: HCL configs now respect only/except using build names instead of
|
||||
types. [GH-9454]
|
||||
* core/hcl: Allow use of `keep_input_artifact` in post processors. [GH-9477]
|
||||
* core/hcl: Share build info with Provisioner and Post-Processor via HCL2
|
||||
variables [GH-9444] [GH-9534] [GH-9622]
|
||||
* core: Add on-error flag option to run error-cleanup-provisioner [GH-9429]
|
||||
* core: communicator/ssh: Add new `ssh_key_exchange_algorithms` option to
|
||||
supply custom key exchange algorithms in SSH client [GH-9634]
|
||||
* core: refactor initialization out from Packer core to allow `validate
|
||||
--syntax-only` to no error when a variable is not set [GH-9627]
|
||||
* hcl2: Handle uint64 buildvars [GH-9585]
|
||||
* post-processor/yandex-export: Allow users to utilize generated variables in
|
||||
templating. [GH-9555]
|
||||
* post-processor/yandex-export: Support Authentication by Service Account Key
|
||||
file [GH-9379]
|
||||
* post-processor/yandex-import: Support creating an Image based on another one
|
||||
[GH-9614]
|
||||
* post-processor/yandex-import: Support using URL from yandex-export [GH-9601]
|
||||
* provisioner/ansible: Add template option for templating the inventory file
|
||||
lines [GH-9438]
|
||||
|
||||
### BUG FIXES:
|
||||
* builder/amazon: Change "Resource" field in
|
||||
`temporary_iam_instance_profile_policy_document` to be an array of strings,
|
||||
not just a single string. [GH-9509]
|
||||
* builder/amazon: HCL2: Add singular `run_volume_tag` block to ebs & ebssurrogate
|
||||
builders. [GH-9457]
|
||||
* builder/amazon: Retry fetching block device mappings if empty. [GH-9480]
|
||||
* builder/azure: Fix data disks URI. [GH-9467]
|
||||
* builder/googlecompute: Fix issue with `use_iap` globally changing a user's
|
||||
gcloud project configuration, by temporarily setting "project" via project
|
||||
flag and not via `gcloud config` [GH-9662]
|
||||
* builder/googlecompute: Fix the "secure boot" validation for uefi_compatible
|
||||
images. [GH-9371]
|
||||
* builder/qemu: Only set up localhost port forwarding if skipnatmapping is
|
||||
false. [GH-9479]
|
||||
* builder/vagrant: Fix box file validation for remote box files specified using
|
||||
`source_path` [GH-9660]
|
||||
* builder/vagrant: Improve validation and error handling around synced_folder.
|
||||
Make sure that synced folder can be defined relative to Packer run
|
||||
directory, not the Vagrant output directory. [GH-9577]
|
||||
* builder/virtualbox-vm: Fix regression where builder would fail if the vm had
|
||||
no snapshots. [GH-9435]
|
||||
* builder/vmware-iso: Try checksum remote file instead of local file. [GH-9584]
|
||||
* builder/google: Fix the "secure boot" validation for uefi_compatible images.
|
||||
[GH-9371]
|
||||
* builder/vmware: Fix a race that occurred when parsing the network config.
|
||||
[GH-9387]
|
||||
* builder/vmware: Update vendor library, enabling retries on 502 errors
|
||||
* builder/vmware: update vendor library, enabling retries on 502 errors
|
||||
[GH-9391]
|
||||
* builder/vsphere-clone: Fix SSH public key injection for cloned templates
|
||||
[GH-9507]
|
||||
* builder/vsphere: Clean up folder path to remove leading slashes. [GH-9542]
|
||||
* builder/vsphere: Deduplicate where Firmware is set in vsphere-iso builder
|
||||
[GH-9557]
|
||||
* builder/vsphere: Fix vsphere ToolsSyncTime and ToolsUpgradePolicy [GH-9515]
|
||||
* builder/vsphere: vSphere driver context is no longer cancelled when Packer
|
||||
context is cancelled. [GH-9576]
|
||||
* communicator/winrm: Add the "no_proxy" environment variable option to have
|
||||
winrm bypass the proxy set by the http_proxy or https_proxy environment
|
||||
vars, when connecting to the remote instance. [GH-9267]
|
||||
* core: Do not print download progress bar if a machine-readable UI is chosen.
|
||||
[GH-9448]
|
||||
* post-processor/amazon-import: Add support for retrying RequestLimitExceeded
|
||||
errors when importing an image [GH-9537]
|
||||
* post-processor/docker-import: Fix crash when using docker-import with HCL.
|
||||
[GH-9670]
|
||||
* post-processor/vsphere: Fix password encoding in vsphere post-processor
|
||||
ovftool call [GH-9589]
|
||||
* post-processor/yandex-export: Fix error handling and docs. [GH-9554]
|
||||
* provisioner/ansible-local: Fix agent auth in SSH communicator [GH-9639]
|
||||
* provisioner/ansible: Correct check for whether PackerHttpAddr is implemented
|
||||
or not [GH-9498]
|
||||
* provisioner/ansible: Quote extra-var packer_build_name to handle names with
|
||||
spaces [GH-9590]
|
||||
|
||||
## 1.6.0 (June 09, 2020)
|
||||
|
||||
@@ -638,11 +216,6 @@
|
||||
|
||||
## 1.5.5 (March 25,2020)
|
||||
|
||||
### BACKWARDS INCOMPATIBILITIES:
|
||||
* core: Interpolation of undefined variables will now error as expected, in
|
||||
previous versions variables were allowed to be set as a command line
|
||||
argument but that was because the validation was being ignored
|
||||
|
||||
### IMPROVEMENTS:
|
||||
* builder/azure: Add support for configurable KeyVault SKU [GH-8879]
|
||||
* builder/hyperv: Add `first_boot_device` setting to allow the selection of the
|
||||
@@ -677,6 +250,8 @@
|
||||
strings [GH-8829]
|
||||
|
||||
### Bug Fixes:
|
||||
* bilder/proxmox: Bump proxmox-api-go to fix upstream bug where users hit open
|
||||
file limit. [GH-8800]
|
||||
* builder/azure: Fix `winrm_password` attribution and allow users to set
|
||||
`winrm_username` [GH-8928]
|
||||
* builder/azure: Fix azure key vault cleanup failure [GH-8905]
|
||||
@@ -687,8 +262,6 @@
|
||||
to fix SSH authentication issue [GH-8942]
|
||||
* builder/proxmox: Add new validation to catch that template_name cannot
|
||||
contain spaces. [GH-8799]
|
||||
* builder/proxmox: Bump proxmox-api-go to fix upstream bug where users hit open
|
||||
file limit. [GH-8800]
|
||||
* builder/vagrant: Fix path validation in ssh config step. [GH-8826]
|
||||
* builder/virtualbox-vm: Fix crash when VM has no snapshots. [GH-8906]
|
||||
* builder/virtualbox: Remove all floppy controllers before adding a new one.
|
||||
@@ -705,12 +278,10 @@
|
||||
provisioners. [GH-8771]
|
||||
* core: Fix bug where user var recursion could fail intermittently when used
|
||||
with env vars [GH-8875]
|
||||
* core: Interpolation of undefined variables will now error as expected
|
||||
* plugins: Make plugin discovery stricter with respect to periods so that users
|
||||
can disable plugins by renaming the extension [GH-8735]
|
||||
* provisioner/salt: Fix `no_exit_on_failure` config to work correctly as
|
||||
expected. [GH-9119]
|
||||
* provisioner/shell: "inline" config option is now a template engine. [GH-8883]
|
||||
* provisioner/salt: Fix `no_exit_on_failure` config to work correctly as expected. [GH-9119]
|
||||
|
||||
## 1.5.4 (February 14, 2020)
|
||||
no-change release to fix code-signing on OSX binaries. Since checksums for these
|
||||
|
||||
+2
-3
@@ -49,8 +49,8 @@
|
||||
/builder/proxmox/ @carlpett
|
||||
/website/pages/docs/builders/proxmox* @carlpett
|
||||
|
||||
/builder/scaleway/ @scaleway/devtools
|
||||
/website/pages/docs/builders/scaleway* @scaleway/devtools
|
||||
/builder/scaleway/ @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
||||
/website/pages/docs/builders/scaleway* @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
||||
|
||||
/builder/hcloud/ @LKaemmerling
|
||||
/website/pages/docs/builders/hcloud* @LKaemmerling
|
||||
@@ -90,6 +90,5 @@
|
||||
/post-processor/exoscale-import/ @falzm @mcorbin
|
||||
/post-processor/googlecompute-export/ crunkleton@google.com
|
||||
/post-processor/yandex-export/ @GennadySpb
|
||||
/post-processor/yandex-import/ @GennadySpb
|
||||
/post-processor/vsphere-template/ nelson@bennu.cl
|
||||
/post-processor/ucloud-import/ @shawnmssu
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM docker.mirror.hashicorp.services/ubuntu:16.04
|
||||
FROM ubuntu:16.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
TEST?=$(shell go list ./...)
|
||||
COUNT?=1
|
||||
VET?=$(shell go list ./...)
|
||||
ACC_TEST_BUILDERS?=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 \
|
||||
releasebin test testacc testrace
|
||||
|
||||
default: install-build-deps install-gen-deps generate dev
|
||||
default: install-build-deps install-gen-deps generate testrace dev releasebin package dev fmt fmt-check mode-check fmt-docs fmt-examples
|
||||
|
||||
ci: testrace ## Test in continuous integration
|
||||
|
||||
@@ -137,7 +136,7 @@ generate-check: generate ## Check go code generation is on par
|
||||
fi
|
||||
|
||||
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
|
||||
provisioners-acctest: install-build-deps generate
|
||||
@@ -146,14 +145,14 @@ provisioners-acctest: install-build-deps generate
|
||||
# testacc runs 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."
|
||||
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
|
||||
@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
|
||||
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
|
||||
rm coverage.out
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Packer
|
||||
|
||||
[![Build Status][circleci-badge]][circleci]
|
||||
[![Build Status][travis-badge]][travis]
|
||||
[![Windows Build Status][appveyor-badge]][appveyor]
|
||||
[](https://pkg.go.dev/github.com/hashicorp/packer)
|
||||
[![GoDoc][godoc-badge]][godoc]
|
||||
[![GoReportCard][report-badge]][report]
|
||||
[](https://codecov.io/gh/hashicorp/packer)
|
||||
|
||||
[circleci-badge]: https://circleci.com/gh/hashicorp/packer.svg?style=svg
|
||||
[circleci]: https://app.circleci.com/pipelines/github/hashicorp/packer
|
||||
[travis-badge]: https://travis-ci.org/hashicorp/packer.svg?branch=master
|
||||
[travis]: https://travis-ci.org/hashicorp/packer
|
||||
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/miavlgnp989e5obc/branch/master?svg=true
|
||||
[appveyor]: https://ci.appveyor.com/project/hashicorp/packer
|
||||
[godoc-badge]: https://godoc.org/github.com/hashicorp/packer?status.svg
|
||||
@@ -95,20 +95,3 @@ See
|
||||
[CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md)
|
||||
for best practices and instructions on setting up your development environment
|
||||
to work on Packer.
|
||||
|
||||
## Unmaintained Plugins
|
||||
As contributors' circumstances change, development on a community maintained
|
||||
plugin can slow. When this happens, the Packer team may mark a plugin as
|
||||
unmaintained, to clearly signal the plugin's status to users.
|
||||
|
||||
What does **unmaintained** mean?
|
||||
|
||||
1. The code repository and all commit history will still be available.
|
||||
1. Documentation will remain on the Packer website.
|
||||
1. Issues and pull requests are monitored as a best effort.
|
||||
1. No active development will be performed by the Packer team.
|
||||
|
||||
If anyone form them community is interested in maintaining a community
|
||||
supported plugin, please feel free to submit contributions via a pull-
|
||||
request for review; reviews are generally prioritized over feature work
|
||||
when possible. For a list of open plugin issues and pending feature requests see the [Packer Issue Tracker](https://github.com/hashicorp/packer/issues/).
|
||||
|
||||
@@ -46,7 +46,6 @@ func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstruct
|
||||
|
||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
|
||||
@@ -105,19 +105,15 @@ type FlatConfig struct {
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
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"`
|
||||
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"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
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"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
@@ -127,7 +123,6 @@ type FlatConfig struct {
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
@@ -137,8 +132,8 @@ type FlatConfig struct {
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
@@ -223,13 +218,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
@@ -243,7 +234,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
|
||||
@@ -422,6 +422,7 @@ func TestBuilderAcc_ECSImageSharing(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// share with catsby
|
||||
const testBuilderAccSharing = `
|
||||
{
|
||||
"builders": [{
|
||||
|
||||
@@ -12,8 +12,6 @@ import (
|
||||
"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 {
|
||||
// The value of disk name is blank by default. [2,
|
||||
// 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
|
||||
// begin with `http://` or `https://`.
|
||||
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_efficiency - efficiency cloud disk
|
||||
// - cloud_ssd - cloud SSD
|
||||
@@ -33,8 +32,6 @@ type AlicloudDiskDevice struct {
|
||||
// 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.
|
||||
// This field is only used in the ECSImagesDiskMappings option, not
|
||||
// the ECSSystemDiskMapping option.
|
||||
SnapshotId string `mapstructure:"disk_snapshot_id" required:"false"`
|
||||
// The value of disk description is blank by
|
||||
// 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.
|
||||
Device string `mapstructure:"disk_device" required:"false"`
|
||||
// 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
|
||||
// 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 for more details.
|
||||
// it was in the source image. Please refer to Introduction of ECS disk encryption
|
||||
// for more details.
|
||||
Encrypted config.Trilean `mapstructure:"disk_encrypted" required:"false"`
|
||||
}
|
||||
|
||||
// The "AlicloudDiskDevices" object is used to define disk mappings for your
|
||||
// instance.
|
||||
type AlicloudDiskDevices struct {
|
||||
// Image disk mapping for the system disk.
|
||||
// See the [disk device configuration](#disk-devices-configuration) section
|
||||
// for more information on options.
|
||||
// Usage example:
|
||||
// Image disk mapping for system
|
||||
// disk.
|
||||
// - `disk_category` (string) - Category of the system disk. Optional values
|
||||
// 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"`
|
||||
// Add one or more data disks to the image.
|
||||
// See the [disk device configuration](#disk-devices-configuration) section
|
||||
// for more information on options.
|
||||
// Usage example:
|
||||
// Add one or more data
|
||||
// disks to the image.
|
||||
//
|
||||
// - `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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/chroot"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/hcl2template"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"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) {
|
||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"ami_description",
|
||||
"snapshot_tags",
|
||||
"snapshot_tag",
|
||||
"tags",
|
||||
"tag",
|
||||
"root_volume_tags",
|
||||
"root_volume_tag",
|
||||
"command_wrapper",
|
||||
"post_mount_commands",
|
||||
"pre_mount_commands",
|
||||
@@ -339,9 +335,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
}
|
||||
|
||||
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
|
||||
generatedData := awscommon.GetGeneratedDataList()
|
||||
generatedData = append(generatedData, "Device", "MountPath")
|
||||
|
||||
generatedData := []string{"SourceAMIName", "Device", "MountPath"}
|
||||
return generatedData, warns, nil
|
||||
}
|
||||
|
||||
@@ -372,7 +366,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand))
|
||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
||||
generatedData := &builder.GeneratedData{State: state}
|
||||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
@@ -402,15 +396,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&StepCreateVolume{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
RootVolumeType: b.config.RootVolumeType,
|
||||
RootVolumeSize: b.config.RootVolumeSize,
|
||||
RootVolumeTags: b.config.RootVolumeTags,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
&StepAttachVolume{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
&StepAttachVolume{},
|
||||
&StepEarlyUnflock{},
|
||||
&chroot.StepPreMountCommands{
|
||||
Commands: b.config.PreMountCommands,
|
||||
@@ -429,14 +420,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&chroot.StepCopyFiles{
|
||||
Files: b.config.CopyFiles,
|
||||
},
|
||||
&awscommon.StepSetGeneratedData{
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&chroot.StepChrootProvision{},
|
||||
&chroot.StepEarlyCleanup{},
|
||||
&StepSnapshot{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
&StepSnapshot{},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
@@ -449,7 +435,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
||||
@@ -41,9 +41,7 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -52,10 +50,8 @@ type FlatConfig struct {
|
||||
RawRegion *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"`
|
||||
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"`
|
||||
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
|
||||
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"`
|
||||
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
|
||||
AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" hcl2-schema-generator:"ami_block_device_mappings,direct" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"`
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts" required:"false" cty:"chroot_mounts" hcl:"chroot_mounts"`
|
||||
CommandWrapper *string `mapstructure:"command_wrapper" required:"false" cty:"command_wrapper" hcl:"command_wrapper"`
|
||||
@@ -120,9 +116,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false},
|
||||
"snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false},
|
||||
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"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},
|
||||
"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},
|
||||
@@ -131,10 +125,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"region": &hcldec.AttrSpec{Name: "region", 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_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
|
||||
"ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},
|
||||
"chroot_mounts": &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.List(cty.String)), Required: false},
|
||||
"command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false},
|
||||
|
||||
@@ -227,25 +227,10 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
if generatedData[1] != "BuildRegion" {
|
||||
t.Fatalf("Generated data should contain BuildRegion")
|
||||
}
|
||||
if generatedData[2] != "SourceAMI" {
|
||||
t.Fatalf("Generated data should contain SourceAMI")
|
||||
}
|
||||
if generatedData[3] != "SourceAMICreationDate" {
|
||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
||||
}
|
||||
if generatedData[4] != "SourceAMIOwner" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
||||
}
|
||||
if generatedData[5] != "SourceAMIOwnerName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
if generatedData[6] != "Device" {
|
||||
if generatedData[1] != "Device" {
|
||||
t.Fatalf("Generated data should contain Device")
|
||||
}
|
||||
if generatedData[7] != "MountPath" {
|
||||
if generatedData[2] != "MountPath" {
|
||||
t.Fatalf("Generated data should contain MountPath")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,8 @@ import (
|
||||
// device string - The location where the volume was attached.
|
||||
// attach_cleanup CleanupFunc
|
||||
type StepAttachVolume struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
attached bool
|
||||
volumeId string
|
||||
attached bool
|
||||
volumeId string
|
||||
}
|
||||
|
||||
func (s *StepAttachVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
@@ -52,7 +51,7 @@ func (s *StepAttachVolume) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
s.volumeId = volumeId
|
||||
|
||||
// Wait for the volume to become attached
|
||||
err = s.PollingConfig.WaitUntilVolumeAttached(ctx, ec2conn, s.volumeId)
|
||||
err = awscommon.WaitUntilVolumeAttached(ctx, ec2conn, s.volumeId)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error waiting for volume: %s", err)
|
||||
state.Put("error", err)
|
||||
@@ -88,7 +87,7 @@ func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error {
|
||||
s.attached = false
|
||||
|
||||
// Wait for the volume to detach
|
||||
err = s.PollingConfig.WaitUntilVolumeDetached(aws.BackgroundContext(), ec2conn, s.volumeId)
|
||||
err = awscommon.WaitUntilVolumeDetached(aws.BackgroundContext(), ec2conn, s.volumeId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error waiting for volume: %s", err)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
// Produces:
|
||||
// volume_id string - The ID of the created volume
|
||||
type StepCreateVolume struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
volumeId string
|
||||
RootVolumeSize int64
|
||||
RootVolumeType string
|
||||
@@ -111,7 +110,7 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
log.Printf("Volume ID: %s", s.volumeId)
|
||||
|
||||
// Wait for the volume to become ready
|
||||
err = s.PollingConfig.WaitUntilVolumeAvailable(ctx, ec2conn, s.volumeId)
|
||||
err = awscommon.WaitUntilVolumeAvailable(ctx, ec2conn, s.volumeId)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error waiting for volume: %s", err)
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
@@ -31,7 +31,7 @@ type StepMountDevice struct {
|
||||
MountPartition string
|
||||
|
||||
mountPath string
|
||||
GeneratedData *packerbuilderdata.GeneratedData
|
||||
GeneratedData *builder.GeneratedData
|
||||
}
|
||||
|
||||
func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// StepPrepareDevice finds an available device and sets it.
|
||||
type StepPrepareDevice struct {
|
||||
GeneratedData *packerbuilderdata.GeneratedData
|
||||
GeneratedData *builder.GeneratedData
|
||||
}
|
||||
|
||||
func (s *StepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
// StepRegisterAMI creates the AMI.
|
||||
type StepRegisterAMI struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
RootVolumeSize int64
|
||||
EnableAMIENASupport confighelper.Trilean
|
||||
EnableAMISriovNetSupport bool
|
||||
@@ -82,7 +81,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
state.Put("amis", amis)
|
||||
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if err := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
|
||||
if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
|
||||
err := fmt.Errorf("Error waiting for AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
|
||||
@@ -16,8 +16,7 @@ import (
|
||||
// Produces:
|
||||
// snapshot_id string - ID of the created snapshot
|
||||
type StepSnapshot struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
snapshotId string
|
||||
snapshotId string
|
||||
}
|
||||
|
||||
func (s *StepSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
@@ -44,7 +43,7 @@ func (s *StepSnapshot) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
ui.Message(fmt.Sprintf("Snapshot ID: %s", s.snapshotId))
|
||||
|
||||
// Wait for the snapshot to be ready
|
||||
err = s.PollingConfig.WaitUntilSnapshotDone(ctx, ec2conn, s.snapshotId)
|
||||
err = awscommon.WaitUntilSnapshotDone(ctx, ec2conn, s.snapshotId)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error waiting for snapshot: %s", err)
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions,AssumeRoleConfig
|
||||
//go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions
|
||||
|
||||
package common
|
||||
|
||||
@@ -11,68 +11,15 @@ import (
|
||||
"strings"
|
||||
|
||||
"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/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
awsbase "github.com/hashicorp/aws-sdk-go-base"
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
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 {
|
||||
Name string `mapstructure:"name"`
|
||||
RoleARN string `mapstructure:"role_arn"`
|
||||
@@ -101,17 +48,10 @@ type AccessConfig struct {
|
||||
// is not required if you are using `use_vault_aws_engine` for
|
||||
// authentication instead.
|
||||
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
|
||||
// provider whose API is compatible with aws EC2. Specify another endpoint
|
||||
// like this https://ec2.custom.endpoint.com.
|
||||
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
|
||||
// using the `sts:DecodeAuthorizationMessage` API. Note: requires that the
|
||||
// effective user/role have permissions to `sts:DecodeAuthorizationMessage`
|
||||
@@ -146,8 +86,6 @@ type AccessConfig struct {
|
||||
// validation of the ami_regions configuration option. Default false.
|
||||
SkipValidation bool `mapstructure:"skip_region_validation" required:"false"`
|
||||
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
|
||||
// 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
|
||||
@@ -178,8 +116,6 @@ type AccessConfig struct {
|
||||
// credential types) and GetFederationToken (for federation\_token
|
||||
// credential types) for more details.
|
||||
//
|
||||
// JSON example:
|
||||
//
|
||||
// ```json
|
||||
// {
|
||||
// "vault_aws_engine": {
|
||||
@@ -189,20 +125,7 @@ type AccessConfig struct {
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// vault_aws_engine {
|
||||
// name = "myrole"
|
||||
// role_arn = "myarn"
|
||||
// ttl = "3600s"
|
||||
// }
|
||||
// ```
|
||||
VaultAWSEngine VaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false"`
|
||||
// [Polling configuration](#polling-configuration) for the AWS waiter. Configures the waiter that checks
|
||||
// resource state.
|
||||
PollingConfig *AWSPollingConfig `mapstructure:"aws_polling" required:"false"`
|
||||
|
||||
getEC2Connection func() ec2iface.EC2API
|
||||
}
|
||||
@@ -214,13 +137,16 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
||||
return c.session, nil
|
||||
}
|
||||
|
||||
// Create new AWS config
|
||||
config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
|
||||
if c.MaxRetries > 0 {
|
||||
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 != "" {
|
||||
config = config.WithRegion(c.RawRegion)
|
||||
}
|
||||
@@ -238,16 +164,6 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
||||
}
|
||||
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{
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
Config: *config,
|
||||
@@ -272,8 +188,10 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
||||
|
||||
cp, err := c.session.Config.Credentials.Get()
|
||||
|
||||
if awserrors.Matches(err, "NoCredentialProviders", "") {
|
||||
return nil, c.NewNoValidCredentialSourcesError(err)
|
||||
if isAWSErr(err, "NoCredentialProviders", "") {
|
||||
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 {
|
||||
@@ -285,6 +203,7 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
||||
if c.DecodeAuthZMessages {
|
||||
DecodeAuthZMessages(c.session)
|
||||
}
|
||||
LogEnvOverrideWarnings()
|
||||
|
||||
return c.session, nil
|
||||
}
|
||||
@@ -304,42 +223,6 @@ func (c *AccessConfig) IsChinaCloud() bool {
|
||||
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 {
|
||||
// const EnvVaultAddress = "VAULT_ADDR"
|
||||
// const EnvVaultToken = "VAULT_TOKEN"
|
||||
@@ -401,21 +284,9 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
fmt.Errorf("`access_key` and `secret_key` must both be either set or not set."))
|
||||
}
|
||||
|
||||
if c.PollingConfig == nil {
|
||||
c.PollingConfig = new(AWSPollingConfig)
|
||||
}
|
||||
c.PollingConfig.LogEnvOverrideWarnings()
|
||||
|
||||
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) {
|
||||
if 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
|
||||
|
||||
import (
|
||||
@@ -6,43 +6,6 @@ import (
|
||||
"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.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatVaultAWSEngineOptions struct {
|
||||
|
||||
@@ -13,7 +13,6 @@ func testAccessConfig() *AccessConfig {
|
||||
getEC2Connection: func() ec2iface.EC2API {
|
||||
return &mockEC2Client{}
|
||||
},
|
||||
PollingConfig: new(AWSPollingConfig),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,32 +79,18 @@ type AMIConfig struct {
|
||||
// Default false.
|
||||
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot" required:"false"`
|
||||
// Whether or not to encrypt the resulting AMI when
|
||||
// copying a provisioned instance to an AMI. By default, Packer will keep
|
||||
// the encryption setting to what it was in the source image. Setting false
|
||||
// will result in an unencrypted image, and true will result in an encrypted
|
||||
// one.
|
||||
//
|
||||
// copying a provisioned instance to an AMI. By default, Packer will keep the
|
||||
// encryption setting to what it was in the source image. Setting false will
|
||||
// result in an unencrypted image, and true will result in an encrypted one.
|
||||
// If you have used the `launch_block_device_mappings` to set an encryption
|
||||
// key and that key is the same as the one you want the image encrypted with
|
||||
// at the end, then you don't need to set this field; leaving it empty will
|
||||
// prevent an unnecessary extra copy step and save you some time.
|
||||
AMIEncryptBootVolume config.Trilean `mapstructure:"encrypt_boot" required:"false"`
|
||||
// ID, alias or ARN of the KMS key to use for AMI encryption. This
|
||||
// only applies to the main `region` -- any regions the AMI gets copied to
|
||||
// copied will be encrypted by the default EBS KMS key for that region,
|
||||
// unless you set region-specific keys in AMIRegionKMSKeyIDs.
|
||||
//
|
||||
// Set this value if you select `encrypt_boot`, but don't want to use the
|
||||
// region's default KMS key.
|
||||
//
|
||||
// If you have a custom kms key you'd like to apply to the launch volume,
|
||||
// and are only building in one region, it is more efficient to leave this
|
||||
// and `encrypt_boot` empty and to instead set the key id in the
|
||||
// launch_block_device_mappings (you can find an example below). This saves
|
||||
// potentially many minutes at the end of the build by preventing Packer
|
||||
// from having to copy and re-encrypt the image at the end of the build.
|
||||
//
|
||||
// For valid formats see *KmsKeyId* in the [AWS API docs -
|
||||
// ID, alias or ARN of the KMS key to use for boot volume encryption. This
|
||||
// only applies to the main `region`, other regions where the AMI will be
|
||||
// copied will be encrypted by the default EBS KMS key. For valid formats
|
||||
// see *KmsKeyId* in the [AWS API docs -
|
||||
// CopyImage](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html).
|
||||
// This field is validated by Packer, when using an alias, you will have to
|
||||
// prefix `kms_key_id` with `alias/`.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// These will be attached when launching your instance. Your
|
||||
// These will be attached when booting a new instance from your AMI. Your
|
||||
// options here may vary depending on the type of VM you use.
|
||||
//
|
||||
// Example use case:
|
||||
@@ -21,31 +21,14 @@ import (
|
||||
// The following mapping will tell Packer to encrypt the root volume of the
|
||||
// build instance at launch using a specific non-default kms key:
|
||||
//
|
||||
// JSON example:
|
||||
//
|
||||
// ```json
|
||||
// launch_block_device_mappings: [
|
||||
// {
|
||||
// "device_name": "/dev/sda1",
|
||||
// "encrypted": true,
|
||||
// "kms_key_id": "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }
|
||||
// ]
|
||||
// [{
|
||||
// "device_name": "/dev/sda1",
|
||||
// "encrypted": true,
|
||||
// "kms_key_id": "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }]
|
||||
// ```
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// launch_block_device_mappings {
|
||||
// device_name = "/dev/sda1"
|
||||
// encrypted = true
|
||||
// kms_key_id = "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Please note that the kms_key_id option in this example exists for
|
||||
// launch_block_device_mappings but not ami_block_device_mappings.
|
||||
//
|
||||
// Documentation for Block Devices Mappings can be found here:
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html
|
||||
//
|
||||
@@ -83,14 +66,12 @@ type BlockDevice struct {
|
||||
// The size of the volume, in GiB. Required if not specifying a
|
||||
// snapshot_id.
|
||||
VolumeSize int64 `mapstructure:"volume_size" required:"false"`
|
||||
// ID, alias or ARN of the KMS key to use for boot volume encryption.
|
||||
// This option exists for launch_block_device_mappings but not
|
||||
// ami_block_device_mappings. The kms key id defined here only applies to
|
||||
// the original build region; if the AMI gets copied to other regions, the
|
||||
// volume in those regions will be encrypted by the default EBS KMS key.
|
||||
// For valid formats see KmsKeyId in the [AWS API docs -
|
||||
// ID, alias or ARN of the KMS key to use for boot volume encryption. This
|
||||
// only applies to the main region, other regions where the AMI will be
|
||||
// copied will be encrypted by the default EBS KMS key. For valid formats
|
||||
// see KmsKeyId in the [AWS API docs -
|
||||
// CopyImage](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html)
|
||||
// This field is validated by Packer. When using an alias, you will have to
|
||||
// This field is validated by Packer, when using an alias, you will have to
|
||||
// prefix kms_key_id with alias/.
|
||||
KmsKeyId string `mapstructure:"kms_key_id" required:"false"`
|
||||
}
|
||||
@@ -134,8 +115,8 @@ func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapp
|
||||
ebsBlockDevice.VolumeSize = aws.Int64(blockDevice.VolumeSize)
|
||||
}
|
||||
|
||||
// IOPS is only valid for io1 and io2 types
|
||||
if blockDevice.VolumeType == "io1" || blockDevice.VolumeType == "io2" {
|
||||
// IOPS is only valid for io1 type
|
||||
if blockDevice.VolumeType == "io1" {
|
||||
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{
|
||||
DeviceName: "/dev/sdb",
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/hashicorp/packer/builder/amazon/common/awserrors"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
)
|
||||
|
||||
@@ -30,7 +31,7 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
||||
err = retry.Config{
|
||||
Tries: 11,
|
||||
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,
|
||||
}.Run(ctx, func(ctx context.Context) error {
|
||||
@@ -53,7 +54,7 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
||||
err = retry.Config{
|
||||
Tries: 11,
|
||||
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,
|
||||
}.Run(ctx, func(ctx context.Context) error {
|
||||
@@ -73,3 +74,14 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
||||
}
|
||||
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 (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ type BuildInfoTemplate struct {
|
||||
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")
|
||||
if !hasSourceAMI {
|
||||
return &BuildInfoTemplate{
|
||||
@@ -40,24 +40,6 @@ func extractBuildInfo(region string, state multistep.StateBag, generatedData *pa
|
||||
SourceAMIOwnerName: aws.StringValue(sourceAMI.ImageOwnerAlias),
|
||||
SourceAMITags: sourceAMITags,
|
||||
}
|
||||
|
||||
generatedData.Put("BuildRegion", buildInfoTemplate.BuildRegion)
|
||||
generatedData.Put("SourceAMI", buildInfoTemplate.SourceAMI)
|
||||
generatedData.Put("SourceAMICreationDate", buildInfoTemplate.SourceAMICreationDate)
|
||||
generatedData.Put("SourceAMIName", buildInfoTemplate.SourceAMIName)
|
||||
generatedData.Put("SourceAMIOwner", buildInfoTemplate.SourceAMIOwner)
|
||||
generatedData.Put("SourceAMIOwnerName", buildInfoTemplate.SourceAMIOwnerName)
|
||||
|
||||
return buildInfoTemplate
|
||||
}
|
||||
|
||||
func GetGeneratedDataList() []string {
|
||||
return []string{
|
||||
"SourceAMIName",
|
||||
"BuildRegion",
|
||||
"SourceAMI",
|
||||
"SourceAMICreationDate",
|
||||
"SourceAMIOwner",
|
||||
"SourceAMIOwnerName",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,6 @@ func testImage() *ec2.Image {
|
||||
Name: aws.String("ami_test_name"),
|
||||
OwnerId: aws.String("ami_test_owner_id"),
|
||||
ImageOwnerAlias: aws.String("ami_test_owner_alias"),
|
||||
RootDeviceType: aws.String("ebs"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String("key-1"),
|
||||
@@ -36,8 +35,8 @@ func testState() multistep.StateBag {
|
||||
return state
|
||||
}
|
||||
|
||||
func testGeneratedData(state multistep.StateBag) packerbuilderdata.GeneratedData {
|
||||
generatedData := packerbuilderdata.GeneratedData{State: state}
|
||||
func testGeneratedData(state multistep.StateBag) builder.GeneratedData {
|
||||
generatedData := builder.GeneratedData{State: state}
|
||||
return generatedData
|
||||
}
|
||||
|
||||
|
||||
@@ -53,14 +53,14 @@ type VpcFilterOptions struct {
|
||||
}
|
||||
|
||||
type Statement struct {
|
||||
Effect string `mapstructure:"Effect" required:"false"`
|
||||
Action []string `mapstructure:"Action" required:"false"`
|
||||
Resource []string `mapstructure:"Resource" required:"false"`
|
||||
Effect string
|
||||
Action []string
|
||||
Resource string
|
||||
}
|
||||
|
||||
type PolicyDocument struct {
|
||||
Version string `mapstructure:"Version" required:"false"`
|
||||
Statement []Statement `mapstructure:"Statement" required:"false"`
|
||||
Version string
|
||||
Statement []Statement
|
||||
}
|
||||
|
||||
type SecurityGroupFilterOptions struct {
|
||||
@@ -89,23 +89,19 @@ type RunConfig struct {
|
||||
// which will stop the instance for you. If this is set to `true`, Packer
|
||||
// *will not* stop the instance but will assume that you will send the stop
|
||||
// signal yourself through your final provisioner. You can do this with a
|
||||
// [windows-shell provisioner](/docs/provisioners/windows-shell). Note that
|
||||
// Packer will still wait for the instance to be stopped, and failing to
|
||||
// send the stop signal yourself, when you have set this flag to `true`,
|
||||
// will cause a timeout.
|
||||
// [windows-shell
|
||||
// provisioner](/docs/provisioners/windows-shell).
|
||||
// Note that Packer will still wait for the instance to be stopped, and
|
||||
// failing to send the stop signal yourself, when you have set this flag to
|
||||
// `true`, will cause a timeout.
|
||||
// Example of a valid shutdown command:
|
||||
//
|
||||
// An example of a valid windows shutdown command in a `windows-shell`
|
||||
// provisioner is :
|
||||
// ```shell-session
|
||||
// ec2config.exe -sysprep
|
||||
// ```json
|
||||
// {
|
||||
// "type": "windows-shell",
|
||||
// "inline": ["\"c:\\Program Files\\Amazon\\Ec2ConfigService\\ec2config.exe\" -sysprep"]
|
||||
// }
|
||||
// ```
|
||||
// or
|
||||
// ```sell-session
|
||||
// "%programfiles%\amazon\ec2configservice\"ec2config.exe -sysprep""
|
||||
// ```
|
||||
// -> Note: The double quotation marks in the command are not required if
|
||||
// your CMD shell is already in the
|
||||
// `C:\Program Files\Amazon\EC2ConfigService\` directory.
|
||||
DisableStopInstance bool `mapstructure:"disable_stop_instance" required:"false"`
|
||||
// Mark instance as [EBS
|
||||
// Optimized](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
|
||||
@@ -166,7 +162,7 @@ type RunConfig struct {
|
||||
// The EC2 instance type to use while building the
|
||||
// AMI, such as t2.small.
|
||||
InstanceType string `mapstructure:"instance_type" required:"true"`
|
||||
// Filters used to populate the `security_group_ids` field. JSON Example:
|
||||
// Filters used to populate the `security_group_ids` field. Example:
|
||||
//
|
||||
// ```json
|
||||
// {
|
||||
@@ -178,16 +174,6 @@ type RunConfig struct {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// HCL2 Example:
|
||||
//
|
||||
// ```hcl
|
||||
// security_group_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "packer"
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the SG's with tag `Class` with the value `packer`.
|
||||
//
|
||||
// - `filters` (map of strings) - filters used to select a
|
||||
@@ -223,39 +209,21 @@ type RunConfig struct {
|
||||
// used when from_scratch is set to true.
|
||||
SourceAmi string `mapstructure:"source_ami" required:"true"`
|
||||
// Filters used to populate the `source_ami`
|
||||
// field. JSON Example:
|
||||
// field. Example:
|
||||
//
|
||||
// ```json
|
||||
// "builders" [
|
||||
// ```json
|
||||
// {
|
||||
// "type": "amazon-ebs",
|
||||
// "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
|
||||
// "filters": {
|
||||
// "virtualization-type": "hvm",
|
||||
// "name": "ubuntu/images/\*ubuntu-xenial-16.04-amd64-server-\*",
|
||||
// "root-device-type": "ebs"
|
||||
// },
|
||||
// "owners": ["099720109477"],
|
||||
// "most_recent": true
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ```
|
||||
//
|
||||
// This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. NOTE:
|
||||
// This will fail unless *exactly* one AMI is returned. In the above example,
|
||||
@@ -323,12 +291,10 @@ type RunConfig struct {
|
||||
// will allow you to create those programatically.
|
||||
SpotTag hcl2template.KeyValues `mapstructure:"spot_tag" required:"false"`
|
||||
// Filters used to populate the `subnet_id` field.
|
||||
// JSON Example:
|
||||
// Example:
|
||||
//
|
||||
// ```json
|
||||
// "builders" [
|
||||
// ```json
|
||||
// {
|
||||
// "type": "amazon-ebs",
|
||||
// "subnet_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "build"
|
||||
@@ -337,21 +303,7 @@ type RunConfig struct {
|
||||
// "random": false
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// subnet_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build"
|
||||
// }
|
||||
// most_free = true
|
||||
// random = false
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ```
|
||||
//
|
||||
// This selects the Subnet with tag `Class` with the value `build`, which has
|
||||
// the most free IP addresses. NOTE: This will fail unless *exactly* one
|
||||
@@ -376,12 +328,6 @@ type RunConfig struct {
|
||||
// subnet-12345def, where Packer will launch the EC2 instance. This field is
|
||||
// required if you are using an non-default VPC.
|
||||
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
|
||||
// generate. By default, Packer generates a name that looks like
|
||||
// `packer_<UUID>`, where <UUID> is a 36 character unique identifier.
|
||||
@@ -402,28 +348,12 @@ type RunConfig struct {
|
||||
// data when launching the instance.
|
||||
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
||||
// Filters used to populate the `vpc_id` field.
|
||||
// JSON Example:
|
||||
// Example:
|
||||
//
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
// "type": "amazon-ebs",
|
||||
// "vpc_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "build",
|
||||
// "isDefault": "false",
|
||||
// "cidr": "/24"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// vpc_filter {
|
||||
// filters = {
|
||||
// {
|
||||
// "vpc_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "build",
|
||||
// "isDefault": "false",
|
||||
// "cidr": "/24"
|
||||
@@ -479,13 +409,6 @@ type RunConfig struct {
|
||||
// terminating the tunnel it will automatically terminate itself after 20 minutes of inactivity.
|
||||
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
|
||||
// left blank, Packer will choose a port for you from available ports.
|
||||
// This option is only used when `ssh_interface` is set `session_manager`.
|
||||
@@ -633,13 +556,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
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||
// FlatPolicyDocument is an auto-generated flat version of PolicyDocument.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatPolicyDocument struct {
|
||||
Version *string `mapstructure:"Version" required:"false" cty:"Version" hcl:"Version"`
|
||||
Statement []FlatStatement `mapstructure:"Statement" required:"false" cty:"Statement" hcl:"Statement"`
|
||||
Version *string `cty:"version" hcl:"version"`
|
||||
Statement []FlatStatement `cty:"statement" hcl:"statement"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (*FlatPolicyDocument) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"Version": &hcldec.AttrSpec{Name: "Version", Type: cty.String, Required: false},
|
||||
"Statement": &hcldec.BlockListSpec{TypeName: "Statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())},
|
||||
"version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false},
|
||||
"statement": &hcldec.BlockListSpec{TypeName: "statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())},
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -89,9 +89,9 @@ func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||
// FlatStatement is an auto-generated flat version of Statement.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatStatement struct {
|
||||
Effect *string `mapstructure:"Effect" required:"false" cty:"Effect" hcl:"Effect"`
|
||||
Action []string `mapstructure:"Action" required:"false" cty:"Action" hcl:"Action"`
|
||||
Resource []string `mapstructure:"Resource" required:"false" cty:"Resource" hcl:"Resource"`
|
||||
Effect *string `cty:"effect" hcl:"effect"`
|
||||
Action []string `cty:"action" hcl:"action"`
|
||||
Resource *string `cty:"resource" hcl:"resource"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (*FlatStatement) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"Effect": &hcldec.AttrSpec{Name: "Effect", Type: 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},
|
||||
"effect": &hcldec.AttrSpec{Name: "effect", Type: cty.String, Required: false},
|
||||
"action": &hcldec.AttrSpec{Name: "action", Type: cty.List(cty.String), Required: false},
|
||||
"resource": &hcldec.AttrSpec{Name: "resource", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -232,23 +232,3 @@ func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
|
||||
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
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type AWSPollingConfig
|
||||
package common
|
||||
|
||||
import (
|
||||
@@ -40,45 +38,12 @@ type StateChangeConf struct {
|
||||
// Following are wrapper functions that use Packer's environment-variables to
|
||||
// determine retry logic, then call the AWS SDK's built-in waiters.
|
||||
|
||||
// Polling configuration for the AWS waiter. Configures the waiter for resources creation or actions like attaching
|
||||
// volumes or importing image.
|
||||
// Usage example:
|
||||
//
|
||||
// In JSON:
|
||||
// ```json
|
||||
// "aws_polling" : {
|
||||
// "delay_seconds": 30,
|
||||
// "max_attempts": 50
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// In HCL2:
|
||||
// ```hcl
|
||||
// aws_polling {
|
||||
// delay_seconds = 30
|
||||
// max_attempts = 50
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
type AWSPollingConfig struct {
|
||||
// Specifies the maximum number of attempts the waiter will check for resource state.
|
||||
// This value can also be set via the AWS_MAX_ATTEMPTS.
|
||||
// If both option and environment variable are set, the max_attempts will be considered over the AWS_MAX_ATTEMPTS.
|
||||
// If none is set, defaults to AWS waiter default which is 40 max_attempts.
|
||||
MaxAttempts int `mapstructure:"max_attempts" required:"false"`
|
||||
// Specifies the delay in seconds between attempts to check the resource state.
|
||||
// This value can also be set via the AWS_POLL_DELAY_SECONDS.
|
||||
// If both option and environment variable are set, the delay_seconds will be considered over the AWS_POLL_DELAY_SECONDS.
|
||||
// If none is set, defaults to AWS waiter default which is 15 seconds.
|
||||
DelaySeconds int `mapstructure:"delay_seconds" required:"false"`
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilAMIAvailable(ctx aws.Context, conn ec2iface.EC2API, imageId string) error {
|
||||
func WaitUntilAMIAvailable(ctx aws.Context, conn ec2iface.EC2API, imageId string) error {
|
||||
imageInput := ec2.DescribeImagesInput{
|
||||
ImageIds: []*string{&imageId},
|
||||
}
|
||||
|
||||
waitOpts := w.getWaiterOptions()
|
||||
waitOpts := getWaiterOptions()
|
||||
if len(waitOpts) == 0 {
|
||||
// Bump this default to 30 minutes because the aws default
|
||||
// of ten minutes doesn't work for some of our long-running copies.
|
||||
@@ -101,7 +66,7 @@ func (w *AWSPollingConfig) WaitUntilAMIAvailable(ctx aws.Context, conn ec2iface.
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilInstanceRunning(ctx aws.Context, conn *ec2.EC2, instanceId string) error {
|
||||
func WaitUntilInstanceRunning(ctx aws.Context, conn *ec2.EC2, instanceId string) error {
|
||||
|
||||
instanceInput := ec2.DescribeInstancesInput{
|
||||
InstanceIds: []*string{&instanceId},
|
||||
@@ -110,11 +75,12 @@ func (w *AWSPollingConfig) WaitUntilInstanceRunning(ctx aws.Context, conn *ec2.E
|
||||
err := conn.WaitUntilInstanceRunningWithContext(
|
||||
ctx,
|
||||
&instanceInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId string) error {
|
||||
func WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId string) error {
|
||||
|
||||
instanceInput := ec2.DescribeInstancesInput{
|
||||
InstanceIds: []*string{&instanceId},
|
||||
}
|
||||
@@ -122,12 +88,12 @@ func (w *AWSPollingConfig) WaitUntilInstanceTerminated(ctx aws.Context, conn *ec
|
||||
err := conn.WaitUntilInstanceTerminatedWithContext(
|
||||
ctx,
|
||||
&instanceInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
// This function works for both requesting and cancelling spot instances.
|
||||
func (w *AWSPollingConfig) WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId string) error {
|
||||
func WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId string) error {
|
||||
spotRequestInput := ec2.DescribeSpotInstanceRequestsInput{
|
||||
SpotInstanceRequestIds: []*string{&spotRequestId},
|
||||
}
|
||||
@@ -135,11 +101,11 @@ func (w *AWSPollingConfig) WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *
|
||||
err := conn.WaitUntilSpotInstanceRequestFulfilledWithContext(
|
||||
ctx,
|
||||
&spotRequestInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
|
||||
func WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
|
||||
volumeInput := ec2.DescribeVolumesInput{
|
||||
VolumeIds: []*string{&volumeId},
|
||||
}
|
||||
@@ -147,11 +113,11 @@ func (w *AWSPollingConfig) WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.E
|
||||
err := conn.WaitUntilVolumeAvailableWithContext(
|
||||
ctx,
|
||||
&volumeInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error {
|
||||
func WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error {
|
||||
snapInput := ec2.DescribeSnapshotsInput{
|
||||
SnapshotIds: []*string{&snapshotID},
|
||||
}
|
||||
@@ -159,13 +125,13 @@ func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2,
|
||||
err := conn.WaitUntilSnapshotCompletedWithContext(
|
||||
ctx,
|
||||
&snapInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
// Wrappers for our custom AWS waiters
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
|
||||
func WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
|
||||
volumeInput := ec2.DescribeVolumesInput{
|
||||
VolumeIds: []*string{&volumeId},
|
||||
}
|
||||
@@ -173,11 +139,11 @@ func (w *AWSPollingConfig) WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC
|
||||
err := WaitForVolumeToBeAttached(conn,
|
||||
ctx,
|
||||
&volumeInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
|
||||
func WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
|
||||
volumeInput := ec2.DescribeVolumesInput{
|
||||
VolumeIds: []*string{&volumeId},
|
||||
}
|
||||
@@ -185,11 +151,11 @@ func (w *AWSPollingConfig) WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC
|
||||
err := WaitForVolumeToBeDetached(conn,
|
||||
ctx,
|
||||
&volumeInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error {
|
||||
func WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error {
|
||||
importInput := ec2.DescribeImportImageTasksInput{
|
||||
ImportTaskIds: []*string{&taskID},
|
||||
}
|
||||
@@ -197,7 +163,7 @@ func (w *AWSPollingConfig) WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2
|
||||
err := WaitForImageToBeImported(conn,
|
||||
ctx,
|
||||
&importInput,
|
||||
w.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -332,18 +298,8 @@ type overridableWaitVars struct {
|
||||
awsTimeoutSeconds envInfo
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) getWaiterOptions() []request.WaiterOption {
|
||||
func getWaiterOptions() []request.WaiterOption {
|
||||
envOverrides := getEnvOverrides()
|
||||
|
||||
if w.MaxAttempts != 0 {
|
||||
envOverrides.awsMaxAttempts.Val = w.MaxAttempts
|
||||
envOverrides.awsMaxAttempts.overridden = true
|
||||
}
|
||||
if w.DelaySeconds != 0 {
|
||||
envOverrides.awsPollDelaySeconds.Val = w.DelaySeconds
|
||||
envOverrides.awsPollDelaySeconds.overridden = true
|
||||
}
|
||||
|
||||
waitOpts := applyEnvOverrides(envOverrides)
|
||||
return waitOpts
|
||||
}
|
||||
@@ -377,38 +333,33 @@ func getEnvOverrides() overridableWaitVars {
|
||||
return envValues
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) LogEnvOverrideWarnings() {
|
||||
pollDelayEnv := os.Getenv("AWS_POLL_DELAY_SECONDS")
|
||||
timeoutSecondsEnv := os.Getenv("AWS_TIMEOUT_SECONDS")
|
||||
maxAttemptsEnv := os.Getenv("AWS_MAX_ATTEMPTS")
|
||||
func LogEnvOverrideWarnings() {
|
||||
pollDelay := os.Getenv("AWS_POLL_DELAY_SECONDS")
|
||||
timeoutSeconds := os.Getenv("AWS_TIMEOUT_SECONDS")
|
||||
maxAttempts := os.Getenv("AWS_MAX_ATTEMPTS")
|
||||
|
||||
maxAttemptsIsSet := maxAttemptsEnv != "" || w.MaxAttempts != 0
|
||||
timeoutSecondsIsSet := timeoutSecondsEnv != ""
|
||||
pollDelayIsSet := pollDelayEnv != "" || w.DelaySeconds != 0
|
||||
|
||||
if maxAttemptsIsSet && timeoutSecondsIsSet {
|
||||
if maxAttempts != "" && timeoutSeconds != "" {
|
||||
warning := fmt.Sprintf("[WARNING] (aws): AWS_MAX_ATTEMPTS and " +
|
||||
"AWS_TIMEOUT_SECONDS are both set. Packer will use " +
|
||||
"AWS_MAX_ATTEMPTS and discard AWS_TIMEOUT_SECONDS.")
|
||||
if !pollDelayIsSet {
|
||||
if pollDelay == "" {
|
||||
warning = fmt.Sprintf("%s Since you have not set the poll delay, "+
|
||||
"Packer will default to a 2-second delay.", warning)
|
||||
}
|
||||
log.Printf(warning)
|
||||
} else if timeoutSecondsIsSet {
|
||||
} else if timeoutSeconds != "" {
|
||||
log.Printf("[WARNING] (aws): env var AWS_TIMEOUT_SECONDS is " +
|
||||
"deprecated in favor of AWS_MAX_ATTEMPTS env or aws_polling_max_attempts config option. " +
|
||||
"If you have not explicitly set AWS_POLL_DELAY_SECONDS env or aws_polling_delay_seconds config option, " +
|
||||
"we are defaulting to a poll delay of 2 seconds, regardless of the AWS waiter's default.")
|
||||
"deprecated in favor of AWS_MAX_ATTEMPTS. If you have not " +
|
||||
"explicitly set AWS_POLL_DELAY_SECONDS, we are defaulting to a " +
|
||||
"poll delay of 2 seconds, regardless of the AWS waiter's default.")
|
||||
}
|
||||
if !maxAttemptsIsSet && !timeoutSecondsIsSet && !pollDelayIsSet {
|
||||
if maxAttempts == "" && timeoutSeconds == "" && pollDelay == "" {
|
||||
log.Printf("[INFO] (aws): No AWS timeout and polling overrides have been set. " +
|
||||
"Packer will default to waiter-specific delays and timeouts. If you would " +
|
||||
"like to customize the length of time between retries and max " +
|
||||
"number of retries you may do so by setting the environment " +
|
||||
"variables AWS_POLL_DELAY_SECONDS and AWS_MAX_ATTEMPTS or the " +
|
||||
"configuration options aws_polling_delay_seconds and aws_polling_max_attempts " +
|
||||
"to your desired values.")
|
||||
"variables AWS_POLL_DELAY_SECONDS and AWS_MAX_ATTEMPTS to your " +
|
||||
"desired values.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type AWSPollingConfig"; DO NOT EDIT.
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// FlatAWSPollingConfig is an auto-generated flat version of AWSPollingConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatAWSPollingConfig struct {
|
||||
MaxAttempts *int `mapstructure:"max_attempts" required:"false" cty:"max_attempts" hcl:"max_attempts"`
|
||||
DelaySeconds *int `mapstructure:"delay_seconds" required:"false" cty:"delay_seconds" hcl:"delay_seconds"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatAWSPollingConfig.
|
||||
// FlatAWSPollingConfig is an auto-generated flat version of AWSPollingConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*AWSPollingConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatAWSPollingConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a AWSPollingConfig.
|
||||
// This spec is used by HCL to read the fields of AWSPollingConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatAWSPollingConfig.
|
||||
func (*FlatAWSPollingConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"max_attempts": &hcldec.AttrSpec{Name: "max_attempts", Type: cty.Number, Required: false},
|
||||
"delay_seconds": &hcldec.AttrSpec{Name: "delay_seconds", Type: cty.Number, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func (s *StepAMIRegionCopy) amiRegionCopy(ctx context.Context, state multistep.S
|
||||
}
|
||||
|
||||
// Wait for the image to become ready
|
||||
if err := s.AccessConfig.PollingConfig.WaitUntilAMIAvailable(ctx, regionconn, *resp.ImageId); err != nil {
|
||||
if err := WaitUntilAMIAvailable(ctx, regionconn, *resp.ImageId); err != nil {
|
||||
return "", snapshotIds, fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s",
|
||||
*resp.ImageId, target, err)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"strconv"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"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/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
@@ -21,8 +20,8 @@ type StepCreateSSMTunnel struct {
|
||||
LocalPortNumber int
|
||||
RemotePortNumber int
|
||||
SSMAgentEnabled bool
|
||||
PauseBeforeSSM time.Duration
|
||||
stopSSMCommand func()
|
||||
instanceId string
|
||||
driver *SSMDriver
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
if err := s.ConfigureLocalHostPort(ctx); err != nil {
|
||||
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)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
s.instanceId = aws.StringValue(instance.InstanceId)
|
||||
|
||||
state.Put("sessionPort", s.LocalPortNumber)
|
||||
|
||||
ssmCtx, ssmCancel := context.WithCancel(ctx)
|
||||
s.stopSSMCommand = ssmCancel
|
||||
|
||||
go func() {
|
||||
if s.driver == nil {
|
||||
ssmconn := ssm.New(s.AWSSession)
|
||||
err := pssm.Session{
|
||||
SvcClient: ssmconn,
|
||||
InstanceID: aws.StringValue(instance.InstanceId),
|
||||
RemotePort: s.RemotePortNumber,
|
||||
LocalPort: s.LocalPortNumber,
|
||||
Region: s.Region,
|
||||
}.Start(ssmCtx, ui)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("ssm error: %s", err))
|
||||
cfg := SSMDriverConfig{
|
||||
SvcClient: ssmconn,
|
||||
Region: s.Region,
|
||||
SvcEndpoint: ssmconn.Endpoint,
|
||||
}
|
||||
}()
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
if !s.SSMAgentEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
if s.stopSSMCommand != nil {
|
||||
s.stopSSMCommand()
|
||||
if err := s.driver.StopSession(); err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,3 +116,19 @@ func (s *StepCreateSSMTunnel) ConfigureLocalHostPort(ctx context.Context) error
|
||||
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/session"
|
||||
"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/helper/multistep"
|
||||
"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
|
||||
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 false
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"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/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
@@ -22,7 +22,7 @@ type StepModifyAMIAttributes struct {
|
||||
Description string
|
||||
Ctx interpolate.Context
|
||||
|
||||
GeneratedData *packerbuilderdata.GeneratedData
|
||||
GeneratedData *builder.GeneratedData
|
||||
}
|
||||
|
||||
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/service/ec2"
|
||||
"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/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
@@ -40,7 +39,7 @@ func (s *StepPreValidate) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
err := retry.Config{
|
||||
Tries: 11,
|
||||
ShouldRetry: func(err error) bool {
|
||||
if awserrors.Matches(err, "AuthFailure", "") {
|
||||
if isAWSErr(err, "AuthFailure", "") {
|
||||
log.Printf("Waiting for Vault-generated AWS credentials" +
|
||||
" to pass authentication... trying again.")
|
||||
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)}})
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
@@ -20,7 +19,6 @@ import (
|
||||
)
|
||||
|
||||
type StepRunSourceInstance struct {
|
||||
PollingConfig *AWSPollingConfig
|
||||
AssociatePublicIpAddress bool
|
||||
LaunchMappings EC2BlockDeviceMappingsBuilder
|
||||
Comm *communicator.Config
|
||||
@@ -34,7 +32,6 @@ type StepRunSourceInstance struct {
|
||||
IsRestricted bool
|
||||
SourceAMI string
|
||||
Tags map[string]string
|
||||
Tenancy string
|
||||
UserData string
|
||||
UserDataFile string
|
||||
VolumeTags map[string]string
|
||||
@@ -197,15 +194,11 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior
|
||||
}
|
||||
|
||||
if s.Tenancy != "" {
|
||||
runOpts.Placement.Tenancy = aws.String(s.Tenancy)
|
||||
}
|
||||
|
||||
var runResp *ec2.Reservation
|
||||
err = retry.Config{
|
||||
Tries: 11,
|
||||
ShouldRetry: func(err error) bool {
|
||||
if awserrors.Matches(err, "InvalidParameterValue", "iamInstanceProfile") {
|
||||
if isAWSErr(err, "InvalidParameterValue", "iamInstanceProfile") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -216,7 +209,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
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")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
@@ -241,7 +234,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
InstanceIds: []*string{aws.String(instanceId)},
|
||||
}
|
||||
|
||||
if err := s.PollingConfig.WaitUntilInstanceRunning(ctx, ec2conn, instanceId); err != nil {
|
||||
if err := WaitUntilInstanceRunning(ctx, ec2conn, instanceId); err != nil {
|
||||
err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
@@ -254,7 +247,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
|
||||
var r *ec2.DescribeInstancesOutput
|
||||
err = retry.Config{Tries: 11, ShouldRetry: func(err error) bool {
|
||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||
if isAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -299,7 +292,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
ec2Tags.Report(ui)
|
||||
// Retry creating tags for about 2.5 minutes
|
||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||
if isAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -371,7 +364,7 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.PollingConfig.WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil {
|
||||
if err := WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,10 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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/retry"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
@@ -26,7 +23,6 @@ type EC2BlockDeviceMappingsBuilder interface {
|
||||
}
|
||||
|
||||
type StepRunSpotInstance struct {
|
||||
PollingConfig *AWSPollingConfig
|
||||
AssociatePublicIpAddress bool
|
||||
LaunchMappings EC2BlockDeviceMappingsBuilder
|
||||
BlockDurationMinutes int64
|
||||
@@ -36,7 +32,6 @@ type StepRunSpotInstance struct {
|
||||
ExpectedRootDevice string
|
||||
InstanceInitiatedShutdownBehavior string
|
||||
InstanceType string
|
||||
Region string
|
||||
SourceAMI string
|
||||
SpotPrice string
|
||||
SpotTags map[string]string
|
||||
@@ -160,7 +155,7 @@ func (s *StepRunSpotInstance) LoadUserData() (string, error) {
|
||||
}
|
||||
|
||||
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.Say("Launching a spot AWS instance...")
|
||||
@@ -199,7 +194,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
}
|
||||
|
||||
// Convert tags from the tag map provided by the user into *ec2.Tag s
|
||||
ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, s.Region, state)
|
||||
ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating tags for source instance: %s", err)
|
||||
state.Put("error", err)
|
||||
@@ -223,14 +218,6 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
}
|
||||
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
|
||||
ui.Message("Loading User Data File...")
|
||||
|
||||
@@ -253,14 +240,6 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
LaunchTemplateName: aws.String(launchTemplateName),
|
||||
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
|
||||
_, err = ec2conn.CreateLaunchTemplate(launchTemplate)
|
||||
@@ -298,53 +277,36 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
Type: aws.String("instant"),
|
||||
}
|
||||
|
||||
var createOutput *ec2.CreateFleetOutput
|
||||
err = retry.Config{
|
||||
Tries: 11,
|
||||
ShouldRetry: func(err error) bool {
|
||||
if strings.Contains(err.Error(), "Invalid IAM Instance Profile name") {
|
||||
// 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
|
||||
})
|
||||
|
||||
// Create the request for the spot instance.
|
||||
req, createOutput := ec2conn.CreateFleetRequest(createFleetInput)
|
||||
ui.Message(fmt.Sprintf("Sending spot request (%s)...", req.RequestID))
|
||||
// Actually send the spot connection request.
|
||||
err = req.Send()
|
||||
if err != nil {
|
||||
if createOutput.FleetId != nil {
|
||||
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 {
|
||||
errString := fmt.Sprintf("Error waiting for fleet request (%s) to become ready:", *createOutput.FleetId)
|
||||
for _, outErr := range createOutput.Errors {
|
||||
errString = errString + aws.StringValue(outErr.ErrorMessage)
|
||||
}
|
||||
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]
|
||||
@@ -362,11 +324,6 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
describeOutput, err = ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{
|
||||
InstanceIds: []*string{aws.String(instanceId)},
|
||||
})
|
||||
if len(describeOutput.Reservations) > 0 && len(describeOutput.Reservations[0].Instances) > 0 {
|
||||
if len(s.LaunchMappings.BuildEC2BlockDeviceMappings()) > 0 && len(describeOutput.Reservations[0].Instances[0].BlockDeviceMappings) == 0 {
|
||||
return fmt.Errorf("Instance has no block devices")
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil || len(describeOutput.Reservations) == 0 || len(describeOutput.Reservations[0].Instances) == 0 {
|
||||
@@ -379,6 +336,14 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
instance := describeOutput.Reservations[0].Instances[0]
|
||||
|
||||
// Tag the spot instance request (not the eventual spot instance)
|
||||
spotTags, err := 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 {
|
||||
spotTags.Report(ui)
|
||||
// Use the instance ID to find out the SIR, so that we can tag the spot
|
||||
@@ -407,7 +372,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
|
||||
// Retry creating tags for about 2.5 minutes
|
||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||
if isAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -438,7 +403,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
if len(volumeIds) > 0 && len(s.VolumeTags) > 0 {
|
||||
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 {
|
||||
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
||||
state.Put("error", err)
|
||||
@@ -496,7 +461,7 @@ func (s *StepRunSpotInstance) Cleanup(state multistep.StateBag) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.PollingConfig.WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil {
|
||||
if err := WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
@@ -31,7 +29,6 @@ func tStateSpot() multistep.StateBag {
|
||||
|
||||
func getBasicStep() *StepRunSpotInstance {
|
||||
stepRunSpotInstance := StepRunSpotInstance{
|
||||
PollingConfig: new(AWSPollingConfig),
|
||||
AssociatePublicIpAddress: false,
|
||||
LaunchMappings: BlockDevices{},
|
||||
BlockDurationMinutes: 0,
|
||||
@@ -45,7 +42,6 @@ func getBasicStep() *StepRunSpotInstance {
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceInitiatedShutdownBehavior: "stop",
|
||||
InstanceType: "t2.micro",
|
||||
Region: "us-east-1",
|
||||
SourceAMI: "",
|
||||
SpotPrice: "auto",
|
||||
SpotTags: nil,
|
||||
@@ -137,226 +133,3 @@ func TestCreateTemplateData_NoEphemeral(t *testing.T) {
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
// &awscommon.StepSetGeneratedData{
|
||||
// GeneratedData: generatedData,
|
||||
// },
|
||||
|
||||
type StepSetGeneratedData struct {
|
||||
GeneratedData *packerbuilderdata.GeneratedData
|
||||
}
|
||||
|
||||
func (s *StepSetGeneratedData) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
|
||||
extractBuildInfo(*ec2conn.Config.Region, state, s.GeneratedData)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepSetGeneratedData) Cleanup(state multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
||||
@@ -6,14 +6,12 @@ import (
|
||||
"time"
|
||||
|
||||
"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/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepStopEBSBackedInstance struct {
|
||||
PollingConfig *AWSPollingConfig
|
||||
Skip bool
|
||||
DisableStopInstance bool
|
||||
}
|
||||
@@ -43,7 +41,7 @@ func (s *StepStopEBSBackedInstance) Run(ctx context.Context, state multistep.Sta
|
||||
|
||||
// Work around this by retrying a few times, up to about 5 minutes.
|
||||
err := retry.Config{Tries: 6, ShouldRetry: func(error) bool {
|
||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||
if isAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -76,7 +74,7 @@ func (s *StepStopEBSBackedInstance) Run(ctx context.Context, state multistep.Sta
|
||||
&ec2.DescribeInstancesInput{
|
||||
InstanceIds: []*string{instance.InstanceId},
|
||||
},
|
||||
s.PollingConfig.getWaiterOptions()...)
|
||||
getWaiterOptions()...)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error waiting for instance to stop: %s", err)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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/packer"
|
||||
"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) {
|
||||
var ec2Tags []*ec2.Tag
|
||||
generatedData := packerbuilderdata.GeneratedData{State: state}
|
||||
generatedData := builder.GeneratedData{State: state}
|
||||
ictx.Data = extractBuildInfo(region, state, &generatedData)
|
||||
|
||||
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/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/hcl2template"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"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) {
|
||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"ami_description",
|
||||
"run_tags",
|
||||
"run_tag",
|
||||
"run_volume_tags",
|
||||
"run_volume_tag",
|
||||
"spot_tags",
|
||||
"spot_tag",
|
||||
"snapshot_tags",
|
||||
"snapshot_tag",
|
||||
"tags",
|
||||
"tag",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
@@ -146,7 +140,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
|
||||
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
|
||||
|
||||
generatedData := awscommon.GetGeneratedDataList()
|
||||
generatedData := []string{"SourceAMIName"}
|
||||
return generatedData, warns, nil
|
||||
}
|
||||
|
||||
@@ -169,13 +163,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state.Put("awsSession", session)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
||||
generatedData := &builder.GeneratedData{State: state}
|
||||
|
||||
var instanceStep multistep.Step
|
||||
|
||||
if b.config.IsSpotInstance() {
|
||||
instanceStep = &awscommon.StepRunSpotInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.LaunchMappings,
|
||||
BlockDurationMinutes: b.config.BlockDurationMinutes,
|
||||
@@ -186,7 +179,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotTags: b.config.SpotTags,
|
||||
@@ -199,7 +191,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
} else {
|
||||
instanceStep = &awscommon.StepRunSourceInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.LaunchMappings,
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
@@ -213,7 +204,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
@@ -277,7 +267,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&awscommon.StepCreateSSMTunnel{
|
||||
AWSSession: session,
|
||||
Region: *ec2conn.Config.Region,
|
||||
PauseBeforeSSM: b.config.PauseBeforeSSM,
|
||||
LocalPortNumber: b.config.SessionManagerPort,
|
||||
RemotePortNumber: b.config.Comm.Port(),
|
||||
SSMAgentEnabled: b.config.SSMAgentEnabled(),
|
||||
@@ -295,15 +284,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
),
|
||||
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&awscommon.StepSetGeneratedData{
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&common.StepProvision{},
|
||||
&common.StepCleanupTempKeys{
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
},
|
||||
&awscommon.StepStopEBSBackedInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
Skip: b.config.IsSpotInstance(),
|
||||
DisableStopInstance: b.config.DisableStopInstance,
|
||||
},
|
||||
@@ -320,7 +305,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
},
|
||||
&stepCreateAMI{
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
||||
@@ -19,9 +19,7 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -31,10 +29,8 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type" hcl:"ami_virtualization_type"`
|
||||
@@ -81,7 +77,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -95,18 +90,14 @@ type FlatConfig struct {
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
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"`
|
||||
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"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
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"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
@@ -116,7 +107,6 @@ type FlatConfig struct {
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
@@ -126,8 +116,8 @@ type FlatConfig struct {
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
@@ -138,7 +128,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -167,9 +156,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_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},
|
||||
"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},
|
||||
"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},
|
||||
"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},
|
||||
@@ -179,10 +166,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
|
||||
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
|
||||
"ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false},
|
||||
"ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false},
|
||||
"ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false},
|
||||
@@ -229,7 +214,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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_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_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},
|
||||
@@ -244,13 +228,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
@@ -264,7 +244,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
@@ -286,7 +265,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"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())},
|
||||
|
||||
@@ -6,7 +6,6 @@ package ebs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
@@ -105,10 +104,10 @@ func checkSnapshotsDeleted(snapshotIds []*string) builderT.TestCheckFunc {
|
||||
|
||||
func TestBuilderAcc_amiSharing(t *testing.T) {
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccSharingPreCheck(t) },
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
Template: buildSharingConfig(os.Getenv("TESTACC_AWS_ACCOUNT_ID")),
|
||||
Check: checkAMISharing(2, os.Getenv("TESTACC_AWS_ACCOUNT_ID"), "all"),
|
||||
Template: testBuilderAccSharing,
|
||||
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 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) {
|
||||
access := &common.AccessConfig{RawRegion: "us-east-1"}
|
||||
session, err := access.Session()
|
||||
@@ -329,6 +314,7 @@ const testBuilderAccForceDeleteSnapshot = `
|
||||
}
|
||||
`
|
||||
|
||||
// share with catsby
|
||||
const testBuilderAccSharing = `
|
||||
{
|
||||
"builders": [{
|
||||
@@ -338,7 +324,7 @@ const testBuilderAccSharing = `
|
||||
"source_ami": "ami-76b2a71e",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "packer-test {{timestamp}}",
|
||||
"ami_users":["%s"],
|
||||
"ami_users":["932021504756"],
|
||||
"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 {
|
||||
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
||||
}
|
||||
@@ -390,7 +351,3 @@ func buildForceDeregisterConfig(val, name string) string {
|
||||
func buildForceDeleteSnapshotConfig(val, name string) string {
|
||||
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
|
||||
}
|
||||
|
||||
func buildSharingConfig(val string) string {
|
||||
return fmt.Sprintf(testBuilderAccSharing, val)
|
||||
}
|
||||
|
||||
@@ -144,25 +144,7 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
if generatedData[1] != "BuildRegion" {
|
||||
t.Fatalf("Generated data should contain BuildRegion")
|
||||
}
|
||||
if generatedData[2] != "SourceAMI" {
|
||||
t.Fatalf("Generated data should contain SourceAMI")
|
||||
}
|
||||
if generatedData[3] != "SourceAMICreationDate" {
|
||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
||||
}
|
||||
if generatedData[4] != "SourceAMIOwner" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
||||
}
|
||||
if generatedData[5] != "SourceAMIOwnerName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,20 +4,16 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||
"github.com/hashicorp/packer/common/random"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type stepCreateAMI struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
image *ec2.Image
|
||||
AMISkipBuildRegion bool
|
||||
}
|
||||
@@ -52,26 +48,7 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
BlockDeviceMappings: config.AMIMappings.BuildEC2BlockDeviceMappings(),
|
||||
}
|
||||
|
||||
var createResp *ec2.CreateImageOutput
|
||||
var err error
|
||||
|
||||
// Create a timeout for the CreateImage call.
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, time.Minute*15)
|
||||
defer cancel()
|
||||
|
||||
err = retry.Config{
|
||||
Tries: 0,
|
||||
ShouldRetry: func(err error) bool {
|
||||
if awserrors.Matches(err, "InvalidParameterValue", "Instance is not in state") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
||||
}.Run(timeoutCtx, func(ctx context.Context) error {
|
||||
createResp, err = ec2conn.CreateImage(createOpts)
|
||||
return err
|
||||
})
|
||||
createResp, err := ec2conn.CreateImage(createOpts)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
@@ -87,23 +64,18 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
|
||||
// Wait for the image to become ready
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if waitErr := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *createResp.ImageId); waitErr != nil {
|
||||
// waitErr should get bubbled up if the issue is a wait timeout
|
||||
err := fmt.Errorf("Error waiting for AMI: %s", waitErr)
|
||||
if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *createResp.ImageId); err != nil {
|
||||
log.Printf("Error waiting for AMI: %s", err)
|
||||
imResp, imerr := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{createResp.ImageId}})
|
||||
if imerr != nil {
|
||||
// If there's a failure describing images, bubble that error up too, but don't erase the waitErr.
|
||||
log.Printf("DescribeImages call was unable to determine reason waiting for AMI failed: %s", imerr)
|
||||
err = fmt.Errorf("Unknown error waiting for AMI; %s. DescribeImages returned an error: %s", waitErr, imerr)
|
||||
log.Printf("Unable to determine reason waiting for AMI failed: %s", err)
|
||||
err = fmt.Errorf("Unknown error waiting for AMI; %s", err)
|
||||
}
|
||||
if imResp != nil && len(imResp.Images) > 0 {
|
||||
// Finally, if there's a stateReason, store that with the wait err
|
||||
image := imResp.Images[0]
|
||||
if image != nil {
|
||||
stateReason := image.StateReason
|
||||
if stateReason != nil {
|
||||
err = fmt.Errorf("Error waiting for AMI: %s. DescribeImages returned the state reason: %s", waitErr, stateReason)
|
||||
}
|
||||
err = fmt.Errorf("Error waiting for AMI. Reason: %s", stateReason)
|
||||
}
|
||||
}
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
package ebssurrogate
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
@@ -38,6 +41,50 @@ func (bds BlockDevices) BuildEC2BlockDeviceMappings() []*ec2.BlockDeviceMapping
|
||||
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) {
|
||||
for _, block := range bds {
|
||||
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/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/hcl2template"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"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) {
|
||||
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"ami_description",
|
||||
"run_tags",
|
||||
"run_tag",
|
||||
"run_volume_tags",
|
||||
"run_volume_tag",
|
||||
"snapshot_tags",
|
||||
"snapshot_tag",
|
||||
"spot_tags",
|
||||
"spot_tag",
|
||||
"tags",
|
||||
"tag",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
@@ -169,7 +163,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
|
||||
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
|
||||
|
||||
generatedData := awscommon.GetGeneratedDataList()
|
||||
generatedData := []string{"SourceAMIName"}
|
||||
return generatedData, warns, nil
|
||||
}
|
||||
|
||||
@@ -192,13 +186,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state.Put("awsSession", session)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
||||
generatedData := &builder.GeneratedData{State: state}
|
||||
|
||||
var instanceStep multistep.Step
|
||||
|
||||
if b.config.IsSpotInstance() {
|
||||
instanceStep = &awscommon.StepRunSpotInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.LaunchMappings,
|
||||
BlockDurationMinutes: b.config.BlockDurationMinutes,
|
||||
@@ -209,7 +202,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||
@@ -221,7 +213,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
} else {
|
||||
instanceStep = &awscommon.StepRunSourceInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.LaunchMappings,
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
@@ -235,7 +226,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
@@ -248,12 +238,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&awscommon.StepPreValidate{
|
||||
DestAmiName: b.config.AMIName,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
VpcId: b.config.VpcId,
|
||||
SubnetId: b.config.SubnetId,
|
||||
HasSubnetFilter: !b.config.SubnetFilter.Empty(),
|
||||
DestAmiName: b.config.AMIName,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
VpcId: b.config.VpcId,
|
||||
SubnetId: b.config.SubnetId,
|
||||
HasSubnetFilter: !b.config.SubnetFilter.Empty(),
|
||||
},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
@@ -301,7 +290,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&awscommon.StepCreateSSMTunnel{
|
||||
AWSSession: session,
|
||||
Region: *ec2conn.Config.Region,
|
||||
PauseBeforeSSM: b.config.PauseBeforeSSM,
|
||||
LocalPortNumber: b.config.SessionManagerPort,
|
||||
RemotePortNumber: b.config.Comm.Port(),
|
||||
SSMAgentEnabled: b.config.SSMAgentEnabled(),
|
||||
@@ -319,15 +307,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
),
|
||||
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&awscommon.StepSetGeneratedData{
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&common.StepProvision{},
|
||||
&common.StepCleanupTempKeys{
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
},
|
||||
&awscommon.StepStopEBSBackedInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
Skip: b.config.IsSpotInstance(),
|
||||
DisableStopInstance: b.config.DisableStopInstance,
|
||||
},
|
||||
@@ -337,11 +321,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&StepSnapshotVolumes{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
LaunchDevices: launchDevices,
|
||||
SnapshotOmitMap: b.config.LaunchMappings.GetOmissions(),
|
||||
SnapshotTags: b.config.SnapshotTags,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
@@ -359,17 +340,15 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
Architecture: b.config.Architecture,
|
||||
LaunchOmitMap: b.config.LaunchMappings.GetOmissions(),
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
Regions: b.config.AMIRegions,
|
||||
AMIKmsKeyId: b.config.AMIKmsKeyId,
|
||||
RegionKeyIds: b.config.AMIRegionKMSKeyIDs,
|
||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||
Name: b.config.AMIName,
|
||||
OriginalRegion: *ec2conn.Config.Region,
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
Regions: b.config.AMIRegions,
|
||||
AMIKmsKeyId: b.config.AMIKmsKeyId,
|
||||
RegionKeyIds: b.config.AMIRegionKMSKeyIDs,
|
||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||
Name: b.config.AMIName,
|
||||
OriginalRegion: *ec2conn.Config.Region,
|
||||
},
|
||||
&awscommon.StepModifyAMIAttributes{
|
||||
Description: b.config.AMIDescription,
|
||||
|
||||
@@ -62,9 +62,7 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -74,10 +72,8 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address" hcl:"associate_public_ip_address"`
|
||||
AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"`
|
||||
BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes" hcl:"block_duration_minutes"`
|
||||
@@ -103,7 +99,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -117,18 +112,14 @@ type FlatConfig struct {
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
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"`
|
||||
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"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
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"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
@@ -138,7 +129,6 @@ type FlatConfig struct {
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
@@ -148,8 +138,8 @@ type FlatConfig struct {
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
@@ -160,7 +150,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -211,9 +200,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_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},
|
||||
"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},
|
||||
"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},
|
||||
"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},
|
||||
@@ -223,10 +210,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
|
||||
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
|
||||
"associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false},
|
||||
"availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false},
|
||||
"block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false},
|
||||
@@ -252,7 +237,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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_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_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},
|
||||
@@ -267,13 +251,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
@@ -287,7 +267,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
@@ -309,7 +288,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false},
|
||||
"ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false},
|
||||
|
||||
@@ -86,25 +86,7 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
if generatedData[1] != "BuildRegion" {
|
||||
t.Fatalf("Generated data should contain BuildRegion")
|
||||
}
|
||||
if generatedData[2] != "SourceAMI" {
|
||||
t.Fatalf("Generated data should contain SourceAMI")
|
||||
}
|
||||
if generatedData[3] != "SourceAMICreationDate" {
|
||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
||||
}
|
||||
if generatedData[4] != "SourceAMIOwner" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
||||
}
|
||||
if generatedData[5] != "SourceAMIOwnerName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
// StepRegisterAMI creates the AMI.
|
||||
type StepRegisterAMI struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
RootDevice RootBlockDevice
|
||||
AMIDevices []*ec2.BlockDeviceMapping
|
||||
LaunchDevices []*ec2.BlockDeviceMapping
|
||||
@@ -87,7 +86,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
|
||||
// Wait for the image to become ready
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if err := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
|
||||
if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
|
||||
err := fmt.Errorf("Error waiting for AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
)
|
||||
|
||||
const sourceDeviceName = "/dev/xvdf"
|
||||
@@ -24,7 +23,6 @@ func newStepRegisterAMI(amiDevices, launchDevices []*ec2.BlockDeviceMapping) *St
|
||||
},
|
||||
AMIDevices: amiDevices,
|
||||
LaunchDevices: launchDevices,
|
||||
PollingConfig: new(common.AWSPollingConfig),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// StepSnapshotVolumes creates snapshots of the created volumes.
|
||||
@@ -20,13 +18,10 @@ import (
|
||||
// Produces:
|
||||
// snapshot_ids map[string]string - IDs of the created snapshots
|
||||
type StepSnapshotVolumes struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
LaunchDevices []*ec2.BlockDeviceMapping
|
||||
snapshotIds map[string]string
|
||||
snapshotMutex sync.Mutex
|
||||
SnapshotOmitMap map[string]bool
|
||||
SnapshotTags map[string]string
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName string, state multistep.StateBag) error {
|
||||
@@ -44,34 +39,12 @@ func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName str
|
||||
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))
|
||||
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{
|
||||
VolumeId: &volumeId,
|
||||
Description: &description,
|
||||
TagSpecifications: tagSpecs,
|
||||
VolumeId: &volumeId,
|
||||
Description: &description,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -83,7 +56,7 @@ func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName str
|
||||
s.snapshotMutex.Unlock()
|
||||
|
||||
// Wait for snapshot to be created
|
||||
err = s.PollingConfig.WaitUntilSnapshotDone(ctx, ec2conn, *createSnapResp.SnapshotId)
|
||||
err = awscommon.WaitUntilSnapshotDone(ctx, ec2conn, *createSnapResp.SnapshotId)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/hcl2template"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
@@ -100,21 +99,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
SourceAMI: `{{ .SourceAMI }} `,
|
||||
}
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
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...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -160,7 +146,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
|
||||
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
|
||||
|
||||
generatedData := awscommon.GetGeneratedDataList()
|
||||
generatedData := []string{"SourceAMIName"}
|
||||
return generatedData, warns, nil
|
||||
}
|
||||
|
||||
@@ -180,13 +166,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state.Put("iam", iam)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
||||
|
||||
var instanceStep multistep.Step
|
||||
|
||||
if b.config.IsSpotInstance() {
|
||||
instanceStep = &awscommon.StepRunSpotInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.launchBlockDevices,
|
||||
BlockDurationMinutes: b.config.BlockDurationMinutes,
|
||||
@@ -197,7 +181,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
@@ -209,7 +192,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
} else {
|
||||
instanceStep = &awscommon.StepRunSourceInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.launchBlockDevices,
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
@@ -223,7 +205,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
@@ -278,7 +259,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&awscommon.StepCreateSSMTunnel{
|
||||
AWSSession: session,
|
||||
Region: *ec2conn.Config.Region,
|
||||
PauseBeforeSSM: b.config.PauseBeforeSSM,
|
||||
LocalPortNumber: b.config.SessionManagerPort,
|
||||
RemotePortNumber: b.config.Comm.Port(),
|
||||
SSMAgentEnabled: b.config.SSMAgentEnabled(),
|
||||
@@ -296,15 +276,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
),
|
||||
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&awscommon.StepSetGeneratedData{
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&common.StepProvision{},
|
||||
&common.StepCleanupTempKeys{
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
},
|
||||
&awscommon.StepStopEBSBackedInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
Skip: b.config.IsSpotInstance(),
|
||||
DisableStopInstance: b.config.DisableStopInstance,
|
||||
},
|
||||
|
||||
@@ -64,9 +64,7 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -76,10 +74,8 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address" hcl:"associate_public_ip_address"`
|
||||
AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"`
|
||||
BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes" hcl:"block_duration_minutes"`
|
||||
@@ -105,7 +101,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -119,18 +114,14 @@ type FlatConfig struct {
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
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"`
|
||||
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"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
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"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
@@ -140,7 +131,6 @@ type FlatConfig struct {
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
@@ -150,8 +140,8 @@ type FlatConfig struct {
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
@@ -162,7 +152,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -191,9 +180,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_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},
|
||||
"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},
|
||||
"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},
|
||||
"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},
|
||||
@@ -203,10 +190,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
|
||||
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
|
||||
"associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false},
|
||||
"availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false},
|
||||
"block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false},
|
||||
@@ -232,7 +217,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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_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_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},
|
||||
@@ -247,13 +231,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
@@ -267,7 +247,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
@@ -289,7 +268,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false},
|
||||
"sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false},
|
||||
|
||||
@@ -105,25 +105,7 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
if generatedData[1] != "BuildRegion" {
|
||||
t.Fatalf("Generated data should contain BuildRegion")
|
||||
}
|
||||
if generatedData[2] != "SourceAMI" {
|
||||
t.Fatalf("Generated data should contain SourceAMI")
|
||||
}
|
||||
if generatedData[3] != "SourceAMICreationDate" {
|
||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
||||
}
|
||||
if generatedData[4] != "SourceAMIOwner" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
||||
}
|
||||
if generatedData[5] != "SourceAMIOwnerName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
command: packer build -var "accesskey=*" -var "secretkey=" -var "shellpath=packages.sh" .\apache.json
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"variables":
|
||||
{
|
||||
"accesskey": "",
|
||||
"secretkey": "",
|
||||
"shellpath": "packages.sh"
|
||||
},
|
||||
"builders":[
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"access_key": "{{user `accesskey`}}",
|
||||
"secret_key": "{{user `secretkey`}}",
|
||||
"region": "ap-south-1",
|
||||
"source_ami": "ami-sa7608343426b",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "apache",
|
||||
"tags": {
|
||||
"OS_Version": "Ubuntu",
|
||||
"Release": "Latest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"provisioners":[
|
||||
{
|
||||
"type": "shell",
|
||||
"script": "{{user `shellpath`}}"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
echo "installing apache "
|
||||
sudo apt-get update
|
||||
sudo apt-get install apache2 -y
|
||||
sudo apt-get update
|
||||
sudo service apache2 restart
|
||||
sudo apache2 --version
|
||||
@@ -1 +0,0 @@
|
||||
command: packer build -var "accesskey=*" -var "secretkey=" -var "shellpath=packages.sh" .\nginx.json
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"variables":
|
||||
{
|
||||
"accesskey": "",
|
||||
"secretkey": "",
|
||||
"shellpath": "packages.sh"
|
||||
},
|
||||
"builders":[
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"access_key": "{{user `accesskey`}}",
|
||||
"secret_key": "{{user `secretkey`}}",
|
||||
"region": "ap-south-1",
|
||||
"source_ami": "ami-sa7608343426b",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "nginx",
|
||||
"tags": {
|
||||
"OS_Version": "Ubuntu",
|
||||
"Release": "Latest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"provisioners":[
|
||||
{
|
||||
"type": "shell",
|
||||
"script": "{{user `shellpath`}}"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
echo "installing nginx "
|
||||
sudo apt-get update
|
||||
sudo apt-get install nginx -y
|
||||
sudo service nginx restart
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"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
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
@@ -228,7 +227,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
}
|
||||
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
|
||||
|
||||
generatedData := awscommon.GetGeneratedDataList()
|
||||
generatedData := []string{"SourceAMIName"}
|
||||
return generatedData, warns, nil
|
||||
}
|
||||
|
||||
@@ -250,13 +249,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state.Put("awsSession", session)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
||||
generatedData := &builder.GeneratedData{State: state}
|
||||
|
||||
var instanceStep multistep.Step
|
||||
|
||||
if b.config.IsSpotInstance() {
|
||||
instanceStep = &awscommon.StepRunSpotInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.LaunchMappings,
|
||||
BlockDurationMinutes: b.config.BlockDurationMinutes,
|
||||
@@ -265,7 +263,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
Debug: b.config.PackerDebug,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||
@@ -276,7 +273,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
} else {
|
||||
instanceStep = &awscommon.StepRunSourceInstance{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
LaunchMappings: b.config.LaunchMappings,
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
@@ -288,7 +284,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
}
|
||||
@@ -346,7 +341,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&awscommon.StepCreateSSMTunnel{
|
||||
AWSSession: session,
|
||||
Region: *ec2conn.Config.Region,
|
||||
PauseBeforeSSM: b.config.PauseBeforeSSM,
|
||||
LocalPortNumber: b.config.SessionManagerPort,
|
||||
RemotePortNumber: b.config.Comm.Port(),
|
||||
SSMAgentEnabled: b.config.SSMAgentEnabled(),
|
||||
@@ -366,9 +360,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
),
|
||||
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&awscommon.StepSetGeneratedData{
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&common.StepProvision{},
|
||||
&common.StepCleanupTempKeys{
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
@@ -391,7 +382,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
||||
@@ -19,9 +19,7 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -31,10 +29,8 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type" hcl:"ami_virtualization_type"`
|
||||
@@ -81,7 +77,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -95,18 +90,14 @@ type FlatConfig struct {
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
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"`
|
||||
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"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
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"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
@@ -116,7 +107,6 @@ type FlatConfig struct {
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
@@ -126,8 +116,8 @@ type FlatConfig struct {
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
@@ -138,7 +128,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -173,9 +162,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_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},
|
||||
"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},
|
||||
"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},
|
||||
"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},
|
||||
@@ -185,10 +172,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
|
||||
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
|
||||
"ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false},
|
||||
"ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false},
|
||||
"ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false},
|
||||
@@ -235,7 +220,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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_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_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},
|
||||
@@ -250,13 +234,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
@@ -270,7 +250,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
@@ -292,7 +271,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"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())},
|
||||
|
||||
@@ -319,25 +319,7 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
if generatedData[1] != "BuildRegion" {
|
||||
t.Fatalf("Generated data should contain BuildRegion")
|
||||
}
|
||||
if generatedData[2] != "SourceAMI" {
|
||||
t.Fatalf("Generated data should contain SourceAMI")
|
||||
}
|
||||
if generatedData[3] != "SourceAMICreationDate" {
|
||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
||||
}
|
||||
if generatedData[4] != "SourceAMIOwner" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
||||
}
|
||||
if generatedData[5] != "SourceAMIOwnerName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
)
|
||||
|
||||
type StepRegisterAMI struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
EnableAMIENASupport confighelper.Trilean
|
||||
EnableAMISriovNetSupport bool
|
||||
AMISkipBuildRegion bool
|
||||
@@ -81,7 +80,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
|
||||
// Wait for the image to become ready
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if err := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
|
||||
if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
|
||||
err := fmt.Errorf("Error waiting for AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
|
||||
@@ -128,8 +128,8 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon
|
||||
}
|
||||
}
|
||||
|
||||
func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storageAccountName string,
|
||||
cloud *azure.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration,
|
||||
func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string,
|
||||
cloud *azure.Environment, SharedGalleryTimeout time.Duration, PollingDuration time.Duration,
|
||||
servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
|
||||
|
||||
var azureClient = &AzureClient{}
|
||||
@@ -141,56 +141,56 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.DisksClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
@@ -203,44 +203,42 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.GalleryImageVersionsClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImageVersionsClient.UserAgent)
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = SharedGalleryTimeout
|
||||
|
||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImagesClient.UserAgent)
|
||||
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
azureClient.GalleryImagesClient.Client.PollingDuration = PollingDuration
|
||||
|
||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
||||
if err != nil {
|
||||
@@ -252,7 +250,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.VaultClient.RequestInspector = withInspection(maxlen)
|
||||
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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
|
||||
// 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.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
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 resourceGroupName != "" && storageAccountName != "" {
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
ERROR: -> DeploymentFailed : At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.
|
||||
ERROR: -> BadRequest
|
||||
ERROR: -> InvalidRequestFormat : Cannot parse the request.
|
||||
ERROR: -> InvalidJson : Error converting value "playground" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.
|
||||
ERROR: -> DeploymentFailed : At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.
|
||||
ERROR: -> BadRequest
|
||||
ERROR: -> InvalidRequestFormat : Cannot parse the request.
|
||||
ERROR: -> InvalidJson : Error converting value "playground" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ERROR: -> ResourceNotFound : The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found.
|
||||
ERROR: -> ResourceNotFound : The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found.
|
||||
|
||||
@@ -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 ...")
|
||||
azureClient, err := NewAzureClient(
|
||||
b.config.ClientConfig.SubscriptionID,
|
||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
||||
b.config.ResourceGroupName,
|
||||
b.config.StorageAccount,
|
||||
b.config.ClientConfig.CloudEnvironment(),
|
||||
@@ -135,7 +134,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
} else {
|
||||
// User is not using Managed Images to build, warning message here that this path is being deprecated
|
||||
ui.Error("Warning: You are using Azure Packer Builder to create VHDs which is being deprecated, consider using Managed Images. Learn more https://www.packer.io/docs/builders/azure/arm#azure-arm-builder-specific-options")
|
||||
ui.Error("Warning: You are using Azure Packer Builder to create VHDs which is being deprecated, consider using Managed Images. Learn more http://aka.ms/packermanagedimage")
|
||||
}
|
||||
|
||||
if b.config.BuildResourceGroupName != "" {
|
||||
@@ -222,6 +221,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
||||
NewStepCaptureImage(azureClient, ui),
|
||||
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
||||
NewStepDeleteResourceGroup(azureClient, ui),
|
||||
NewStepDeleteOSDisk(azureClient, ui),
|
||||
NewStepDeleteAdditionalDisks(azureClient, ui),
|
||||
}
|
||||
} else if b.config.OSType == constants.Target_Windows {
|
||||
steps = []multistep.Step{
|
||||
@@ -262,6 +264,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
||||
NewStepCaptureImage(azureClient, ui),
|
||||
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
||||
NewStepDeleteResourceGroup(azureClient, ui),
|
||||
NewStepDeleteOSDisk(azureClient, ui),
|
||||
NewStepDeleteAdditionalDisks(azureClient, ui),
|
||||
)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType)
|
||||
@@ -388,7 +393,7 @@ func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, resou
|
||||
func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
|
||||
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.ArmDeploymentName, b.config.tmpDeploymentName)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package arm
|
||||
// * ARM_CLIENT_ID
|
||||
// * ARM_CLIENT_SECRET
|
||||
// * ARM_SUBSCRIPTION_ID
|
||||
// * ARM_OBJECT_ID
|
||||
// * ARM_STORAGE_ACCOUNT
|
||||
//
|
||||
// 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) {
|
||||
if os.Getenv(DeviceLoginAcceptanceTest) == "" {
|
||||
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) {
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
@@ -171,7 +151,7 @@ const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
||||
|
||||
"build_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",
|
||||
"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 = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -279,11 +223,7 @@ const testBuilderAccManagedDiskLinux = `
|
||||
"image_sku": "16.04-LTS",
|
||||
|
||||
"location": "South Central US",
|
||||
"vm_size": "Standard_DS2_v2",
|
||||
"azure_tags": {
|
||||
"env": "testing",
|
||||
"builder": "packer"
|
||||
}
|
||||
"vm_size": "Standard_DS2_v2"
|
||||
}]
|
||||
}
|
||||
`
|
||||
@@ -318,6 +258,7 @@ const testBuilderAccBlobWindows = `
|
||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}",
|
||||
"object_id": "{{env ` + "`ARM_OBJECT_ID`" + `}}",
|
||||
"storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}"
|
||||
},
|
||||
"builders": [{
|
||||
@@ -326,6 +267,7 @@ const testBuilderAccBlobWindows = `
|
||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
||||
"object_id": "{{user ` + "`object_id`" + `}}",
|
||||
|
||||
"storage_account": "{{user ` + "`storage_account`" + `}}",
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+36
-76
@@ -90,7 +90,6 @@ type SharedImageGallery struct {
|
||||
}
|
||||
|
||||
type SharedImageGalleryDestination struct {
|
||||
SigDestinationSubscription string `mapstructure:"subscription"`
|
||||
SigDestinationResourceGroup string `mapstructure:"resource_group"`
|
||||
SigDestinationGalleryName string `mapstructure:"gallery_name"`
|
||||
SigDestinationImageName string `mapstructure:"image_name"`
|
||||
@@ -104,8 +103,7 @@ type Config struct {
|
||||
// Authentication via OAUTH
|
||||
ClientConfig client.Config `mapstructure:",squash"`
|
||||
|
||||
// A list of one or more fully-qualified resource IDs of user assigned
|
||||
// managed identities to be configured on the VM.
|
||||
// If set with one or more resource ids of user assigned managed identities, they will be configured on the VM.
|
||||
// See [documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token)
|
||||
// for how to acquire tokens within the VM.
|
||||
// To assign a user assigned managed identity to a VM, the provided account or service principal must have [Managed Identity Operator](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#managed-identity-operator)
|
||||
@@ -119,64 +117,31 @@ type Config struct {
|
||||
// Use a [Shared 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
|
||||
// build type* - the target must be a *Managed Image*. When using shared_image_gallery as a source, image_publisher,
|
||||
// image_offer, image_sku, image_version, and custom_managed_image_name should not be set.
|
||||
// build type* - the target must be a *Managed Image*.
|
||||
//
|
||||
// In JSON
|
||||
// ```json
|
||||
// "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"
|
||||
// ```
|
||||
// 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"
|
||||
// ```
|
||||
// "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"`
|
||||
// The name of the Shared Image Gallery under which the managed image will be published as Shared Gallery Image version.
|
||||
//
|
||||
// Following is an example.
|
||||
//
|
||||
// In JSON
|
||||
// ```json
|
||||
// "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"
|
||||
// ```
|
||||
// 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"
|
||||
// ```
|
||||
// "shared_image_gallery_destination": {
|
||||
// "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"`
|
||||
// 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
|
||||
@@ -288,7 +253,7 @@ type Config struct {
|
||||
// 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
|
||||
// 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
|
||||
// containing a `name` and a `value` field. In HCL2 mode the
|
||||
// [`dynamic_block`](/docs/configuration/from-1.5/expressions#dynamic-blocks)
|
||||
@@ -513,7 +478,7 @@ func (c *Config) toImageParameters() *compute.Image {
|
||||
},
|
||||
},
|
||||
Location: to.StringPtr(c.Location),
|
||||
Tags: azcommon.MapToAzureTags(c.AzureTags),
|
||||
Tags: c.AzureTags,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +545,6 @@ func (c *Config) createCertificate() (string, error) {
|
||||
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
c.ctx.Funcs = azcommon.TemplateFuncs
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
}, raws...)
|
||||
@@ -597,7 +561,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
}
|
||||
|
||||
// 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()
|
||||
if err != nil {
|
||||
@@ -730,10 +697,10 @@ func setUserNamePassword(c *Config) error {
|
||||
}
|
||||
c.UserName = c.Comm.SSHUsername
|
||||
|
||||
// if user has an explicit wish to use an SSH password, we'll set it
|
||||
if c.Comm.SSHPassword != "" {
|
||||
c.Password = c.Comm.SSHPassword
|
||||
if c.Comm.SSHPassword == "" {
|
||||
c.Comm.SSHPassword = c.Password
|
||||
}
|
||||
c.Password = c.Comm.SSHPassword
|
||||
|
||||
if c.Comm.Type == "ssh" {
|
||||
return nil
|
||||
@@ -805,8 +772,8 @@ func assertTagProperties(c *Config, errs *packer.MultiError) {
|
||||
if len(k) > 512 {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 512 character limit", k, len(k)))
|
||||
}
|
||||
if len(v) > 256 {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", v, len(v)))
|
||||
if len(*v) > 256 {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", *v, len(*v)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -820,10 +787,6 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
||||
for _, rid := range c.UserAssignedManagedIdentities {
|
||||
r, err := client.ParseResourceID(rid)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error parsing resource ID from `user_assigned_managed_identities`; please make sure"+
|
||||
" that this value follows the full resource id format: "+
|
||||
"/subscriptions/<SUBSCRIPTON_ID>/resourcegroups/<RESOURCE_GROUP>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<USER_ASSIGNED_IDENTITY_NAME>.\n"+
|
||||
" Original error: %s", err)
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
} else {
|
||||
if !strings.EqualFold(r.Provider, "Microsoft.ManagedIdentity") {
|
||||
@@ -1010,9 +973,6 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
||||
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"))
|
||||
}
|
||||
if c.SharedGalleryDestination.SigDestinationSubscription == "" {
|
||||
c.SharedGalleryDestination.SigDestinationSubscription = c.ClientConfig.SubscriptionID
|
||||
}
|
||||
}
|
||||
if c.SharedGalleryTimeout == 0 {
|
||||
// default to a one-hour timeout. In the sdk, the default is 15 m.
|
||||
@@ -1061,13 +1021,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"))
|
||||
} else {
|
||||
if c.AzureTags == nil {
|
||||
c.AzureTags = make(map[string]string)
|
||||
c.AzureTags = make(map[string]*string)
|
||||
}
|
||||
|
||||
c.AzureTags["PlanInfo"] = c.PlanInfo.PlanName
|
||||
c.AzureTags["PlanProduct"] = c.PlanInfo.PlanProduct
|
||||
c.AzureTags["PlanPublisher"] = c.PlanInfo.PlanPublisher
|
||||
c.AzureTags["PlanPromotionCode"] = c.PlanInfo.PlanPromotionCode
|
||||
c.AzureTags["PlanInfo"] = &c.PlanInfo.PlanName
|
||||
c.AzureTags["PlanProduct"] = &c.PlanInfo.PlanProduct
|
||||
c.AzureTags["PlanPublisher"] = &c.PlanInfo.PlanPublisher
|
||||
c.AzureTags["PlanPromotionCode"] = &c.PlanInfo.PlanPromotionCode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ type FlatConfig struct {
|
||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -79,19 +78,15 @@ type FlatConfig struct {
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
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"`
|
||||
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"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
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"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
@@ -101,7 +96,6 @@ type FlatConfig struct {
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
@@ -111,8 +105,8 @@ type FlatConfig struct {
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
@@ -152,7 +146,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"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},
|
||||
@@ -208,13 +201,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
@@ -228,7 +217,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
@@ -317,7 +305,6 @@ func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec {
|
||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatSharedImageGalleryDestination struct {
|
||||
SigDestinationSubscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"`
|
||||
SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"`
|
||||
SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"`
|
||||
SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
||||
@@ -337,7 +324,6 @@ func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() m
|
||||
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
||||
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"subscription": &hcldec.AttrSpec{Name: "subscription", 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},
|
||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"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/hcl2template"
|
||||
)
|
||||
|
||||
// List of configuration parameters that are required by the ARM builder.
|
||||
@@ -73,8 +71,8 @@ func TestConfigUserNameOverride(t *testing.T) {
|
||||
if c.Password != c.tmpAdminPassword {
|
||||
t.Errorf("Expected 'Password' to be set to generated password, but found %q!", c.Password)
|
||||
}
|
||||
if c.Comm.SSHPassword != "" {
|
||||
t.Errorf("Expected 'c.Comm.SSHPassword' to be empty, but found %q!", c.Comm.SSHPassword)
|
||||
if c.Comm.SSHPassword != c.tmpAdminPassword {
|
||||
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to generated password, but found %q!", c.Comm.SSHPassword)
|
||||
}
|
||||
if c.UserName != "override_username" {
|
||||
t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName)
|
||||
@@ -912,55 +910,32 @@ func TestConfigShouldAcceptTags(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
c := Config{
|
||||
AzureTag: hcl2template.NameValues{
|
||||
{Name: "tag03", Value: "value03"},
|
||||
},
|
||||
}
|
||||
var c Config
|
||||
_, err := c.Prepare(config, getPackerConfiguration())
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(c.AzureTags, map[string]string{
|
||||
"tag01": "value01",
|
||||
"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,
|
||||
if len(c.AzureTags) != 2 {
|
||||
t.Fatalf("expected to find 2 tags, but got %d", len(c.AzureTags))
|
||||
}
|
||||
|
||||
c := Config{
|
||||
AzureTag: hcl2template.NameValues{
|
||||
{Name: "tag03", Value: "value03"},
|
||||
},
|
||||
if _, ok := c.AzureTags["tag01"]; !ok {
|
||||
t.Error("expected to find key=\"tag01\", but did not")
|
||||
}
|
||||
_, err := c.Prepare(config, getPackerConfiguration())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if _, ok := c.AzureTags["tag02"]; !ok {
|
||||
t.Error("expected to find key=\"tag02\", but did not")
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(c.AzureTags, map[string]string{
|
||||
"tag03": "value03",
|
||||
}); diff != "" {
|
||||
t.Fatalf("unexpected azure tags: %s", diff)
|
||||
value := c.AzureTags["tag01"]
|
||||
if *value != "value01" {
|
||||
t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", *value)
|
||||
}
|
||||
|
||||
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{} {
|
||||
m := make(map[string]interface{})
|
||||
func getArmBuilderConfiguration() map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, v := range requiredConfigValues {
|
||||
m[v] = "ignored00"
|
||||
}
|
||||
@@ -2118,14 +2093,6 @@ func getPackerCommunicatorConfiguration() map[string]string {
|
||||
return config
|
||||
}
|
||||
|
||||
func getPackerSSHPasswordCommunicatorConfiguration() map[string]string {
|
||||
config := map[string]string{
|
||||
"ssh_password": "superS3cret",
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func TestConfigShouldRejectMalformedUserAssignedManagedIdentities(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"capture_name_prefix": "ignore",
|
||||
|
||||
@@ -61,13 +61,7 @@ func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.State
|
||||
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
var location = state.Get(constants.ArmLocation).(string)
|
||||
tags, ok := state.Get(constants.ArmTags).(map[string]*string)
|
||||
if !ok {
|
||||
err := fmt.Errorf("failed to extract tags from state bag")
|
||||
state.Put(constants.Error, err)
|
||||
s.error(err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
var tags = state.Get(constants.ArmTags).(map[string]*string)
|
||||
|
||||
exists, err := s.exists(ctx, resourceGroupName)
|
||||
if err != nil {
|
||||
@@ -117,30 +111,27 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) {
|
||||
if state.Get(constants.ArmIsExistingResourceGroup).(bool) {
|
||||
ui.Say("\nThe resource group was not created by Packer, not deleting ...")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ui.Say("\nCleanup requested, deleting resource group ...")
|
||||
|
||||
ctx := context.TODO()
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
if exists, err := s.exists(ctx, resourceGroupName); !exists || err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say("\nCleanup requested, deleting resource group ...")
|
||||
f, err := s.client.GroupsClient.Delete(ctx, resourceGroupName)
|
||||
if err == nil {
|
||||
if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
||||
s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName))
|
||||
} else {
|
||||
err = f.WaitForCompletionRef(ctx, s.client.GroupsClient.Client)
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
ctx := context.TODO()
|
||||
f, err := s.client.GroupsClient.Delete(ctx, resourceGroupName)
|
||||
if err == nil {
|
||||
if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
||||
s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName))
|
||||
} else {
|
||||
err = f.WaitForCompletionRef(ctx, s.client.GroupsClient.Client)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceGroupName, err))
|
||||
return
|
||||
}
|
||||
if !state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
||||
ui.Say("Resource group has been deleted.")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceGroupName, err))
|
||||
return
|
||||
}
|
||||
if !state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
||||
ui.Say("Resource group has been deleted.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,32 +220,3 @@ func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag {
|
||||
stateBag.Put(constants.ArmTags, tags)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,8 @@ func NewStepDeleteAdditionalDisks(client *AzureClient, ui packer.Ui) *StepDelete
|
||||
|
||||
func (s *StepDeleteAdditionalDisk) deleteBlob(storageContainerName string, blobName string) error {
|
||||
blob := s.client.BlobStorageClient.GetContainerReference(storageContainerName).GetBlobReference(blobName)
|
||||
_, err := blob.BreakLease(nil)
|
||||
if err != nil && !strings.Contains(err.Error(), "LeaseNotPresentWithLeaseOperation") {
|
||||
s.say(s.client.LastError.Error())
|
||||
return err
|
||||
}
|
||||
err := blob.Delete(nil)
|
||||
|
||||
err = blob.Delete(nil)
|
||||
if err != nil {
|
||||
s.say(s.client.LastError.Error())
|
||||
}
|
||||
@@ -61,11 +56,7 @@ func (s *StepDeleteAdditionalDisk) deleteManagedDisk(ctx context.Context, resour
|
||||
func (s *StepDeleteAdditionalDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Deleting the temporary Additional disk ...")
|
||||
|
||||
var dataDisks []string
|
||||
|
||||
if disks := state.Get(constants.ArmAdditionalDiskVhds); disks != nil {
|
||||
dataDisks = disks.([]string)
|
||||
}
|
||||
var dataDisks = state.Get(constants.ArmAdditionalDiskVhds).([]string)
|
||||
var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool)
|
||||
var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
@@ -89,26 +80,25 @@ func (s *StepDeleteAdditionalDisk) Run(ctx context.Context, state multistep.Stat
|
||||
s.say("Failed to delete the managed Additional Disk!")
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
u, err := url.Parse(additionaldisk)
|
||||
if err != nil {
|
||||
s.say("Failed to parse the Additional Disk's VHD URI!")
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
|
||||
xs := strings.Split(u.Path, "/")
|
||||
if len(xs) < 3 {
|
||||
err = errors.New("Failed to parse Additional Disk's VHD URI!")
|
||||
} else {
|
||||
var storageAccountName = xs[1]
|
||||
var blobName = strings.Join(xs[2:], "/")
|
||||
u, err := url.Parse(additionaldisk)
|
||||
if err != nil {
|
||||
s.say("Failed to parse the Additional Disk's VHD URI!")
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
|
||||
err = s.delete(storageAccountName, blobName)
|
||||
}
|
||||
if err != nil {
|
||||
return processStepResult(err, s.error, state)
|
||||
xs := strings.Split(u.Path, "/")
|
||||
if len(xs) < 3 {
|
||||
err = errors.New("Failed to parse Additional Disk's VHD URI!")
|
||||
} else {
|
||||
var storageAccountName = xs[1]
|
||||
var blobName = strings.Join(xs[2:], "/")
|
||||
|
||||
err = s.delete(storageAccountName, blobName)
|
||||
}
|
||||
if err != nil {
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepDeleteOSDisk struct {
|
||||
client *AzureClient
|
||||
delete func(string, string) error
|
||||
deleteManaged func(context.Context, string, string) error
|
||||
say func(message string)
|
||||
error func(e error)
|
||||
}
|
||||
|
||||
func NewStepDeleteOSDisk(client *AzureClient, ui packer.Ui) *StepDeleteOSDisk {
|
||||
var step = &StepDeleteOSDisk{
|
||||
client: client,
|
||||
say: func(message string) { ui.Say(message) },
|
||||
error: func(e error) { ui.Error(e.Error()) },
|
||||
}
|
||||
|
||||
step.delete = step.deleteBlob
|
||||
step.deleteManaged = step.deleteManagedDisk
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepDeleteOSDisk) deleteBlob(storageContainerName string, blobName string) error {
|
||||
blob := s.client.BlobStorageClient.GetContainerReference(storageContainerName).GetBlobReference(blobName)
|
||||
err := blob.Delete(nil)
|
||||
|
||||
if err != nil {
|
||||
s.say(s.client.LastError.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepDeleteOSDisk) deleteManagedDisk(ctx context.Context, resourceGroupName string, imageName string) error {
|
||||
xs := strings.Split(imageName, "/")
|
||||
diskName := xs[len(xs)-1]
|
||||
f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName)
|
||||
if err == nil {
|
||||
err = f.WaitForCompletionRef(ctx, s.client.DisksClient.Client)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepDeleteOSDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Deleting the temporary OS disk ...")
|
||||
|
||||
var osDisk = state.Get(constants.ArmOSDiskVhd).(string)
|
||||
var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool)
|
||||
var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
|
||||
if isManagedDisk && !isExistingResourceGroup {
|
||||
s.say(fmt.Sprintf(" -> OS Disk : skipping, managed disk was used..."))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
s.say(fmt.Sprintf(" -> OS Disk : '%s'", osDisk))
|
||||
|
||||
var err error
|
||||
if isManagedDisk {
|
||||
err = s.deleteManaged(ctx, resourceGroupName, osDisk)
|
||||
if err != nil {
|
||||
s.say("Failed to delete the managed OS Disk!")
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
u, err := url.Parse(osDisk)
|
||||
if err != nil {
|
||||
s.say("Failed to parse the OS Disk's VHD URI!")
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
|
||||
xs := strings.Split(u.Path, "/")
|
||||
if len(xs) < 3 {
|
||||
err = errors.New("Failed to parse OS Disk's VHD URI!")
|
||||
} else {
|
||||
var storageAccountName = xs[1]
|
||||
var blobName = strings.Join(xs[2:], "/")
|
||||
|
||||
err = s.delete(storageAccountName, blobName)
|
||||
}
|
||||
return processStepResult(err, s.error, state)
|
||||
}
|
||||
|
||||
func (*StepDeleteOSDisk) Cleanup(multistep.StateBag) {
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepDeleteOSDiskShouldFailIfGetFails(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteOSDisk("http://storage.blob.core.windows.net/images/pkrvm_os.vhd")
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldPassIfGetPasses(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteOSDisk("http://storage.blob.core.windows.net/images/pkrvm_os.vhd")
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
||||
var actualStorageContainerName string
|
||||
var actualBlobName string
|
||||
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(storageContainerName string, blobName string) error {
|
||||
actualStorageContainerName = storageContainerName
|
||||
actualBlobName = blobName
|
||||
return nil
|
||||
},
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteOSDisk("http://storage.blob.core.windows.net/images/pkrvm_os.vhd")
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if actualStorageContainerName != "images" {
|
||||
t.Fatalf("Expected the storage container name to be 'images', but found '%s'.", actualStorageContainerName)
|
||||
}
|
||||
|
||||
if actualBlobName != "pkrvm_os.vhd" {
|
||||
t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldHandleComplexStorageContainerNames(t *testing.T) {
|
||||
var actualStorageContainerName string
|
||||
var actualBlobName string
|
||||
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(storageContainerName string, blobName string) error {
|
||||
actualStorageContainerName = storageContainerName
|
||||
actualBlobName = blobName
|
||||
return nil
|
||||
},
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteOSDisk("http://storage.blob.core.windows.net/abc/def/pkrvm_os.vhd")
|
||||
testSubject.Run(context.Background(), stateBag)
|
||||
|
||||
if actualStorageContainerName != "abc" {
|
||||
t.Fatalf("Expected the storage container name to be 'abc/def', but found '%s'.", actualStorageContainerName)
|
||||
}
|
||||
|
||||
if actualBlobName != "def/pkrvm_os.vhd" {
|
||||
t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldFailIfVHDNameCannotBeURLParsed(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
||||
}
|
||||
|
||||
// Invalid URL per https://golang.org/src/net/url/url_test.go
|
||||
stateBag := DeleteTestStateBagStepDeleteOSDisk("http://[fe80::1%en0]/")
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
if result != multistep.ActionHalt {
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%v'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
||||
t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
func TestStepDeleteOSDiskShouldFailIfVHDNameIsTooShort(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteOSDisk("storage.blob.core.windows.net/abc")
|
||||
|
||||
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 not stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldPassIfManagedDiskInTempResourceGroup(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk")
|
||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldFailIfManagedDiskInExistingResourceGroupFailsToDelete(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
deleteManaged: func(context.Context, string, string) error { return errors.New("UNIT TEST FAIL!") },
|
||||
}
|
||||
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk")
|
||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
||||
|
||||
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 not stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteOSDiskShouldFailIfManagedDiskInExistingResourceGroupIsDeleted(t *testing.T) {
|
||||
var testSubject = &StepDeleteOSDisk{
|
||||
delete: func(string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
||||
}
|
||||
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk")
|
||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteTestStateBagStepDeleteOSDisk(osDiskVhd string) multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
stateBag.Put(constants.ArmOSDiskVhd, osDiskVhd)
|
||||
stateBag.Put(constants.ArmIsManagedImage, false)
|
||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
const (
|
||||
maxResourcesToDelete = 50
|
||||
)
|
||||
|
||||
type StepDeleteResourceGroup struct {
|
||||
client *AzureClient
|
||||
delete func(ctx context.Context, state multistep.StateBag, resourceGroupName string) error
|
||||
say func(message string)
|
||||
error func(e error)
|
||||
}
|
||||
|
||||
func NewStepDeleteResourceGroup(client *AzureClient, ui packer.Ui) *StepDeleteResourceGroup {
|
||||
var step = &StepDeleteResourceGroup{
|
||||
client: client,
|
||||
say: func(message string) { ui.Say(message) },
|
||||
error: func(e error) { ui.Error(e.Error()) },
|
||||
}
|
||||
|
||||
step.delete = step.deleteResourceGroup
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) deleteResourceGroup(ctx context.Context, state multistep.StateBag, resourceGroupName string) error {
|
||||
var err error
|
||||
if state.Get(constants.ArmIsExistingResourceGroup).(bool) {
|
||||
s.say("\nThe resource group was not created by Packer, only deleting individual resources ...")
|
||||
var deploymentName = state.Get(constants.ArmDeploymentName).(string)
|
||||
err = s.deleteDeploymentResources(ctx, deploymentName, resourceGroupName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if keyVaultDeploymentName, ok := state.GetOk(constants.ArmKeyVaultDeploymentName); ok {
|
||||
// Only delete if custom keyvault was not provided.
|
||||
if exists := state.Get(constants.ArmIsExistingKeyVault).(bool); !exists {
|
||||
s.say("\n Deleting the keyvault deployment because it was created by Packer...")
|
||||
err = s.deleteDeploymentResources(ctx, keyVaultDeploymentName.(string), resourceGroupName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
s.say("\nThe resource group was created by Packer, deleting ...")
|
||||
f, err := s.client.GroupsClient.Delete(ctx, resourceGroupName)
|
||||
if err == nil {
|
||||
if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
||||
// No need to wait for the completion for delete if request is Accepted
|
||||
s.say(fmt.Sprintf("\nResource Group is being deleted, not waiting for deletion due to config. Resource Group Name '%s'", resourceGroupName))
|
||||
} else {
|
||||
f.WaitForCompletionRef(ctx, s.client.GroupsClient.Client)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.say(s.client.LastError.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) deleteDeploymentResources(ctx context.Context, deploymentName, resourceGroupName string) error {
|
||||
maxResources := int32(maxResourcesToDelete)
|
||||
|
||||
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))
|
||||
|
||||
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 = deploymentOperations.Next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) 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)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Deleting resource group ...")
|
||||
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
||||
|
||||
err := s.delete(ctx, state, resourceGroupName)
|
||||
if err != nil {
|
||||
state.Put(constants.Error, err)
|
||||
s.error(err)
|
||||
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put(constants.ArmIsResourceGroupCreated, false)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (*StepDeleteResourceGroup) Cleanup(multistep.StateBag) {
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepDeleteResourceGroupShouldFailIfDeleteFails(t *testing.T) {
|
||||
var testSubject = &StepDeleteResourceGroup{
|
||||
delete: func(context.Context, multistep.StateBag, string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteResourceGroup()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteResourceGroupShouldPassIfDeletePasses(t *testing.T) {
|
||||
var testSubject = &StepDeleteResourceGroup{
|
||||
delete: func(context.Context, multistep.StateBag, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteResourceGroup()
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteResourceGroupShouldDeleteStateBagArmResourceGroupCreated(t *testing.T) {
|
||||
var testSubject = &StepDeleteResourceGroup{
|
||||
delete: func(context.Context, multistep.StateBag, string) error {
|
||||
return nil
|
||||
},
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := DeleteTestStateBagStepDeleteResourceGroup()
|
||||
testSubject.Run(context.Background(), stateBag)
|
||||
|
||||
value, ok := stateBag.GetOk(constants.ArmIsResourceGroupCreated)
|
||||
if !ok {
|
||||
t.Fatal("Expected the resource bag value arm.IsResourceGroupCreated to exist")
|
||||
}
|
||||
|
||||
if value.(bool) {
|
||||
t.Fatalf("Expected arm.IsResourceGroupCreated to be false, but got %q", value)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteTestStateBagStepDeleteResourceGroup() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
||||
stateBag.Put(constants.ArmIsResourceGroupCreated, "Unit Test: IsResourceGroupCreated")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
@@ -6,10 +6,8 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
@@ -17,7 +15,7 @@ import (
|
||||
type StepDeployTemplate struct {
|
||||
client *AzureClient
|
||||
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)
|
||||
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
|
||||
say func(message string)
|
||||
@@ -38,54 +36,12 @@ func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, de
|
||||
}
|
||||
|
||||
step.deploy = step.deployTemplate
|
||||
step.delete = step.deleteDeploymentResources
|
||||
step.delete = deleteResource
|
||||
step.disk = step.getImageDetails
|
||||
step.deleteDisk = step.deleteImage
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Deploying deployment template ...")
|
||||
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
||||
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name))
|
||||
|
||||
return processStepResult(
|
||||
s.deploy(ctx, resourceGroupName, s.name),
|
||||
s.error, state)
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
defer func() {
|
||||
err := s.deleteTemplate(context.Background(), state)
|
||||
if err != nil {
|
||||
s.say(s.client.LastError.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
// Only clean up if this is an existing resource group that has been verified to exist.
|
||||
// ArmIsResourceGroupCreated is set in step_create_resource_group to true, when Packer has verified that the resource group exists.
|
||||
// ArmIsExistingResourceGroup is set to true when build_resource_group is set in the Packer configuration.
|
||||
existingResourceGroup := state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||
resourceGroupCreated := state.Get(constants.ArmIsResourceGroupCreated).(bool)
|
||||
if !existingResourceGroup || !resourceGroupCreated {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("\nThe resource group was not created by Packer, deleting individual resources ...")
|
||||
|
||||
deploymentName := s.name
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
err := s.deleteDeploymentResources(context.TODO(), deploymentName, resourceGroupName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceGroupName)
|
||||
}
|
||||
|
||||
NewStepDeleteAdditionalDisks(s.client, ui).Run(context.TODO(), state)
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
||||
deployment, err := s.factory(s.config)
|
||||
if err != nil {
|
||||
@@ -93,50 +49,61 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa
|
||||
}
|
||||
|
||||
f, err := s.client.DeploymentsClient.CreateOrUpdate(ctx, resourceGroupName, deploymentName, *deployment)
|
||||
if err == nil {
|
||||
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||
}
|
||||
if err != nil {
|
||||
s.say(s.client.LastError.Error())
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||
func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep.StateBag) error {
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
var deploymentName = s.name
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName))
|
||||
|
||||
f, err := s.client.DeploymentsClient.Delete(ctx, resourceGroupName, deploymentName)
|
||||
if err == nil {
|
||||
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||
}
|
||||
if err != nil {
|
||||
s.say(s.client.LastError.Error())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep.StateBag) error {
|
||||
deploymentName := s.name
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Deploying deployment template ...")
|
||||
|
||||
ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName))
|
||||
f, err := s.client.DeploymentsClient.Delete(ctx, resourceGroupName, deploymentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
|
||||
return f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
||||
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name))
|
||||
|
||||
return processStepResult(
|
||||
s.deploy(ctx, resourceGroupName, s.name),
|
||||
s.error, state)
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) {
|
||||
//We can't depend on constants.ArmOSDiskVhd being set
|
||||
var imageName, imageType string
|
||||
var imageName string
|
||||
var imageType string
|
||||
vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "")
|
||||
if err != nil {
|
||||
return imageName, imageType, err
|
||||
} else {
|
||||
if vm.StorageProfile.OsDisk.Vhd != nil {
|
||||
imageType = "image"
|
||||
imageName = *vm.StorageProfile.OsDisk.Vhd.URI
|
||||
} else {
|
||||
imageType = "Microsoft.Compute/disks"
|
||||
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
||||
}
|
||||
}
|
||||
|
||||
if vm.StorageProfile.OsDisk.Vhd != nil {
|
||||
imageType = "image"
|
||||
imageName = *vm.StorageProfile.OsDisk.Vhd.URI
|
||||
return imageType, imageName, nil
|
||||
}
|
||||
|
||||
imageType = "Microsoft.Compute/disks"
|
||||
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
||||
|
||||
return imageType, imageName, nil
|
||||
}
|
||||
|
||||
@@ -191,7 +158,6 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string,
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// VHD image
|
||||
u, err := url.Parse(imageName)
|
||||
if err != nil {
|
||||
@@ -205,66 +171,75 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string,
|
||||
var blobName = strings.Join(xs[2:], "/")
|
||||
|
||||
blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName)
|
||||
_, err = blob.BreakLease(nil)
|
||||
if err != nil && !strings.Contains(err.Error(), "LeaseNotPresentWithLeaseOperation") {
|
||||
s.say(s.client.LastError.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return blob.Delete(nil)
|
||||
err = blob.Delete(nil)
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
defer s.deleteTemplate(context.Background(), state)
|
||||
|
||||
//Only clean up if this was an existing resource group and the resource group
|
||||
//is marked as created
|
||||
var existingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||
var resourceGroupCreated = state.Get(constants.ArmIsResourceGroupCreated).(bool)
|
||||
if !existingResourceGroup || !resourceGroupCreated {
|
||||
return
|
||||
}
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("\nThe resource group was not created by Packer, deleting individual resources ...")
|
||||
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
var computeName = state.Get(constants.ArmComputeName).(string)
|
||||
var deploymentName = s.name
|
||||
imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceGroupName)
|
||||
return err
|
||||
ui.Error("Could not retrieve OS Image details")
|
||||
}
|
||||
|
||||
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
|
||||
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))
|
||||
}
|
||||
|
||||
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,
|
||||
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
|
||||
}
|
||||
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 {
|
||||
s.reportIfError(err, resourceName)
|
||||
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
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 has to be
|
||||
// deleted separately
|
||||
ui.Say(fmt.Sprintf(" -> %s : '%s'", imageType, imageName))
|
||||
err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
|
||||
if err = deploymentOperations.Next(); err != nil {
|
||||
return err
|
||||
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", imageName, 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,16 +55,10 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||
|
||||
switch config.OSType {
|
||||
case constants.Target_Linux:
|
||||
err = builder.BuildLinux(config.sshAuthorizedKey, config.Comm.SSHPassword == "") // if ssh password is not explicitly specified, disable password auth
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.BuildLinux(config.sshAuthorizedKey)
|
||||
case constants.Target_Windows:
|
||||
osType = compute.Windows
|
||||
err = builder.BuildWindows(config.tmpKeyVaultName, config.tmpWinRMCertificateUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.BuildWindows(config.tmpKeyVaultName, config.tmpWinRMCertificateUrl)
|
||||
}
|
||||
|
||||
if len(config.UserAssignedManagedIdentities) != 0 {
|
||||
@@ -74,15 +68,9 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||
}
|
||||
|
||||
if config.ImageUrl != "" {
|
||||
err = builder.SetImageUrl(config.ImageUrl, osType, config.diskCachingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetImageUrl(config.ImageUrl, osType, config.diskCachingType)
|
||||
} else if config.CustomManagedImageName != "" {
|
||||
err = builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType, config.diskCachingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType, config.diskCachingType)
|
||||
} else if config.ManagedImageName != "" && config.ImagePublisher != "" {
|
||||
imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s",
|
||||
config.ClientConfig.SubscriptionID,
|
||||
@@ -104,62 +92,38 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||
config.SharedGallery.ImageVersion)
|
||||
}
|
||||
|
||||
err = builder.SetSharedGalleryImage(config.Location, imageID, config.diskCachingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetSharedGalleryImage(config.Location, imageID, config.diskCachingType)
|
||||
} else {
|
||||
err = builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, config.diskCachingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, config.diskCachingType)
|
||||
}
|
||||
|
||||
if config.OSDiskSizeGB > 0 {
|
||||
err = builder.SetOSDiskSizeGB(config.OSDiskSizeGB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetOSDiskSizeGB(config.OSDiskSizeGB)
|
||||
}
|
||||
|
||||
if len(config.AdditionalDiskSize) > 0 {
|
||||
isManaged := config.CustomManagedImageName != "" || (config.ManagedImageName != "" && config.ImagePublisher != "") || config.SharedGallery.Subscription != ""
|
||||
err = builder.SetAdditionalDisks(config.AdditionalDiskSize, config.tmpDataDiskName, isManaged, config.diskCachingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetAdditionalDisks(config.AdditionalDiskSize, config.tmpDataDiskName, isManaged, config.diskCachingType)
|
||||
}
|
||||
|
||||
if config.customData != "" {
|
||||
err = builder.SetCustomData(config.customData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetCustomData(config.customData)
|
||||
}
|
||||
|
||||
if config.PlanInfo.PlanName != "" {
|
||||
err = builder.SetPlanInfo(config.PlanInfo.PlanName, config.PlanInfo.PlanProduct, config.PlanInfo.PlanPublisher, config.PlanInfo.PlanPromotionCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.SetPlanInfo(config.PlanInfo.PlanName, config.PlanInfo.PlanProduct, config.PlanInfo.PlanPublisher, config.PlanInfo.PlanPromotionCode)
|
||||
}
|
||||
|
||||
if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp {
|
||||
err = builder.SetPrivateVirtualNetworkWithPublicIp(
|
||||
builder.SetPrivateVirtualNetworkWithPublicIp(
|
||||
config.VirtualNetworkResourceGroupName,
|
||||
config.VirtualNetworkName,
|
||||
config.VirtualNetworkSubnetName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if config.VirtualNetworkName != "" {
|
||||
err = builder.SetVirtualNetwork(
|
||||
builder.SetVirtualNetwork(
|
||||
config.VirtualNetworkResourceGroupName,
|
||||
config.VirtualNetworkName,
|
||||
config.VirtualNetworkSubnetName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if config.AllowedInboundIpAddresses != nil && len(config.AllowedInboundIpAddresses) >= 1 && config.Comm.Port() != 0 {
|
||||
@@ -176,11 +140,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||
}
|
||||
}
|
||||
|
||||
err = builder.SetTags(&config.AzureTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builder.SetTags(&config.AzureTags)
|
||||
doc, _ := builder.ToJSON()
|
||||
return createDeploymentParameters(*doc, params)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"tenantId": "[parameters('tenantId')]"
|
||||
}
|
||||
],
|
||||
"enableSoftDelete": "true",
|
||||
"enabledForDeployment": "true",
|
||||
"enabledForTemplateDeployment": "true",
|
||||
"sku": {
|
||||
|
||||
@@ -149,10 +149,10 @@
|
||||
]
|
||||
},
|
||||
"osProfile": {
|
||||
"adminPassword": "[parameters('adminPassword')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"computerName": "[parameters('vmName')]",
|
||||
"linuxConfiguration": {
|
||||
"disablePasswordAuthentication": true,
|
||||
"ssh": {
|
||||
"publicKeys": [
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user