Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0e9b26457 | |||
| 155d6c2e32 | |||
| 105173a7c7 | |||
| b82704cc70 | |||
| c2b2bb2271 | |||
| 8445b6ff20 |
@@ -7,7 +7,7 @@ version: 2.1
|
||||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/circleci/golang:1.15
|
||||
- image: circleci/golang:1.15
|
||||
resource_class: medium+
|
||||
darwin:
|
||||
macos:
|
||||
@@ -153,7 +153,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,14 +167,14 @@ 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
|
||||
- image: node:12
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
|
||||
@@ -27,4 +27,3 @@ Thumbs.db
|
||||
/packer.exe
|
||||
.project
|
||||
cache
|
||||
/.vscode/
|
||||
|
||||
+3
-89
@@ -1,67 +1,10 @@
|
||||
## 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]
|
||||
## 1.6.5 (Upcoming)
|
||||
|
||||
### 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
|
||||
* core/hcl2: Packer HCL's "Coalesce" function now behaves same way as
|
||||
Terraform's. [GH-10016]
|
||||
* core/HCL: Hide sensitive variables from output. [GH-10031]
|
||||
* 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
|
||||
@@ -71,38 +14,9 @@
|
||||
|
||||
### 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)
|
||||
|
||||
|
||||
+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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -107,8 +107,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -223,8 +221,6 @@ 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},
|
||||
|
||||
@@ -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",
|
||||
@@ -372,7 +368,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{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"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"
|
||||
)
|
||||
@@ -272,7 +271,7 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
||||
|
||||
cp, err := c.session.Config.Credentials.Get()
|
||||
|
||||
if awserrors.Matches(err, "NoCredentialProviders", "") {
|
||||
if IsAWSErr(err, "NoCredentialProviders", "") {
|
||||
return nil, c.NewNoValidCredentialSourcesError(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package awserrors
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
)
|
||||
|
||||
// Returns true if the err matches all these conditions:
|
||||
// * err is of type awserr.Error
|
||||
// * Error.Code() matches code
|
||||
// * Error.Message() contains message
|
||||
func Matches(err error, code string, message string) bool {
|
||||
if err, ok := err.(awserr.Error); ok {
|
||||
return err.Code() == code && strings.Contains(err.Message(), message)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -134,8 +134,8 @@ func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapp
|
||||
ebsBlockDevice.VolumeSize = aws.Int64(blockDevice.VolumeSize)
|
||||
}
|
||||
|
||||
// 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{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -376,12 +376,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.
|
||||
@@ -548,6 +542,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
msg := fmt.Errorf(`no iam_instance_profile defined; session_manager connectivity requires a valid instance profile with AmazonSSMManagedInstanceCore permissions. Alternatively a temporary_iam_instance_profile_policy_document can be used.`)
|
||||
errs = append(errs, msg)
|
||||
}
|
||||
|
||||
if c.PauseBeforeSSM == 0 {
|
||||
c.PauseBeforeSSM = 10 * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
if c.Comm.SSHKeyPairName != "" {
|
||||
@@ -633,13 +631,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,197 @@
|
||||
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", aws.StringValue(input.Target))
|
||||
|
||||
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, ok := <-stderrCh:
|
||||
if !ok {
|
||||
stderrCh = nil
|
||||
break
|
||||
}
|
||||
|
||||
if output != "" {
|
||||
log.Printf("[ERROR] %s: %s", prefix, output)
|
||||
}
|
||||
case output, ok := <-stdoutCh:
|
||||
if !ok {
|
||||
stdoutCh = nil
|
||||
break
|
||||
}
|
||||
|
||||
if output != "" {
|
||||
log.Printf("[DEBUG] %s: %s", prefix, output)
|
||||
}
|
||||
}
|
||||
|
||||
if stdoutCh == nil && stderrCh == nil {
|
||||
log.Printf("[DEBUG] %s: %s", prefix, "active session has been terminated; stopping all log polling processes.")
|
||||
return
|
||||
}
|
||||
}
|
||||
}(ctx, sessionManagerPluginName)
|
||||
|
||||
log.Printf("[DEBUG %s] opening session tunnel to instance %q for session %q", sessionManagerPluginName,
|
||||
aws.StringValue(d.sessionParams.Target),
|
||||
aws.StringValue(d.session.SessionId),
|
||||
)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
err = fmt.Errorf("error encountered when calling %s: %s\n", sessionManagerPluginName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopSession terminates an active Session Manager session
|
||||
func (d *SSMDriver) StopSession() error {
|
||||
|
||||
if d.session == nil || d.session.SessionId == nil {
|
||||
return fmt.Errorf("Unable to find a valid session to instance %q; skipping the termination step",
|
||||
aws.StringValue(d.sessionParams.Target))
|
||||
}
|
||||
|
||||
_, err := d.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: d.session.SessionId})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error terminating SSM Session %q. Please terminate the session manually: %s", aws.StringValue(d.session.SessionId), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Args validates the driver inputs before returning an ordered set of arguments to pass to the driver command.
|
||||
func (d *SSMDriver) Args() ([]string, error) {
|
||||
if d.session == nil {
|
||||
return nil, fmt.Errorf("an active Amazon SSM Session is required before trying to open a session tunnel")
|
||||
}
|
||||
|
||||
// AWS session-manager-plugin requires a valid session be passed in JSON.
|
||||
sessionDetails, err := json.Marshal(d.session)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encountered in reading session details %s", err)
|
||||
}
|
||||
|
||||
// AWS session-manager-plugin requires the parameters used in the session to be passed in JSON as well.
|
||||
sessionParameters, err := json.Marshal(d.sessionParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encountered in reading session parameter details %s", err)
|
||||
}
|
||||
|
||||
// Args must be in this order
|
||||
args := []string{
|
||||
string(sessionDetails),
|
||||
d.Region,
|
||||
sessionCommand,
|
||||
d.ProfileName,
|
||||
string(sessionParameters),
|
||||
d.SvcEndpoint,
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func NewSSMDriverWithMockSvc(svc *MockSSMSvc) *SSMDriver {
|
||||
config := SSMDriverConfig{
|
||||
SvcClient: svc,
|
||||
Region: "east",
|
||||
ProfileName: "default",
|
||||
SvcEndpoint: "example.com",
|
||||
}
|
||||
|
||||
driver := SSMDriver{
|
||||
SSMDriverConfig: config,
|
||||
pluginCmdFunc: func(ctx context.Context) error { return nil },
|
||||
}
|
||||
|
||||
return &driver
|
||||
}
|
||||
func TestSSMDriver_StartSession(t *testing.T) {
|
||||
mockSvc := MockSSMSvc{}
|
||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||
|
||||
if driver.SvcClient == nil {
|
||||
t.Fatalf("SvcClient for driver should not be nil")
|
||||
}
|
||||
|
||||
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||
if err != nil {
|
||||
t.Fatalf("calling StartSession should not error but got %v", err)
|
||||
}
|
||||
|
||||
if !mockSvc.StartSessionCalled {
|
||||
t.Fatalf("expected test to call ssm mocks but didn't")
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
t.Errorf("expected session to be set after a successful call to StartSession")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(session, MockStartSessionOutput()) {
|
||||
t.Errorf("expected session to be %v but got %v", MockStartSessionOutput(), session)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSMDriver_StartSessionWithError(t *testing.T) {
|
||||
mockSvc := MockSSMSvc{StartSessionError: fmt.Errorf("bogus error")}
|
||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||
|
||||
if driver.SvcClient == nil {
|
||||
t.Fatalf("SvcClient for driver should not be nil")
|
||||
}
|
||||
|
||||
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||
if err == nil {
|
||||
t.Fatalf("StartSession should have thrown an error but didn't")
|
||||
}
|
||||
|
||||
if !mockSvc.StartSessionCalled {
|
||||
t.Errorf("expected test to call StartSession mock but didn't")
|
||||
}
|
||||
|
||||
if session != nil {
|
||||
t.Errorf("expected session to be nil after a bad StartSession call, but got %v", session)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSMDriver_StopSession(t *testing.T) {
|
||||
mockSvc := MockSSMSvc{}
|
||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||
|
||||
if driver.SvcClient == nil {
|
||||
t.Fatalf("SvcClient for driver should not be nil")
|
||||
}
|
||||
|
||||
// Calling StopSession before StartSession should fail
|
||||
err := driver.StopSession()
|
||||
if err == nil {
|
||||
t.Fatalf("calling StopSession() on a driver that has no started session should fail")
|
||||
}
|
||||
|
||||
if driver.session != nil {
|
||||
t.Errorf("expected session to be default to nil")
|
||||
}
|
||||
|
||||
if mockSvc.TerminateSessionCalled {
|
||||
t.Fatalf("a call to TerminateSession should not occur when there is no valid SSM session")
|
||||
}
|
||||
|
||||
// Lets try calling start session, then stopping to see what happens.
|
||||
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||
if err != nil {
|
||||
t.Fatalf("calling StartSession should not error but got %v", err)
|
||||
}
|
||||
|
||||
if !mockSvc.StartSessionCalled {
|
||||
t.Fatalf("expected test to call StartSession mock but didn't")
|
||||
}
|
||||
|
||||
if session == nil || driver.session != session {
|
||||
t.Errorf("expected session to be set after a successful call to StartSession")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(session, MockStartSessionOutput()) {
|
||||
t.Errorf("expected session to be %v but got %v", MockStartSessionOutput(), session)
|
||||
}
|
||||
|
||||
err = driver.StopSession()
|
||||
if err != nil {
|
||||
t.Errorf("calling StopSession() on a driver on a started session should not fail")
|
||||
}
|
||||
|
||||
if !mockSvc.TerminateSessionCalled {
|
||||
t.Fatalf("expected test to call StopSession mock but didn't")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSSMDriver_Args(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
ProfileName string
|
||||
SkipStartSession bool
|
||||
ErrorExpected bool
|
||||
}{
|
||||
{
|
||||
Name: "NilSession",
|
||||
SkipStartSession: true,
|
||||
ErrorExpected: true,
|
||||
},
|
||||
{
|
||||
Name: "NonNilSession",
|
||||
ErrorExpected: false,
|
||||
},
|
||||
{
|
||||
Name: "SessionWithProfileName",
|
||||
ProfileName: "default",
|
||||
ErrorExpected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
mockSvc := MockSSMSvc{}
|
||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
||||
driver.ProfileName = tc.ProfileName
|
||||
|
||||
if driver.SvcClient == nil {
|
||||
t.Fatalf("svcclient for driver should not be nil")
|
||||
}
|
||||
|
||||
if !tc.SkipStartSession {
|
||||
_, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
||||
if err != nil {
|
||||
t.Fatalf("got an error when calling StartSession %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
args, err := driver.Args()
|
||||
if tc.ErrorExpected && err == nil {
|
||||
t.Fatalf("Driver.Args with a %q should have failed but instead no error was returned", tc.Name)
|
||||
}
|
||||
|
||||
if tc.ErrorExpected {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("got an error when it should've worked %v", err)
|
||||
}
|
||||
|
||||
// validate launch script
|
||||
expectedArgString := fmt.Sprintf(`{"SessionId":"packerid","StreamUrl":"http://packer.io","TokenValue":"packer-token"} east StartSession %s {"DocumentName":"AWS-StartPortForwardingSession","Parameters":{"localPortNumber":["8001"],"portNumber":["22"]},"Target":"fakeinstance"} example.com`, tc.ProfileName)
|
||||
argString := strings.Join(args, " ")
|
||||
if argString != expectedArgString {
|
||||
t.Errorf("Expected launch script to be %q but got %q", expectedArgString, argString)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/ssm"
|
||||
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
||||
)
|
||||
|
||||
type MockSSMSvc struct {
|
||||
ssmiface.SSMAPI
|
||||
StartSessionError error
|
||||
TerminateSessionError error
|
||||
StartSessionCalled bool
|
||||
TerminateSessionCalled bool
|
||||
}
|
||||
|
||||
func (svc *MockSSMSvc) StartSessionWithContext(ctx aws.Context, input *ssm.StartSessionInput, options ...request.Option) (*ssm.StartSessionOutput, error) {
|
||||
svc.StartSessionCalled = true
|
||||
return MockStartSessionOutput(), svc.StartSessionError
|
||||
}
|
||||
func (svc *MockSSMSvc) TerminateSession(input *ssm.TerminateSessionInput) (*ssm.TerminateSessionOutput, error) {
|
||||
svc.TerminateSessionCalled = true
|
||||
return new(ssm.TerminateSessionOutput), svc.TerminateSessionError
|
||||
}
|
||||
|
||||
func MockPluginCmdFunc(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func MockStartSessionOutput() *ssm.StartSessionOutput {
|
||||
id, url, token := "packerid", "http://packer.io", "packer-token"
|
||||
output := ssm.StartSessionOutput{
|
||||
SessionId: &id,
|
||||
StreamUrl: &url,
|
||||
TokenValue: &token,
|
||||
}
|
||||
return &output
|
||||
}
|
||||
|
||||
func MockStartSessionInput(instance string) ssm.StartSessionInput {
|
||||
params := map[string][]*string{
|
||||
"portNumber": []*string{aws.String("22")},
|
||||
"localPortNumber": []*string{aws.String("8001")},
|
||||
}
|
||||
|
||||
input := ssm.StartSessionInput{
|
||||
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
||||
Parameters: params,
|
||||
Target: aws.String(instance),
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
@@ -3,13 +3,13 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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 +21,9 @@ type StepCreateSSMTunnel struct {
|
||||
LocalPortNumber int
|
||||
RemotePortNumber int
|
||||
SSMAgentEnabled bool
|
||||
instanceId string
|
||||
PauseBeforeSSM time.Duration
|
||||
stopSSMCommand func()
|
||||
driver *SSMDriver
|
||||
}
|
||||
|
||||
// Run executes the Packer build step that creates a session tunnel.
|
||||
@@ -35,7 +36,7 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
||||
|
||||
// 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))
|
||||
ui.Say(fmt.Sprintf("Waiting %s for establishing the SSM session...", s.PauseBeforeSSM))
|
||||
select {
|
||||
case <-time.After(s.PauseBeforeSSM):
|
||||
break
|
||||
@@ -60,38 +61,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 +129,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"
|
||||
@@ -34,7 +33,6 @@ type StepRunSourceInstance struct {
|
||||
IsRestricted bool
|
||||
SourceAMI string
|
||||
Tags map[string]string
|
||||
Tenancy string
|
||||
UserData string
|
||||
UserDataFile string
|
||||
VolumeTags map[string]string
|
||||
@@ -197,15 +195,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 +210,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())
|
||||
@@ -254,7 +248,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 +293,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
|
||||
|
||||
@@ -11,8 +11,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/random"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
@@ -36,7 +34,6 @@ type StepRunSpotInstance struct {
|
||||
ExpectedRootDevice string
|
||||
InstanceInitiatedShutdownBehavior string
|
||||
InstanceType string
|
||||
Region string
|
||||
SourceAMI string
|
||||
SpotPrice string
|
||||
SpotTags map[string]string
|
||||
@@ -160,7 +157,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 +196,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 +220,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 +242,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)
|
||||
@@ -379,6 +360,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 +396,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 +427,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)
|
||||
|
||||
@@ -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"
|
||||
@@ -45,7 +43,6 @@ func getBasicStep() *StepRunSpotInstance {
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceInitiatedShutdownBehavior: "stop",
|
||||
InstanceType: "t2.micro",
|
||||
Region: "us-east-1",
|
||||
SourceAMI: "",
|
||||
SpotPrice: "auto",
|
||||
SpotTags: nil,
|
||||
@@ -137,226 +134,3 @@ func TestCreateTemplateData_NoEphemeral(t *testing.T) {
|
||||
// t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
||||
// }
|
||||
}
|
||||
|
||||
type runSpotEC2ConnMock struct {
|
||||
ec2iface.EC2API
|
||||
|
||||
CreateLaunchTemplateParams []*ec2.CreateLaunchTemplateInput
|
||||
CreateLaunchTemplateFn func(*ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error)
|
||||
|
||||
CreateFleetParams []*ec2.CreateFleetInput
|
||||
CreateFleetFn func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error)
|
||||
|
||||
CreateTagsParams []*ec2.CreateTagsInput
|
||||
CreateTagsFn func(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error)
|
||||
|
||||
DescribeInstancesParams []*ec2.DescribeInstancesInput
|
||||
DescribeInstancesFn func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
|
||||
}
|
||||
|
||||
func (m *runSpotEC2ConnMock) CreateLaunchTemplate(req *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
||||
m.CreateLaunchTemplateParams = append(m.CreateLaunchTemplateParams, req)
|
||||
resp, err := m.CreateLaunchTemplateFn(req)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *runSpotEC2ConnMock) CreateFleet(req *ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
||||
m.CreateFleetParams = append(m.CreateFleetParams, req)
|
||||
if m.CreateFleetFn != nil {
|
||||
resp, err := m.CreateFleetFn(req)
|
||||
return resp, err
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *runSpotEC2ConnMock) DescribeInstances(req *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
||||
m.DescribeInstancesParams = append(m.DescribeInstancesParams, req)
|
||||
if m.DescribeInstancesFn != nil {
|
||||
resp, err := m.DescribeInstancesFn(req)
|
||||
return resp, err
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *runSpotEC2ConnMock) CreateTags(req *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
|
||||
m.CreateTagsParams = append(m.CreateTagsParams, req)
|
||||
if m.CreateTagsFn != nil {
|
||||
resp, err := m.CreateTagsFn(req)
|
||||
return resp, err
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func defaultEc2Mock(instanceId, spotRequestId, volumeId *string) *runSpotEC2ConnMock {
|
||||
instance := &ec2.Instance{
|
||||
InstanceId: instanceId,
|
||||
SpotInstanceRequestId: spotRequestId,
|
||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
||||
{
|
||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
||||
VolumeId: volumeId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return &runSpotEC2ConnMock{
|
||||
CreateLaunchTemplateFn: func(in *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
||||
return &ec2.CreateLaunchTemplateOutput{
|
||||
LaunchTemplate: nil,
|
||||
Warning: nil,
|
||||
}, nil
|
||||
},
|
||||
CreateFleetFn: func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
||||
return &ec2.CreateFleetOutput{
|
||||
Errors: nil,
|
||||
FleetId: nil,
|
||||
Instances: []*ec2.CreateFleetInstance{
|
||||
{
|
||||
InstanceIds: []*string{instanceId},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
DescribeInstancesFn: func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
||||
return &ec2.DescribeInstancesOutput{
|
||||
NextToken: nil,
|
||||
Reservations: []*ec2.Reservation{
|
||||
{
|
||||
Instances: []*ec2.Instance{instance},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
instanceId := aws.String("test-instance-id")
|
||||
spotRequestId := aws.String("spot-id")
|
||||
volumeId := aws.String("volume-id")
|
||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId)
|
||||
|
||||
uiMock := packer.TestUi(t)
|
||||
|
||||
state := tStateSpot()
|
||||
state.Put("ec2", ec2Mock)
|
||||
state.Put("ui", uiMock)
|
||||
state.Put("source_image", testImage())
|
||||
|
||||
stepRunSpotInstance := getBasicStep()
|
||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
||||
stepRunSpotInstance.SpotTags = map[string]string{
|
||||
"spot-tag": "spot-tag-value",
|
||||
}
|
||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
||||
"volume-tag": "volume-tag-value",
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
action := stepRunSpotInstance.Run(ctx, state)
|
||||
|
||||
if err := state.Get("error"); err != nil {
|
||||
t.Fatalf("should not error, but: %v", err)
|
||||
}
|
||||
|
||||
if action != multistep.ActionContinue {
|
||||
t.Fatalf("shoul continue, but: %v", action)
|
||||
}
|
||||
|
||||
if len(ec2Mock.CreateLaunchTemplateParams) != 1 {
|
||||
t.Fatalf("createLaunchTemplate should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
||||
}
|
||||
launchTemplateName := ec2Mock.CreateLaunchTemplateParams[0].LaunchTemplateName
|
||||
|
||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 1 {
|
||||
t.Fatalf("exactly one launch template tag specification expected")
|
||||
}
|
||||
if *ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].ResourceType != "launch-template" {
|
||||
t.Fatalf("resource type 'launch-template' expected")
|
||||
}
|
||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags) != 1 {
|
||||
t.Fatalf("1 launch template tag expected")
|
||||
}
|
||||
|
||||
nameTag := ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags[0]
|
||||
if *nameTag.Key != "spot-tag" || *nameTag.Value != "spot-tag-value" {
|
||||
t.Fatalf("expected spot-tag: spot-tag-value")
|
||||
}
|
||||
|
||||
if len(ec2Mock.CreateFleetParams) != 1 {
|
||||
t.Fatalf("createFleet should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
||||
}
|
||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.DefaultTargetCapacityType != "spot" {
|
||||
t.Fatalf("capacity type should be spot")
|
||||
}
|
||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.TotalTargetCapacity != 1 {
|
||||
t.Fatalf("target capacity should be 1")
|
||||
}
|
||||
if len(ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs) != 1 {
|
||||
t.Fatalf("exactly one launch config template expected")
|
||||
}
|
||||
if *ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs[0].LaunchTemplateSpecification.LaunchTemplateName != *launchTemplateName {
|
||||
t.Fatalf("launchTemplateName should match in createLaunchTemplate and createFleet requests")
|
||||
}
|
||||
|
||||
if len(ec2Mock.DescribeInstancesParams) != 1 {
|
||||
t.Fatalf("describeInstancesParams should be invoked once, but invoked %v", len(ec2Mock.DescribeInstancesParams))
|
||||
}
|
||||
if *ec2Mock.DescribeInstancesParams[0].InstanceIds[0] != *instanceId {
|
||||
t.Fatalf("instanceId should match from createFleet response")
|
||||
}
|
||||
|
||||
uiMock.Say(fmt.Sprintf("%v", ec2Mock.CreateTagsParams))
|
||||
if len(ec2Mock.CreateTagsParams) != 3 {
|
||||
t.Fatalf("createTags should be invoked 3 times")
|
||||
}
|
||||
if len(ec2Mock.CreateTagsParams[0].Resources) != 1 || *ec2Mock.CreateTagsParams[0].Resources[0] != *spotRequestId {
|
||||
t.Fatalf("should create tags for spot request")
|
||||
}
|
||||
if len(ec2Mock.CreateTagsParams[1].Resources) != 1 || *ec2Mock.CreateTagsParams[1].Resources[0] != *instanceId {
|
||||
t.Fatalf("should create tags for instance")
|
||||
}
|
||||
if len(ec2Mock.CreateTagsParams[2].Resources) != 1 || ec2Mock.CreateTagsParams[2].Resources[0] != volumeId {
|
||||
t.Fatalf("should create tags for volume")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun_NoSpotTags(t *testing.T) {
|
||||
instanceId := aws.String("test-instance-id")
|
||||
spotRequestId := aws.String("spot-id")
|
||||
volumeId := aws.String("volume-id")
|
||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId)
|
||||
|
||||
uiMock := packer.TestUi(t)
|
||||
|
||||
state := tStateSpot()
|
||||
state.Put("ec2", ec2Mock)
|
||||
state.Put("ui", uiMock)
|
||||
state.Put("source_image", testImage())
|
||||
|
||||
stepRunSpotInstance := getBasicStep()
|
||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
||||
"volume-tag": "volume-tag-value",
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
action := stepRunSpotInstance.Run(ctx, state)
|
||||
|
||||
if err := state.Get("error"); err != nil {
|
||||
t.Fatalf("should not error, but: %v", err)
|
||||
}
|
||||
|
||||
if action != multistep.ActionContinue {
|
||||
t.Fatalf("shoul continue, but: %v", action)
|
||||
}
|
||||
|
||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 0 {
|
||||
t.Fatalf("0 launch template tags expected")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// },
|
||||
|
||||
type StepSetGeneratedData struct {
|
||||
GeneratedData *packerbuilderdata.GeneratedData
|
||||
GeneratedData *builder.GeneratedData
|
||||
}
|
||||
|
||||
func (s *StepSetGeneratedData) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
@@ -6,7 +6,6 @@ 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"
|
||||
@@ -43,7 +42,7 @@ func (s *StepStopEBSBackedInstance) Run(ctx context.Context, state multistep.Sta
|
||||
|
||||
// Work around this by retrying a few times, up to about 5 minutes.
|
||||
err := retry.Config{Tries: 6, ShouldRetry: func(error) bool {
|
||||
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -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...)
|
||||
@@ -169,7 +163,7 @@ 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
|
||||
|
||||
@@ -186,7 +180,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,
|
||||
@@ -213,7 +206,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
|
||||
@@ -81,7 +81,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"`
|
||||
@@ -96,8 +95,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -229,7 +226,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,8 +240,6 @@ 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},
|
||||
|
||||
@@ -246,14 +246,6 @@ 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) {
|
||||
}
|
||||
|
||||
@@ -358,31 +350,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)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||
"github.com/hashicorp/packer/common/random"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
@@ -62,7 +61,7 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
err = retry.Config{
|
||||
Tries: 0,
|
||||
ShouldRetry: func(err error) bool {
|
||||
if awserrors.Matches(err, "InvalidParameterValue", "Instance is not in state") {
|
||||
if awscommon.IsAWSErr(err, "InvalidParameterValue", "Instance is not in state") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -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...)
|
||||
@@ -192,7 +186,7 @@ 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
|
||||
|
||||
@@ -209,7 +203,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,
|
||||
@@ -235,7 +228,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
@@ -340,8 +332,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
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,
|
||||
|
||||
@@ -103,7 +103,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"`
|
||||
@@ -118,8 +117,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -252,7 +249,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,8 +263,6 @@ 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},
|
||||
|
||||
@@ -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.
|
||||
@@ -25,8 +23,6 @@ type StepSnapshotVolumes struct {
|
||||
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 +40,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
|
||||
|
||||
@@ -12,9 +12,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"
|
||||
@@ -100,21 +100,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
|
||||
@@ -180,7 +167,7 @@ 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}
|
||||
generatedData := &builder.GeneratedData{State: state}
|
||||
|
||||
var instanceStep multistep.Step
|
||||
|
||||
@@ -197,7 +184,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,
|
||||
@@ -223,7 +209,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
|
||||
@@ -105,7 +105,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"`
|
||||
@@ -120,8 +119,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -232,7 +229,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,8 +243,6 @@ 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},
|
||||
|
||||
@@ -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{
|
||||
@@ -250,7 +249,7 @@ 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
|
||||
|
||||
@@ -265,7 +264,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,
|
||||
@@ -288,7 +286,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
Tags: b.config.RunTags,
|
||||
Tenancy: b.config.Tenancy,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
}
|
||||
|
||||
@@ -81,7 +81,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"`
|
||||
@@ -96,8 +95,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -235,7 +232,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,8 +246,6 @@ 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},
|
||||
|
||||
+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.
|
||||
|
||||
@@ -222,6 +222,7 @@ 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),
|
||||
NewStepDeleteAdditionalDisks(azureClient, ui),
|
||||
}
|
||||
} else if b.config.OSType == constants.Target_Windows {
|
||||
steps = []multistep.Step{
|
||||
@@ -262,6 +263,7 @@ 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),
|
||||
NewStepDeleteAdditionalDisks(azureClient, ui),
|
||||
)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType)
|
||||
|
||||
@@ -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": {
|
||||
@@ -318,6 +262,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 +271,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 +325,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"
|
||||
}
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
@@ -580,7 +580,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...)
|
||||
|
||||
@@ -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"`
|
||||
@@ -81,8 +80,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -152,7 +149,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,8 +204,6 @@ 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},
|
||||
|
||||
@@ -61,11 +61,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)
|
||||
|
||||
@@ -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,7 +36,7 @@ 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
|
||||
@@ -64,9 +62,8 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
}()
|
||||
|
||||
// Only clean up if this is an existing resource group that has been verified to exist.
|
||||
// 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.
|
||||
//Only clean up if this was an existing resource group and the resource group
|
||||
//is marked as created
|
||||
existingResourceGroup := state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
||||
resourceGroupCreated := state.Get(constants.ArmIsResourceGroupCreated).(bool)
|
||||
if !existingResourceGroup || !resourceGroupCreated {
|
||||
@@ -78,12 +75,74 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
|
||||
deploymentName := s.name
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
err := s.deleteDeploymentResources(context.TODO(), deploymentName, resourceGroupName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceGroupName)
|
||||
|
||||
// Get image disk details before deleting the image; otherwise we won't be able to
|
||||
// delete the disk as the image request will return a 404
|
||||
computeName := state.Get(constants.ArmComputeName).(string)
|
||||
imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName)
|
||||
|
||||
if err != nil && !strings.Contains(err.Error(), "ResourceNotFound") {
|
||||
ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err))
|
||||
}
|
||||
|
||||
NewStepDeleteAdditionalDisks(s.client, ui).Run(context.TODO(), state)
|
||||
ui.Say(" -> Deployment Resources within: " + deploymentName)
|
||||
if deploymentName != "" {
|
||||
maxResources := int32(50)
|
||||
deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(context.TODO(), resourceGroupName, deploymentName, &maxResources)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceGroupName, err))
|
||||
}
|
||||
|
||||
for deploymentOperations.NotDone() {
|
||||
deploymentOperation := deploymentOperations.Value()
|
||||
// Sometimes an empty operation is added to the list by Azure
|
||||
if deploymentOperation.Properties.TargetResource == nil {
|
||||
if err := deploymentOperations.Next(); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error moving to to next deployment operation ...\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceGroupName, err))
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf(" -> %s : '%s'",
|
||||
*deploymentOperation.Properties.TargetResource.ResourceType,
|
||||
*deploymentOperation.Properties.TargetResource.ResourceName))
|
||||
|
||||
err = s.delete(context.TODO(), s.client,
|
||||
*deploymentOperation.Properties.TargetResource.ResourceType,
|
||||
*deploymentOperation.Properties.TargetResource.ResourceName,
|
||||
resourceGroupName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err))
|
||||
}
|
||||
|
||||
if err = deploymentOperations.Next(); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceGroupName, err))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The disk is not defined as an operation in the template so it has to be deleted separately
|
||||
if imageType == "" && imageName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf(" -> %s : '%s'", imageType, imageName))
|
||||
err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", imageName, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
||||
@@ -93,31 +152,27 @@ 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
|
||||
}
|
||||
|
||||
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)
|
||||
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 {
|
||||
return err
|
||||
if err == nil {
|
||||
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||
}
|
||||
|
||||
return f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) {
|
||||
@@ -131,12 +186,11 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupN
|
||||
if vm.StorageProfile.OsDisk.Vhd != nil {
|
||||
imageType = "image"
|
||||
imageName = *vm.StorageProfile.OsDisk.Vhd.URI
|
||||
return imageType, imageName, nil
|
||||
} else {
|
||||
imageType = "Microsoft.Compute/disks"
|
||||
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
||||
}
|
||||
|
||||
imageType = "Microsoft.Compute/disks"
|
||||
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
||||
|
||||
return imageType, imageName, nil
|
||||
}
|
||||
|
||||
@@ -213,58 +267,3 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string,
|
||||
|
||||
return blob.Delete(nil)
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, deploymentName, resourceGroupName string) error {
|
||||
var maxResources int32 = 50
|
||||
deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(ctx, resourceGroupName, deploymentName, &maxResources)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceGroupName)
|
||||
return err
|
||||
}
|
||||
|
||||
for deploymentOperations.NotDone() {
|
||||
deploymentOperation := deploymentOperations.Value()
|
||||
// Sometimes an empty operation is added to the list by Azure
|
||||
if deploymentOperation.Properties.TargetResource == nil {
|
||||
_ = deploymentOperations.Next()
|
||||
continue
|
||||
}
|
||||
|
||||
resourceName := *deploymentOperation.Properties.TargetResource.ResourceName
|
||||
resourceType := *deploymentOperation.Properties.TargetResource.ResourceType
|
||||
|
||||
s.say(fmt.Sprintf(" -> %s : '%s'", resourceType, resourceName))
|
||||
|
||||
err = retry.Config{
|
||||
Tries: 10,
|
||||
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
||||
}.Run(ctx, func(ctx context.Context) error {
|
||||
err := deleteResource(ctx, s.client,
|
||||
resourceType,
|
||||
resourceName,
|
||||
resourceGroupName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
|
||||
if err = deploymentOperations.Next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) reportIfError(err error, resourceName string) {
|
||||
if err != nil {
|
||||
s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceName, err.Error()))
|
||||
s.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"tenantId": "[parameters('tenantId')]"
|
||||
}
|
||||
],
|
||||
"enableSoftDelete": "true",
|
||||
"enabledForDeployment": "true",
|
||||
"enabledForTemplateDeployment": "true",
|
||||
"sku": {
|
||||
|
||||
@@ -153,7 +153,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc()
|
||||
md := &mapstructure.Metadata{}
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderID,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
|
||||
@@ -24,7 +24,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"`
|
||||
FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch" hcl:"from_scratch"`
|
||||
Source *string `mapstructure:"source" required:"true" cty:"source" hcl:"source"`
|
||||
CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper" hcl:"command_wrapper"`
|
||||
@@ -77,7 +76,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||
"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},
|
||||
"from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false},
|
||||
"source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false},
|
||||
"command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false},
|
||||
|
||||
@@ -16,9 +16,8 @@ type SharedImageGalleryDestination struct {
|
||||
ImageName string `mapstructure:"image_name" required:"true"`
|
||||
ImageVersion string `mapstructure:"image_version" required:"true"`
|
||||
|
||||
TargetRegions []TargetRegion `mapstructure:"target_regions"`
|
||||
ExcludeFromLatest bool `mapstructure:"exclude_from_latest"`
|
||||
ExcludeFromLatestTypo bool `mapstructure:"exlude_from_latest" undocumented:"true"`
|
||||
TargetRegions []TargetRegion `mapstructure:"target_regions"`
|
||||
ExcludeFromLatest bool `mapstructure:"exlude_from_latest"`
|
||||
}
|
||||
|
||||
// TargetRegion describes a region where the shared image should be replicated
|
||||
@@ -62,10 +61,5 @@ func (sigd *SharedImageGalleryDestination) Validate(prefix string) (errs []error
|
||||
warns = append(warns,
|
||||
fmt.Sprintf("%s.target_regions is empty; image will only be available in the region of the gallery", prefix))
|
||||
}
|
||||
if sigd.ExcludeFromLatestTypo == true && sigd.ExcludeFromLatest == false {
|
||||
warns = append(warns,
|
||||
fmt.Sprintf("%s.exlude_from_latest is being deprecated, please use exclude_from_latest", prefix))
|
||||
sigd.ExcludeFromLatest = sigd.ExcludeFromLatestTypo
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,13 +9,12 @@ import (
|
||||
// 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 {
|
||||
ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"`
|
||||
GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"`
|
||||
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
|
||||
ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"`
|
||||
TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"`
|
||||
ExcludeFromLatest *bool `mapstructure:"exclude_from_latest" cty:"exclude_from_latest" hcl:"exclude_from_latest"`
|
||||
ExcludeFromLatestTypo *bool `mapstructure:"exlude_from_latest" undocumented:"true" cty:"exlude_from_latest" hcl:"exlude_from_latest"`
|
||||
ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"`
|
||||
GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"`
|
||||
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
|
||||
ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"`
|
||||
TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"`
|
||||
ExcludeFromLatest *bool `mapstructure:"exlude_from_latest" cty:"exlude_from_latest" hcl:"exlude_from_latest"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatSharedImageGalleryDestination.
|
||||
@@ -30,13 +29,12 @@ func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() m
|
||||
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
||||
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"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},
|
||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
||||
"target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())},
|
||||
"exclude_from_latest": &hcldec.AttrSpec{Name: "exclude_from_latest", Type: cty.Bool, Required: false},
|
||||
"exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false},
|
||||
"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},
|
||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
||||
"target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())},
|
||||
"exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ func TestSharedImageGalleryDestination_ResourceID(t *testing.T) {
|
||||
|
||||
func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
||||
type fields struct {
|
||||
ResourceGroup string
|
||||
GalleryName string
|
||||
ImageName string
|
||||
ImageVersion string
|
||||
TargetRegions []TargetRegion
|
||||
ExcludeFromLatest bool
|
||||
ExcludeFromLatestTypo bool
|
||||
ResourceGroup string
|
||||
GalleryName string
|
||||
ImageName string
|
||||
ImageVersion string
|
||||
TargetRegions []TargetRegion
|
||||
ExcludeFromLatest bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -67,29 +66,6 @@ func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
||||
},
|
||||
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
|
||||
},
|
||||
{
|
||||
name: "warn if using exlude_from_latest",
|
||||
fields: fields{
|
||||
ResourceGroup: "ResourceGroup",
|
||||
GalleryName: "GalleryName",
|
||||
ImageName: "ImageName",
|
||||
ImageVersion: "0.1.2",
|
||||
TargetRegions: []TargetRegion{
|
||||
TargetRegion{
|
||||
Name: "region1",
|
||||
ReplicaCount: 5,
|
||||
StorageAccountType: "Standard_ZRS",
|
||||
},
|
||||
TargetRegion{
|
||||
Name: "region2",
|
||||
ReplicaCount: 3,
|
||||
StorageAccountType: "Standard_LRS",
|
||||
},
|
||||
},
|
||||
ExcludeFromLatestTypo: true,
|
||||
},
|
||||
wantWarns: []string{"sigdest.exlude_from_latest is being deprecated, please use exclude_from_latest"},
|
||||
},
|
||||
{
|
||||
name: "version format",
|
||||
wantErrs: []string{
|
||||
@@ -129,13 +105,12 @@ func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sigd := &SharedImageGalleryDestination{
|
||||
ResourceGroup: tt.fields.ResourceGroup,
|
||||
GalleryName: tt.fields.GalleryName,
|
||||
ImageName: tt.fields.ImageName,
|
||||
ImageVersion: tt.fields.ImageVersion,
|
||||
TargetRegions: tt.fields.TargetRegions,
|
||||
ExcludeFromLatest: tt.fields.ExcludeFromLatest,
|
||||
ExcludeFromLatestTypo: tt.fields.ExcludeFromLatestTypo,
|
||||
ResourceGroup: tt.fields.ResourceGroup,
|
||||
GalleryName: tt.fields.GalleryName,
|
||||
ImageName: tt.fields.ImageName,
|
||||
ImageVersion: tt.fields.ImageVersion,
|
||||
TargetRegions: tt.fields.TargetRegions,
|
||||
ExcludeFromLatest: tt.fields.ExcludeFromLatest,
|
||||
}
|
||||
gotErrs, gotWarns := sigd.Validate("sigdest")
|
||||
|
||||
|
||||
@@ -54,14 +54,6 @@ type Config struct {
|
||||
SubscriptionID string `mapstructure:"subscription_id"`
|
||||
|
||||
authType string
|
||||
|
||||
// Flag to use Azure CLI authentication. Defaults to false.
|
||||
// CLI auth will use the information from an active `az login` session to connect to Azure and set the subscription id and tenant id associated to the signed in account.
|
||||
// If enabled, it will use the authentication provided by the `az` CLI.
|
||||
// Azure CLI authentication will use the credential marked as `isDefault` and can be verified using `az account show`.
|
||||
// Works with normal authentication (`az login`) and service principals (`az login --service-principal --username APP_ID --password PASSWORD --tenant TENANT_ID`).
|
||||
// Ignores all other configurations if enabled.
|
||||
UseAzureCLIAuth bool `mapstructure:"use_azure_cli_auth" required:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -70,7 +62,6 @@ const (
|
||||
authTypeClientSecret = "ClientSecret"
|
||||
authTypeClientCert = "ClientCertificate"
|
||||
authTypeClientBearerJWT = "ClientBearerJWT"
|
||||
authTypeAzureCLI = "AzureCLI"
|
||||
)
|
||||
|
||||
const DefaultCloudEnvironmentName = "Public"
|
||||
@@ -133,10 +124,6 @@ func (c Config) Validate(errs *packer.MultiError) {
|
||||
// readable by the ObjectID of the App. There may be another way to handle
|
||||
// this case, but I am not currently aware of it - send feedback.
|
||||
|
||||
if c.UseCLI() {
|
||||
return
|
||||
}
|
||||
|
||||
if c.UseMSI() {
|
||||
return
|
||||
}
|
||||
@@ -206,10 +193,6 @@ func (c Config) useDeviceLogin() bool {
|
||||
c.ClientCertPath == ""
|
||||
}
|
||||
|
||||
func (c Config) UseCLI() bool {
|
||||
return c.UseAzureCLIAuth == true
|
||||
}
|
||||
|
||||
func (c Config) UseMSI() bool {
|
||||
return c.SubscriptionID == "" &&
|
||||
c.ClientID == "" &&
|
||||
@@ -247,9 +230,6 @@ func (c Config) GetServicePrincipalToken(
|
||||
case authTypeDeviceLogin:
|
||||
say("Getting tokens using device flow")
|
||||
auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID)
|
||||
case authTypeAzureCLI:
|
||||
say("Getting tokens using Azure CLI")
|
||||
auth = NewCliOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID)
|
||||
case authTypeMSI:
|
||||
say("Getting tokens using Managed Identity for Azure")
|
||||
auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment)
|
||||
@@ -288,8 +268,6 @@ func (c *Config) FillParameters() error {
|
||||
if c.authType == "" {
|
||||
if c.useDeviceLogin() {
|
||||
c.authType = authTypeDeviceLogin
|
||||
} else if c.UseCLI() {
|
||||
c.authType = authTypeAzureCLI
|
||||
} else if c.UseMSI() {
|
||||
c.authType = authTypeMSI
|
||||
} else if c.ClientSecret != "" {
|
||||
@@ -317,16 +295,6 @@ func (c *Config) FillParameters() error {
|
||||
}
|
||||
}
|
||||
|
||||
if c.authType == authTypeAzureCLI {
|
||||
tenantID, subscriptionID, err := getIDsFromAzureCLI()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error fetching tenantID and subscriptionID from Azure CLI (are you logged on using `az login`?): %v", err)
|
||||
}
|
||||
|
||||
c.TenantID = tenantID
|
||||
c.SubscriptionID = subscriptionID
|
||||
}
|
||||
|
||||
if c.TenantID == "" {
|
||||
tenantID, err := findTenantID(*c.cloudEnvironment, c.SubscriptionID)
|
||||
if err != nil {
|
||||
|
||||
@@ -29,13 +29,6 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) {
|
||||
config: Config{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "use_azure_cli_auth will trigger Azure CLI auth",
|
||||
config: Config{
|
||||
UseAzureCLIAuth: true,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "subscription_id is set will trigger device flow",
|
||||
config: Config{
|
||||
@@ -165,26 +158,6 @@ func Test_ClientConfig_DeviceLogin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ClientConfig_AzureCli(t *testing.T) {
|
||||
// Azure CLI tests skipped unless env 'AZURE_CLI_AUTH' is set, and an active `az login` session has been established
|
||||
getEnvOrSkip(t, "AZURE_CLI_AUTH")
|
||||
|
||||
cfg := Config{
|
||||
UseAzureCLIAuth: true,
|
||||
cloudEnvironment: getCloud(),
|
||||
}
|
||||
assertValid(t, cfg)
|
||||
|
||||
err := cfg.FillParameters()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected nil err, but got: %v", err)
|
||||
}
|
||||
|
||||
if cfg.authType != authTypeAzureCLI {
|
||||
t.Fatalf("Expected authType to be %q, but got: %q", authTypeAzureCLI, cfg.authType)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ClientConfig_ClientPassword(t *testing.T) {
|
||||
cfg := Config{
|
||||
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/azure/cli"
|
||||
)
|
||||
|
||||
// for managed identity auth
|
||||
type cliOAuthTokenProvider struct {
|
||||
env azure.Environment
|
||||
say func(string)
|
||||
tenantID string
|
||||
}
|
||||
|
||||
func NewCliOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider {
|
||||
return &cliOAuthTokenProvider{
|
||||
env: env,
|
||||
say: say,
|
||||
tenantID: tenantID,
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *cliOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
|
||||
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint)
|
||||
}
|
||||
|
||||
func (tp *cliOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
|
||||
token, err := cli.GetTokenFromCLI(resource)
|
||||
if err != nil {
|
||||
tp.say(fmt.Sprintf("unable to get token from azure cli: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oAuthConfig, err := adal.NewOAuthConfig(resource, tp.tenantID)
|
||||
if err != nil {
|
||||
tp.say(fmt.Sprintf("unable to generate OAuth Config: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adalToken, err := token.ToADALToken()
|
||||
if err != nil {
|
||||
tp.say(fmt.Sprintf("unable to get ADAL Token from azure cli token: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(*oAuthConfig, clientIDs[tp.env.Name], resource, adalToken)
|
||||
if err != nil {
|
||||
tp.say(fmt.Sprintf("unable to get service principal token from adal token: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Custom refresh function to make it possible to use Azure CLI to refresh tokens.
|
||||
// Inspired by HashiCorps go-azure-helpers: https://github.com/hashicorp/go-azure-helpers/blob/373622ce2effb0cf299051ea019cb657f357a4d8/authentication/auth_method_azure_cli_token.go#L96-L109
|
||||
var customRefreshFunc adal.TokenRefresh = func(ctx context.Context, resource string) (*adal.Token, error) {
|
||||
token, err := cli.GetTokenFromCLI(resource)
|
||||
if err != nil {
|
||||
tp.say(fmt.Sprintf("token refresh - unable to get token from azure cli: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adalToken, err := token.ToADALToken()
|
||||
if err != nil {
|
||||
tp.say(fmt.Sprintf("token refresh - unable to get ADAL Token from azure cli token: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adalToken, nil
|
||||
}
|
||||
|
||||
spt.SetCustomRefreshFunc(customRefreshFunc)
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
// getIDsFromAzureCLI returns the TenantID and SubscriptionID from an active Azure CLI login session
|
||||
func getIDsFromAzureCLI() (string, string, error) {
|
||||
profilePath, err := cli.ProfilePath()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
profile, err := cli.LoadProfile(profilePath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
for _, p := range profile.Subscriptions {
|
||||
if p.IsDefault {
|
||||
return p.TenantID, p.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", errors.New("Unable to find default subscription")
|
||||
}
|
||||
@@ -85,7 +85,6 @@ type Properties struct {
|
||||
DNSSettings *network.PublicIPAddressDNSSettings `json:"dnsSettings,omitempty"`
|
||||
EnabledForDeployment *string `json:"enabledForDeployment,omitempty"`
|
||||
EnabledForTemplateDeployment *string `json:"enabledForTemplateDeployment,omitempty"`
|
||||
EnableSoftDelete *string `json:"enableSoftDelete,omitempty"`
|
||||
HardwareProfile *compute.HardwareProfile `json:"hardwareProfile,omitempty"`
|
||||
IPConfigurations *[]network.IPConfiguration `json:"ipConfigurations,omitempty"`
|
||||
NetworkProfile *compute.NetworkProfile `json:"networkProfile,omitempty"`
|
||||
|
||||
@@ -554,7 +554,6 @@ const KeyVault = `{
|
||||
"properties": {
|
||||
"enabledForDeployment": "true",
|
||||
"enabledForTemplateDeployment": "true",
|
||||
"enableSoftDelete": "true",
|
||||
"tenantId": "[parameters('tenantId')]",
|
||||
"accessPolicies": [
|
||||
{
|
||||
|
||||
@@ -389,7 +389,6 @@ func newConfig(raws ...interface{}) (*Config, []string, error) {
|
||||
var c Config
|
||||
c.ctx.Funcs = TemplateFuncs
|
||||
err := config.Decode(&c, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
}, raws...)
|
||||
|
||||
@@ -51,7 +51,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"`
|
||||
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"`
|
||||
SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" cty:"shared_image_gallery" hcl:"shared_image_gallery"`
|
||||
@@ -94,8 +93,6 @@ type FlatConfig struct {
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -164,7 +161,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},
|
||||
"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},
|
||||
"shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())},
|
||||
@@ -207,8 +203,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_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},
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"variables": {
|
||||
"client_id": "{{env `ARM_CLIENT_ID`}}",
|
||||
"client_secret": "{{env `ARM_CLIENT_SECRET`}}",
|
||||
"subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
|
||||
"resource_group": "{{env `ARM_IMAGE_RESOURCEGROUP_ID`}}",
|
||||
"gallery_name": "{{env `ARM_GALLERY_NAME`}}"
|
||||
},
|
||||
"builders": [{
|
||||
"type": "azure-chroot",
|
||||
|
||||
"client_id": "{{user `client_id`}}",
|
||||
"client_secret": "{{user `client_secret`}}",
|
||||
"subscription_id": "{{user `subscription_id`}}",
|
||||
|
||||
"source": "Canonical:UbuntuServer:20.04-LTS:latest",
|
||||
|
||||
"shared_image_destination": {
|
||||
"resource_group": "{{user `resource_group`}}",
|
||||
"gallery_name": "{{user `gallery_name`}}",
|
||||
"image_name": "MyUbuntuOSImage",
|
||||
"image_version": "1.0.0",
|
||||
"exclude_from_latest": false,
|
||||
"target_regions": [
|
||||
{
|
||||
"name": "eastus",
|
||||
"replicas": "1",
|
||||
"storage_account_type": "standard_zrs"
|
||||
}
|
||||
]
|
||||
}
|
||||
}],
|
||||
"provisioners": [{
|
||||
"inline": [
|
||||
"apt update",
|
||||
"apt upgrade -y"
|
||||
],
|
||||
"inline_shebang": "/bin/sh -x",
|
||||
"type": "shell"
|
||||
}]
|
||||
}
|
||||
@@ -29,8 +29,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -144,8 +142,6 @@ 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},
|
||||
|
||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -110,8 +108,6 @@ 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},
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
@@ -52,7 +52,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state.Put("config", &b.config)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
generatedData := &packerbuilderdata.GeneratedData{State: state}
|
||||
generatedData := &builder.GeneratedData{State: state}
|
||||
|
||||
// Setup the driver that will talk to Docker
|
||||
state.Put("driver", driver)
|
||||
|
||||
@@ -127,7 +127,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
var md mapstructure.Metadata
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
Metadata: &md,
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
|
||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -123,8 +121,6 @@ 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},
|
||||
|
||||
@@ -3,7 +3,7 @@ package docker
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/hashicorp/packer/common/shell-local/localexec"
|
||||
"github.com/hashicorp/packer/helper/builder/localexec"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ package docker
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
type StepSetGeneratedData struct {
|
||||
GeneratedData *packerbuilderdata.GeneratedData
|
||||
GeneratedData *builder.GeneratedData
|
||||
}
|
||||
|
||||
func (s *StepSetGeneratedData) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
@@ -4,14 +4,14 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/common/packerbuilderdata"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepSetGeneratedData_Run(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepSetGeneratedData)
|
||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
||||
step.GeneratedData = &builder.GeneratedData{State: state}
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.Sha256Result = "80B3BB1B1696E73A9B19DEEF92F664F8979F948DF348088B61F9A3477655AF64"
|
||||
state.Put("image_id", "12345")
|
||||
@@ -33,7 +33,7 @@ func TestStepSetGeneratedData_Run(t *testing.T) {
|
||||
|
||||
// Image ID not implement
|
||||
state = testState(t)
|
||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
||||
step.GeneratedData = &builder.GeneratedData{State: state}
|
||||
driver = state.Get("driver").(*MockDriver)
|
||||
notImplementedMsg := "ERR_IMAGE_SHA256_NOT_FOUND"
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package packerbuilderdata
|
||||
package builder
|
||||
|
||||
import "github.com/hashicorp/packer/helper/multistep"
|
||||
|
||||
// This is used in the BasicPlaceholderData() func in the packer/provisioner.go
|
||||
// To force users to access generated data via the "generated" func.
|
||||
const PlaceholderMsg = "To set this dynamically in the Packer template, " +
|
||||
"you must use the `build` function"
|
||||
|
||||
// GeneratedData manages variables exported by a builder after
|
||||
// it started. It uses the builder's multistep.StateBag internally, make sure it
|
||||
// is not nil before calling any functions.
|
||||
@@ -1,4 +1,4 @@
|
||||
package packerbuilderdata
|
||||
package builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -59,16 +59,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
// Build the steps.
|
||||
steps := []multistep.Step{
|
||||
new(StepCheckExistingImage),
|
||||
&communicator.StepSSHKeyGen{
|
||||
CommConf: &b.config.Comm,
|
||||
SSHTemporaryKeyPair: b.config.Comm.SSH.SSHTemporaryKeyPair,
|
||||
&StepCreateSSHKey{
|
||||
Debug: b.config.PackerDebug,
|
||||
DebugKeyPath: fmt.Sprintf("gce_%s.pem", b.config.PackerBuildName),
|
||||
},
|
||||
multistep.If(b.config.PackerDebug && b.config.Comm.SSHPrivateKeyFile == "",
|
||||
&communicator.StepDumpSSHKey{
|
||||
Path: fmt.Sprintf("gce_%s.pem", b.config.PackerBuildName),
|
||||
SSH: &b.config.Comm.SSH,
|
||||
},
|
||||
),
|
||||
&StepImportOSLoginSSHKey{
|
||||
Debug: b.config.PackerDebug,
|
||||
},
|
||||
|
||||
@@ -76,8 +76,6 @@ type Config struct {
|
||||
EnableIntegrityMonitoring bool `mapstructure:"enable_integrity_monitoring" required:"false"`
|
||||
// Whether to use an IAP proxy.
|
||||
IAPConfig `mapstructure:",squash"`
|
||||
// Skip creating the image. Useful for setting to `true` during a build test stage. Defaults to `false`.
|
||||
SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"`
|
||||
// The unique name of the resulting image. Defaults to
|
||||
// `packer-{{timestamp}}`.
|
||||
ImageName string `mapstructure:"image_name" required:"false"`
|
||||
@@ -87,19 +85,13 @@ type Config struct {
|
||||
// * kmsKeyName - The name of the encryption key that is stored in Google Cloud KMS.
|
||||
// * RawKey: - A 256-bit customer-supplied encryption key, encodes in RFC 4648 base64.
|
||||
//
|
||||
// examples:
|
||||
// example:
|
||||
//
|
||||
// ```json
|
||||
// {
|
||||
// "kmsKeyName": "projects/${project}/locations/${region}/keyRings/computeEngine/cryptoKeys/computeEngine/cryptoKeyVersions/4"
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// ```hcl
|
||||
// image_encryption_key {
|
||||
// kmsKeyName = "projects/${var.project}/locations/${var.region}/keyRings/computeEngine/cryptoKeys/computeEngine/cryptoKeyVersions/4"
|
||||
// }
|
||||
// ```
|
||||
ImageEncryptionKey *CustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false"`
|
||||
// The name of the image family to which the resulting image belongs. You
|
||||
// can create disks by specifying an image family instead of a specific
|
||||
@@ -297,7 +289,6 @@ type Config struct {
|
||||
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
c.ctx.Funcs = TemplateFuncs
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
@@ -545,11 +536,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
type CustomerEncryptionKey struct {
|
||||
// KmsKeyName: The name of the encryption key that is stored in Google
|
||||
// Cloud KMS.
|
||||
KmsKeyName string `mapstructure:"kmsKeyName" json:"kmsKeyName,omitempty"`
|
||||
KmsKeyName string `json:"kmsKeyName,omitempty"`
|
||||
|
||||
// RawKey: Specifies a 256-bit customer-supplied encryption key, encoded
|
||||
// in RFC 4648 base64 to either encrypt or decrypt this resource.
|
||||
RawKey string `mapstructure:"rawKey" json:"rawKey,omitempty"`
|
||||
RawKey string `json:"rawKey,omitempty"`
|
||||
}
|
||||
|
||||
func (k *CustomerEncryptionKey) ComputeType() *compute.CustomerEncryptionKey {
|
||||
|
||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -83,7 +81,6 @@ type FlatConfig struct {
|
||||
IAPHashBang *string `mapstructure:"iap_hashbang" required:"false" cty:"iap_hashbang" hcl:"iap_hashbang"`
|
||||
IAPExt *string `mapstructure:"iap_ext" required:"false" cty:"iap_ext" hcl:"iap_ext"`
|
||||
IAPTunnelLaunchWait *int `mapstructure:"iap_tunnel_launch_wait" required:"false" cty:"iap_tunnel_launch_wait" hcl:"iap_tunnel_launch_wait"`
|
||||
SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"`
|
||||
ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"`
|
||||
ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"`
|
||||
ImageEncryptionKey *FlatCustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false" cty:"image_encryption_key" hcl:"image_encryption_key"`
|
||||
@@ -146,8 +143,6 @@ 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},
|
||||
@@ -205,7 +200,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"iap_hashbang": &hcldec.AttrSpec{Name: "iap_hashbang", Type: cty.String, Required: false},
|
||||
"iap_ext": &hcldec.AttrSpec{Name: "iap_ext", Type: cty.String, Required: false},
|
||||
"iap_tunnel_launch_wait": &hcldec.AttrSpec{Name: "iap_tunnel_launch_wait", Type: cty.Number, Required: false},
|
||||
"skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false},
|
||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
|
||||
"image_encryption_key": &hcldec.BlockSpec{TypeName: "image_encryption_key", Nested: hcldec.ObjectSpec((*FlatCustomerEncryptionKey)(nil).HCL2Spec())},
|
||||
@@ -246,8 +240,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
// FlatCustomerEncryptionKey is an auto-generated flat version of CustomerEncryptionKey.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatCustomerEncryptionKey struct {
|
||||
KmsKeyName *string `mapstructure:"kmsKeyName" json:"kmsKeyName,omitempty" cty:"kmsKeyName" hcl:"kmsKeyName"`
|
||||
RawKey *string `mapstructure:"rawKey" json:"rawKey,omitempty" cty:"rawKey" hcl:"rawKey"`
|
||||
KmsKeyName *string `json:"kmsKeyName,omitempty" cty:"kms_key_name" hcl:"kms_key_name"`
|
||||
RawKey *string `json:"rawKey,omitempty" cty:"raw_key" hcl:"raw_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatCustomerEncryptionKey.
|
||||
@@ -262,8 +256,8 @@ func (*CustomerEncryptionKey) FlatMapstructure() interface{ HCL2Spec() map[strin
|
||||
// The decoded values from this spec will then be applied to a FlatCustomerEncryptionKey.
|
||||
func (*FlatCustomerEncryptionKey) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"kmsKeyName": &hcldec.AttrSpec{Name: "kmsKeyName", Type: cty.String, Required: false},
|
||||
"rawKey": &hcldec.AttrSpec{Name: "rawKey", Type: cty.String, Required: false},
|
||||
"kms_key_name": &hcldec.AttrSpec{Name: "kms_key_name", Type: cty.String, Required: false},
|
||||
"raw_key": &hcldec.AttrSpec{Name: "raw_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -23,11 +23,6 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if config.SkipCreateImage {
|
||||
ui.Say("Skipping image creation...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if config.PackerForce && config.imageAlreadyExists {
|
||||
ui.Say("Deleting previous image...")
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// StepCreateSSHKey represents a Packer build step that generates SSH key pairs.
|
||||
type StepCreateSSHKey struct {
|
||||
Debug bool
|
||||
DebugKeyPath string
|
||||
}
|
||||
|
||||
// Run executes the Packer build step that generates SSH key pairs.
|
||||
// The key pairs are added to the ssh config
|
||||
func (s *StepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
if config.Comm.SSHPrivateKeyFile != "" {
|
||||
ui.Say("Using existing SSH private key")
|
||||
privateKeyBytes, err := config.Comm.ReadSSHPrivateKeyFile()
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
config.Comm.SSHPrivateKey = privateKeyBytes
|
||||
config.Comm.SSHPublicKey = nil
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui.Say("Creating temporary SSH key for instance...")
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
priv_blk := pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Headers: nil,
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||
}
|
||||
|
||||
pub, err := ssh.NewPublicKey(&priv.PublicKey)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
config.Comm.SSHPrivateKey = pem.EncodeToMemory(&priv_blk)
|
||||
config.Comm.SSHPublicKey = ssh.MarshalAuthorizedKey(pub)
|
||||
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
|
||||
f, err := os.Create(s.DebugKeyPath)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Write out the key
|
||||
err = pem.Encode(f, &priv_blk)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Nothing to clean up. SSH keys are associated with a single GCE instance.
|
||||
func (s *StepCreateSSHKey) Cleanup(state multistep.StateBag) {}
|
||||
@@ -0,0 +1,86 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepCreateSSHKey_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCreateSSHKey)
|
||||
}
|
||||
|
||||
func TestStepCreateSSHKey_privateKey(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateSSHKey)
|
||||
cfg := state.Get("config").(*Config)
|
||||
cfg.Comm.SSHPrivateKeyFile = "test-fixtures/fake-key"
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we have a public/private key
|
||||
if len(cfg.Comm.SSHPrivateKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateSSHKey(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateSSHKey)
|
||||
cfg := state.Get("config").(*Config)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we have a public/private key
|
||||
if len(cfg.Comm.SSHPrivateKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
if len(cfg.Comm.SSHPublicKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateSSHKey_debug(t *testing.T) {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
tf.Close()
|
||||
|
||||
state := testState(t)
|
||||
step := new(StepCreateSSHKey)
|
||||
cfg := state.Get("config").(*Config)
|
||||
step.Debug = true
|
||||
step.DebugKeyPath = tf.Name()
|
||||
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we have a public/private key
|
||||
if len(cfg.Comm.SSHPrivateKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
if len(cfg.Comm.SSHPublicKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
if _, err := os.Stat(tf.Name()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -108,8 +106,6 @@ 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},
|
||||
|
||||
@@ -25,8 +25,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -131,8 +129,6 @@ 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},
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
powershell "github.com/hashicorp/packer/builder/hyperv/common/powershell"
|
||||
"github.com/hashicorp/packer/builder/hyperv/common/powershell/hyperv"
|
||||
"github.com/hashicorp/packer/common"
|
||||
powershell "github.com/hashicorp/packer/common/powershell"
|
||||
"github.com/hashicorp/packer/common/powershell/hyperv"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/builder/hyperv/common/powershell"
|
||||
"github.com/hashicorp/packer/builder/hyperv/common/powershell/hyperv"
|
||||
"github.com/hashicorp/packer/common/powershell"
|
||||
"github.com/hashicorp/packer/common/powershell/hyperv"
|
||||
)
|
||||
|
||||
type HypervPS4Driver struct {
|
||||
|
||||
@@ -89,7 +89,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: hypervcommon.BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
|
||||
@@ -38,8 +38,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -159,8 +157,6 @@ 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},
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
|
||||
powershell "github.com/hashicorp/packer/builder/hyperv/common/powershell"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
powershell "github.com/hashicorp/packer/common/powershell"
|
||||
"github.com/hashicorp/packer/common/shutdowncommand"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
@@ -82,7 +82,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: hypervcommon.BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
|
||||
@@ -38,8 +38,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -161,8 +159,6 @@ 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},
|
||||
|
||||
@@ -17,7 +17,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: BUILDER_ID,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
|
||||
@@ -26,8 +26,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -109,8 +107,6 @@ 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},
|
||||
|
||||
@@ -3,7 +3,6 @@ package linode
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
@@ -249,43 +248,3 @@ func TestBuilderPrepare_Label(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_StateTimeout(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Test default
|
||||
_, warnings, err := b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if b.config.StateTimeout != 5*time.Minute {
|
||||
t.Errorf("invalid: %s", b.config.StateTimeout)
|
||||
}
|
||||
|
||||
// Test set
|
||||
config["state_timeout"] = "5m"
|
||||
b = Builder{}
|
||||
_, warnings, err = b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
// Test bad
|
||||
config["state_timeout"] = "tubes"
|
||||
b = Builder{}
|
||||
_, warnings, err = b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
+10
-17
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
@@ -25,17 +24,16 @@ type Config struct {
|
||||
|
||||
PersonalAccessToken string `mapstructure:"linode_token"`
|
||||
|
||||
Region string `mapstructure:"region"`
|
||||
InstanceType string `mapstructure:"instance_type"`
|
||||
Label string `mapstructure:"instance_label"`
|
||||
Tags []string `mapstructure:"instance_tags"`
|
||||
Image string `mapstructure:"image"`
|
||||
SwapSize int `mapstructure:"swap_size"`
|
||||
RootPass string `mapstructure:"root_pass"`
|
||||
RootSSHKey string `mapstructure:"root_ssh_key"`
|
||||
ImageLabel string `mapstructure:"image_label"`
|
||||
Description string `mapstructure:"image_description"`
|
||||
StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"`
|
||||
Region string `mapstructure:"region"`
|
||||
InstanceType string `mapstructure:"instance_type"`
|
||||
Label string `mapstructure:"instance_label"`
|
||||
Tags []string `mapstructure:"instance_tags"`
|
||||
Image string `mapstructure:"image"`
|
||||
SwapSize int `mapstructure:"swap_size"`
|
||||
RootPass string `mapstructure:"root_pass"`
|
||||
RootSSHKey string `mapstructure:"root_ssh_key"`
|
||||
ImageLabel string `mapstructure:"image_label"`
|
||||
Description string `mapstructure:"image_description"`
|
||||
}
|
||||
|
||||
func createRandomRootPassword() (string, error) {
|
||||
@@ -96,11 +94,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if c.StateTimeout == 0 {
|
||||
// Default to 5 minute timeouts waiting for state change
|
||||
c.StateTimeout = 5 * time.Minute
|
||||
}
|
||||
|
||||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -76,7 +74,6 @@ type FlatConfig struct {
|
||||
RootSSHKey *string `mapstructure:"root_ssh_key" cty:"root_ssh_key" hcl:"root_ssh_key"`
|
||||
ImageLabel *string `mapstructure:"image_label" cty:"image_label" hcl:"image_label"`
|
||||
Description *string `mapstructure:"image_description" cty:"image_description" hcl:"image_description"`
|
||||
StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -106,8 +103,6 @@ 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},
|
||||
@@ -158,7 +153,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"root_ssh_key": &hcldec.AttrSpec{Name: "root_ssh_key", Type: cty.String, Required: false},
|
||||
"image_label": &hcldec.AttrSpec{Name: "image_label", Type: cty.String, Required: false},
|
||||
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
|
||||
"state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ type stepShutdownLinode struct {
|
||||
}
|
||||
|
||||
func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
c := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
instance := state.Get("instance").(*linodego.Instance)
|
||||
|
||||
@@ -26,7 +25,7 @@ func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
_, err := s.client.WaitForInstanceStatus(ctx, instance.ID, linodego.InstanceOffline, int(c.StateTimeout.Seconds()))
|
||||
_, err := s.client.WaitForInstanceStatus(ctx, instance.ID, linodego.InstanceOffline, 120)
|
||||
if err != nil {
|
||||
err = errors.New("Error shutting down Linode: " + err.Error())
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -36,8 +36,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
@@ -118,8 +116,6 @@ 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},
|
||||
|
||||
@@ -21,7 +21,6 @@ type Config struct {
|
||||
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateFilter: &interpolate.RenderFilter{},
|
||||
}, raws...)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user