Merge branch 'master' into issue8178
This commit is contained in:
commit
00d0297d26
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: You're experiencing an issue with Packer that is different than the documented behavior.
|
about: You're experiencing an issue with Packer that is different than the documented behavior.
|
||||||
|
labels: bug
|
||||||
---
|
---
|
||||||
|
|
||||||
When filing a bug, please include the following headings if possible. Any
|
When filing a bug, please include the following headings if possible. Any
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature_requests.md
vendored
2
.github/ISSUE_TEMPLATE/feature_requests.md
vendored
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: If you have something you think Packer could improve or add support for.
|
about: If you have something you think Packer could improve or add support for.
|
||||||
|
labels: enhancement
|
||||||
---
|
---
|
||||||
|
|
||||||
Please search the existing issues for relevant feature requests, and use the
|
Please search the existing issues for relevant feature requests, and use the
|
||||||
|
2
.github/ISSUE_TEMPLATE/question.md
vendored
2
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Question
|
name: Question
|
||||||
about: If you have a question, please check out our other community resources instead of opening an issue.
|
about: If you have a question, please check out our other community resources instead of opening an issue.
|
||||||
|
labels: question
|
||||||
---
|
---
|
||||||
|
|
||||||
Issues on GitHub are intended to be related to bugs or feature requests, so we
|
Issues on GitHub are intended to be related to bugs or feature requests, so we
|
||||||
|
24
.github/ISSUE_TEMPLATE/ssh_or_winrm_times_out.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/ssh_or_winrm_times_out.md
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: SSH or WinRM times out
|
||||||
|
about: I have a waiting SSH or WinRM error.
|
||||||
|
labels: communicator-question
|
||||||
|
---
|
||||||
|
|
||||||
|
Got one of the following errors ? See if the related guides can help.
|
||||||
|
|
||||||
|
* `Waiting for WinRM to become available` ?
|
||||||
|
|
||||||
|
- See our basic WinRm Packer guide: https://www.packer.io/guides/automatic-operating-system-installs/autounattend_windows.html
|
||||||
|
|
||||||
|
* `Waiting for SSH to become available` ?
|
||||||
|
|
||||||
|
- See our basic SSH Packer guide: https://www.packer.io/guides/automatic-operating-system-installs/preseed_ubuntu.html
|
||||||
|
|
||||||
|
|
||||||
|
Issues on GitHub are intended to be related to bugs or feature requests, so we recommend using our other community resources instead of asking here if you have a question.
|
||||||
|
|
||||||
|
- Packer Guides: https://www.packer.io/guides/index.html
|
||||||
|
- Discussion List: https://groups.google.com/group/packer-tool
|
||||||
|
- Any other questions can be sent to the packer section of the HashiCorp
|
||||||
|
forum: https://discuss.hashicorp.com/c/packer
|
||||||
|
- Packer community links: https://www.packer.io/community.html
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,3 +1,38 @@
|
|||||||
|
## 1.4.5 (Upcoming)
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
* builder/azure-arm: Allow specification of polling duration [GH-8226]
|
||||||
|
* builder/azure-chroot: Add Azure chroot builder [GH-8185]
|
||||||
|
* builder/azure: Deploy NSG if list of IP addresses is provided in config
|
||||||
|
[GH-8203]
|
||||||
|
* builder/oracle-oci: Support defined tags for oci builder [GH-8172]
|
||||||
|
* builder/proxmox: Add ability to select CPU type [GH-8201]
|
||||||
|
* builder/proxmox: Add support for SCSI controller selection [GH-8199]
|
||||||
|
* builder/tencent: Add retry on remote api call [GH-8250]
|
||||||
|
* communicator/winrm: Prevent busy loop while waiting for WinRM connection
|
||||||
|
[GH-8213]
|
||||||
|
* core: Add strftime function in templates [GH-8208]
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
* builder/amazon: Fix region copy for non-ebs amazon builders [GH-8212]
|
||||||
|
* builder/amazon: Fix spot instance bug where builder would fail if one
|
||||||
|
availability zone could not support the requested spot instance type, even
|
||||||
|
if another AZ could do so. [GH-8184]
|
||||||
|
* builder/azure: Fix build failure after a retry config generation error.
|
||||||
|
[GH-8209]
|
||||||
|
* builder/docker: Use a unique temp dir for each build to prevent concurrent
|
||||||
|
builds from stomping on each other [GH-8192]
|
||||||
|
* builder/hyperv: Improve filter for determining which files to compact
|
||||||
|
[GH-8248]
|
||||||
|
* builder/hyperv: Use first adapter, rather than failing, when multiple
|
||||||
|
adapters are attached to host OS's VM switch [GH-8234]
|
||||||
|
* builder/openstack: Warn instead of failing on terminate if instance is
|
||||||
|
already shut down [GH-8176]
|
||||||
|
* post-processor/digitalocean-import: Fix panic when 'image_regions' not set
|
||||||
|
[GH-8179]
|
||||||
|
* provisioner/powershell: Fix powershell syntax error causing failed builds
|
||||||
|
[GH-8195]
|
||||||
|
|
||||||
## 1.4.4 (October 1, 2019)
|
## 1.4.4 (October 1, 2019)
|
||||||
|
|
||||||
### IMPROVEMENTS:
|
### IMPROVEMENTS:
|
||||||
|
72
CODEOWNERS
72
CODEOWNERS
@ -3,24 +3,62 @@
|
|||||||
# builders
|
# builders
|
||||||
|
|
||||||
/builder/alicloud/ @chhaj5236
|
/builder/alicloud/ @chhaj5236
|
||||||
/builder/azure/ @paulmey
|
/website/source/docs/builders/alicloud* @chhaj5236
|
||||||
/builder/hyperv/ @taliesins
|
|
||||||
/builder/jdcloud/ @XiaohanLiang @remrain
|
/builder/azure/ @paulmey
|
||||||
/builder/linode/ @displague @ctreatma @stvnjacobs
|
/website/source/docs/builders/azure* @paulmey
|
||||||
/builder/lxc/ @ChrisLundquist
|
|
||||||
/builder/lxd/ @ChrisLundquist
|
/builder/hyperv/ @taliesins
|
||||||
/builder/oneandone/ @jasmingacic
|
/website/source/docs/builders/hyperv* @taliesins
|
||||||
/builder/oracle/ @prydie @owainlewis
|
|
||||||
/builder/profitbricks/ @jasmingacic
|
/builder/jdcloud/ @XiaohanLiang @remrain
|
||||||
/builder/triton/ @sean-
|
/website/source/docs/builders/jdcloud* @XiaohanLiang @remrain
|
||||||
/builder/ncloud/ @YuSungDuk
|
|
||||||
/builder/proxmox/ @carlpett
|
/builder/linode/ @displague @ctreatma @stvnjacobs
|
||||||
|
/website/source/docs/builders/linode* @displague @ctreatma @stvnjacobs
|
||||||
|
|
||||||
|
/builder/lxc/ @ChrisLundquist
|
||||||
|
/website/source/docs/builders/lxc* @ChrisLundquist
|
||||||
|
|
||||||
|
/builder/lxd/ @ChrisLundquist
|
||||||
|
/website/source/docs/builders/lxd* @ChrisLundquist
|
||||||
|
|
||||||
|
/builder/oneandone/ @jasmingacic
|
||||||
|
/website/source/docs/builders/oneandone* @jasmingacic
|
||||||
|
|
||||||
|
/builder/oracle/ @prydie @owainlewis
|
||||||
|
/website/source/docs/builders/oracle* @prydie @owainlewis
|
||||||
|
|
||||||
|
/builder/profitbricks/ @jasmingacic
|
||||||
|
/website/source/docs/builders/profitbricks* @jasmingacic
|
||||||
|
|
||||||
|
/builder/triton/ @sean-
|
||||||
|
/website/source/docs/builders/triton* @sean-
|
||||||
|
|
||||||
|
/builder/ncloud/ @YuSungDuk
|
||||||
|
/website/source/docs/builders/ncloud* @YuSungDuk
|
||||||
|
|
||||||
|
/builder/proxmox/ @carlpett
|
||||||
|
/website/source/docs/builders/proxmox* @carlpett
|
||||||
|
|
||||||
/builder/scaleway/ @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
/builder/scaleway/ @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
||||||
/builder/hcloud/ @LKaemmerling
|
/website/source/docs/builders/scaleway* @sieben @mvaude @jqueuniet @fflorens @brmzkw
|
||||||
/builder/hyperone @m110 @gregorybrzeski @ad-m
|
|
||||||
/builder/ucloud/ @shawnmssu
|
/builder/hcloud/ @LKaemmerling
|
||||||
/builder/yandex @GennadySpb @alexanderKhaustov @seukyaso
|
/website/source/docs/builders/hcloud* @LKaemmerling
|
||||||
/builder/osc @marinsalinas
|
|
||||||
|
/builder/hyperone/ @m110 @gregorybrzeski @ad-m
|
||||||
|
/website/source/docs/builders/hyperone* @m110 @gregorybrzeski @ad-m
|
||||||
|
|
||||||
|
/builder/ucloud/ @shawnmssu
|
||||||
|
/website/source/docs/builders/ucloud* @shawnmssu
|
||||||
|
|
||||||
|
/builder/yandex/ @GennadySpb @alexanderKhaustov @seukyaso
|
||||||
|
/website/source/docs/builders/yandex* @GennadySpb @alexanderKhaustov @seukyaso
|
||||||
|
|
||||||
|
/builder/osc/ @marinsalinas
|
||||||
|
/website/source/docs/builders/osc* @marinsalinas
|
||||||
|
|
||||||
|
|
||||||
# provisioners
|
# provisioners
|
||||||
|
|
||||||
|
3
Makefile
3
Makefile
@ -53,6 +53,7 @@ install-gen-deps: ## Install dependencies for code generation
|
|||||||
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/mna/pigeon@master)
|
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/mna/pigeon@master)
|
||||||
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/alvaroloes/enumer@master)
|
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/alvaroloes/enumer@master)
|
||||||
@go install ./cmd/struct-markdown
|
@go install ./cmd/struct-markdown
|
||||||
|
@go install ./cmd/mapstructure-to-hcl2
|
||||||
|
|
||||||
dev: ## Build and install a development build
|
dev: ## Build and install a development build
|
||||||
@grep 'const VersionPrerelease = ""' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \
|
@grep 'const VersionPrerelease = ""' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \
|
||||||
@ -97,6 +98,8 @@ fmt-examples:
|
|||||||
generate: install-gen-deps ## Generate dynamically generated code
|
generate: install-gen-deps ## Generate dynamically generated code
|
||||||
@echo "==> removing autogenerated markdown..."
|
@echo "==> removing autogenerated markdown..."
|
||||||
@find website/source/ -type f | xargs grep -l '^<!-- Code generated' | xargs rm
|
@find website/source/ -type f | xargs grep -l '^<!-- Code generated' | xargs rm
|
||||||
|
@echo "==> removing autogenerated code..."
|
||||||
|
@find post-processor common helper template builder provisioner -type f | xargs grep -l '^// Code generated' | xargs rm
|
||||||
go generate ./...
|
go generate ./...
|
||||||
go fmt common/bootcommand/boot_command.go
|
go fmt common/bootcommand/boot_command.go
|
||||||
go fmt command/plugin.go
|
go fmt command/plugin.go
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:generate mapstructure-to-hcl2 -type Config,AlicloudDiskDevice
|
||||||
|
|
||||||
// The alicloud contains a packer.Builder implementation that
|
// The alicloud contains a packer.Builder implementation that
|
||||||
// builds ecs images for alicloud.
|
// builds ecs images for alicloud.
|
||||||
package ecs
|
package ecs
|
||||||
|
237
builder/alicloud/ecs/builder.hcl2spec.go
Normal file
237
builder/alicloud/ecs/builder.hcl2spec.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type Config,AlicloudDiskDevice"; DO NOT EDIT.
|
||||||
|
package ecs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatAlicloudDiskDevice is an auto-generated flat version of AlicloudDiskDevice.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatAlicloudDiskDevice struct {
|
||||||
|
DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name"`
|
||||||
|
DiskCategory *string `mapstructure:"disk_category" required:"false" cty:"disk_category"`
|
||||||
|
DiskSize *int `mapstructure:"disk_size" required:"false" cty:"disk_size"`
|
||||||
|
SnapshotId *string `mapstructure:"disk_snapshot_id" required:"false" cty:"disk_snapshot_id"`
|
||||||
|
Description *string `mapstructure:"disk_description" required:"false" cty:"disk_description"`
|
||||||
|
DeleteWithInstance *bool `mapstructure:"disk_delete_with_instance" required:"false" cty:"disk_delete_with_instance"`
|
||||||
|
Device *string `mapstructure:"disk_device" required:"false" cty:"disk_device"`
|
||||||
|
Encrypted *bool `mapstructure:"disk_encrypted" required:"false" cty:"disk_encrypted"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatAlicloudDiskDevice.
|
||||||
|
// FlatAlicloudDiskDevice is an auto-generated flat version of AlicloudDiskDevice.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*AlicloudDiskDevice) FlatMapstructure() interface{} { return new(FlatAlicloudDiskDevice) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatAlicloudDiskDevice.
|
||||||
|
// This spec is used by HCL to read the fields of FlatAlicloudDiskDevice.
|
||||||
|
func (*FlatAlicloudDiskDevice) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false},
|
||||||
|
"disk_category": &hcldec.AttrSpec{Name: "disk_category", Type: cty.String, Required: false},
|
||||||
|
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||||
|
"disk_snapshot_id": &hcldec.AttrSpec{Name: "disk_snapshot_id", Type: cty.String, Required: false},
|
||||||
|
"disk_description": &hcldec.AttrSpec{Name: "disk_description", Type: cty.String, Required: false},
|
||||||
|
"disk_delete_with_instance": &hcldec.AttrSpec{Name: "disk_delete_with_instance", Type: cty.Bool, Required: false},
|
||||||
|
"disk_device": &hcldec.AttrSpec{Name: "disk_device", Type: cty.String, Required: false},
|
||||||
|
"disk_encrypted": &hcldec.AttrSpec{Name: "disk_encrypted", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatConfig struct {
|
||||||
|
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
|
||||||
|
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
|
||||||
|
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
|
||||||
|
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
|
||||||
|
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
|
||||||
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
|
||||||
|
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
|
||||||
|
AlicloudAccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"`
|
||||||
|
AlicloudSecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"`
|
||||||
|
AlicloudRegion *string `mapstructure:"region" required:"true" cty:"region"`
|
||||||
|
AlicloudSkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"`
|
||||||
|
SecurityToken *string `mapstructure:"security_token" required:"false" cty:"security_token"`
|
||||||
|
AlicloudImageName *string `mapstructure:"image_name" required:"true" cty:"image_name"`
|
||||||
|
AlicloudImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version"`
|
||||||
|
AlicloudImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"`
|
||||||
|
AlicloudImageShareAccounts []string `mapstructure:"image_share_account" required:"false" cty:"image_share_account"`
|
||||||
|
AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account" cty:"image_unshare_account"`
|
||||||
|
AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions"`
|
||||||
|
AlicloudImageDestinationNames []string `mapstructure:"image_copy_names" required:"false" cty:"image_copy_names"`
|
||||||
|
ImageEncrypted *bool `mapstructure:"image_encrypted" required:"false" cty:"image_encrypted"`
|
||||||
|
AlicloudImageForceDelete *bool `mapstructure:"image_force_delete" required:"false" cty:"image_force_delete"`
|
||||||
|
AlicloudImageForceDeleteSnapshots *bool `mapstructure:"image_force_delete_snapshots" required:"false" cty:"image_force_delete_snapshots"`
|
||||||
|
AlicloudImageForceDeleteInstances *bool `mapstructure:"image_force_delete_instances" cty:"image_force_delete_instances"`
|
||||||
|
AlicloudImageIgnoreDataDisks *bool `mapstructure:"image_ignore_data_disks" required:"false" cty:"image_ignore_data_disks"`
|
||||||
|
AlicloudImageTags map[string]string `mapstructure:"tags" required:"false" cty:"tags"`
|
||||||
|
ECSSystemDiskMapping *FlatAlicloudDiskDevice `mapstructure:"system_disk_mapping" required:"false" cty:"system_disk_mapping"`
|
||||||
|
ECSImagesDiskMappings []FlatAlicloudDiskDevice `mapstructure:"image_disk_mappings" required:"false" cty:"image_disk_mappings"`
|
||||||
|
AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" cty:"associate_public_ip_address"`
|
||||||
|
ZoneId *string `mapstructure:"zone_id" required:"false" cty:"zone_id"`
|
||||||
|
IOOptimized *bool `mapstructure:"io_optimized" required:"false" cty:"io_optimized"`
|
||||||
|
InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"`
|
||||||
|
Description *string `mapstructure:"description" cty:"description"`
|
||||||
|
AlicloudSourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image"`
|
||||||
|
ForceStopInstance *bool `mapstructure:"force_stop_instance" required:"false" cty:"force_stop_instance"`
|
||||||
|
DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"`
|
||||||
|
SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"`
|
||||||
|
SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name"`
|
||||||
|
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"`
|
||||||
|
UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"`
|
||||||
|
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"`
|
||||||
|
VpcName *string `mapstructure:"vpc_name" required:"false" cty:"vpc_name"`
|
||||||
|
CidrBlock *string `mapstructure:"vpc_cidr_block" required:"false" cty:"vpc_cidr_block"`
|
||||||
|
VSwitchId *string `mapstructure:"vswitch_id" required:"false" cty:"vswitch_id"`
|
||||||
|
VSwitchName *string `mapstructure:"vswitch_name" required:"false" cty:"vswitch_name"`
|
||||||
|
InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name"`
|
||||||
|
InternetChargeType *string `mapstructure:"internet_charge_type" required:"false" cty:"internet_charge_type"`
|
||||||
|
InternetMaxBandwidthOut *int `mapstructure:"internet_max_bandwidth_out" required:"false" cty:"internet_max_bandwidth_out"`
|
||||||
|
WaitSnapshotReadyTimeout *int `mapstructure:"wait_snapshot_ready_timeout" required:"false" cty:"wait_snapshot_ready_timeout"`
|
||||||
|
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||||
|
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||||
|
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||||
|
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
|
||||||
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
|
||||||
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
|
||||||
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
|
||||||
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
|
||||||
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
|
||||||
|
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
|
||||||
|
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
|
||||||
|
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
|
||||||
|
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
|
||||||
|
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
|
||||||
|
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
|
||||||
|
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
|
||||||
|
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
|
||||||
|
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
|
||||||
|
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
|
||||||
|
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
|
||||||
|
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
|
||||||
|
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
|
||||||
|
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
|
||||||
|
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
|
||||||
|
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
|
||||||
|
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
|
||||||
|
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
|
||||||
|
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
|
||||||
|
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
|
||||||
|
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
|
||||||
|
SSHPublicKey []byte `cty:"ssh_public_key"`
|
||||||
|
SSHPrivateKey []byte `cty:"ssh_private_key"`
|
||||||
|
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
|
||||||
|
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
|
||||||
|
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
|
||||||
|
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
|
||||||
|
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
|
||||||
|
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
||||||
|
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
||||||
|
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
||||||
|
SSHPrivateIp *bool `mapstructure:"ssh_private_ip" required:"false" cty:"ssh_private_ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatConfig.
|
||||||
|
// This spec is used by HCL to read the fields of FlatConfig.
|
||||||
|
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||||
|
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||||
|
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
||||||
|
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
||||||
|
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
||||||
|
"packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false},
|
||||||
|
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||||
|
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
|
||||||
|
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||||
|
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||||
|
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||||
|
"security_token": &hcldec.AttrSpec{Name: "security_token", Type: cty.String, Required: false},
|
||||||
|
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||||
|
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
||||||
|
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
|
||||||
|
"image_share_account": &hcldec.AttrSpec{Name: "image_share_account", Type: cty.List(cty.String), Required: false},
|
||||||
|
"image_unshare_account": &hcldec.AttrSpec{Name: "image_unshare_account", Type: cty.List(cty.String), Required: false},
|
||||||
|
"image_copy_regions": &hcldec.AttrSpec{Name: "image_copy_regions", Type: cty.List(cty.String), Required: false},
|
||||||
|
"image_copy_names": &hcldec.AttrSpec{Name: "image_copy_names", Type: cty.List(cty.String), Required: false},
|
||||||
|
"image_encrypted": &hcldec.AttrSpec{Name: "image_encrypted", Type: cty.Bool, Required: false},
|
||||||
|
"image_force_delete": &hcldec.AttrSpec{Name: "image_force_delete", Type: cty.Bool, Required: false},
|
||||||
|
"image_force_delete_snapshots": &hcldec.AttrSpec{Name: "image_force_delete_snapshots", Type: cty.Bool, Required: false},
|
||||||
|
"image_force_delete_instances": &hcldec.AttrSpec{Name: "image_force_delete_instances", Type: cty.Bool, Required: false},
|
||||||
|
"image_ignore_data_disks": &hcldec.AttrSpec{Name: "image_ignore_data_disks", Type: cty.Bool, Required: false},
|
||||||
|
"tags": &hcldec.BlockAttrsSpec{TypeName: "tags", ElementType: cty.String, Required: false},
|
||||||
|
"system_disk_mapping": &hcldec.BlockSpec{TypeName: "system_disk_mapping", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())},
|
||||||
|
"image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}},
|
||||||
|
"associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false},
|
||||||
|
"zone_id": &hcldec.AttrSpec{Name: "zone_id", Type: cty.String, Required: false},
|
||||||
|
"io_optimized": &hcldec.AttrSpec{Name: "io_optimized", Type: cty.Bool, Required: false},
|
||||||
|
"instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false},
|
||||||
|
"description": &hcldec.AttrSpec{Name: "description", Type: cty.String, Required: false},
|
||||||
|
"source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false},
|
||||||
|
"force_stop_instance": &hcldec.AttrSpec{Name: "force_stop_instance", Type: cty.Bool, Required: false},
|
||||||
|
"disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false},
|
||||||
|
"security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false},
|
||||||
|
"security_group_name": &hcldec.AttrSpec{Name: "security_group_name", Type: cty.String, Required: false},
|
||||||
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
|
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
|
||||||
|
"vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false},
|
||||||
|
"vpc_name": &hcldec.AttrSpec{Name: "vpc_name", Type: cty.String, Required: false},
|
||||||
|
"vpc_cidr_block": &hcldec.AttrSpec{Name: "vpc_cidr_block", Type: cty.String, Required: false},
|
||||||
|
"vswitch_id": &hcldec.AttrSpec{Name: "vswitch_id", Type: cty.String, Required: false},
|
||||||
|
"vswitch_name": &hcldec.AttrSpec{Name: "vswitch_name", Type: cty.String, Required: false},
|
||||||
|
"instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false},
|
||||||
|
"internet_charge_type": &hcldec.AttrSpec{Name: "internet_charge_type", Type: cty.String, Required: false},
|
||||||
|
"internet_max_bandwidth_out": &hcldec.AttrSpec{Name: "internet_max_bandwidth_out", Type: cty.Number, Required: false},
|
||||||
|
"wait_snapshot_ready_timeout": &hcldec.AttrSpec{Name: "wait_snapshot_ready_timeout", Type: cty.Number, Required: false},
|
||||||
|
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||||
|
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||||
|
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_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},
|
||||||
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||||
|
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||||
|
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
||||||
|
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||||
|
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
||||||
|
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
||||||
|
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
||||||
|
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
||||||
|
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
||||||
|
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
||||||
|
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
||||||
|
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||||
|
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||||
|
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_private_ip": &hcldec.AttrSpec{Name: "ssh_private_ip", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -1,69 +0,0 @@
|
|||||||
//go:generate struct-markdown
|
|
||||||
|
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BlockDevice struct {
|
|
||||||
awscommon.BlockDevice `mapstructure:",squash"`
|
|
||||||
// ID, alias or ARN of the KMS key to use for boot volume encryption. This
|
|
||||||
// only applies to the main region, other regions where the AMI will be
|
|
||||||
// copied will be encrypted by the default EBS KMS key. For valid formats
|
|
||||||
// see KmsKeyId in the [AWS API docs -
|
|
||||||
// CopyImage](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html)
|
|
||||||
// This field is validated by Packer, when using an alias, you will have to
|
|
||||||
// prefix kms_key_id with alias/.
|
|
||||||
KmsKeyId string `mapstructure:"kms_key_id" required:"false"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlockDevices []BlockDevice
|
|
||||||
|
|
||||||
func (bds BlockDevices) BuildEC2BlockDeviceMappings() []*ec2.BlockDeviceMapping {
|
|
||||||
var blockDevices []*ec2.BlockDeviceMapping
|
|
||||||
|
|
||||||
for _, blockDevice := range bds {
|
|
||||||
blockDevices = append(blockDevices, blockDevice.BuildEC2BlockDeviceMapping())
|
|
||||||
}
|
|
||||||
return blockDevices
|
|
||||||
}
|
|
||||||
|
|
||||||
func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapping {
|
|
||||||
mapping := blockDevice.BlockDevice.BuildEC2BlockDeviceMapping()
|
|
||||||
|
|
||||||
if blockDevice.KmsKeyId != "" {
|
|
||||||
mapping.Ebs.KmsKeyId = aws.String(blockDevice.KmsKeyId)
|
|
||||||
}
|
|
||||||
return mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockDevice) Prepare(ctx *interpolate.Context) error {
|
|
||||||
err := b.BlockDevice.Prepare(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn that encrypted must be true when setting kms_key_id
|
|
||||||
if b.KmsKeyId != "" && b.Encrypted.True() {
|
|
||||||
return fmt.Errorf("The device %v, must also have `encrypted: "+
|
|
||||||
"true` when setting a kms_key_id.", b.DeviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = interpolate.RenderInterface(&b, ctx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bds BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) {
|
|
||||||
for _, block := range bds {
|
|
||||||
if err := block.Prepare(ctx); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errs
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type Config,BlockDevices,BlockDevice
|
||||||
|
|
||||||
// The chroot package is able to create an Amazon AMI without requiring the
|
// The chroot package is able to create an Amazon AMI without requiring the
|
||||||
// launch of a new instance for every build. It does this by attaching and
|
// launch of a new instance for every build. It does this by attaching and
|
||||||
@ -37,7 +38,7 @@ type Config struct {
|
|||||||
// entry for your root volume, `root_volume_size` and `root_device_name`.
|
// entry for your root volume, `root_volume_size` and `root_device_name`.
|
||||||
// See the [BlockDevices](#block-devices-configuration) documentation for
|
// See the [BlockDevices](#block-devices-configuration) documentation for
|
||||||
// fields.
|
// fields.
|
||||||
AMIMappings BlockDevices `mapstructure:"ami_block_device_mappings" required:"false"`
|
AMIMappings awscommon.BlockDevices `mapstructure:"ami_block_device_mappings" hcl2-schema-generator:"ami_block_device_mappings,direct" required:"false"`
|
||||||
// This is a list of devices to mount into the chroot environment. This
|
// This is a list of devices to mount into the chroot environment. This
|
||||||
// configuration parameter requires some additional documentation which is
|
// configuration parameter requires some additional documentation which is
|
||||||
// in the Chroot Mounts section. Please read that section for more
|
// in the Chroot Mounts section. Please read that section for more
|
||||||
@ -168,6 +169,14 @@ type Config struct {
|
|||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetContext() interpolate.Context {
|
||||||
|
return c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
type interpolateContextProvider interface {
|
||||||
|
GetContext() interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
type wrappedCommandTemplate struct {
|
type wrappedCommandTemplate struct {
|
||||||
Command string
|
Command string
|
||||||
}
|
}
|
||||||
@ -392,8 +401,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
&StepPostMountCommands{
|
&StepPostMountCommands{
|
||||||
Commands: b.config.PostMountCommands,
|
Commands: b.config.PostMountCommands,
|
||||||
},
|
},
|
||||||
&StepMountExtra{},
|
&StepMountExtra{
|
||||||
&StepCopyFiles{},
|
ChrootMounts: b.config.ChrootMounts,
|
||||||
|
},
|
||||||
|
&StepCopyFiles{
|
||||||
|
Files: b.config.CopyFiles,
|
||||||
|
},
|
||||||
&StepChrootProvision{},
|
&StepChrootProvision{},
|
||||||
&StepEarlyCleanup{},
|
&StepEarlyCleanup{},
|
||||||
&StepSnapshot{},
|
&StepSnapshot{},
|
||||||
@ -408,6 +421,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
RootVolumeSize: b.config.RootVolumeSize,
|
RootVolumeSize: b.config.RootVolumeSize,
|
||||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
EnableAMIENASupport: b.config.AMIENASupport,
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||||
},
|
},
|
||||||
&awscommon.StepAMIRegionCopy{
|
&awscommon.StepAMIRegionCopy{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
|
140
builder/amazon/chroot/builder.hcl2spec.go
Normal file
140
builder/amazon/chroot/builder.hcl2spec.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type Config,BlockDevices,BlockDevice"; DO NOT EDIT.
|
||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatConfig struct {
|
||||||
|
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
|
||||||
|
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
|
||||||
|
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
|
||||||
|
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
|
||||||
|
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
|
||||||
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
|
||||||
|
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
|
||||||
|
AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name"`
|
||||||
|
AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description"`
|
||||||
|
AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type"`
|
||||||
|
AMIUsers []string `mapstructure:"ami_users" required:"false" cty:"ami_users"`
|
||||||
|
AMIGroups []string `mapstructure:"ami_groups" required:"false" cty:"ami_groups"`
|
||||||
|
AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"`
|
||||||
|
AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"`
|
||||||
|
AMISkipRegionValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"`
|
||||||
|
AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"`
|
||||||
|
AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"`
|
||||||
|
AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"`
|
||||||
|
AMIForceDeregister *bool `mapstructure:"force_deregister" required:"false" cty:"force_deregister"`
|
||||||
|
AMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" required:"false" cty:"force_delete_snapshot"`
|
||||||
|
AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot" required:"false" cty:"encrypt_boot"`
|
||||||
|
AMIKmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"`
|
||||||
|
AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids" required:"false" cty:"region_kms_key_ids"`
|
||||||
|
AMISkipBuildRegion *bool `mapstructure:"skip_save_build_region" cty:"skip_save_build_region"`
|
||||||
|
SnapshotTags common.TagMap `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"`
|
||||||
|
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"`
|
||||||
|
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"`
|
||||||
|
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"`
|
||||||
|
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"`
|
||||||
|
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"`
|
||||||
|
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"`
|
||||||
|
MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"`
|
||||||
|
ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"`
|
||||||
|
RawRegion *string `mapstructure:"region" required:"true" cty:"region"`
|
||||||
|
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"`
|
||||||
|
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"`
|
||||||
|
Token *string `mapstructure:"token" required:"false" cty:"token"`
|
||||||
|
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"`
|
||||||
|
AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" hcl2-schema-generator:"ami_block_device_mappings,direct" required:"false" cty:"ami_block_device_mappings"`
|
||||||
|
ChrootMounts [][]string `mapstructure:"chroot_mounts" required:"false" cty:"chroot_mounts"`
|
||||||
|
CommandWrapper *string `mapstructure:"command_wrapper" required:"false" cty:"command_wrapper"`
|
||||||
|
CopyFiles []string `mapstructure:"copy_files" required:"false" cty:"copy_files"`
|
||||||
|
DevicePath *string `mapstructure:"device_path" required:"false" cty:"device_path"`
|
||||||
|
NVMEDevicePath *string `mapstructure:"nvme_device_path" required:"false" cty:"nvme_device_path"`
|
||||||
|
FromScratch *bool `mapstructure:"from_scratch" required:"false" cty:"from_scratch"`
|
||||||
|
MountOptions []string `mapstructure:"mount_options" required:"false" cty:"mount_options"`
|
||||||
|
MountPartition *string `mapstructure:"mount_partition" required:"false" cty:"mount_partition"`
|
||||||
|
MountPath *string `mapstructure:"mount_path" required:"false" cty:"mount_path"`
|
||||||
|
PostMountCommands []string `mapstructure:"post_mount_commands" required:"false" cty:"post_mount_commands"`
|
||||||
|
PreMountCommands []string `mapstructure:"pre_mount_commands" required:"false" cty:"pre_mount_commands"`
|
||||||
|
RootDeviceName *string `mapstructure:"root_device_name" required:"false" cty:"root_device_name"`
|
||||||
|
RootVolumeSize *int64 `mapstructure:"root_volume_size" required:"false" cty:"root_volume_size"`
|
||||||
|
RootVolumeType *string `mapstructure:"root_volume_type" required:"false" cty:"root_volume_type"`
|
||||||
|
SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"`
|
||||||
|
SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"`
|
||||||
|
RootVolumeTags common.TagMap `mapstructure:"root_volume_tags" required:"false" cty:"root_volume_tags"`
|
||||||
|
Architecture *string `mapstructure:"ami_architecture" required:"false" cty:"ami_architecture"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatConfig.
|
||||||
|
// This spec is used by HCL to read the fields of FlatConfig.
|
||||||
|
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||||
|
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||||
|
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
||||||
|
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
||||||
|
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
||||||
|
"packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false},
|
||||||
|
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false},
|
||||||
|
"ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false},
|
||||||
|
"ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false},
|
||||||
|
"ami_users": &hcldec.AttrSpec{Name: "ami_users", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_groups": &hcldec.AttrSpec{Name: "ami_groups", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_product_codes": &hcldec.AttrSpec{Name: "ami_product_codes", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false},
|
||||||
|
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||||
|
"tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false},
|
||||||
|
"ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false},
|
||||||
|
"sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false},
|
||||||
|
"force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false},
|
||||||
|
"force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false},
|
||||||
|
"encrypt_boot": &hcldec.AttrSpec{Name: "encrypt_boot", Type: cty.Bool, Required: false},
|
||||||
|
"kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false},
|
||||||
|
"region_kms_key_ids": &hcldec.BlockAttrsSpec{TypeName: "region_kms_key_ids", ElementType: cty.String, Required: false},
|
||||||
|
"skip_save_build_region": &hcldec.AttrSpec{Name: "skip_save_build_region", Type: cty.Bool, Required: false},
|
||||||
|
"snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false},
|
||||||
|
"snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false},
|
||||||
|
"snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false},
|
||||||
|
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
|
||||||
|
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
|
||||||
|
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
|
||||||
|
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
|
||||||
|
"mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false},
|
||||||
|
"profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false},
|
||||||
|
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||||
|
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||||
|
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
|
||||||
|
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||||
|
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||||
|
"ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}},
|
||||||
|
"chroot_mounts": &hcldec.BlockListSpec{TypeName: "chroot_mounts", Nested: &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.String), Required: false}},
|
||||||
|
"command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false},
|
||||||
|
"copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false},
|
||||||
|
"device_path": &hcldec.AttrSpec{Name: "device_path", Type: cty.String, Required: false},
|
||||||
|
"nvme_device_path": &hcldec.AttrSpec{Name: "nvme_device_path", Type: cty.String, Required: false},
|
||||||
|
"from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false},
|
||||||
|
"mount_options": &hcldec.AttrSpec{Name: "mount_options", Type: cty.List(cty.String), Required: false},
|
||||||
|
"mount_partition": &hcldec.AttrSpec{Name: "mount_partition", Type: cty.String, Required: false},
|
||||||
|
"mount_path": &hcldec.AttrSpec{Name: "mount_path", Type: cty.String, Required: false},
|
||||||
|
"post_mount_commands": &hcldec.AttrSpec{Name: "post_mount_commands", Type: cty.List(cty.String), Required: false},
|
||||||
|
"pre_mount_commands": &hcldec.AttrSpec{Name: "pre_mount_commands", Type: cty.List(cty.String), Required: false},
|
||||||
|
"root_device_name": &hcldec.AttrSpec{Name: "root_device_name", Type: cty.String, Required: false},
|
||||||
|
"root_volume_size": &hcldec.AttrSpec{Name: "root_volume_size", Type: cty.Number, Required: false},
|
||||||
|
"root_volume_type": &hcldec.AttrSpec{Name: "root_volume_type", Type: cty.String, Required: false},
|
||||||
|
"source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false},
|
||||||
|
"source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())},
|
||||||
|
"root_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false},
|
||||||
|
"ami_architecture": &hcldec.AttrSpec{Name: "ami_architecture", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -17,20 +17,20 @@ import (
|
|||||||
// copy_files_cleanup CleanupFunc - A function to clean up the copied files
|
// copy_files_cleanup CleanupFunc - A function to clean up the copied files
|
||||||
// early.
|
// early.
|
||||||
type StepCopyFiles struct {
|
type StepCopyFiles struct {
|
||||||
|
Files []string
|
||||||
files []string
|
files []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCopyFiles) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepCopyFiles) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
|
||||||
mountPath := state.Get("mount_path").(string)
|
mountPath := state.Get("mount_path").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||||
stderr := new(bytes.Buffer)
|
stderr := new(bytes.Buffer)
|
||||||
|
|
||||||
s.files = make([]string, 0, len(config.CopyFiles))
|
s.files = make([]string, 0, len(s.Files))
|
||||||
if len(config.CopyFiles) > 0 {
|
if len(s.Files) > 0 {
|
||||||
ui.Say("Copying files from host to chroot...")
|
ui.Say("Copying files from host to chroot...")
|
||||||
for _, path := range config.CopyFiles {
|
for _, path := range s.Files {
|
||||||
ui.Message(path)
|
ui.Message(path)
|
||||||
chrootPath := filepath.Join(mountPath, path)
|
chrootPath := filepath.Join(mountPath, path)
|
||||||
log.Printf("Copying '%s' to '%s'", path, chrootPath)
|
log.Printf("Copying '%s' to '%s'", path, chrootPath)
|
||||||
|
@ -17,19 +17,19 @@ import (
|
|||||||
// Produces:
|
// Produces:
|
||||||
// mount_extra_cleanup CleanupFunc - To perform early cleanup
|
// mount_extra_cleanup CleanupFunc - To perform early cleanup
|
||||||
type StepMountExtra struct {
|
type StepMountExtra struct {
|
||||||
mounts []string
|
ChrootMounts [][]string
|
||||||
|
mounts []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepMountExtra) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepMountExtra) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
|
||||||
mountPath := state.Get("mount_path").(string)
|
mountPath := state.Get("mount_path").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||||
|
|
||||||
s.mounts = make([]string, 0, len(config.ChrootMounts))
|
s.mounts = make([]string, 0, len(s.ChrootMounts))
|
||||||
|
|
||||||
ui.Say("Mounting additional paths within the chroot...")
|
ui.Say("Mounting additional paths within the chroot...")
|
||||||
for _, mountInfo := range config.ChrootMounts {
|
for _, mountInfo := range s.ChrootMounts {
|
||||||
innerPath := mountPath + mountInfo[2]
|
innerPath := mountPath + mountInfo[2]
|
||||||
|
|
||||||
if err := os.MkdirAll(innerPath, 0755); err != nil {
|
if err := os.MkdirAll(innerPath, 0755); err != nil {
|
||||||
|
@ -19,7 +19,7 @@ type StepPostMountCommands struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepPostMountCommands) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepPostMountCommands) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
config := state.Get("config").(interpolateContextProvider)
|
||||||
device := state.Get("device").(string)
|
device := state.Get("device").(string)
|
||||||
mountPath := state.Get("mount_path").(string)
|
mountPath := state.Get("mount_path").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
@ -29,7 +29,7 @@ func (s *StepPostMountCommands) Run(ctx context.Context, state multistep.StateBa
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
ictx := config.ctx
|
ictx := config.GetContext()
|
||||||
ictx.Data = &postMountCommandsData{
|
ictx.Data = &postMountCommandsData{
|
||||||
Device: device,
|
Device: device,
|
||||||
MountPath: mountPath,
|
MountPath: mountPath,
|
||||||
|
@ -17,7 +17,7 @@ type StepPreMountCommands struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepPreMountCommands) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepPreMountCommands) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
config := state.Get("config").(interpolateContextProvider)
|
||||||
device := state.Get("device").(string)
|
device := state.Get("device").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||||
@ -26,7 +26,7 @@ func (s *StepPreMountCommands) Run(ctx context.Context, state multistep.StateBag
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
ictx := config.ctx
|
ictx := config.GetContext()
|
||||||
ictx.Data = &preMountCommandsData{Device: device}
|
ictx.Data = &preMountCommandsData{Device: device}
|
||||||
|
|
||||||
ui.Say("Running device setup commands...")
|
ui.Say("Running device setup commands...")
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
|
"github.com/hashicorp/packer/common/random"
|
||||||
confighelper "github.com/hashicorp/packer/helper/config"
|
confighelper "github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@ -17,6 +18,7 @@ type StepRegisterAMI struct {
|
|||||||
RootVolumeSize int64
|
RootVolumeSize int64
|
||||||
EnableAMIENASupport confighelper.Trilean
|
EnableAMIENASupport confighelper.Trilean
|
||||||
EnableAMISriovNetSupport bool
|
EnableAMISriovNetSupport bool
|
||||||
|
AMISkipBuildRegion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
@ -29,12 +31,29 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
|
|
||||||
var registerOpts *ec2.RegisterImageInput
|
var registerOpts *ec2.RegisterImageInput
|
||||||
|
|
||||||
|
// Create the image
|
||||||
|
amiName := config.AMIName
|
||||||
|
state.Put("intermediary_image", false)
|
||||||
|
if config.AMIEncryptBootVolume.True() || s.AMISkipBuildRegion {
|
||||||
|
state.Put("intermediary_image", true)
|
||||||
|
|
||||||
|
// From AWS SDK docs: You can encrypt a copy of an unencrypted snapshot,
|
||||||
|
// but you cannot use it to create an unencrypted copy of an encrypted
|
||||||
|
// snapshot. Your default CMK for EBS is used unless you specify a
|
||||||
|
// non-default key using KmsKeyId.
|
||||||
|
|
||||||
|
// If encrypt_boot is nil or true, we need to create a temporary image
|
||||||
|
// so that in step_region_copy, we can copy it with the correct
|
||||||
|
// encryption
|
||||||
|
amiName = random.AlphaNum(7)
|
||||||
|
}
|
||||||
|
|
||||||
// Source Image is only required to be passed if the image is not from scratch
|
// Source Image is only required to be passed if the image is not from scratch
|
||||||
if config.FromScratch {
|
if config.FromScratch {
|
||||||
registerOpts = buildBaseRegisterOpts(config, nil, s.RootVolumeSize, snapshotID)
|
registerOpts = buildBaseRegisterOpts(config, nil, s.RootVolumeSize, snapshotID, amiName)
|
||||||
} else {
|
} else {
|
||||||
image := state.Get("source_image").(*ec2.Image)
|
image := state.Get("source_image").(*ec2.Image)
|
||||||
registerOpts = buildBaseRegisterOpts(config, image, s.RootVolumeSize, snapshotID)
|
registerOpts = buildBaseRegisterOpts(config, image, s.RootVolumeSize, snapshotID, amiName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.EnableAMISriovNetSupport {
|
if s.EnableAMISriovNetSupport {
|
||||||
@ -75,7 +94,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {}
|
func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
||||||
// Builds the base register opts with architecture, name, root block device, mappings, virtualizationtype
|
// Builds the base register opts with architecture, name, root block device, mappings, virtualizationtype
|
||||||
func buildBaseRegisterOpts(config *Config, sourceImage *ec2.Image, rootVolumeSize int64, snapshotID string) *ec2.RegisterImageInput {
|
func buildBaseRegisterOpts(config *Config, sourceImage *ec2.Image, rootVolumeSize int64, snapshotID string, amiName string) *ec2.RegisterImageInput {
|
||||||
var (
|
var (
|
||||||
mappings []*ec2.BlockDeviceMapping
|
mappings []*ec2.BlockDeviceMapping
|
||||||
rootDeviceName string
|
rootDeviceName string
|
||||||
@ -117,7 +136,7 @@ func buildBaseRegisterOpts(config *Config, sourceImage *ec2.Image, rootVolumeSiz
|
|||||||
|
|
||||||
if config.FromScratch {
|
if config.FromScratch {
|
||||||
return &ec2.RegisterImageInput{
|
return &ec2.RegisterImageInput{
|
||||||
Name: &config.AMIName,
|
Name: &amiName,
|
||||||
Architecture: aws.String(config.Architecture),
|
Architecture: aws.String(config.Architecture),
|
||||||
RootDeviceName: aws.String(rootDeviceName),
|
RootDeviceName: aws.String(rootDeviceName),
|
||||||
VirtualizationType: aws.String(config.AMIVirtType),
|
VirtualizationType: aws.String(config.AMIVirtType),
|
||||||
@ -125,12 +144,12 @@ func buildBaseRegisterOpts(config *Config, sourceImage *ec2.Image, rootVolumeSiz
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildRegisterOptsFromExistingImage(config, sourceImage, newMappings, rootDeviceName)
|
return buildRegisterOptsFromExistingImage(config, sourceImage, newMappings, rootDeviceName, amiName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildRegisterOptsFromExistingImage(config *Config, image *ec2.Image, mappings []*ec2.BlockDeviceMapping, rootDeviceName string) *ec2.RegisterImageInput {
|
func buildRegisterOptsFromExistingImage(config *Config, image *ec2.Image, mappings []*ec2.BlockDeviceMapping, rootDeviceName string, amiName string) *ec2.RegisterImageInput {
|
||||||
registerOpts := &ec2.RegisterImageInput{
|
registerOpts := &ec2.RegisterImageInput{
|
||||||
Name: &config.AMIName,
|
Name: &amiName,
|
||||||
Architecture: image.Architecture,
|
Architecture: image.Architecture,
|
||||||
RootDeviceName: &rootDeviceName,
|
RootDeviceName: &rootDeviceName,
|
||||||
BlockDeviceMappings: mappings,
|
BlockDeviceMappings: mappings,
|
||||||
|
@ -30,7 +30,7 @@ func TestStepRegisterAmi_buildRegisterOpts_pv(t *testing.T) {
|
|||||||
|
|
||||||
blockDevices := []*ec2.BlockDeviceMapping{}
|
blockDevices := []*ec2.BlockDeviceMapping{}
|
||||||
|
|
||||||
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName)
|
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName, config.AMIName)
|
||||||
|
|
||||||
expected := config.AMIVirtType
|
expected := config.AMIVirtType
|
||||||
if *opts.VirtualizationType != expected {
|
if *opts.VirtualizationType != expected {
|
||||||
@ -64,7 +64,7 @@ func TestStepRegisterAmi_buildRegisterOpts_hvm(t *testing.T) {
|
|||||||
|
|
||||||
blockDevices := []*ec2.BlockDeviceMapping{}
|
blockDevices := []*ec2.BlockDeviceMapping{}
|
||||||
|
|
||||||
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName)
|
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName, config.AMIName)
|
||||||
|
|
||||||
expected := config.AMIVirtType
|
expected := config.AMIVirtType
|
||||||
if *opts.VirtualizationType != expected {
|
if *opts.VirtualizationType != expected {
|
||||||
@ -92,14 +92,14 @@ func TestStepRegisterAmi_buildRegisterOptsFromScratch(t *testing.T) {
|
|||||||
config := Config{
|
config := Config{
|
||||||
FromScratch: true,
|
FromScratch: true,
|
||||||
PackerConfig: common.PackerConfig{},
|
PackerConfig: common.PackerConfig{},
|
||||||
AMIMappings: []BlockDevice{
|
AMIMappings: []amazon.BlockDevice{
|
||||||
{BlockDevice: amazon.BlockDevice{
|
amazon.BlockDevice{
|
||||||
DeviceName: rootDeviceName,
|
DeviceName: rootDeviceName,
|
||||||
}},
|
},
|
||||||
},
|
},
|
||||||
RootDeviceName: rootDeviceName,
|
RootDeviceName: rootDeviceName,
|
||||||
}
|
}
|
||||||
registerOpts := buildBaseRegisterOpts(&config, nil, 10, snapshotID)
|
registerOpts := buildBaseRegisterOpts(&config, nil, 10, snapshotID, config.AMIName)
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 1 {
|
if len(registerOpts.BlockDeviceMappings) != 1 {
|
||||||
t.Fatal("Expected block device mapping of length 1")
|
t.Fatal("Expected block device mapping of length 1")
|
||||||
@ -138,7 +138,7 @@ func TestStepRegisterAmi_buildRegisterOptFromExistingImage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotID)
|
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotID, config.AMIName)
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 2 {
|
if len(registerOpts.BlockDeviceMappings) != 2 {
|
||||||
t.Fatal("Expected block device mapping of length 2")
|
t.Fatal("Expected block device mapping of length 2")
|
||||||
@ -167,10 +167,10 @@ func TestStepRegisterAmi_buildRegisterOptFromExistingImageWithBlockDeviceMapping
|
|||||||
config := Config{
|
config := Config{
|
||||||
FromScratch: false,
|
FromScratch: false,
|
||||||
PackerConfig: common.PackerConfig{},
|
PackerConfig: common.PackerConfig{},
|
||||||
AMIMappings: []BlockDevice{
|
AMIMappings: []amazon.BlockDevice{
|
||||||
{BlockDevice: amazon.BlockDevice{
|
amazon.BlockDevice{
|
||||||
DeviceName: rootDeviceName,
|
DeviceName: rootDeviceName,
|
||||||
}},
|
},
|
||||||
},
|
},
|
||||||
RootDeviceName: rootDeviceName,
|
RootDeviceName: rootDeviceName,
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ func TestStepRegisterAmi_buildRegisterOptFromExistingImageWithBlockDeviceMapping
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotId)
|
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotId, config.AMIName)
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 1 {
|
if len(registerOpts.BlockDeviceMappings) != 1 {
|
||||||
t.Fatal("Expected block device mapping of length 1")
|
t.Fatal("Expected block device mapping of length 1")
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type VaultAWSEngineOptions
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
|
33
builder/amazon/common/access_config.hcl2spec.go
Normal file
33
builder/amazon/common/access_config.hcl2spec.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type VaultAWSEngineOptions"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatVaultAWSEngineOptions is an auto-generated flat version of VaultAWSEngineOptions.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatVaultAWSEngineOptions struct {
|
||||||
|
Name *string `mapstructure:"name" cty:"name"`
|
||||||
|
RoleARN *string `mapstructure:"role_arn" cty:"role_arn"`
|
||||||
|
TTL *string `mapstructure:"ttl" required:"false" cty:"ttl"`
|
||||||
|
EngineName *string `mapstructure:"engine_name" cty:"engine_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatVaultAWSEngineOptions.
|
||||||
|
// FlatVaultAWSEngineOptions is an auto-generated flat version of VaultAWSEngineOptions.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*VaultAWSEngineOptions) FlatMapstructure() interface{} { return new(FlatVaultAWSEngineOptions) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatVaultAWSEngineOptions.
|
||||||
|
// This spec is used by HCL to read the fields of FlatVaultAWSEngineOptions.
|
||||||
|
func (*FlatVaultAWSEngineOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
|
||||||
|
"role_arn": &hcldec.AttrSpec{Name: "role_arn", Type: cty.String, Required: false},
|
||||||
|
"ttl": &hcldec.AttrSpec{Name: "ttl", Type: cty.String, Required: false},
|
||||||
|
"engine_name": &hcldec.AttrSpec{Name: "engine_name", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type BlockDevice
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
|
45
builder/amazon/common/block_device.hcl2spec.go
Normal file
45
builder/amazon/common/block_device.hcl2spec.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type BlockDevice"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatBlockDevice is an auto-generated flat version of BlockDevice.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatBlockDevice struct {
|
||||||
|
DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"`
|
||||||
|
DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"`
|
||||||
|
Encrypted *bool `mapstructure:"encrypted" required:"false" cty:"encrypted"`
|
||||||
|
IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"`
|
||||||
|
NoDevice *bool `mapstructure:"no_device" required:"false" cty:"no_device"`
|
||||||
|
SnapshotId *string `mapstructure:"snapshot_id" required:"false" cty:"snapshot_id"`
|
||||||
|
VirtualName *string `mapstructure:"virtual_name" required:"false" cty:"virtual_name"`
|
||||||
|
VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"`
|
||||||
|
VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"`
|
||||||
|
KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatBlockDevice.
|
||||||
|
// FlatBlockDevice is an auto-generated flat version of BlockDevice.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice.
|
||||||
|
// This spec is used by HCL to read the fields of FlatBlockDevice.
|
||||||
|
func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false},
|
||||||
|
"device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false},
|
||||||
|
"encrypted": &hcldec.AttrSpec{Name: "encrypted", Type: cty.Bool, Required: false},
|
||||||
|
"iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false},
|
||||||
|
"no_device": &hcldec.AttrSpec{Name: "no_device", Type: cty.Bool, Required: false},
|
||||||
|
"snapshot_id": &hcldec.AttrSpec{Name: "snapshot_id", Type: cty.String, Required: false},
|
||||||
|
"virtual_name": &hcldec.AttrSpec{Name: "virtual_name", Type: cty.String, Required: false},
|
||||||
|
"volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false},
|
||||||
|
"volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false},
|
||||||
|
"kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -5,12 +5,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Build a slice of EC2 (AMI/Subnet/VPC) filter options from the filters provided.
|
// Build a slice of EC2 (AMI/Subnet/VPC) filter options from the filters provided.
|
||||||
func buildEc2Filters(input map[*string]*string) []*ec2.Filter {
|
func buildEc2Filters(input map[string]string) []*ec2.Filter {
|
||||||
var filters []*ec2.Filter
|
var filters []*ec2.Filter
|
||||||
for k, v := range input {
|
for k, v := range input {
|
||||||
filters = append(filters, &ec2.Filter{
|
filters = append(filters, &ec2.Filter{
|
||||||
Name: k,
|
Name: &k,
|
||||||
Values: []*string{v},
|
Values: []*string{&v},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return filters
|
return filters
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
@ -18,11 +19,19 @@ import (
|
|||||||
var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$")
|
var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$")
|
||||||
|
|
||||||
type AmiFilterOptions struct {
|
type AmiFilterOptions struct {
|
||||||
Filters map[*string]*string
|
Filters map[string]string
|
||||||
Owners []*string
|
Owners []string
|
||||||
MostRecent bool `mapstructure:"most_recent"`
|
MostRecent bool `mapstructure:"most_recent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *AmiFilterOptions) GetOwners() []*string {
|
||||||
|
res := make([]*string, 0, len(d.Owners))
|
||||||
|
for _, owner := range d.Owners {
|
||||||
|
res = append(res, &owner)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func (d *AmiFilterOptions) Empty() bool {
|
func (d *AmiFilterOptions) Empty() bool {
|
||||||
return len(d.Owners) == 0 && len(d.Filters) == 0
|
return len(d.Owners) == 0 && len(d.Filters) == 0
|
||||||
}
|
}
|
||||||
@ -32,7 +41,7 @@ func (d *AmiFilterOptions) NoOwner() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SubnetFilterOptions struct {
|
type SubnetFilterOptions struct {
|
||||||
Filters map[*string]*string
|
Filters map[string]string
|
||||||
MostFree bool `mapstructure:"most_free"`
|
MostFree bool `mapstructure:"most_free"`
|
||||||
Random bool `mapstructure:"random"`
|
Random bool `mapstructure:"random"`
|
||||||
}
|
}
|
||||||
@ -42,7 +51,16 @@ func (d *SubnetFilterOptions) Empty() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VpcFilterOptions struct {
|
type VpcFilterOptions struct {
|
||||||
Filters map[*string]*string
|
Filters map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PolicyDocument struct {
|
||||||
|
Version string
|
||||||
|
Statement []struct {
|
||||||
|
Effect string
|
||||||
|
Action []string
|
||||||
|
Resource string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *VpcFilterOptions) Empty() bool {
|
func (d *VpcFilterOptions) Empty() bool {
|
||||||
@ -50,7 +68,7 @@ func (d *VpcFilterOptions) Empty() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SecurityGroupFilterOptions struct {
|
type SecurityGroupFilterOptions struct {
|
||||||
Filters map[*string]*string
|
Filters map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SecurityGroupFilterOptions) Empty() bool {
|
func (d *SecurityGroupFilterOptions) Empty() bool {
|
||||||
@ -124,6 +142,25 @@ type RunConfig struct {
|
|||||||
// profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
// profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
||||||
// to launch the EC2 instance with.
|
// to launch the EC2 instance with.
|
||||||
IamInstanceProfile string `mapstructure:"iam_instance_profile" required:"false"`
|
IamInstanceProfile string `mapstructure:"iam_instance_profile" required:"false"`
|
||||||
|
// Temporary IAM instance profile policy document
|
||||||
|
// If IamInstanceProfile is specified it will be used instead. Example:
|
||||||
|
//
|
||||||
|
// ```json
|
||||||
|
//{
|
||||||
|
// "Version": "2012-10-17",
|
||||||
|
// "Statement": [
|
||||||
|
// {
|
||||||
|
// "Action": [
|
||||||
|
// "logs:*"
|
||||||
|
// ],
|
||||||
|
// "Effect": "Allow",
|
||||||
|
// "Resource": "*"
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
//}
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
TemporaryIamInstanceProfilePolicyDocument *PolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false"`
|
||||||
// Automatically terminate instances on
|
// Automatically terminate instances on
|
||||||
// shutdown in case Packer exits ungracefully. Possible values are stop and
|
// shutdown in case Packer exits ungracefully. Possible values are stop and
|
||||||
// terminate. Defaults to stop.
|
// terminate. Defaults to stop.
|
||||||
|
97
builder/amazon/common/run_config.hcl2spec.go
Normal file
97
builder/amazon/common/run_config.hcl2spec.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatAmiFilterOptions is an auto-generated flat version of AmiFilterOptions.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatAmiFilterOptions struct {
|
||||||
|
Filters map[string]string `cty:"filters"`
|
||||||
|
Owners []string `cty:"owners"`
|
||||||
|
MostRecent *bool `mapstructure:"most_recent" cty:"most_recent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatAmiFilterOptions.
|
||||||
|
// FlatAmiFilterOptions is an auto-generated flat version of AmiFilterOptions.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*AmiFilterOptions) FlatMapstructure() interface{} { return new(FlatAmiFilterOptions) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatAmiFilterOptions.
|
||||||
|
// This spec is used by HCL to read the fields of FlatAmiFilterOptions.
|
||||||
|
func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false},
|
||||||
|
"owners": &hcldec.AttrSpec{Name: "owners", Type: cty.List(cty.String), Required: false},
|
||||||
|
"most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatSecurityGroupFilterOptions struct {
|
||||||
|
Filters map[string]string `cty:"filters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatSecurityGroupFilterOptions.
|
||||||
|
// FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*SecurityGroupFilterOptions) FlatMapstructure() interface{} {
|
||||||
|
return new(FlatSecurityGroupFilterOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatSecurityGroupFilterOptions.
|
||||||
|
// This spec is used by HCL to read the fields of FlatSecurityGroupFilterOptions.
|
||||||
|
func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatSubnetFilterOptions struct {
|
||||||
|
Filters map[string]string `cty:"filters"`
|
||||||
|
MostFree *bool `mapstructure:"most_free" cty:"most_free"`
|
||||||
|
Random *bool `mapstructure:"random" cty:"random"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatSubnetFilterOptions.
|
||||||
|
// FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*SubnetFilterOptions) FlatMapstructure() interface{} { return new(FlatSubnetFilterOptions) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatSubnetFilterOptions.
|
||||||
|
// This spec is used by HCL to read the fields of FlatSubnetFilterOptions.
|
||||||
|
func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false},
|
||||||
|
"most_free": &hcldec.AttrSpec{Name: "most_free", Type: cty.Bool, Required: false},
|
||||||
|
"random": &hcldec.AttrSpec{Name: "random", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatVpcFilterOptions is an auto-generated flat version of VpcFilterOptions.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatVpcFilterOptions struct {
|
||||||
|
Filters map[string]string `cty:"filters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatVpcFilterOptions.
|
||||||
|
// FlatVpcFilterOptions is an auto-generated flat version of VpcFilterOptions.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*VpcFilterOptions) FlatMapstructure() interface{} { return new(FlatVpcFilterOptions) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatVpcFilterOptions.
|
||||||
|
// This spec is used by HCL to read the fields of FlatVpcFilterOptions.
|
||||||
|
func (*FlatVpcFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -73,7 +73,7 @@ func TestRunConfigPrepare_SourceAmiFilterOwnersBlank(t *testing.T) {
|
|||||||
c := testConfigFilter()
|
c := testConfigFilter()
|
||||||
filter_key := "name"
|
filter_key := "name"
|
||||||
filter_value := "foo"
|
filter_value := "foo"
|
||||||
c.SourceAmiFilter = AmiFilterOptions{Filters: map[*string]*string{&filter_key: &filter_value}}
|
c.SourceAmiFilter = AmiFilterOptions{Filters: map[string]string{filter_key: filter_value}}
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
if err := c.Prepare(nil); len(err) != 1 {
|
||||||
t.Fatalf("Should error if Owners is not specified)")
|
t.Fatalf("Should error if Owners is not specified)")
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) {
|
|||||||
owner := "123"
|
owner := "123"
|
||||||
filter_key := "name"
|
filter_key := "name"
|
||||||
filter_value := "foo"
|
filter_value := "foo"
|
||||||
goodFilter := AmiFilterOptions{Owners: []*string{&owner}, Filters: map[*string]*string{&filter_key: &filter_value}}
|
goodFilter := AmiFilterOptions{Owners: []string{owner}, Filters: map[string]string{filter_key: filter_value}}
|
||||||
c.SourceAmiFilter = goodFilter
|
c.SourceAmiFilter = goodFilter
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
if err := c.Prepare(nil); len(err) != 0 {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
|
194
builder/amazon/common/step_iam_instance_profile.go
Normal file
194
builder/amazon/common/step_iam_instance_profile.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
"github.com/hashicorp/packer/common/uuid"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepIamInstanceProfile struct {
|
||||||
|
IamInstanceProfile string
|
||||||
|
TemporaryIamInstanceProfilePolicyDocument *PolicyDocument
|
||||||
|
createdInstanceProfileName string
|
||||||
|
createdRoleName string
|
||||||
|
createdPolicyName string
|
||||||
|
roleIsAttached bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepIamInstanceProfile) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
iamsvc := state.Get("iam").(*iam.IAM)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
state.Put("iamInstanceProfile", "")
|
||||||
|
|
||||||
|
if len(s.IamInstanceProfile) > 0 {
|
||||||
|
_, err := iamsvc.GetInstanceProfile(
|
||||||
|
&iam.GetInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(s.IamInstanceProfile),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Couldn't find specified instance profile: %s", err)
|
||||||
|
log.Printf("[DEBUG] %s", err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
log.Printf("Using specified instance profile: %v", s.IamInstanceProfile)
|
||||||
|
state.Put("iamInstanceProfile", s.IamInstanceProfile)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.TemporaryIamInstanceProfilePolicyDocument != nil {
|
||||||
|
// Create the profile
|
||||||
|
profileName := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
||||||
|
|
||||||
|
policy, err := json.Marshal(s.TemporaryIamInstanceProfilePolicyDocument)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Creating temporary instance profile for this instance: %s", profileName))
|
||||||
|
|
||||||
|
profileResp, err := iamsvc.CreateInstanceProfile(&iam.CreateInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(profileName),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
s.createdInstanceProfileName = aws.StringValue(profileResp.InstanceProfile.InstanceProfileName)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for temporary instance profile: %s", s.createdInstanceProfileName)
|
||||||
|
err = iamsvc.WaitUntilInstanceProfileExists(&iam.GetInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(s.createdInstanceProfileName),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("[DEBUG] Found instance profile %s", s.createdInstanceProfileName)
|
||||||
|
} else {
|
||||||
|
err := fmt.Errorf("Timed out waiting for instance profile %s: %s", s.createdInstanceProfileName, err)
|
||||||
|
log.Printf("[DEBUG] %s", err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Creating temporary role for this instance: %s", profileName))
|
||||||
|
|
||||||
|
roleResp, err := iamsvc.CreateRole(&iam.CreateRoleInput{
|
||||||
|
RoleName: aws.String(profileName),
|
||||||
|
Description: aws.String("Temporary role for Packer"),
|
||||||
|
AssumeRolePolicyDocument: aws.String("{\"Version\": \"2012-10-17\",\"Statement\": [{\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"ec2.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.createdRoleName = aws.StringValue(roleResp.Role.RoleName)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for temporary role: %s", s.createdInstanceProfileName)
|
||||||
|
err = iamsvc.WaitUntilRoleExists(&iam.GetRoleInput{
|
||||||
|
RoleName: aws.String(s.createdRoleName),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("[DEBUG] Found temporary role %s", s.createdRoleName)
|
||||||
|
} else {
|
||||||
|
err := fmt.Errorf("Timed out waiting for temporary role %s: %s", s.createdRoleName, err)
|
||||||
|
log.Printf("[DEBUG] %s", err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Attaching policy to the temporary role: %s", profileName))
|
||||||
|
|
||||||
|
_, err = iamsvc.PutRolePolicy(&iam.PutRolePolicyInput{
|
||||||
|
RoleName: roleResp.Role.RoleName,
|
||||||
|
PolicyName: aws.String(profileName),
|
||||||
|
PolicyDocument: aws.String(string(policy)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.createdPolicyName = aws.StringValue(roleResp.Role.RoleName)
|
||||||
|
|
||||||
|
_, err = iamsvc.AddRoleToInstanceProfile(&iam.AddRoleToInstanceProfileInput{
|
||||||
|
RoleName: roleResp.Role.RoleName,
|
||||||
|
InstanceProfileName: profileResp.InstanceProfile.InstanceProfileName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.roleIsAttached = true
|
||||||
|
state.Put("iamInstanceProfile", aws.StringValue(profileResp.InstanceProfile.InstanceProfileName))
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepIamInstanceProfile) Cleanup(state multistep.StateBag) {
|
||||||
|
iamsvc := state.Get("iam").(*iam.IAM)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if s.roleIsAttached == true {
|
||||||
|
ui.Say("Detaching temporary role from instance profile...")
|
||||||
|
|
||||||
|
_, err := iamsvc.RemoveRoleFromInstanceProfile(&iam.RemoveRoleFromInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(s.createdInstanceProfileName),
|
||||||
|
RoleName: aws.String(s.createdRoleName),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf(
|
||||||
|
"Error %s. Please delete the role manually: %s", err.Error(), s.createdRoleName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.createdPolicyName != "" {
|
||||||
|
ui.Say("Removing policy from temporary role...")
|
||||||
|
iamsvc.DeleteRolePolicy(&iam.DeleteRolePolicyInput{
|
||||||
|
PolicyName: aws.String(s.createdPolicyName),
|
||||||
|
RoleName: aws.String(s.createdRoleName),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if s.createdRoleName != "" {
|
||||||
|
ui.Say("Deleting temporary role...")
|
||||||
|
|
||||||
|
_, err = iamsvc.DeleteRole(&iam.DeleteRoleInput{RoleName: &s.createdRoleName})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf(
|
||||||
|
"Error %s. Please delete the role manually: %s", err.Error(), s.createdRoleName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.createdInstanceProfileName != "" {
|
||||||
|
ui.Say("Deleting temporary instance profile...")
|
||||||
|
|
||||||
|
_, err = iamsvc.DeleteInstanceProfile(&iam.DeleteInstanceProfileInput{
|
||||||
|
InstanceProfileName: &s.createdInstanceProfileName})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf(
|
||||||
|
"Error %s. Please delete the instance profile manually: %s", err.Error(), s.createdInstanceProfileName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,6 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@ -53,7 +52,7 @@ func (s *StepNetworkInfo) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
if s.VpcId == "" && !s.VpcFilter.Empty() {
|
if s.VpcId == "" && !s.VpcFilter.Empty() {
|
||||||
params := &ec2.DescribeVpcsInput{}
|
params := &ec2.DescribeVpcsInput{}
|
||||||
params.Filters = buildEc2Filters(s.VpcFilter.Filters)
|
params.Filters = buildEc2Filters(s.VpcFilter.Filters)
|
||||||
s.VpcFilter.Filters[aws.String("state")] = aws.String("available")
|
s.VpcFilter.Filters["state"] = "available"
|
||||||
|
|
||||||
log.Printf("Using VPC Filters %v", params)
|
log.Printf("Using VPC Filters %v", params)
|
||||||
|
|
||||||
@ -79,13 +78,13 @@ func (s *StepNetworkInfo) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
// Subnet
|
// Subnet
|
||||||
if s.SubnetId == "" && !s.SubnetFilter.Empty() {
|
if s.SubnetId == "" && !s.SubnetFilter.Empty() {
|
||||||
params := &ec2.DescribeSubnetsInput{}
|
params := &ec2.DescribeSubnetsInput{}
|
||||||
s.SubnetFilter.Filters[aws.String("state")] = aws.String("available")
|
s.SubnetFilter.Filters["state"] = "available"
|
||||||
|
|
||||||
if s.VpcId != "" {
|
if s.VpcId != "" {
|
||||||
s.SubnetFilter.Filters[aws.String("vpc-id")] = &s.VpcId
|
s.SubnetFilter.Filters["vpc-id"] = s.VpcId
|
||||||
}
|
}
|
||||||
if s.AvailabilityZone != "" {
|
if s.AvailabilityZone != "" {
|
||||||
s.SubnetFilter.Filters[aws.String("availability-zone")] = &s.AvailabilityZone
|
s.SubnetFilter.Filters["availabilityZone"] = s.AvailabilityZone
|
||||||
}
|
}
|
||||||
params.Filters = buildEc2Filters(s.SubnetFilter.Filters)
|
params.Filters = buildEc2Filters(s.SubnetFilter.Filters)
|
||||||
log.Printf("Using Subnet Filters %v", params)
|
log.Printf("Using Subnet Filters %v", params)
|
||||||
|
@ -28,7 +28,6 @@ type StepRunSourceInstance struct {
|
|||||||
EbsOptimized bool
|
EbsOptimized bool
|
||||||
EnableT2Unlimited bool
|
EnableT2Unlimited bool
|
||||||
ExpectedRootDevice string
|
ExpectedRootDevice string
|
||||||
IamInstanceProfile string
|
|
||||||
InstanceInitiatedShutdownBehavior string
|
InstanceInitiatedShutdownBehavior string
|
||||||
InstanceType string
|
InstanceType string
|
||||||
IsRestricted bool
|
IsRestricted bool
|
||||||
@ -45,6 +44,8 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
|
||||||
securityGroupIds := aws.StringSlice(state.Get("securityGroupIds").([]string))
|
securityGroupIds := aws.StringSlice(state.Get("securityGroupIds").([]string))
|
||||||
|
iamInstanceProfile := aws.String(state.Get("iamInstanceProfile").(string))
|
||||||
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
userData := s.UserData
|
userData := s.UserData
|
||||||
@ -110,7 +111,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||||||
UserData: &userData,
|
UserData: &userData,
|
||||||
MaxCount: aws.Int64(1),
|
MaxCount: aws.Int64(1),
|
||||||
MinCount: aws.Int64(1),
|
MinCount: aws.Int64(1),
|
||||||
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile},
|
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: iamInstanceProfile},
|
||||||
BlockDeviceMappings: s.LaunchMappings.BuildEC2BlockDeviceMappings(),
|
BlockDeviceMappings: s.LaunchMappings.BuildEC2BlockDeviceMappings(),
|
||||||
Placement: &ec2.Placement{AvailabilityZone: &az},
|
Placement: &ec2.Placement{AvailabilityZone: &az},
|
||||||
EbsOptimized: &s.EbsOptimized,
|
EbsOptimized: &s.EbsOptimized,
|
||||||
|
@ -31,7 +31,6 @@ type StepRunSpotInstance struct {
|
|||||||
Comm *communicator.Config
|
Comm *communicator.Config
|
||||||
EbsOptimized bool
|
EbsOptimized bool
|
||||||
ExpectedRootDevice string
|
ExpectedRootDevice string
|
||||||
IamInstanceProfile string
|
|
||||||
InstanceInitiatedShutdownBehavior string
|
InstanceInitiatedShutdownBehavior string
|
||||||
InstanceType string
|
InstanceType string
|
||||||
SourceAMI string
|
SourceAMI string
|
||||||
@ -69,12 +68,14 @@ func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
|
|||||||
launchMappingRequests = append(launchMappingRequests, launchRequest)
|
launchMappingRequests = append(launchMappingRequests, launchRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iamInstanceProfile := aws.String(state.Get("iamInstanceProfile").(string))
|
||||||
|
|
||||||
// Create a launch template.
|
// Create a launch template.
|
||||||
templateData := ec2.RequestLaunchTemplateData{
|
templateData := ec2.RequestLaunchTemplateData{
|
||||||
BlockDeviceMappings: launchMappingRequests,
|
BlockDeviceMappings: launchMappingRequests,
|
||||||
DisableApiTermination: aws.Bool(false),
|
DisableApiTermination: aws.Bool(false),
|
||||||
EbsOptimized: &s.EbsOptimized,
|
EbsOptimized: &s.EbsOptimized,
|
||||||
IamInstanceProfile: &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{Name: &s.IamInstanceProfile},
|
IamInstanceProfile: &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{Name: iamInstanceProfile},
|
||||||
ImageId: &s.SourceAMI,
|
ImageId: &s.SourceAMI,
|
||||||
InstanceMarketOptions: marketOptions,
|
InstanceMarketOptions: marketOptions,
|
||||||
Placement: &ec2.LaunchTemplatePlacementRequest{
|
Placement: &ec2.LaunchTemplatePlacementRequest{
|
||||||
@ -274,15 +275,20 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
|||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(createOutput.Errors) > 0 {
|
if len(createOutput.Instances) == 0 {
|
||||||
errString := fmt.Sprintf("Error waiting for fleet request (%s) to become ready:", *createOutput.FleetId)
|
// We can end up with errors because one of the allowed availability
|
||||||
for _, outErr := range createOutput.Errors {
|
// zones doesn't have one of the allowed instance types; as long as
|
||||||
errString = errString + fmt.Sprintf("%s", *outErr.ErrorMessage)
|
// an instance is launched, these errors aren't important.
|
||||||
|
if len(createOutput.Errors) > 0 {
|
||||||
|
errString := fmt.Sprintf("Error waiting for fleet request (%s) to become ready:", *createOutput.FleetId)
|
||||||
|
for _, outErr := range createOutput.Errors {
|
||||||
|
errString = errString + fmt.Sprintf("%s", *outErr.ErrorMessage)
|
||||||
|
}
|
||||||
|
err = fmt.Errorf(errString)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
err = fmt.Errorf(errString)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceId = *createOutput.Instances[0].InstanceIds[0]
|
instanceId = *createOutput.Instances[0].InstanceIds[0]
|
||||||
|
@ -2,6 +2,7 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -73,6 +74,7 @@ func tStateSpot() multistep.StateBag {
|
|||||||
})
|
})
|
||||||
state.Put("availability_zone", "us-east-1c")
|
state.Put("availability_zone", "us-east-1c")
|
||||||
state.Put("securityGroupIds", []string{"sg-0b8984db72f213dc3"})
|
state.Put("securityGroupIds", []string{"sg-0b8984db72f213dc3"})
|
||||||
|
state.Put("iamInstanceProfile", "packer-123")
|
||||||
state.Put("subnet_id", "subnet-077fde4e")
|
state.Put("subnet_id", "subnet-077fde4e")
|
||||||
state.Put("source_image", "")
|
state.Put("source_image", "")
|
||||||
return state
|
return state
|
||||||
@ -91,7 +93,6 @@ func getBasicStep() *StepRunSpotInstance {
|
|||||||
},
|
},
|
||||||
EbsOptimized: false,
|
EbsOptimized: false,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: "",
|
|
||||||
InstanceInitiatedShutdownBehavior: "stop",
|
InstanceInitiatedShutdownBehavior: "stop",
|
||||||
InstanceType: "t2.micro",
|
InstanceType: "t2.micro",
|
||||||
SourceAMI: "",
|
SourceAMI: "",
|
||||||
@ -125,6 +126,10 @@ func TestCreateTemplateData(t *testing.T) {
|
|||||||
t.Fatalf("Template should have contained a networkInterface object: recieved %#v", template.NetworkInterfaces)
|
t.Fatalf("Template should have contained a networkInterface object: recieved %#v", template.NetworkInterfaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *template.IamInstanceProfile.Name != state.Get("iamInstanceProfile") {
|
||||||
|
t.Fatalf("Template should have contained a InstanceProfile name: recieved %#v", template.IamInstanceProfile.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// Rerun, this time testing that we set security group IDs
|
// Rerun, this time testing that we set security group IDs
|
||||||
state.Put("subnet_id", "")
|
state.Put("subnet_id", "")
|
||||||
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
||||||
@ -132,4 +137,13 @@ func TestCreateTemplateData(t *testing.T) {
|
|||||||
if template.NetworkInterfaces != nil {
|
if template.NetworkInterfaces != nil {
|
||||||
t.Fatalf("Template shouldn't contain network interfaces object if subnet_id is unset.")
|
t.Fatalf("Template shouldn't contain network interfaces object if subnet_id is unset.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rerun, this time testing that instance doesn't have instance profile is iamInstanceProfile is unset
|
||||||
|
state.Put("iamInstanceProfile", "")
|
||||||
|
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
||||||
|
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
||||||
|
fmt.Println(template.IamInstanceProfile)
|
||||||
|
if *template.IamInstanceProfile.Name != "" {
|
||||||
|
t.Fatalf("Template shouldn't contain instance profile if iamInstanceProfile is unset.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func (s *StepSecurityGroup) Run(ctx context.Context, state multistep.StateBag) m
|
|||||||
|
|
||||||
params := &ec2.DescribeSecurityGroupsInput{}
|
params := &ec2.DescribeSecurityGroupsInput{}
|
||||||
if vpcId != "" {
|
if vpcId != "" {
|
||||||
s.SecurityGroupFilter.Filters[aws.String("vpc-id")] = &vpcId
|
s.SecurityGroupFilter.Filters["vpc-id"] = vpcId
|
||||||
}
|
}
|
||||||
params.Filters = buildEc2Filters(s.SecurityGroupFilter.Filters)
|
params.Filters = buildEc2Filters(s.SecurityGroupFilter.Filters)
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ func (s *StepSourceAMIInfo) Run(ctx context.Context, state multistep.StateBag) m
|
|||||||
params.Filters = buildEc2Filters(s.AmiFilters.Filters)
|
params.Filters = buildEc2Filters(s.AmiFilters.Filters)
|
||||||
}
|
}
|
||||||
if len(s.AmiFilters.Owners) > 0 {
|
if len(s.AmiFilters.Owners) > 0 {
|
||||||
params.Owners = s.AmiFilters.Owners
|
params.Owners = s.AmiFilters.GetOwners()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Using AMI Filters %v", params)
|
log.Printf("Using AMI Filters %v", params)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type Config
|
||||||
|
|
||||||
// The amazonebs package contains a packer.Builder implementation that
|
// The amazonebs package contains a packer.Builder implementation that
|
||||||
// builds AMIs for Amazon EC2.
|
// builds AMIs for Amazon EC2.
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
@ -126,13 +128,14 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
}
|
}
|
||||||
|
|
||||||
ec2conn := ec2.New(session)
|
ec2conn := ec2.New(session)
|
||||||
|
iam := iam.New(session)
|
||||||
// Setup the state bag and initial state for the steps
|
// Setup the state bag and initial state for the steps
|
||||||
state := new(multistep.BasicStateBag)
|
state := new(multistep.BasicStateBag)
|
||||||
state.Put("config", &b.config)
|
state.Put("config", &b.config)
|
||||||
state.Put("access_config", &b.config.AccessConfig)
|
state.Put("access_config", &b.config.AccessConfig)
|
||||||
state.Put("ami_config", &b.config.AMIConfig)
|
state.Put("ami_config", &b.config.AMIConfig)
|
||||||
state.Put("ec2", ec2conn)
|
state.Put("ec2", ec2conn)
|
||||||
|
state.Put("iam", iam)
|
||||||
state.Put("awsSession", session)
|
state.Put("awsSession", session)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
@ -149,7 +152,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
@ -171,7 +173,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
@ -217,6 +218,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
CommConfig: &b.config.RunConfig.Comm,
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepIamInstanceProfile{
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument,
|
||||||
|
},
|
||||||
&awscommon.StepCleanupVolumes{
|
&awscommon.StepCleanupVolumes{
|
||||||
LaunchMappings: b.config.LaunchMappings,
|
LaunchMappings: b.config.LaunchMappings,
|
||||||
},
|
},
|
||||||
|
244
builder/amazon/ebs/builder.hcl2spec.go
Normal file
244
builder/amazon/ebs/builder.hcl2spec.go
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||||
|
package ebs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatConfig struct {
|
||||||
|
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
|
||||||
|
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
|
||||||
|
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
|
||||||
|
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
|
||||||
|
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
|
||||||
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
|
||||||
|
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
|
||||||
|
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"`
|
||||||
|
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"`
|
||||||
|
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"`
|
||||||
|
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"`
|
||||||
|
MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"`
|
||||||
|
ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"`
|
||||||
|
RawRegion *string `mapstructure:"region" required:"true" cty:"region"`
|
||||||
|
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"`
|
||||||
|
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"`
|
||||||
|
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"`
|
||||||
|
Token *string `mapstructure:"token" required:"false" cty:"token"`
|
||||||
|
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"`
|
||||||
|
AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name"`
|
||||||
|
AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description"`
|
||||||
|
AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type"`
|
||||||
|
AMIUsers []string `mapstructure:"ami_users" required:"false" cty:"ami_users"`
|
||||||
|
AMIGroups []string `mapstructure:"ami_groups" required:"false" cty:"ami_groups"`
|
||||||
|
AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"`
|
||||||
|
AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"`
|
||||||
|
AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"`
|
||||||
|
AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"`
|
||||||
|
AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"`
|
||||||
|
AMIForceDeregister *bool `mapstructure:"force_deregister" required:"false" cty:"force_deregister"`
|
||||||
|
AMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" required:"false" cty:"force_delete_snapshot"`
|
||||||
|
AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot" required:"false" cty:"encrypt_boot"`
|
||||||
|
AMIKmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"`
|
||||||
|
AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids" required:"false" cty:"region_kms_key_ids"`
|
||||||
|
AMISkipBuildRegion *bool `mapstructure:"skip_save_build_region" cty:"skip_save_build_region"`
|
||||||
|
SnapshotTags common.TagMap `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"`
|
||||||
|
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"`
|
||||||
|
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"`
|
||||||
|
AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"`
|
||||||
|
AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"`
|
||||||
|
BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"`
|
||||||
|
DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"`
|
||||||
|
EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"`
|
||||||
|
EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"`
|
||||||
|
IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"`
|
||||||
|
InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"`
|
||||||
|
InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"`
|
||||||
|
SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"`
|
||||||
|
RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"`
|
||||||
|
SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"`
|
||||||
|
SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"`
|
||||||
|
SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"`
|
||||||
|
SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"`
|
||||||
|
SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"`
|
||||||
|
SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"`
|
||||||
|
SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"`
|
||||||
|
SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"`
|
||||||
|
SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"`
|
||||||
|
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"`
|
||||||
|
TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"`
|
||||||
|
TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"`
|
||||||
|
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"`
|
||||||
|
UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"`
|
||||||
|
VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"`
|
||||||
|
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"`
|
||||||
|
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"`
|
||||||
|
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||||
|
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||||
|
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||||
|
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
|
||||||
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
|
||||||
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
|
||||||
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
|
||||||
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
|
||||||
|
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
|
||||||
|
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
|
||||||
|
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
|
||||||
|
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
|
||||||
|
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
|
||||||
|
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
|
||||||
|
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
|
||||||
|
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
|
||||||
|
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
|
||||||
|
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
|
||||||
|
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
|
||||||
|
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
|
||||||
|
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
|
||||||
|
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
|
||||||
|
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
|
||||||
|
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
|
||||||
|
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
|
||||||
|
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
|
||||||
|
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
|
||||||
|
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
|
||||||
|
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
|
||||||
|
SSHPublicKey []byte `cty:"ssh_public_key"`
|
||||||
|
SSHPrivateKey []byte `cty:"ssh_private_key"`
|
||||||
|
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
|
||||||
|
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
|
||||||
|
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
|
||||||
|
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
|
||||||
|
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
|
||||||
|
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
||||||
|
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
||||||
|
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
||||||
|
SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"`
|
||||||
|
AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings"`
|
||||||
|
LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings"`
|
||||||
|
VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatConfig.
|
||||||
|
// This spec is used by HCL to read the fields of FlatConfig.
|
||||||
|
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||||
|
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||||
|
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
||||||
|
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
||||||
|
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
||||||
|
"packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false},
|
||||||
|
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||||
|
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
|
||||||
|
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
|
||||||
|
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
|
||||||
|
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
|
||||||
|
"mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false},
|
||||||
|
"profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false},
|
||||||
|
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||||
|
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
|
||||||
|
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
|
||||||
|
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
|
||||||
|
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||||
|
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
|
||||||
|
"ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false},
|
||||||
|
"ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false},
|
||||||
|
"ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false},
|
||||||
|
"ami_users": &hcldec.AttrSpec{Name: "ami_users", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_groups": &hcldec.AttrSpec{Name: "ami_groups", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_product_codes": &hcldec.AttrSpec{Name: "ami_product_codes", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false},
|
||||||
|
"tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false},
|
||||||
|
"ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false},
|
||||||
|
"sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false},
|
||||||
|
"force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false},
|
||||||
|
"force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false},
|
||||||
|
"encrypt_boot": &hcldec.AttrSpec{Name: "encrypt_boot", Type: cty.Bool, Required: false},
|
||||||
|
"kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false},
|
||||||
|
"region_kms_key_ids": &hcldec.BlockAttrsSpec{TypeName: "region_kms_key_ids", ElementType: cty.String, Required: false},
|
||||||
|
"skip_save_build_region": &hcldec.AttrSpec{Name: "skip_save_build_region", Type: cty.Bool, Required: false},
|
||||||
|
"snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false},
|
||||||
|
"snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false},
|
||||||
|
"snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false},
|
||||||
|
"associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false},
|
||||||
|
"availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false},
|
||||||
|
"block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false},
|
||||||
|
"disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false},
|
||||||
|
"ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false},
|
||||||
|
"enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false},
|
||||||
|
"iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false},
|
||||||
|
"shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false},
|
||||||
|
"instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false},
|
||||||
|
"security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())},
|
||||||
|
"run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false},
|
||||||
|
"security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false},
|
||||||
|
"security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false},
|
||||||
|
"source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false},
|
||||||
|
"source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())},
|
||||||
|
"spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false},
|
||||||
|
"spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false},
|
||||||
|
"spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false},
|
||||||
|
"spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false},
|
||||||
|
"subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())},
|
||||||
|
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
||||||
|
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||||
|
"temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false},
|
||||||
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
|
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
|
||||||
|
"vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())},
|
||||||
|
"vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false},
|
||||||
|
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
|
||||||
|
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||||
|
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||||
|
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||||
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||||
|
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||||
|
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
||||||
|
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||||
|
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
||||||
|
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
||||||
|
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
||||||
|
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
||||||
|
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
||||||
|
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
||||||
|
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
||||||
|
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||||
|
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||||
|
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||||
|
"ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}},
|
||||||
|
"launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}},
|
||||||
|
"run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -8,6 +8,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
@ -164,7 +165,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ec2conn := ec2.New(session)
|
ec2conn := ec2.New(session)
|
||||||
|
iam := iam.New(session)
|
||||||
|
|
||||||
// Setup the state bag and initial state for the steps
|
// Setup the state bag and initial state for the steps
|
||||||
state := new(multistep.BasicStateBag)
|
state := new(multistep.BasicStateBag)
|
||||||
@ -172,6 +175,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("access_config", &b.config.AccessConfig)
|
state.Put("access_config", &b.config.AccessConfig)
|
||||||
state.Put("ami_config", &b.config.AMIConfig)
|
state.Put("ami_config", &b.config.AMIConfig)
|
||||||
state.Put("ec2", ec2conn)
|
state.Put("ec2", ec2conn)
|
||||||
|
state.Put("iam", iam)
|
||||||
state.Put("awsSession", session)
|
state.Put("awsSession", session)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
@ -188,7 +192,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
@ -210,7 +213,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
@ -258,6 +260,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
CommConfig: &b.config.RunConfig.Comm,
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepIamInstanceProfile{
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument,
|
||||||
|
},
|
||||||
&awscommon.StepCleanupVolumes{
|
&awscommon.StepCleanupVolumes{
|
||||||
LaunchMappings: b.config.LaunchMappings.Common(),
|
LaunchMappings: b.config.LaunchMappings.Common(),
|
||||||
},
|
},
|
||||||
@ -308,6 +314,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
EnableAMIENASupport: b.config.AMIENASupport,
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
Architecture: b.config.Architecture,
|
Architecture: b.config.Architecture,
|
||||||
LaunchOmitMap: b.config.LaunchMappings.GetOmissions(),
|
LaunchOmitMap: b.config.LaunchMappings.GetOmissions(),
|
||||||
|
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||||
},
|
},
|
||||||
&awscommon.StepAMIRegionCopy{
|
&awscommon.StepAMIRegionCopy{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
|
"github.com/hashicorp/packer/common/random"
|
||||||
confighelper "github.com/hashicorp/packer/helper/config"
|
confighelper "github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@ -22,6 +23,7 @@ type StepRegisterAMI struct {
|
|||||||
Architecture string
|
Architecture string
|
||||||
image *ec2.Image
|
image *ec2.Image
|
||||||
LaunchOmitMap map[string]bool
|
LaunchOmitMap map[string]bool
|
||||||
|
AMISkipBuildRegion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
@ -34,8 +36,25 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
|
|
||||||
blockDevices := s.combineDevices(snapshotIds)
|
blockDevices := s.combineDevices(snapshotIds)
|
||||||
|
|
||||||
|
// Create the image
|
||||||
|
amiName := config.AMIName
|
||||||
|
state.Put("intermediary_image", false)
|
||||||
|
if config.AMIEncryptBootVolume.True() || s.AMISkipBuildRegion {
|
||||||
|
state.Put("intermediary_image", true)
|
||||||
|
|
||||||
|
// From AWS SDK docs: You can encrypt a copy of an unencrypted snapshot,
|
||||||
|
// but you cannot use it to create an unencrypted copy of an encrypted
|
||||||
|
// snapshot. Your default CMK for EBS is used unless you specify a
|
||||||
|
// non-default key using KmsKeyId.
|
||||||
|
|
||||||
|
// If encrypt_boot is nil or true, we need to create a temporary image
|
||||||
|
// so that in step_region_copy, we can copy it with the correct
|
||||||
|
// encryption
|
||||||
|
amiName = random.AlphaNum(7)
|
||||||
|
}
|
||||||
|
|
||||||
registerOpts := &ec2.RegisterImageInput{
|
registerOpts := &ec2.RegisterImageInput{
|
||||||
Name: &config.AMIName,
|
Name: &amiName,
|
||||||
Architecture: aws.String(s.Architecture),
|
Architecture: aws.String(s.Architecture),
|
||||||
RootDeviceName: aws.String(s.RootDevice.DeviceName),
|
RootDeviceName: aws.String(s.RootDevice.DeviceName),
|
||||||
VirtualizationType: aws.String(config.AMIVirtType),
|
VirtualizationType: aws.String(config.AMIVirtType),
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
@ -141,12 +142,14 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ec2conn := ec2.New(session)
|
ec2conn := ec2.New(session)
|
||||||
|
iam := iam.New(session)
|
||||||
|
|
||||||
// Setup the state bag and initial state for the steps
|
// Setup the state bag and initial state for the steps
|
||||||
state := new(multistep.BasicStateBag)
|
state := new(multistep.BasicStateBag)
|
||||||
state.Put("config", &b.config)
|
state.Put("config", &b.config)
|
||||||
state.Put("access_config", &b.config.AccessConfig)
|
state.Put("access_config", &b.config.AccessConfig)
|
||||||
state.Put("ec2", ec2conn)
|
state.Put("ec2", ec2conn)
|
||||||
|
state.Put("iam", iam)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
@ -162,7 +165,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
@ -184,7 +186,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
@ -224,6 +225,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
CommConfig: &b.config.RunConfig.Comm,
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepIamInstanceProfile{
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument,
|
||||||
|
},
|
||||||
instanceStep,
|
instanceStep,
|
||||||
&stepTagEBSVolumes{
|
&stepTagEBSVolumes{
|
||||||
VolumeMapping: b.config.VolumeMappings,
|
VolumeMapping: b.config.VolumeMappings,
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -229,6 +230,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ec2conn := ec2.New(session)
|
ec2conn := ec2.New(session)
|
||||||
|
iam := iam.New(session)
|
||||||
|
|
||||||
// Setup the state bag and initial state for the steps
|
// Setup the state bag and initial state for the steps
|
||||||
state := new(multistep.BasicStateBag)
|
state := new(multistep.BasicStateBag)
|
||||||
@ -236,6 +238,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
state.Put("access_config", &b.config.AccessConfig)
|
state.Put("access_config", &b.config.AccessConfig)
|
||||||
state.Put("ami_config", &b.config.AMIConfig)
|
state.Put("ami_config", &b.config.AMIConfig)
|
||||||
state.Put("ec2", ec2conn)
|
state.Put("ec2", ec2conn)
|
||||||
|
state.Put("iam", iam)
|
||||||
state.Put("awsSession", session)
|
state.Put("awsSession", session)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
@ -251,7 +254,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Comm: &b.config.RunConfig.Comm,
|
Comm: &b.config.RunConfig.Comm,
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
@ -270,7 +272,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
InstanceType: b.config.InstanceType,
|
InstanceType: b.config.InstanceType,
|
||||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||||
SourceAMI: b.config.SourceAmi,
|
SourceAMI: b.config.SourceAmi,
|
||||||
@ -313,6 +314,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
SecurityGroupIds: b.config.SecurityGroupIds,
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepIamInstanceProfile{
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
TemporaryIamInstanceProfilePolicyDocument: b.config.TemporaryIamInstanceProfilePolicyDocument,
|
||||||
|
},
|
||||||
instanceStep,
|
instanceStep,
|
||||||
&awscommon.StepGetPassword{
|
&awscommon.StepGetPassword{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
@ -350,6 +355,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
&StepRegisterAMI{
|
&StepRegisterAMI{
|
||||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
EnableAMIENASupport: b.config.AMIENASupport,
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||||
},
|
},
|
||||||
&awscommon.StepAMIRegionCopy{
|
&awscommon.StepAMIRegionCopy{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
|
"github.com/hashicorp/packer/common/random"
|
||||||
confighelper "github.com/hashicorp/packer/helper/config"
|
confighelper "github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
type StepRegisterAMI struct {
|
type StepRegisterAMI struct {
|
||||||
EnableAMIENASupport confighelper.Trilean
|
EnableAMIENASupport confighelper.Trilean
|
||||||
EnableAMISriovNetSupport bool
|
EnableAMISriovNetSupport bool
|
||||||
|
AMISkipBuildRegion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
@ -24,9 +26,27 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
|
|||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say("Registering the AMI...")
|
ui.Say("Registering the AMI...")
|
||||||
|
|
||||||
|
// Create the image
|
||||||
|
amiName := config.AMIName
|
||||||
|
state.Put("intermediary_image", false)
|
||||||
|
if config.AMIEncryptBootVolume.True() || s.AMISkipBuildRegion {
|
||||||
|
state.Put("intermediary_image", true)
|
||||||
|
|
||||||
|
// From AWS SDK docs: You can encrypt a copy of an unencrypted snapshot,
|
||||||
|
// but you cannot use it to create an unencrypted copy of an encrypted
|
||||||
|
// snapshot. Your default CMK for EBS is used unless you specify a
|
||||||
|
// non-default key using KmsKeyId.
|
||||||
|
|
||||||
|
// If encrypt_boot is nil or true, we need to create a temporary image
|
||||||
|
// so that in step_region_copy, we can copy it with the correct
|
||||||
|
// encryption
|
||||||
|
amiName = random.AlphaNum(7)
|
||||||
|
}
|
||||||
|
|
||||||
registerOpts := &ec2.RegisterImageInput{
|
registerOpts := &ec2.RegisterImageInput{
|
||||||
ImageLocation: &manifestPath,
|
ImageLocation: &manifestPath,
|
||||||
Name: aws.String(config.AMIName),
|
Name: aws.String(amiName),
|
||||||
BlockDeviceMappings: config.AMIMappings.BuildEC2BlockDeviceMappings(),
|
BlockDeviceMappings: config.AMIMappings.BuildEC2BlockDeviceMappings(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ type AzureClient struct {
|
|||||||
network.InterfacesClient
|
network.InterfacesClient
|
||||||
network.SubnetsClient
|
network.SubnetsClient
|
||||||
network.VirtualNetworksClient
|
network.VirtualNetworksClient
|
||||||
|
network.SecurityGroupsClient
|
||||||
compute.ImagesClient
|
compute.ImagesClient
|
||||||
compute.VirtualMachinesClient
|
compute.VirtualMachinesClient
|
||||||
common.VaultClient
|
common.VaultClient
|
||||||
@ -127,7 +128,7 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string,
|
func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string,
|
||||||
cloud *azure.Environment, SharedGalleryTimeout time.Duration,
|
cloud *azure.Environment, SharedGalleryTimeout time.Duration, PollingDuration time.Duration,
|
||||||
servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
|
servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
|
||||||
|
|
||||||
var azureClient = &AzureClient{}
|
var azureClient = &AzureClient{}
|
||||||
@ -139,72 +140,90 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string
|
|||||||
azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
|
azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentsClient.UserAgent)
|
azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentsClient.UserAgent)
|
||||||
|
azureClient.DeploymentsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen)
|
azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentOperationsClient.UserAgent)
|
azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentOperationsClient.UserAgent)
|
||||||
|
azureClient.DeploymentOperationsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.DisksClient.RequestInspector = withInspection(maxlen)
|
azureClient.DisksClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DisksClient.UserAgent)
|
azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DisksClient.UserAgent)
|
||||||
|
azureClient.DisksClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
|
azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GroupsClient.UserAgent)
|
azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GroupsClient.UserAgent)
|
||||||
|
azureClient.GroupsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
|
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.ImagesClient.UserAgent)
|
azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.ImagesClient.UserAgent)
|
||||||
|
azureClient.ImagesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
|
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.InterfacesClient.UserAgent)
|
azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.InterfacesClient.UserAgent)
|
||||||
|
azureClient.InterfacesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
|
azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SubnetsClient.UserAgent)
|
azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SubnetsClient.UserAgent)
|
||||||
|
azureClient.SubnetsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
|
azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualNetworksClient.UserAgent)
|
azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualNetworksClient.UserAgent)
|
||||||
|
azureClient.VirtualNetworksClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
|
azureClient.SecurityGroupsClient = network.NewSecurityGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
|
azureClient.SecurityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
|
azureClient.SecurityGroupsClient.RequestInspector = withInspection(maxlen)
|
||||||
|
azureClient.SecurityGroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
|
azureClient.SecurityGroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SecurityGroupsClient.UserAgent)
|
||||||
|
|
||||||
azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
|
azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.PublicIPAddressesClient.UserAgent)
|
azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.PublicIPAddressesClient.UserAgent)
|
||||||
|
azureClient.PublicIPAddressesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
|
azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
|
azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
|
||||||
azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualMachinesClient.UserAgent)
|
azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualMachinesClient.UserAgent)
|
||||||
|
azureClient.VirtualMachinesClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.SnapshotsClient = compute.NewSnapshotsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.SnapshotsClient = compute.NewSnapshotsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.SnapshotsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.SnapshotsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen)
|
azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.SnapshotsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SnapshotsClient.UserAgent)
|
azureClient.SnapshotsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SnapshotsClient.UserAgent)
|
||||||
|
azureClient.SnapshotsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
|
azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.AccountsClient.UserAgent)
|
azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.AccountsClient.UserAgent)
|
||||||
|
azureClient.AccountsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||||
azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||||
@ -218,6 +237,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string
|
|||||||
azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen)
|
azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImagesClient.UserAgent)
|
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GalleryImagesClient.UserAgent)
|
||||||
|
azureClient.GalleryImageVersionsClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -229,6 +249,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string
|
|||||||
azureClient.VaultClient.RequestInspector = withInspection(maxlen)
|
azureClient.VaultClient.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClient.UserAgent)
|
azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClient.UserAgent)
|
||||||
|
azureClient.VaultClient.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
// TODO(boumenot) - SDK still does not have a full KeyVault client.
|
// TODO(boumenot) - SDK still does not have a full KeyVault client.
|
||||||
// There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN
|
// There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN
|
||||||
@ -243,6 +264,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string
|
|||||||
azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen)
|
azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen)
|
||||||
azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||||
azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClientDelete.UserAgent)
|
azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClientDelete.UserAgent)
|
||||||
|
azureClient.VaultClientDelete.Client.PollingDuration = PollingDuration
|
||||||
|
|
||||||
// If this is a managed disk build, this should be ignored.
|
// If this is a managed disk build, this should be ignored.
|
||||||
if resourceGroupName != "" && storageAccountName != "" {
|
if resourceGroupName != "" && storageAccountName != "" {
|
||||||
|
@ -79,8 +79,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||||||
b.config.ClientConfig.SubscriptionID,
|
b.config.ClientConfig.SubscriptionID,
|
||||||
b.config.ResourceGroupName,
|
b.config.ResourceGroupName,
|
||||||
b.config.StorageAccount,
|
b.config.StorageAccount,
|
||||||
b.config.ClientConfig.CloudEnvironment,
|
b.config.ClientConfig.CloudEnvironment(),
|
||||||
b.config.SharedGalleryTimeout,
|
b.config.SharedGalleryTimeout,
|
||||||
|
b.config.PollingDurationTimeout,
|
||||||
spnCloud,
|
spnCloud,
|
||||||
spnKeyVault)
|
spnKeyVault)
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -311,6 +313,14 @@ type Config struct {
|
|||||||
// 4. PlanPromotionCode
|
// 4. PlanPromotionCode
|
||||||
//
|
//
|
||||||
PlanInfo PlanInformation `mapstructure:"plan_info" required:"false"`
|
PlanInfo PlanInformation `mapstructure:"plan_info" required:"false"`
|
||||||
|
// The default PollingDuration for azure is 15mins, this property will override
|
||||||
|
// that value. See [Azure DefaultPollingDuration](https://godoc.org/github.com/Azure/go-autorest/autorest#pkg-constants)
|
||||||
|
// If your Packer build is failing on the
|
||||||
|
// ARM deployment step with the error `Original Error:
|
||||||
|
// context deadline exceeded`, then you probably need to increase this timeout from
|
||||||
|
// its default of "15m" (valid time units include `s` for seconds, `m` for
|
||||||
|
// minutes, and `h` for hours.)
|
||||||
|
PollingDurationTimeout time.Duration `mapstructure:"polling_duration_timeout" required:"false"`
|
||||||
// If either Linux or Windows is specified Packer will
|
// If either Linux or Windows is specified Packer will
|
||||||
// automatically configure authentication credentials for the provisioned
|
// automatically configure authentication credentials for the provisioned
|
||||||
// machine. For Linux this configures an SSH authorized key. For Windows
|
// machine. For Linux this configures an SSH authorized key. For Windows
|
||||||
@ -342,6 +352,13 @@ type Config struct {
|
|||||||
// are None, ReadOnly, and ReadWrite. The default value is ReadWrite.
|
// are None, ReadOnly, and ReadWrite. The default value is ReadWrite.
|
||||||
DiskCachingType string `mapstructure:"disk_caching_type" required:"false"`
|
DiskCachingType string `mapstructure:"disk_caching_type" required:"false"`
|
||||||
diskCachingType compute.CachingTypes
|
diskCachingType compute.CachingTypes
|
||||||
|
// Specify the list of IP addresses and CIDR blocks that should be
|
||||||
|
// allowed access to the VM. If provided, an Azure Network Security
|
||||||
|
// Group will be created with corresponding rules and be bound to
|
||||||
|
// the subnet of the VM.
|
||||||
|
// Providing `allowed_inbound_ip_addresses` in combination with
|
||||||
|
// `virtual_network_name` is not allowed.
|
||||||
|
AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses"`
|
||||||
|
|
||||||
// Runtime Values
|
// Runtime Values
|
||||||
UserName string
|
UserName string
|
||||||
@ -357,6 +374,7 @@ type Config struct {
|
|||||||
tmpOSDiskName string
|
tmpOSDiskName string
|
||||||
tmpSubnetName string
|
tmpSubnetName string
|
||||||
tmpVirtualNetworkName string
|
tmpVirtualNetworkName string
|
||||||
|
tmpNsgName string
|
||||||
tmpWinRMCertificateUrl string
|
tmpWinRMCertificateUrl string
|
||||||
|
|
||||||
// Authentication with the VM via SSH
|
// Authentication with the VM via SSH
|
||||||
@ -604,6 +622,7 @@ func setRuntimeValues(c *Config) {
|
|||||||
c.tmpOSDiskName = tempName.OSDiskName
|
c.tmpOSDiskName = tempName.OSDiskName
|
||||||
c.tmpSubnetName = tempName.SubnetName
|
c.tmpSubnetName = tempName.SubnetName
|
||||||
c.tmpVirtualNetworkName = tempName.VirtualNetworkName
|
c.tmpVirtualNetworkName = tempName.VirtualNetworkName
|
||||||
|
c.tmpNsgName = tempName.NsgName
|
||||||
c.tmpKeyVaultName = tempName.KeyVaultName
|
c.tmpKeyVaultName = tempName.KeyVaultName
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,6 +891,16 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
|||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name"))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.AllowedInboundIpAddresses != nil && len(c.AllowedInboundIpAddresses) >= 1 {
|
||||||
|
if c.VirtualNetworkName != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_name is specified, allowed_inbound_ip_addresses cannot be specified"))
|
||||||
|
} else {
|
||||||
|
if ok, err := assertAllowedInboundIpAddresses(c.AllowedInboundIpAddresses, "allowed_inbound_ip_addresses"); !ok {
|
||||||
|
errs = packer.MultiErrorAppend(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// Plan Info
|
// Plan Info
|
||||||
if c.PlanInfo.PlanName != "" || c.PlanInfo.PlanProduct != "" || c.PlanInfo.PlanPublisher != "" || c.PlanInfo.PlanPromotionCode != "" {
|
if c.PlanInfo.PlanName != "" || c.PlanInfo.PlanProduct != "" || c.PlanInfo.PlanPublisher != "" || c.PlanInfo.PlanPromotionCode != "" {
|
||||||
@ -889,6 +918,13 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
// Polling Duration Timeout
|
||||||
|
if c.PollingDurationTimeout == 0 {
|
||||||
|
// In the sdk, the default is 15 m.
|
||||||
|
c.PollingDurationTimeout = 15 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// OS
|
// OS
|
||||||
if strings.EqualFold(c.OSType, constants.Target_Linux) {
|
if strings.EqualFold(c.OSType, constants.Target_Linux) {
|
||||||
@ -943,6 +979,17 @@ func assertManagedImageDataDiskSnapshotName(name, setting string) (bool, error)
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertAllowedInboundIpAddresses(ipAddresses []string, setting string) (bool, error) {
|
||||||
|
for _, ipAddress := range ipAddresses {
|
||||||
|
if net.ParseIP(ipAddress) == nil {
|
||||||
|
if _, _, err := net.ParseCIDR(ipAddress); err != nil {
|
||||||
|
return false, fmt.Errorf("The setting %s must only contain valid IP addresses or CIDR blocks", setting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func assertResourceGroupName(rgn, setting string) (bool, error) {
|
func assertResourceGroupName(rgn, setting string) (bool, error) {
|
||||||
if !isValidAzureName(reResourceGroupName, rgn) {
|
if !isValidAzureName(reResourceGroupName, rgn) {
|
||||||
return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validResourceGroupNameRe)
|
return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validResourceGroupNameRe)
|
||||||
|
299
builder/azure/arm/config.hcl2spec.go
Normal file
299
builder/azure/arm/config.hcl2spec.go
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
// Code generated by "mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation"; DO NOT EDIT.
|
||||||
|
package arm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatConfig struct {
|
||||||
|
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
|
||||||
|
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
|
||||||
|
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
|
||||||
|
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
|
||||||
|
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
|
||||||
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
|
||||||
|
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
|
||||||
|
CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name"`
|
||||||
|
ClientID *string `mapstructure:"client_id" cty:"client_id"`
|
||||||
|
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret"`
|
||||||
|
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path"`
|
||||||
|
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt"`
|
||||||
|
ObjectID *string `mapstructure:"object_id" cty:"object_id"`
|
||||||
|
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id"`
|
||||||
|
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id"`
|
||||||
|
CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix"`
|
||||||
|
CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name"`
|
||||||
|
SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" required:"false" cty:"shared_image_gallery"`
|
||||||
|
SharedGalleryDestination *FlatSharedImageGalleryDestination `mapstructure:"shared_image_gallery_destination" cty:"shared_image_gallery_destination"`
|
||||||
|
SharedGalleryTimeout *string `mapstructure:"shared_image_gallery_timeout" cty:"shared_image_gallery_timeout"`
|
||||||
|
ImagePublisher *string `mapstructure:"image_publisher" required:"true" cty:"image_publisher"`
|
||||||
|
ImageOffer *string `mapstructure:"image_offer" required:"true" cty:"image_offer"`
|
||||||
|
ImageSku *string `mapstructure:"image_sku" required:"true" cty:"image_sku"`
|
||||||
|
ImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version"`
|
||||||
|
ImageUrl *string `mapstructure:"image_url" required:"false" cty:"image_url"`
|
||||||
|
CustomManagedImageResourceGroupName *string `mapstructure:"custom_managed_image_resource_group_name" required:"false" cty:"custom_managed_image_resource_group_name"`
|
||||||
|
CustomManagedImageName *string `mapstructure:"custom_managed_image_name" required:"false" cty:"custom_managed_image_name"`
|
||||||
|
Location *string `mapstructure:"location" cty:"location"`
|
||||||
|
VMSize *string `mapstructure:"vm_size" required:"false" cty:"vm_size"`
|
||||||
|
ManagedImageResourceGroupName *string `mapstructure:"managed_image_resource_group_name" cty:"managed_image_resource_group_name"`
|
||||||
|
ManagedImageName *string `mapstructure:"managed_image_name" cty:"managed_image_name"`
|
||||||
|
ManagedImageStorageAccountType *string `mapstructure:"managed_image_storage_account_type" required:"false" cty:"managed_image_storage_account_type"`
|
||||||
|
ManagedImageOSDiskSnapshotName *string `mapstructure:"managed_image_os_disk_snapshot_name" required:"false" cty:"managed_image_os_disk_snapshot_name"`
|
||||||
|
ManagedImageDataDiskSnapshotPrefix *string `mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" cty:"managed_image_data_disk_snapshot_prefix"`
|
||||||
|
ManagedImageZoneResilient *bool `mapstructure:"managed_image_zone_resilient" required:"false" cty:"managed_image_zone_resilient"`
|
||||||
|
AzureTags map[string]*string `mapstructure:"azure_tags" required:"false" cty:"azure_tags"`
|
||||||
|
ResourceGroupName *string `mapstructure:"resource_group_name" cty:"resource_group_name"`
|
||||||
|
StorageAccount *string `mapstructure:"storage_account" cty:"storage_account"`
|
||||||
|
TempComputeName *string `mapstructure:"temp_compute_name" required:"false" cty:"temp_compute_name"`
|
||||||
|
TempResourceGroupName *string `mapstructure:"temp_resource_group_name" cty:"temp_resource_group_name"`
|
||||||
|
BuildResourceGroupName *string `mapstructure:"build_resource_group_name" cty:"build_resource_group_name"`
|
||||||
|
PrivateVirtualNetworkWithPublicIp *bool `mapstructure:"private_virtual_network_with_public_ip" required:"false" cty:"private_virtual_network_with_public_ip"`
|
||||||
|
VirtualNetworkName *string `mapstructure:"virtual_network_name" required:"false" cty:"virtual_network_name"`
|
||||||
|
VirtualNetworkSubnetName *string `mapstructure:"virtual_network_subnet_name" required:"false" cty:"virtual_network_subnet_name"`
|
||||||
|
VirtualNetworkResourceGroupName *string `mapstructure:"virtual_network_resource_group_name" required:"false" cty:"virtual_network_resource_group_name"`
|
||||||
|
CustomDataFile *string `mapstructure:"custom_data_file" required:"false" cty:"custom_data_file"`
|
||||||
|
PlanInfo *FlatPlanInformation `mapstructure:"plan_info" required:"false" cty:"plan_info"`
|
||||||
|
PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout"`
|
||||||
|
OSType *string `mapstructure:"os_type" required:"false" cty:"os_type"`
|
||||||
|
OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" required:"false" cty:"os_disk_size_gb"`
|
||||||
|
AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size"`
|
||||||
|
DiskCachingType *string `mapstructure:"disk_caching_type" required:"false" cty:"disk_caching_type"`
|
||||||
|
UserName *string `cty:"user_name"`
|
||||||
|
Password *string `cty:"password"`
|
||||||
|
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||||
|
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||||
|
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||||
|
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
|
||||||
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
|
||||||
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
|
||||||
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
|
||||||
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
|
||||||
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
|
||||||
|
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
|
||||||
|
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
|
||||||
|
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
|
||||||
|
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
|
||||||
|
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
|
||||||
|
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
|
||||||
|
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
|
||||||
|
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
|
||||||
|
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
|
||||||
|
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
|
||||||
|
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
|
||||||
|
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
|
||||||
|
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
|
||||||
|
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
|
||||||
|
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
|
||||||
|
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
|
||||||
|
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
|
||||||
|
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
|
||||||
|
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
|
||||||
|
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
|
||||||
|
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
|
||||||
|
SSHPublicKey []byte `cty:"ssh_public_key"`
|
||||||
|
SSHPrivateKey []byte `cty:"ssh_private_key"`
|
||||||
|
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
|
||||||
|
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
|
||||||
|
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
|
||||||
|
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
|
||||||
|
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
|
||||||
|
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
||||||
|
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
||||||
|
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
||||||
|
AsyncResourceGroupDelete *bool `mapstructure:"async_resourcegroup_delete" required:"false" cty:"async_resourcegroup_delete"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatConfig.
|
||||||
|
// This spec is used by HCL to read the fields of FlatConfig.
|
||||||
|
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||||
|
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||||
|
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
||||||
|
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
||||||
|
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
||||||
|
"packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false},
|
||||||
|
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||||
|
"cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false},
|
||||||
|
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
|
||||||
|
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
|
||||||
|
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
|
||||||
|
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
|
||||||
|
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||||
|
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||||
|
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
||||||
|
"capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false},
|
||||||
|
"capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false},
|
||||||
|
"shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())},
|
||||||
|
"shared_image_gallery_destination": &hcldec.BlockSpec{TypeName: "shared_image_gallery_destination", Nested: hcldec.ObjectSpec((*FlatSharedImageGalleryDestination)(nil).HCL2Spec())},
|
||||||
|
"shared_image_gallery_timeout": &hcldec.AttrSpec{Name: "shared_image_gallery_timeout", Type: cty.String, Required: false},
|
||||||
|
"image_publisher": &hcldec.AttrSpec{Name: "image_publisher", Type: cty.String, Required: false},
|
||||||
|
"image_offer": &hcldec.AttrSpec{Name: "image_offer", Type: cty.String, Required: false},
|
||||||
|
"image_sku": &hcldec.AttrSpec{Name: "image_sku", Type: cty.String, Required: false},
|
||||||
|
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
||||||
|
"image_url": &hcldec.AttrSpec{Name: "image_url", Type: cty.String, Required: false},
|
||||||
|
"custom_managed_image_resource_group_name": &hcldec.AttrSpec{Name: "custom_managed_image_resource_group_name", Type: cty.String, Required: false},
|
||||||
|
"custom_managed_image_name": &hcldec.AttrSpec{Name: "custom_managed_image_name", Type: cty.String, Required: false},
|
||||||
|
"location": &hcldec.AttrSpec{Name: "location", Type: cty.String, Required: false},
|
||||||
|
"vm_size": &hcldec.AttrSpec{Name: "vm_size", Type: cty.String, Required: false},
|
||||||
|
"managed_image_resource_group_name": &hcldec.AttrSpec{Name: "managed_image_resource_group_name", Type: cty.String, Required: false},
|
||||||
|
"managed_image_name": &hcldec.AttrSpec{Name: "managed_image_name", Type: cty.String, Required: false},
|
||||||
|
"managed_image_storage_account_type": &hcldec.AttrSpec{Name: "managed_image_storage_account_type", Type: cty.String, Required: false},
|
||||||
|
"managed_image_os_disk_snapshot_name": &hcldec.AttrSpec{Name: "managed_image_os_disk_snapshot_name", Type: cty.String, Required: false},
|
||||||
|
"managed_image_data_disk_snapshot_prefix": &hcldec.AttrSpec{Name: "managed_image_data_disk_snapshot_prefix", Type: cty.String, Required: false},
|
||||||
|
"managed_image_zone_resilient": &hcldec.AttrSpec{Name: "managed_image_zone_resilient", Type: cty.Bool, Required: false},
|
||||||
|
"azure_tags": &hcldec.BlockAttrsSpec{TypeName: "azure_tags", ElementType: cty.String, Required: false},
|
||||||
|
"resource_group_name": &hcldec.AttrSpec{Name: "resource_group_name", Type: cty.String, Required: false},
|
||||||
|
"storage_account": &hcldec.AttrSpec{Name: "storage_account", Type: cty.String, Required: false},
|
||||||
|
"temp_compute_name": &hcldec.AttrSpec{Name: "temp_compute_name", Type: cty.String, Required: false},
|
||||||
|
"temp_resource_group_name": &hcldec.AttrSpec{Name: "temp_resource_group_name", Type: cty.String, Required: false},
|
||||||
|
"build_resource_group_name": &hcldec.AttrSpec{Name: "build_resource_group_name", Type: cty.String, Required: false},
|
||||||
|
"private_virtual_network_with_public_ip": &hcldec.AttrSpec{Name: "private_virtual_network_with_public_ip", Type: cty.Bool, Required: false},
|
||||||
|
"virtual_network_name": &hcldec.AttrSpec{Name: "virtual_network_name", Type: cty.String, Required: false},
|
||||||
|
"virtual_network_subnet_name": &hcldec.AttrSpec{Name: "virtual_network_subnet_name", Type: cty.String, Required: false},
|
||||||
|
"virtual_network_resource_group_name": &hcldec.AttrSpec{Name: "virtual_network_resource_group_name", Type: cty.String, Required: false},
|
||||||
|
"custom_data_file": &hcldec.AttrSpec{Name: "custom_data_file", Type: cty.String, Required: false},
|
||||||
|
"plan_info": &hcldec.BlockSpec{TypeName: "plan_info", Nested: hcldec.ObjectSpec((*FlatPlanInformation)(nil).HCL2Spec())},
|
||||||
|
"polling_duration_timeout": &hcldec.AttrSpec{Name: "polling_duration_timeout", Type: cty.String, Required: false},
|
||||||
|
"os_type": &hcldec.AttrSpec{Name: "os_type", Type: cty.String, Required: false},
|
||||||
|
"os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false},
|
||||||
|
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"disk_caching_type": &hcldec.AttrSpec{Name: "disk_caching_type", Type: cty.String, Required: false},
|
||||||
|
"user_name": &hcldec.AttrSpec{Name: "user_name", Type: cty.String, Required: false},
|
||||||
|
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||||
|
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||||
|
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||||
|
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_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},
|
||||||
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||||
|
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||||
|
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
||||||
|
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||||
|
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||||
|
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
||||||
|
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
||||||
|
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
||||||
|
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
||||||
|
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
||||||
|
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
||||||
|
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
||||||
|
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
||||||
|
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
||||||
|
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
||||||
|
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
||||||
|
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||||
|
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||||
|
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||||
|
"async_resourcegroup_delete": &hcldec.AttrSpec{Name: "async_resourcegroup_delete", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatPlanInformation is an auto-generated flat version of PlanInformation.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatPlanInformation struct {
|
||||||
|
PlanName *string `mapstructure:"plan_name" cty:"plan_name"`
|
||||||
|
PlanProduct *string `mapstructure:"plan_product" cty:"plan_product"`
|
||||||
|
PlanPublisher *string `mapstructure:"plan_publisher" cty:"plan_publisher"`
|
||||||
|
PlanPromotionCode *string `mapstructure:"plan_promotion_code" cty:"plan_promotion_code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatPlanInformation.
|
||||||
|
// FlatPlanInformation is an auto-generated flat version of PlanInformation.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*PlanInformation) FlatMapstructure() interface{} { return new(FlatPlanInformation) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatPlanInformation.
|
||||||
|
// This spec is used by HCL to read the fields of FlatPlanInformation.
|
||||||
|
func (*FlatPlanInformation) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"plan_name": &hcldec.AttrSpec{Name: "plan_name", Type: cty.String, Required: false},
|
||||||
|
"plan_product": &hcldec.AttrSpec{Name: "plan_product", Type: cty.String, Required: false},
|
||||||
|
"plan_publisher": &hcldec.AttrSpec{Name: "plan_publisher", Type: cty.String, Required: false},
|
||||||
|
"plan_promotion_code": &hcldec.AttrSpec{Name: "plan_promotion_code", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatSharedImageGallery struct {
|
||||||
|
Subscription *string `mapstructure:"subscription" cty:"subscription"`
|
||||||
|
ResourceGroup *string `mapstructure:"resource_group" cty:"resource_group"`
|
||||||
|
GalleryName *string `mapstructure:"gallery_name" cty:"gallery_name"`
|
||||||
|
ImageName *string `mapstructure:"image_name" cty:"image_name"`
|
||||||
|
ImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatSharedImageGallery.
|
||||||
|
// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*SharedImageGallery) FlatMapstructure() interface{} { return new(FlatSharedImageGallery) }
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatSharedImageGallery.
|
||||||
|
// This spec is used by HCL to read the fields of FlatSharedImageGallery.
|
||||||
|
func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false},
|
||||||
|
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
||||||
|
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
||||||
|
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||||
|
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group"`
|
||||||
|
SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name"`
|
||||||
|
SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name"`
|
||||||
|
SigDestinationImageVersion *string `mapstructure:"image_version" cty:"image_version"`
|
||||||
|
SigDestinationReplicationRegions []string `mapstructure:"replication_regions" cty:"replication_regions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatSharedImageGalleryDestination.
|
||||||
|
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*SharedImageGalleryDestination) FlatMapstructure() interface{} {
|
||||||
|
return new(FlatSharedImageGalleryDestination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcldec.Spec of a FlatSharedImageGalleryDestination.
|
||||||
|
// This spec is used by HCL to read the fields of 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},
|
||||||
|
"replication_regions": &hcldec.AttrSpec{Name: "replication_regions", Type: cty.List(cty.String), Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -270,6 +270,132 @@ func TestConfigVirtualNetworkSubnetNameMustBeSetWithVirtualNetworkName(t *testin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigAllowedInboundIpAddressesIsOptional(t *testing.T) {
|
||||||
|
config := map[string]string{
|
||||||
|
"capture_name_prefix": "ignore",
|
||||||
|
"capture_container_name": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"image_url": "ignore",
|
||||||
|
"storage_account": "ignore",
|
||||||
|
"resource_group_name": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
"communicator": "none",
|
||||||
|
"virtual_network_name": "MyVirtualNetwork",
|
||||||
|
}
|
||||||
|
|
||||||
|
c, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if c.AllowedInboundIpAddresses != nil {
|
||||||
|
t.Errorf("Expected Config to set allowed_inbound_ip_addresses to nil, but got %v", c.AllowedInboundIpAddresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) {
|
||||||
|
ipValue0 := "127.0.0.1"
|
||||||
|
ipValue1 := "127.0.0.2"
|
||||||
|
cidrValue2 := "192.168.100.0/24"
|
||||||
|
cidrValue3 := "10.10.1.16/32"
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"capture_name_prefix": "ignore",
|
||||||
|
"capture_container_name": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"image_url": "ignore",
|
||||||
|
"storage_account": "ignore",
|
||||||
|
"resource_group_name": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
"communicator": "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
config["allowed_inbound_ip_addresses"] = ipValue0
|
||||||
|
c, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 1 ||
|
||||||
|
c.AllowedInboundIpAddresses[0] != ipValue0 {
|
||||||
|
t.Errorf("Expected 'allowed_inbound_ip_addresses' to have one element (%s), but got '%v'.", ipValue0, c.AllowedInboundIpAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["allowed_inbound_ip_addresses"] = cidrValue2
|
||||||
|
c, _, err = newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 1 ||
|
||||||
|
c.AllowedInboundIpAddresses[0] != cidrValue2 {
|
||||||
|
t.Errorf("Expected 'allowed_inbound_ip_addresses' to have one element (%s), but got '%v'.", cidrValue2, c.AllowedInboundIpAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["allowed_inbound_ip_addresses"] = []string{ipValue0, cidrValue2, ipValue1, cidrValue3}
|
||||||
|
c, _, err = newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 4 ||
|
||||||
|
c.AllowedInboundIpAddresses[0] != ipValue0 || c.AllowedInboundIpAddresses[1] != cidrValue2 ||
|
||||||
|
c.AllowedInboundIpAddresses[2] != ipValue1 || c.AllowedInboundIpAddresses[3] != cidrValue3 {
|
||||||
|
t.Errorf("Expected 'allowed_inbound_ip_addresses' to have four elements (%s %s %s %s), but got '%v'.", ipValue0, cidrValue2, ipValue1,
|
||||||
|
cidrValue3, c.AllowedInboundIpAddresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigShouldRejectIncorrectInboundIpAddresses(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"capture_name_prefix": "ignore",
|
||||||
|
"capture_container_name": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"image_url": "ignore",
|
||||||
|
"storage_account": "ignore",
|
||||||
|
"resource_group_name": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
"communicator": "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
config["allowed_inbound_ip_addresses"] = []string{"127.0.0.1", "127.0.0.two"}
|
||||||
|
c, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["allowed_inbound_ip_addresses"] = []string{"192.168.100.1000/24", "10.10.1.16/32"}
|
||||||
|
c, _, err = newConfig(config, getPackerConfiguration())
|
||||||
|
if err == nil {
|
||||||
|
// 192.168.100.1000/24 is invalid
|
||||||
|
t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigShouldRejectInboundIpAddressesWithVirtualNetwork(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"capture_name_prefix": "ignore",
|
||||||
|
"capture_container_name": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"image_url": "ignore",
|
||||||
|
"storage_account": "ignore",
|
||||||
|
"resource_group_name": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
"communicator": "none",
|
||||||
|
"allowed_inbound_ip_addresses": "127.0.0.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["virtual_network_name"] = "some_vnet_name"
|
||||||
|
_, _, err = newConfig(config, getPackerConfiguration())
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected configuration creation to fail, but it succeeded with allowed_inbound_ip_addresses and virtual_network_name both specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigShouldDefaultToPublicCloud(t *testing.T) {
|
func TestConfigShouldDefaultToPublicCloud(t *testing.T) {
|
||||||
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
|
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
|
||||||
|
|
||||||
@ -277,8 +403,8 @@ func TestConfigShouldDefaultToPublicCloud(t *testing.T) {
|
|||||||
t.Errorf("Expected 'CloudEnvironmentName' to default to 'Public', but got '%s'.", c.ClientConfig.CloudEnvironmentName)
|
t.Errorf("Expected 'CloudEnvironmentName' to default to 'Public', but got '%s'.", c.ClientConfig.CloudEnvironmentName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ClientConfig.CloudEnvironment == nil || c.ClientConfig.CloudEnvironment.Name != "AzurePublicCloud" {
|
if c.ClientConfig.CloudEnvironment() == nil || c.ClientConfig.CloudEnvironment().Name != "AzurePublicCloud" {
|
||||||
t.Errorf("Expected 'cloudEnvironment' to be set to 'AzurePublicCloud', but got '%s'.", c.ClientConfig.CloudEnvironment)
|
t.Errorf("Expected 'cloudEnvironment' to be set to 'AzurePublicCloud', but got '%s'.", c.ClientConfig.CloudEnvironment())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,8 +453,8 @@ func TestConfigInstantiatesCorrectAzureEnvironment(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ClientConfig.CloudEnvironment == nil || c.ClientConfig.CloudEnvironment.Name != x.environmentName {
|
if c.ClientConfig.CloudEnvironment() == nil || c.ClientConfig.CloudEnvironment().Name != x.environmentName {
|
||||||
t.Errorf("Expected 'cloudEnvironment' to be set to '%s', but got '%s'.", x.environmentName, c.ClientConfig.CloudEnvironment)
|
t.Errorf("Expected 'cloudEnvironment' to be set to '%s', but got '%s'.", x.environmentName, c.ClientConfig.CloudEnvironment())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,6 +506,10 @@ func TestSystemShouldDefineRuntimeValues(t *testing.T) {
|
|||||||
if c.tmpOSDiskName == "" {
|
if c.tmpOSDiskName == "" {
|
||||||
t.Errorf("Expected tmpOSDiskName to not be empty, but it was '%s'!", c.tmpOSDiskName)
|
t.Errorf("Expected tmpOSDiskName to not be empty, but it was '%s'!", c.tmpOSDiskName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.tmpNsgName == "" {
|
||||||
|
t.Errorf("Expected tmpNsgName to not be empty, but it was '%s'!", c.tmpNsgName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) {
|
func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) {
|
||||||
|
@ -95,7 +95,7 @@ func (s *StepDeleteResourceGroup) deleteDeploymentResources(ctx context.Context,
|
|||||||
resourceType,
|
resourceType,
|
||||||
resourceName))
|
resourceName))
|
||||||
|
|
||||||
err := retry.Config{
|
retry.Config{
|
||||||
Tries: 10,
|
Tries: 10,
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
@ -106,7 +106,7 @@ func (s *StepDeleteResourceGroup) deleteDeploymentResources(ctx context.Context,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.reportIfError(err, resourceName)
|
s.reportIfError(err, resourceName)
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err = deploymentOperations.Next(); err != nil {
|
if err = deploymentOperations.Next(); err != nil {
|
||||||
|
@ -115,6 +115,12 @@ func deleteResource(ctx context.Context, client *AzureClient, resourceType strin
|
|||||||
err = f.WaitForCompletionRef(ctx, client.VirtualNetworksClient.Client)
|
err = f.WaitForCompletionRef(ctx, client.VirtualNetworksClient.Client)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
case "Microsoft.Network/networkSecurityGroups":
|
||||||
|
f, err := client.SecurityGroupsClient.Delete(ctx, resourceGroupName, resourceName)
|
||||||
|
if err == nil {
|
||||||
|
err = f.WaitForCompletionRef(ctx, client.SecurityGroupsClient.Client)
|
||||||
|
}
|
||||||
|
return err
|
||||||
case "Microsoft.Network/publicIPAddresses":
|
case "Microsoft.Network/publicIPAddresses":
|
||||||
f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName)
|
f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -40,6 +40,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
|||||||
SubnetName: &template.TemplateParameter{Value: config.tmpSubnetName},
|
SubnetName: &template.TemplateParameter{Value: config.tmpSubnetName},
|
||||||
StorageAccountBlobEndpoint: &template.TemplateParameter{Value: config.storageAccountBlobEndpoint},
|
StorageAccountBlobEndpoint: &template.TemplateParameter{Value: config.storageAccountBlobEndpoint},
|
||||||
VirtualNetworkName: &template.TemplateParameter{Value: config.tmpVirtualNetworkName},
|
VirtualNetworkName: &template.TemplateParameter{Value: config.tmpVirtualNetworkName},
|
||||||
|
NsgName: &template.TemplateParameter{Value: config.tmpNsgName},
|
||||||
VMSize: &template.TemplateParameter{Value: config.VMSize},
|
VMSize: &template.TemplateParameter{Value: config.VMSize},
|
||||||
VMName: &template.TemplateParameter{Value: config.tmpComputeName},
|
VMName: &template.TemplateParameter{Value: config.tmpComputeName},
|
||||||
}
|
}
|
||||||
@ -117,6 +118,13 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
|||||||
config.VirtualNetworkSubnetName)
|
config.VirtualNetworkSubnetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.AllowedInboundIpAddresses != nil && len(config.AllowedInboundIpAddresses) >= 1 && config.Comm.Port() != 0 {
|
||||||
|
err = builder.SetNetworkSecurityGroup(config.AllowedInboundIpAddresses, config.Comm.Port())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder.SetTags(&config.AzureTags)
|
builder.SetTags(&config.AzureTags)
|
||||||
doc, _ := builder.ToJSON()
|
doc, _ := builder.ToJSON()
|
||||||
return createDeploymentParameters(*doc, params)
|
return createDeploymentParameters(*doc, params)
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -189,6 +192,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -194,6 +197,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -160,6 +163,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -158,6 +161,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -119,6 +122,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -178,6 +181,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -159,6 +162,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -158,6 +161,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -161,6 +164,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -139,6 +142,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -172,6 +175,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -173,6 +176,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -0,0 +1,227 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {
|
||||||
|
"adminPassword": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"adminUsername": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dnsNameForPublicIP": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nicName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"osDiskName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"publicIPAddressName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"storageAccountBlobEndpoint": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"subnetName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"virtualNetworkName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vmName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vmSize": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[parameters('publicIPAddressName')]",
|
||||||
|
"properties": {
|
||||||
|
"dnsSettings": {
|
||||||
|
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
||||||
|
},
|
||||||
|
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Network/publicIPAddresses"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
||||||
|
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
||||||
|
],
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[parameters('nicName')]",
|
||||||
|
"properties": {
|
||||||
|
"ipConfigurations": [
|
||||||
|
{
|
||||||
|
"name": "ipconfig",
|
||||||
|
"properties": {
|
||||||
|
"privateIPAllocationMethod": "Dynamic",
|
||||||
|
"publicIPAddress": {
|
||||||
|
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
||||||
|
},
|
||||||
|
"subnet": {
|
||||||
|
"id": "[variables('subnetRef')]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Network/networkInterfaces"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('apiVersion')]",
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
||||||
|
],
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[parameters('vmName')]",
|
||||||
|
"properties": {
|
||||||
|
"diagnosticsProfile": {
|
||||||
|
"bootDiagnostics": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hardwareProfile": {
|
||||||
|
"vmSize": "[parameters('vmSize')]"
|
||||||
|
},
|
||||||
|
"networkProfile": {
|
||||||
|
"networkInterfaces": [
|
||||||
|
{
|
||||||
|
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"osProfile": {
|
||||||
|
"adminPassword": "[parameters('adminPassword')]",
|
||||||
|
"adminUsername": "[parameters('adminUsername')]",
|
||||||
|
"computerName": "[parameters('vmName')]",
|
||||||
|
"secrets": [
|
||||||
|
{
|
||||||
|
"sourceVault": {
|
||||||
|
"id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--keyvault-name--')]"
|
||||||
|
},
|
||||||
|
"vaultCertificates": [
|
||||||
|
{
|
||||||
|
"certificateStore": "My",
|
||||||
|
"certificateUrl": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"windowsConfiguration": {
|
||||||
|
"provisionVMAgent": true,
|
||||||
|
"winRM": {
|
||||||
|
"listeners": [
|
||||||
|
{
|
||||||
|
"certificateUrl": "",
|
||||||
|
"protocol": "https"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"storageProfile": {
|
||||||
|
"imageReference": {
|
||||||
|
"offer": "--image-offer--",
|
||||||
|
"publisher": "--image-publisher--",
|
||||||
|
"sku": "--image-sku--",
|
||||||
|
"version": "--version--"
|
||||||
|
},
|
||||||
|
"osDisk": {
|
||||||
|
"caching": "ReadWrite",
|
||||||
|
"createOption": "FromImage",
|
||||||
|
"managedDisk": {
|
||||||
|
"storageAccountType": "Standard_LRS"
|
||||||
|
},
|
||||||
|
"name": "[parameters('osDiskName')]",
|
||||||
|
"osType": "Windows"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Compute/virtualMachines"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('networkSecurityGroupsApiVersion')]",
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[parameters('nsgName')]",
|
||||||
|
"properties": {
|
||||||
|
"securityRules": [
|
||||||
|
{
|
||||||
|
"name": "AllowIPsToSshWinRMInbound",
|
||||||
|
"properties": {
|
||||||
|
"access": "Allow",
|
||||||
|
"description": "Allow inbound traffic from specified IP addresses",
|
||||||
|
"destinationAddressPrefix": "VirtualNetwork",
|
||||||
|
"destinationPortRange": "5985",
|
||||||
|
"direction": "Inbound",
|
||||||
|
"priority": 100,
|
||||||
|
"protocol": "Tcp",
|
||||||
|
"sourceAddressPrefixes": [
|
||||||
|
"127.0.0.1",
|
||||||
|
"192.168.100.0/24"
|
||||||
|
],
|
||||||
|
"sourcePortRange": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Network/networkSecurityGroups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat('Microsoft.Network/networkSecurityGroups/', parameters('nsgName'))]"
|
||||||
|
],
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[variables('virtualNetworkName')]",
|
||||||
|
"properties": {
|
||||||
|
"addressSpace": {
|
||||||
|
"addressPrefixes": [
|
||||||
|
"[variables('addressPrefix')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"subnets": [
|
||||||
|
{
|
||||||
|
"name": "[variables('subnetName')]",
|
||||||
|
"properties": {
|
||||||
|
"addressPrefix": "[variables('subnetAddressPrefix')]",
|
||||||
|
"networkSecurityGroup": {
|
||||||
|
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Network/virtualNetworks"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"addressPrefix": "10.0.0.0/16",
|
||||||
|
"apiVersion": "2017-03-30",
|
||||||
|
"location": "[resourceGroup().location]",
|
||||||
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
|
"publicIPAddressType": "Dynamic",
|
||||||
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
"subnetAddressPrefix": "10.0.0.0/24",
|
||||||
|
"subnetName": "[parameters('subnetName')]",
|
||||||
|
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
||||||
|
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
||||||
|
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
||||||
|
"virtualNetworksApiVersion": "2017-04-01",
|
||||||
|
"vmStorageAccountContainerName": "images",
|
||||||
|
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
||||||
|
}
|
||||||
|
}
|
@ -425,6 +425,40 @@ func TestVirtualMachineDeployment12(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the VM template is correct when building with list of allowed IP addresses
|
||||||
|
func TestVirtualMachineDeployment13(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"location": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"os_type": constants.Target_Windows,
|
||||||
|
"communicator": "winrm",
|
||||||
|
"winrm_username": "ignore",
|
||||||
|
"image_publisher": "--image-publisher--",
|
||||||
|
"image_offer": "--image-offer--",
|
||||||
|
"image_sku": "--image-sku--",
|
||||||
|
"image_version": "--version--",
|
||||||
|
"managed_image_name": "ManagedImageName",
|
||||||
|
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
||||||
|
"allowed_inbound_ip_addresses": []string{"127.0.0.1", "192.168.100.0/24"},
|
||||||
|
}
|
||||||
|
|
||||||
|
c, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c.tmpKeyVaultName = "--keyvault-name--"
|
||||||
|
|
||||||
|
deployment, err := GetVirtualMachineDeployment(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the link values are not set, and the concrete values are set.
|
// Ensure the link values are not set, and the concrete values are set.
|
||||||
func TestKeyVaultDeployment00(t *testing.T) {
|
func TestKeyVaultDeployment00(t *testing.T) {
|
||||||
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
|
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
|
||||||
|
@ -19,6 +19,7 @@ type TempName struct {
|
|||||||
SubnetName string
|
SubnetName string
|
||||||
PublicIPAddressName string
|
PublicIPAddressName string
|
||||||
VirtualNetworkName string
|
VirtualNetworkName string
|
||||||
|
NsgName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTempName() *TempName {
|
func NewTempName() *TempName {
|
||||||
@ -33,6 +34,7 @@ func NewTempName() *TempName {
|
|||||||
tempName.PublicIPAddressName = fmt.Sprintf("pkrip%s", suffix)
|
tempName.PublicIPAddressName = fmt.Sprintf("pkrip%s", suffix)
|
||||||
tempName.SubnetName = fmt.Sprintf("pkrsn%s", suffix)
|
tempName.SubnetName = fmt.Sprintf("pkrsn%s", suffix)
|
||||||
tempName.VirtualNetworkName = fmt.Sprintf("pkrvn%s", suffix)
|
tempName.VirtualNetworkName = fmt.Sprintf("pkrvn%s", suffix)
|
||||||
|
tempName.NsgName = fmt.Sprintf("pkrsg%s", suffix)
|
||||||
tempName.ResourceGroupName = fmt.Sprintf("packer-Resource-Group-%s", suffix)
|
tempName.ResourceGroupName = fmt.Sprintf("packer-Resource-Group-%s", suffix)
|
||||||
|
|
||||||
tempName.AdminPassword = generatePassword()
|
tempName.AdminPassword = generatePassword()
|
||||||
|
@ -41,6 +41,10 @@ func TestTempNameShouldCreatePrefixedRandomNames(t *testing.T) {
|
|||||||
if strings.Index(tempName.VirtualNetworkName, "pkrvn") != 0 {
|
if strings.Index(tempName.VirtualNetworkName, "pkrvn") != 0 {
|
||||||
t.Errorf("Expected VirtualNetworkName to begin with 'pkrvn', but got '%s'!", tempName.VirtualNetworkName)
|
t.Errorf("Expected VirtualNetworkName to begin with 'pkrvn', but got '%s'!", tempName.VirtualNetworkName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.Index(tempName.NsgName, "pkrsg") != 0 {
|
||||||
|
t.Errorf("Expected NsgName to begin with 'pkrsg', but got '%s'!", tempName.NsgName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTempAdminPassword(t *testing.T) {
|
func TestTempAdminPassword(t *testing.T) {
|
||||||
@ -92,4 +96,8 @@ func TestTempNameShouldHaveSameSuffix(t *testing.T) {
|
|||||||
if strings.HasSuffix(tempName.VirtualNetworkName, suffix) != true {
|
if strings.HasSuffix(tempName.VirtualNetworkName, suffix) != true {
|
||||||
t.Errorf("Expected VirtualNetworkName to end with '%s', but the value is '%s'!", suffix, tempName.VirtualNetworkName)
|
t.Errorf("Expected VirtualNetworkName to end with '%s', but the value is '%s'!", suffix, tempName.VirtualNetworkName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(tempName.NsgName, suffix) != true {
|
||||||
|
t.Errorf("Expected NsgName to end with '%s', but the value is '%s'!", suffix, tempName.NsgName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
452
builder/azure/chroot/builder.go
Normal file
452
builder/azure/chroot/builder.go
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
//go:generate struct-markdown
|
||||||
|
|
||||||
|
// Package chroot is able to create an Azure managed image without requiring the
|
||||||
|
// launch of a new virtual machine for every build. It does this by attaching and
|
||||||
|
// mounting the root disk and chrooting into that directory.
|
||||||
|
// It then creates a managed image from that attached disk.
|
||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/chroot"
|
||||||
|
azcommon "github.com/hashicorp/packer/builder/azure/common"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/config"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuilderId is the unique ID for this builder
|
||||||
|
const BuilderId = "azure.chroot"
|
||||||
|
|
||||||
|
// Config is the configuration that is chained through the steps and settable
|
||||||
|
// from the template.
|
||||||
|
type Config struct {
|
||||||
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
ClientConfig client.Config `mapstructure:",squash"`
|
||||||
|
|
||||||
|
// When set to `true`, starts with an empty, unpartitioned disk. Defaults to `false`.
|
||||||
|
FromScratch bool `mapstructure:"from_scratch"`
|
||||||
|
// Either a managed disk resource ID or a publisher:offer:sku:version specifier for plaform image sources.
|
||||||
|
Source string `mapstructure:"source" required:"true"`
|
||||||
|
sourceType sourceType
|
||||||
|
|
||||||
|
// How to run shell commands. This may be useful to set environment variables or perhaps run
|
||||||
|
// a command with sudo or so on. This is a configuration template where the `.Command` variable
|
||||||
|
// is replaced with the command to be run. Defaults to `{{.Command}}`.
|
||||||
|
CommandWrapper string `mapstructure:"command_wrapper"`
|
||||||
|
// A series of commands to execute after attaching the root volume and before mounting the chroot.
|
||||||
|
// This is not required unless using `from_scratch`. If so, this should include any partitioning
|
||||||
|
// and filesystem creation commands. The path to the device is provided by `{{.Device}}`.
|
||||||
|
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
||||||
|
// Options to supply the `mount` command when mounting devices. Each option will be prefixed with
|
||||||
|
// `-o` and supplied to the `mount` command ran by Packer. Because this command is ran in a shell,
|
||||||
|
// user discretion is advised. See this manual page for the `mount` command for valid file system specific options.
|
||||||
|
MountOptions []string `mapstructure:"mount_options"`
|
||||||
|
// The partition number containing the / partition. By default this is the first partition of the volume.
|
||||||
|
MountPartition string `mapstructure:"mount_partition"`
|
||||||
|
// The path where the volume will be mounted. This is where the chroot environment will be. This defaults
|
||||||
|
// to `/mnt/packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration template where the `.Device`
|
||||||
|
// variable is replaced with the name of the device where the volume is attached.
|
||||||
|
MountPath string `mapstructure:"mount_path"`
|
||||||
|
// As `pre_mount_commands`, but the commands are executed after mounting the root device and before the
|
||||||
|
// extra mount and copy steps. The device and mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
|
||||||
|
PostMountCommands []string `mapstructure:"post_mount_commands"`
|
||||||
|
// This is a list of devices to mount into the chroot environment. This configuration parameter requires
|
||||||
|
// some additional documentation which is in the "Chroot Mounts" section below. Please read that section
|
||||||
|
// for more information on how to use this.
|
||||||
|
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||||
|
// Paths to files on the running Azure instance that will be copied into the chroot environment prior to
|
||||||
|
// provisioning. Defaults to `/etc/resolv.conf` so that DNS lookups work. Pass an empty list to skip copying
|
||||||
|
// `/etc/resolv.conf`. You may need to do this if you're building an image that uses systemd.
|
||||||
|
CopyFiles []string `mapstructure:"copy_files"`
|
||||||
|
|
||||||
|
// The name of the temporary disk that will be created in the resource group of the VM that Packer is
|
||||||
|
// running on. Will be generated if not set.
|
||||||
|
TemporaryOSDiskName string `mapstructure:"temporary_os_disk_name"`
|
||||||
|
// Try to resize the OS disk to this size on the first copy. Disks can only be englarged. If not specified,
|
||||||
|
// the disk will keep its original size. Required when using `from_scratch`
|
||||||
|
OSDiskSizeGB int32 `mapstructure:"os_disk_size_gb"`
|
||||||
|
// The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes)
|
||||||
|
// to use for the OS Disk. Defaults to `Standard_LRS`.
|
||||||
|
OSDiskStorageAccountType string `mapstructure:"os_disk_storage_account_type"`
|
||||||
|
// The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes)
|
||||||
|
// specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly`
|
||||||
|
OSDiskCacheType string `mapstructure:"os_disk_cache_type"`
|
||||||
|
// If set to `true`, leaves the temporary disk behind in the Packer VM resource group. Defaults to `false`
|
||||||
|
OSDiskSkipCleanup bool `mapstructure:"os_disk_skip_cleanup"`
|
||||||
|
|
||||||
|
// The image to create using this build.
|
||||||
|
ImageResourceID string `mapstructure:"image_resource_id" required:"true"`
|
||||||
|
// The [Hyper-V generation type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#hypervgenerationtypes).
|
||||||
|
// Defaults to `V1`.
|
||||||
|
ImageHyperVGeneration string `mapstructure:"image_hyperv_generation"`
|
||||||
|
|
||||||
|
ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type sourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
sourcePlatformImage sourceType = "PlatformImage"
|
||||||
|
sourceDisk sourceType = "Disk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetContext implements ContextProvider to allow steps to use the config context
|
||||||
|
// for template interpolation
|
||||||
|
func (c *Config) GetContext() interpolate.Context {
|
||||||
|
return c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config Config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
|
b.config.ctx.Funcs = azcommon.TemplateFuncs
|
||||||
|
b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc()
|
||||||
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
|
Interpolate: true,
|
||||||
|
InterpolateContext: &b.config.ctx,
|
||||||
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
Exclude: []string{
|
||||||
|
// these fields are interpolated in the steps,
|
||||||
|
// when more information is available
|
||||||
|
"command_wrapper",
|
||||||
|
"post_mount_commands",
|
||||||
|
"pre_mount_commands",
|
||||||
|
"mount_path",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, raws...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs *packer.MultiError
|
||||||
|
var warns []string
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
err = b.config.ClientConfig.SetDefaultValues()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ChrootMounts == nil {
|
||||||
|
b.config.ChrootMounts = make([][]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b.config.ChrootMounts) == 0 {
|
||||||
|
b.config.ChrootMounts = [][]string{
|
||||||
|
{"proc", "proc", "/proc"},
|
||||||
|
{"sysfs", "sysfs", "/sys"},
|
||||||
|
{"bind", "/dev", "/dev"},
|
||||||
|
{"devpts", "devpts", "/dev/pts"},
|
||||||
|
{"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default copy file if we're not giving our own
|
||||||
|
if b.config.CopyFiles == nil {
|
||||||
|
if !b.config.FromScratch {
|
||||||
|
b.config.CopyFiles = []string{"/etc/resolv.conf"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.CommandWrapper == "" {
|
||||||
|
b.config.CommandWrapper = "{{.Command}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.MountPath == "" {
|
||||||
|
b.config.MountPath = "/mnt/packer-azure-chroot-disks/{{.Device}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.MountPartition == "" {
|
||||||
|
b.config.MountPartition = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.TemporaryOSDiskName == "" {
|
||||||
|
|
||||||
|
if def, err := interpolate.Render("PackerTemp-{{timestamp}}", &b.config.ctx); err == nil {
|
||||||
|
b.config.TemporaryOSDiskName = def
|
||||||
|
} else {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary disk name: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.OSDiskStorageAccountType == "" {
|
||||||
|
b.config.OSDiskStorageAccountType = string(compute.PremiumLRS)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.OSDiskCacheType == "" {
|
||||||
|
b.config.OSDiskCacheType = string(compute.CachingTypesReadOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ImageHyperVGeneration == "" {
|
||||||
|
b.config.ImageHyperVGeneration = string(compute.V1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks, accumulate any errors or warnings
|
||||||
|
|
||||||
|
if b.config.FromScratch {
|
||||||
|
if b.config.Source != "" {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, errors.New("source cannot be specified when building from_scratch"))
|
||||||
|
}
|
||||||
|
if b.config.OSDiskSizeGB == 0 {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, errors.New("os_disk_size_gb is required with from_scratch"))
|
||||||
|
}
|
||||||
|
if len(b.config.PreMountCommands) == 0 {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, errors.New("pre_mount_commands is required with from_scratch"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := client.ParsePlatformImageURN(b.config.Source); err == nil {
|
||||||
|
log.Println("Source is platform image:", b.config.Source)
|
||||||
|
b.config.sourceType = sourcePlatformImage
|
||||||
|
} else if id, err := azure.ParseResourceID(b.config.Source); err == nil &&
|
||||||
|
strings.EqualFold(id.Provider, "Microsoft.Compute") && strings.EqualFold(id.ResourceType, "disks") {
|
||||||
|
log.Println("Source is a disk resource ID:", b.config.Source)
|
||||||
|
b.config.sourceType = sourceDisk
|
||||||
|
} else {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, fmt.Errorf("source: %q is not a valid platform image specifier, nor is it a disk resource ID", b.config.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkDiskCacheType(b.config.OSDiskCacheType); err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("os_disk_cache_type: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkStorageAccountType(b.config.OSDiskStorageAccountType); err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("os_disk_storage_account_type: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ImageResourceID == "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, errors.New("image_resource_id is required"))
|
||||||
|
} else {
|
||||||
|
r, err := azure.ParseResourceID(b.config.ImageResourceID)
|
||||||
|
if err != nil ||
|
||||||
|
!strings.EqualFold(r.Provider, "Microsoft.Compute") ||
|
||||||
|
!strings.EqualFold(r.ResourceType, "images") {
|
||||||
|
errs = packer.MultiErrorAppend(fmt.Errorf(
|
||||||
|
"image_resource_id: %q is not a valid image resource id", b.config.ImageResourceID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkHyperVGeneration(b.config.ImageHyperVGeneration); err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("image_hyperv_generation: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs != nil {
|
||||||
|
return warns, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
packer.LogSecretFilter.Set(b.config.ClientConfig.ClientSecret, b.config.ClientConfig.ClientJWT)
|
||||||
|
return warns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDiskCacheType(s string) interface{} {
|
||||||
|
for _, v := range compute.PossibleCachingTypesValues() {
|
||||||
|
if compute.CachingTypes(s) == v {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%q is not a valid value %v",
|
||||||
|
s, compute.PossibleCachingTypesValues())
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkStorageAccountType(s string) interface{} {
|
||||||
|
for _, v := range compute.PossibleDiskStorageAccountTypesValues() {
|
||||||
|
if compute.DiskStorageAccountTypes(s) == v {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%q is not a valid value %v",
|
||||||
|
s, compute.PossibleDiskStorageAccountTypesValues())
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHyperVGeneration(s string) interface{} {
|
||||||
|
for _, v := range compute.PossibleHyperVGenerationValues() {
|
||||||
|
if compute.HyperVGeneration(s) == v {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%q is not a valid value %v",
|
||||||
|
s, compute.PossibleHyperVGenerationValues())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return nil, errors.New("the azure-chroot builder only works on Linux environments")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.config.ClientConfig.FillParameters()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error setting Azure client defaults: %v", err)
|
||||||
|
}
|
||||||
|
azcli, err := client.New(b.config.ClientConfig, ui.Say)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating Azure client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedCommand := func(command string) (string, error) {
|
||||||
|
ictx := b.config.ctx
|
||||||
|
ictx.Data = &struct{ Command string }{Command: command}
|
||||||
|
return interpolate.Render(b.config.CommandWrapper, &ictx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the state bag and initial state for the steps
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("config", &b.config)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
state.Put("azureclient", azcli)
|
||||||
|
state.Put("wrappedCommand", chroot.CommandWrapper(wrappedCommand))
|
||||||
|
|
||||||
|
info, err := azcli.MetadataClient().GetComputeInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("MetadataClient().GetComputeInfo(): error: %+v", err)
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"Error retrieving information ARM resource ID and location" +
|
||||||
|
"of the VM that Packer is running on.\n" +
|
||||||
|
"Please verify that Packer is running on a proper Azure VM.")
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Put("instance", info)
|
||||||
|
|
||||||
|
// Build the steps
|
||||||
|
var steps []multistep.Step
|
||||||
|
|
||||||
|
if b.config.FromScratch {
|
||||||
|
steps = append(steps,
|
||||||
|
&StepCreateNewDisk{
|
||||||
|
SubscriptionID: info.SubscriptionID,
|
||||||
|
ResourceGroup: info.ResourceGroupName,
|
||||||
|
DiskName: b.config.TemporaryOSDiskName,
|
||||||
|
DiskSizeGB: b.config.OSDiskSizeGB,
|
||||||
|
DiskStorageAccountType: b.config.OSDiskStorageAccountType,
|
||||||
|
HyperVGeneration: b.config.ImageHyperVGeneration,
|
||||||
|
Location: info.Location,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
switch b.config.sourceType {
|
||||||
|
case sourcePlatformImage:
|
||||||
|
|
||||||
|
if pi, err := client.ParsePlatformImageURN(b.config.Source); err == nil {
|
||||||
|
if strings.EqualFold(pi.Version, "latest") {
|
||||||
|
|
||||||
|
vmi, err := azcli.VirtualMachineImagesClient().GetLatest(ctx, pi.Publisher, pi.Offer, pi.Sku, info.Location)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error retieving latest version of %q: %v", b.config.Source, err)
|
||||||
|
}
|
||||||
|
pi.Version = to.String(vmi.Name)
|
||||||
|
log.Println("Resolved latest version of source image:", pi.Version)
|
||||||
|
}
|
||||||
|
steps = append(steps,
|
||||||
|
|
||||||
|
&StepCreateNewDisk{
|
||||||
|
SubscriptionID: info.SubscriptionID,
|
||||||
|
ResourceGroup: info.ResourceGroupName,
|
||||||
|
DiskName: b.config.TemporaryOSDiskName,
|
||||||
|
DiskSizeGB: b.config.OSDiskSizeGB,
|
||||||
|
DiskStorageAccountType: b.config.OSDiskStorageAccountType,
|
||||||
|
HyperVGeneration: b.config.ImageHyperVGeneration,
|
||||||
|
Location: info.Location,
|
||||||
|
PlatformImage: pi,
|
||||||
|
|
||||||
|
SkipCleanup: b.config.OSDiskSkipCleanup,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
panic("Unknown image source: " + b.config.Source)
|
||||||
|
}
|
||||||
|
case sourceDisk:
|
||||||
|
steps = append(steps,
|
||||||
|
&StepVerifySourceDisk{
|
||||||
|
SourceDiskResourceID: b.config.Source,
|
||||||
|
SubscriptionID: info.SubscriptionID,
|
||||||
|
Location: info.Location,
|
||||||
|
},
|
||||||
|
&StepCreateNewDisk{
|
||||||
|
SubscriptionID: info.SubscriptionID,
|
||||||
|
ResourceGroup: info.ResourceGroupName,
|
||||||
|
DiskName: b.config.TemporaryOSDiskName,
|
||||||
|
DiskSizeGB: b.config.OSDiskSizeGB,
|
||||||
|
DiskStorageAccountType: b.config.OSDiskStorageAccountType,
|
||||||
|
HyperVGeneration: b.config.ImageHyperVGeneration,
|
||||||
|
SourceDiskResourceID: b.config.Source,
|
||||||
|
Location: info.Location,
|
||||||
|
|
||||||
|
SkipCleanup: b.config.OSDiskSkipCleanup,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("Unknown source type: %+q", b.config.sourceType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag
|
||||||
|
&chroot.StepPreMountCommands{
|
||||||
|
Commands: b.config.PreMountCommands,
|
||||||
|
},
|
||||||
|
&StepMountDevice{
|
||||||
|
MountOptions: b.config.MountOptions,
|
||||||
|
MountPartition: b.config.MountPartition,
|
||||||
|
MountPath: b.config.MountPath,
|
||||||
|
},
|
||||||
|
&chroot.StepPostMountCommands{
|
||||||
|
Commands: b.config.PostMountCommands,
|
||||||
|
},
|
||||||
|
&chroot.StepMountExtra{
|
||||||
|
ChrootMounts: b.config.ChrootMounts,
|
||||||
|
},
|
||||||
|
&chroot.StepCopyFiles{
|
||||||
|
Files: b.config.CopyFiles,
|
||||||
|
},
|
||||||
|
&chroot.StepChrootProvision{},
|
||||||
|
&chroot.StepEarlyCleanup{},
|
||||||
|
&StepCreateImage{
|
||||||
|
ImageResourceID: b.config.ImageResourceID,
|
||||||
|
ImageOSState: string(compute.Generalized),
|
||||||
|
OSDiskCacheType: b.config.OSDiskCacheType,
|
||||||
|
OSDiskStorageAccountType: b.config.OSDiskStorageAccountType,
|
||||||
|
Location: info.Location,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run!
|
||||||
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
|
b.runner.Run(ctx, state)
|
||||||
|
|
||||||
|
// If there was an error, return that
|
||||||
|
if rawErr, ok := state.GetOk("error"); ok {
|
||||||
|
return nil, rawErr.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the artifact and return it
|
||||||
|
artifact := &azcommon.Artifact{
|
||||||
|
Resources: []string{b.config.ImageResourceID},
|
||||||
|
BuilderIdValue: BuilderId,
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ packer.Builder = &Builder{}
|
70
builder/azure/chroot/builder_test.go
Normal file
70
builder/azure/chroot/builder_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuilder_Prepare(t *testing.T) {
|
||||||
|
type config map[string]interface{}
|
||||||
|
type regexMatchers map[string]string // map of regex : error message
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
config config
|
||||||
|
validate func(Config)
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "HappyPathFromPlatformImage",
|
||||||
|
config: config{
|
||||||
|
"client_id": "123",
|
||||||
|
"client_secret": "456",
|
||||||
|
"subscription_id": "789",
|
||||||
|
"image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}",
|
||||||
|
"source": "credativ:Debian:9:latest",
|
||||||
|
},
|
||||||
|
validate: func(c Config) {
|
||||||
|
if c.OSDiskSizeGB != 0 {
|
||||||
|
t.Errorf("Expected OSDiskSizeGB to be 0, was %+v", c.OSDiskSizeGB)
|
||||||
|
}
|
||||||
|
if c.MountPartition != "1" {
|
||||||
|
t.Errorf("Expected MountPartition to be %s, but found %s", "1", c.MountPartition)
|
||||||
|
}
|
||||||
|
if c.OSDiskStorageAccountType != string(compute.PremiumLRS) {
|
||||||
|
t.Errorf("Expected OSDiskStorageAccountType to be %s, but found %s", string(compute.PremiumLRS), c.OSDiskStorageAccountType)
|
||||||
|
}
|
||||||
|
if c.OSDiskCacheType != string(compute.CachingTypesReadOnly) {
|
||||||
|
t.Errorf("Expected OSDiskCacheType to be %s, but found %s", string(compute.CachingTypesReadOnly), c.OSDiskCacheType)
|
||||||
|
}
|
||||||
|
if c.ImageHyperVGeneration != string(compute.V1) {
|
||||||
|
t.Errorf("Expected ImageHyperVGeneration to be %s, but found %s", string(compute.V1), c.ImageHyperVGeneration)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HappyPathFromPlatformImage",
|
||||||
|
config: config{
|
||||||
|
"image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}",
|
||||||
|
"source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
b := &Builder{}
|
||||||
|
|
||||||
|
_, err := b.Prepare(tt.config)
|
||||||
|
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Builder.Prepare() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.validate != nil {
|
||||||
|
tt.validate(b.config)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
222
builder/azure/chroot/diskattacher.go
Normal file
222
builder/azure/chroot/diskattacher.go
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiskAttacher interface {
|
||||||
|
AttachDisk(ctx context.Context, disk string) (lun int32, err error)
|
||||||
|
DiskPathForLun(lun int32) string
|
||||||
|
WaitForDevice(ctx context.Context, i int32) (device string, err error)
|
||||||
|
DetachDisk(ctx context.Context, disk string) (err error)
|
||||||
|
WaitForDetach(ctx context.Context, diskID string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var NewDiskAttacher = func(azureClient client.AzureClientSet) DiskAttacher {
|
||||||
|
return &diskAttacher{
|
||||||
|
azcli: azureClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type diskAttacher struct {
|
||||||
|
azcli client.AzureClientSet
|
||||||
|
|
||||||
|
vm *client.ComputeInfo // store info about this VM so that we don't have to ask metadata service on every call
|
||||||
|
}
|
||||||
|
|
||||||
|
func (diskAttacher) DiskPathForLun(lun int32) string {
|
||||||
|
return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
|
||||||
|
path := da.DiskPathForLun(lun)
|
||||||
|
|
||||||
|
for {
|
||||||
|
link, err := os.Readlink(path)
|
||||||
|
if err == nil {
|
||||||
|
return filepath.Abs("/dev/disk/azure/scsi1/" + link)
|
||||||
|
} else if err != os.ErrNotExist {
|
||||||
|
if pe, ok := err.(*os.PathError); ok && pe.Err != syscall.ENOENT {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
// continue
|
||||||
|
case <-ctx.Done():
|
||||||
|
return "", ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error {
|
||||||
|
log.Println("Fetching list of disks currently attached to VM")
|
||||||
|
currentDisks, err := da.getDisks(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Removing %q from list of disks currently attached to VM", diskID)
|
||||||
|
newDisks := []compute.DataDisk{}
|
||||||
|
for _, disk := range currentDisks {
|
||||||
|
if disk.ManagedDisk != nil &&
|
||||||
|
!strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
|
||||||
|
newDisks = append(newDisks, disk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(currentDisks) == len(newDisks) {
|
||||||
|
return DiskNotFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Updating new list of disks attached to VM")
|
||||||
|
err = da.setDisks(ctx, newDisks)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
|
||||||
|
for { // loop until disk is not attached, timeout or error
|
||||||
|
list, err := da.getDisks(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if findDiskInList(list, diskID) == nil {
|
||||||
|
log.Println("Disk is no longer in VM model, assuming detached")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Second): //continue
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var DiskNotFoundError = errors.New("Disk not found")
|
||||||
|
|
||||||
|
func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) {
|
||||||
|
dataDisks, err := da.getDisks(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if disk is already attached, remember lun if found
|
||||||
|
if disk := findDiskInList(dataDisks, diskID); disk != nil {
|
||||||
|
// disk is already attached, just take this lun
|
||||||
|
if disk.Lun == nil {
|
||||||
|
return -1, errors.New("disk is attached, but lun was not set in VM model (possibly an error in the Azure APIs)")
|
||||||
|
}
|
||||||
|
return to.Int32(disk.Lun), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// disk was not found on VM, go and actually attach it
|
||||||
|
|
||||||
|
var lun int32 = -1
|
||||||
|
findFreeLun:
|
||||||
|
for lun = 0; lun < 64; lun++ {
|
||||||
|
for _, v := range dataDisks {
|
||||||
|
if to.Int32(v.Lun) == lun {
|
||||||
|
continue findFreeLun
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no datadisk is using this lun
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// append new data disk to collection
|
||||||
|
dataDisks = append(dataDisks, compute.DataDisk{
|
||||||
|
CreateOption: compute.DiskCreateOptionTypesAttach,
|
||||||
|
ManagedDisk: &compute.ManagedDiskParameters{
|
||||||
|
ID: to.StringPtr(diskID),
|
||||||
|
},
|
||||||
|
Lun: to.Int32Ptr(lun),
|
||||||
|
})
|
||||||
|
|
||||||
|
// prepare resource object for update operation
|
||||||
|
err = da.setDisks(ctx, dataDisks)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return lun, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) {
|
||||||
|
// getting resource info for this VM
|
||||||
|
if da.vm == nil {
|
||||||
|
vm, err := da.azcli.MetadataClient().GetComputeInfo()
|
||||||
|
if err != nil {
|
||||||
|
return compute.VirtualMachine{}, err
|
||||||
|
}
|
||||||
|
da.vm = vm
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve actual VM
|
||||||
|
vmResource, err := da.azcli.VirtualMachinesClient().Get(ctx, da.vm.ResourceGroupName, da.vm.Name, "")
|
||||||
|
if err != nil {
|
||||||
|
return compute.VirtualMachine{}, err
|
||||||
|
}
|
||||||
|
if vmResource.StorageProfile == nil {
|
||||||
|
return compute.VirtualMachine{}, errors.New("properties.storageProfile is not set on VM, this is unexpected")
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmResource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da diskAttacher) getDisks(ctx context.Context) ([]compute.DataDisk, error) {
|
||||||
|
vmResource, err := da.getThisVM(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return []compute.DataDisk{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return *vmResource.StorageProfile.DataDisks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da diskAttacher) setDisks(ctx context.Context, disks []compute.DataDisk) error {
|
||||||
|
vmResource, err := da.getThisVM(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := azure.ParseResourceID(to.String(vmResource.ID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vmResource.StorageProfile.DataDisks = &disks
|
||||||
|
vmResource.Resources = nil
|
||||||
|
|
||||||
|
// update the VM resource, attach disk
|
||||||
|
_, err = da.azcli.VirtualMachinesClient().CreateOrUpdate(ctx, id.ResourceGroup, id.ResourceName, vmResource)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func findDiskInList(list []compute.DataDisk, diskID string) *compute.DataDisk {
|
||||||
|
for _, disk := range list {
|
||||||
|
if disk.ManagedDisk != nil &&
|
||||||
|
strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
|
||||||
|
return &disk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
86
builder/azure/chroot/diskattacher_test.go
Normal file
86
builder/azure/chroot/diskattacher_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testvm = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/Microsoft.Compute/virtualMachines/testVM"
|
||||||
|
testdisk = "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/testGroup2/Microsoft.Compute/disks/testDisk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests assume current machine is capable of running chroot builder (i.e. an Azure VM)
|
||||||
|
|
||||||
|
func Test_DiskAttacherAttachesDiskToVM(t *testing.T) {
|
||||||
|
azcli, err := client.GetTestClientSet(t) // integration test
|
||||||
|
require.Nil(t, err)
|
||||||
|
da := NewDiskAttacher(azcli)
|
||||||
|
testDiskName := t.Name()
|
||||||
|
|
||||||
|
vm, err := azcli.MetadataClient().GetComputeInfo()
|
||||||
|
require.Nil(t, err, "Test needs to run on an Azure VM, unable to retrieve VM information")
|
||||||
|
t.Log("Creating new disk '", testDiskName, "' in ", vm.ResourceGroupName)
|
||||||
|
|
||||||
|
disk, err := azcli.DisksClient().Get(context.TODO(), vm.ResourceGroupName, testDiskName)
|
||||||
|
if err == nil {
|
||||||
|
t.Log("Disk already exists")
|
||||||
|
if disk.DiskState == compute.Attached {
|
||||||
|
t.Log("Disk is attached, assuming to this machine, trying to detach")
|
||||||
|
err = da.DetachDisk(context.TODO(), to.String(disk.ID))
|
||||||
|
require.Nil(t, err)
|
||||||
|
}
|
||||||
|
t.Log("Deleting disk")
|
||||||
|
result, err := azcli.DisksClient().Delete(context.TODO(), vm.ResourceGroupName, testDiskName)
|
||||||
|
require.Nil(t, err)
|
||||||
|
err = result.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
||||||
|
require.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Creating disk")
|
||||||
|
r, err := azcli.DisksClient().CreateOrUpdate(context.TODO(), vm.ResourceGroupName, testDiskName, compute.Disk{
|
||||||
|
Location: to.StringPtr(vm.Location),
|
||||||
|
Sku: &compute.DiskSku{
|
||||||
|
Name: compute.StandardLRS,
|
||||||
|
},
|
||||||
|
DiskProperties: &compute.DiskProperties{
|
||||||
|
DiskSizeGB: to.Int32Ptr(30),
|
||||||
|
CreationData: &compute.CreationData{CreateOption: compute.Empty},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
err = r.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
t.Log("Retrieving disk properties")
|
||||||
|
d, err := azcli.DisksClient().Get(context.TODO(), vm.ResourceGroupName, testDiskName)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.NotNil(t, d)
|
||||||
|
|
||||||
|
t.Log("Attaching disk")
|
||||||
|
lun, err := da.AttachDisk(context.TODO(), to.String(d.ID))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
t.Log("Waiting for device")
|
||||||
|
dev, err := da.WaitForDevice(context.TODO(), lun)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
t.Log("Device path:", dev)
|
||||||
|
|
||||||
|
t.Log("Detaching disk")
|
||||||
|
err = da.DetachDisk(context.TODO(), to.String(d.ID))
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
t.Log("Deleting disk")
|
||||||
|
result, err := azcli.DisksClient().Delete(context.TODO(), vm.ResourceGroupName, testDiskName)
|
||||||
|
if err == nil {
|
||||||
|
err = result.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
}
|
83
builder/azure/chroot/step_attach_disk.go
Normal file
83
builder/azure/chroot/step_attach_disk.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ multistep.Step = &StepAttachDisk{}
|
||||||
|
|
||||||
|
type StepAttachDisk struct {
|
||||||
|
attached bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAttachDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
azcli := state.Get("azureclient").(client.AzureClientSet)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
diskResourceID := state.Get("os_disk_resource_id").(string)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Attaching disk '%s'", diskResourceID))
|
||||||
|
|
||||||
|
da := NewDiskAttacher(azcli)
|
||||||
|
lun, err := da.AttachDisk(ctx, diskResourceID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepAttachDisk.Run: error: %+v", err)
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"error attaching disk '%s': %v", diskResourceID, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Disk attached, waiting for device to show up")
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Minute*3) // in case is not configured correctly
|
||||||
|
defer cancel()
|
||||||
|
device, err := da.WaitForDevice(ctx, lun)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepAttachDisk.Run: error: %+v", err)
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"error attaching disk '%s': %v", diskResourceID, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Disk available at %q", device))
|
||||||
|
s.attached = true
|
||||||
|
state.Put("device", device)
|
||||||
|
state.Put("attach_cleanup", s)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAttachDisk) Cleanup(state multistep.StateBag) {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
if err := s.CleanupFunc(state); err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAttachDisk) CleanupFunc(state multistep.StateBag) error {
|
||||||
|
|
||||||
|
if s.attached {
|
||||||
|
azcli := state.Get("azureclient").(client.AzureClientSet)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
diskResourceID := state.Get("os_disk_resource_id").(string)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Detaching disk '%s'", diskResourceID))
|
||||||
|
|
||||||
|
da := NewDiskAttacher(azcli)
|
||||||
|
err := da.DetachDisk(context.Background(), diskResourceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error detaching %q: %v", diskResourceID, err)
|
||||||
|
}
|
||||||
|
s.attached = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
131
builder/azure/chroot/step_attach_disk_test.go
Normal file
131
builder/azure/chroot/step_attach_disk_test.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStepAttachDisk_Run(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
GetDiskResponseCode int
|
||||||
|
GetDiskResponseBody string
|
||||||
|
|
||||||
|
attachError error
|
||||||
|
waitForDeviceError error
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want multistep.StepAction
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "HappyPath",
|
||||||
|
want: multistep.ActionContinue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AttachError",
|
||||||
|
fields: fields{
|
||||||
|
attachError: errors.New("unit test"),
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WaitForDeviceError",
|
||||||
|
fields: fields{
|
||||||
|
waitForDeviceError: errors.New("unit test"),
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &StepAttachDisk{}
|
||||||
|
|
||||||
|
NewDiskAttacher = func(azcli client.AzureClientSet) DiskAttacher {
|
||||||
|
return &fakeDiskAttacher{
|
||||||
|
attachError: tt.fields.attachError,
|
||||||
|
waitForDeviceError: tt.fields.waitForDeviceError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dm := compute.NewDisksClient("subscriptionId")
|
||||||
|
dm.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
Request: r,
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(tt.fields.GetDiskResponseBody)),
|
||||||
|
StatusCode: tt.fields.GetDiskResponseCode,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
errorBuffer := &strings.Builder{}
|
||||||
|
ui := &packer.BasicUi{
|
||||||
|
Reader: strings.NewReader(""),
|
||||||
|
Writer: ioutil.Discard,
|
||||||
|
ErrorWriter: errorBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("azureclient", &client.AzureClientSetMock{})
|
||||||
|
state.Put("ui", ui)
|
||||||
|
state.Put("os_disk_resource_id", "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1")
|
||||||
|
|
||||||
|
got := s.Run(context.TODO(), state)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("StepAttachDisk.Run() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got == multistep.ActionHalt {
|
||||||
|
if _, ok := state.GetOk("error"); !ok {
|
||||||
|
t.Fatal("Expected 'error' to be set in statebag after failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeDiskAttacher struct {
|
||||||
|
attachError error
|
||||||
|
waitForDeviceError error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DiskAttacher = &fakeDiskAttacher{}
|
||||||
|
|
||||||
|
func (da *fakeDiskAttacher) AttachDisk(ctx context.Context, disk string) (lun int32, err error) {
|
||||||
|
if da.attachError != nil {
|
||||||
|
return 0, da.attachError
|
||||||
|
}
|
||||||
|
return 3, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *fakeDiskAttacher) DiskPathForLun(lun int32) string {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *fakeDiskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
|
||||||
|
if da.waitForDeviceError != nil {
|
||||||
|
return "", da.waitForDeviceError
|
||||||
|
}
|
||||||
|
if lun == 3 {
|
||||||
|
return "/dev/sdq", nil
|
||||||
|
}
|
||||||
|
panic("expected lun==3")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *fakeDiskAttacher) DetachDisk(ctx context.Context, disk string) (err error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *fakeDiskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
89
builder/azure/chroot/step_create_image.go
Normal file
89
builder/azure/chroot/step_create_image.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ multistep.Step = &StepCreateImage{}
|
||||||
|
|
||||||
|
type StepCreateImage struct {
|
||||||
|
ImageResourceID string
|
||||||
|
ImageOSState string
|
||||||
|
OSDiskStorageAccountType string
|
||||||
|
OSDiskCacheType string
|
||||||
|
Location string
|
||||||
|
|
||||||
|
imageResource azure.Resource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
azcli := state.Get("azureclient").(client.AzureClientSet)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
diskResourceID := state.Get("os_disk_resource_id").(string)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Creating image %s\n using %s for os disk.",
|
||||||
|
s.ImageResourceID,
|
||||||
|
diskResourceID))
|
||||||
|
|
||||||
|
var err error
|
||||||
|
s.imageResource, err = azure.ParseResourceID(s.ImageResourceID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepCreateImage.Run: error: %+v", err)
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"error parsing image resource id '%s': %v", s.ImageResourceID, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
image := compute.Image{
|
||||||
|
Location: to.StringPtr(s.Location),
|
||||||
|
ImageProperties: &compute.ImageProperties{
|
||||||
|
StorageProfile: &compute.ImageStorageProfile{
|
||||||
|
OsDisk: &compute.ImageOSDisk{
|
||||||
|
OsState: compute.OperatingSystemStateTypes(s.ImageOSState),
|
||||||
|
OsType: compute.Linux,
|
||||||
|
ManagedDisk: &compute.SubResource{
|
||||||
|
ID: &diskResourceID,
|
||||||
|
},
|
||||||
|
StorageAccountType: compute.StorageAccountTypes(s.OSDiskStorageAccountType),
|
||||||
|
Caching: compute.CachingTypes(s.OSDiskCacheType),
|
||||||
|
},
|
||||||
|
// DataDisks: nil,
|
||||||
|
// ZoneResilient: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Tags: nil,
|
||||||
|
}
|
||||||
|
f, err := azcli.ImagesClient().CreateOrUpdate(
|
||||||
|
ctx,
|
||||||
|
s.imageResource.ResourceGroup,
|
||||||
|
s.imageResource.ResourceName,
|
||||||
|
image)
|
||||||
|
if err == nil {
|
||||||
|
log.Println("Image creation in process...")
|
||||||
|
err = f.WaitForCompletionRef(ctx, azcli.PollClient())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepCreateImage.Run: error: %+v", err)
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"error creating image '%s': %v", s.ImageResourceID, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
log.Printf("Image creation complete: %s", f.Status())
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StepCreateImage) Cleanup(bag multistep.StateBag) {} // this is the final artifact, don't delete
|
110
builder/azure/chroot/step_create_new_disk.go
Normal file
110
builder/azure/chroot/step_create_new_disk.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ multistep.Step = &StepCreateNewDisk{}
|
||||||
|
|
||||||
|
type StepCreateNewDisk struct {
|
||||||
|
SubscriptionID, ResourceGroup, DiskName string
|
||||||
|
DiskSizeGB int32 // optional, ignored if 0
|
||||||
|
DiskStorageAccountType string // from compute.DiskStorageAccountTypes
|
||||||
|
HyperVGeneration string
|
||||||
|
|
||||||
|
Location string
|
||||||
|
PlatformImage *client.PlatformImage
|
||||||
|
|
||||||
|
SourceDiskResourceID string
|
||||||
|
|
||||||
|
SkipCleanup bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StepCreateNewDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
azcli := state.Get("azureclient").(client.AzureClientSet)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
diskResourceID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s",
|
||||||
|
s.SubscriptionID,
|
||||||
|
s.ResourceGroup,
|
||||||
|
s.DiskName)
|
||||||
|
state.Put("os_disk_resource_id", diskResourceID)
|
||||||
|
ui.Say(fmt.Sprintf("Creating disk '%s'", diskResourceID))
|
||||||
|
|
||||||
|
disk := compute.Disk{
|
||||||
|
Location: to.StringPtr(s.Location),
|
||||||
|
Sku: &compute.DiskSku{
|
||||||
|
Name: compute.DiskStorageAccountTypes(s.DiskStorageAccountType),
|
||||||
|
},
|
||||||
|
//Zones: nil,
|
||||||
|
DiskProperties: &compute.DiskProperties{
|
||||||
|
OsType: "Linux",
|
||||||
|
HyperVGeneration: compute.HyperVGeneration(s.HyperVGeneration),
|
||||||
|
CreationData: &compute.CreationData{},
|
||||||
|
},
|
||||||
|
//Tags: map[string]*string{
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.DiskSizeGB > 0 {
|
||||||
|
disk.DiskProperties.DiskSizeGB = to.Int32Ptr(s.DiskSizeGB)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.SourceDiskResourceID != "" {
|
||||||
|
disk.CreationData.CreateOption = compute.Copy
|
||||||
|
disk.CreationData.SourceResourceID = to.StringPtr(s.SourceDiskResourceID)
|
||||||
|
} else if s.PlatformImage == nil {
|
||||||
|
disk.CreationData.CreateOption = compute.Empty
|
||||||
|
} else {
|
||||||
|
disk.CreationData.CreateOption = compute.FromImage
|
||||||
|
disk.CreationData.ImageReference = &compute.ImageDiskReference{
|
||||||
|
ID: to.StringPtr(fmt.Sprintf(
|
||||||
|
"/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s",
|
||||||
|
s.SubscriptionID, s.Location, s.PlatformImage.Publisher, s.PlatformImage.Offer, s.PlatformImage.Sku, s.PlatformImage.Version)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := azcli.DisksClient().CreateOrUpdate(ctx, s.ResourceGroup, s.DiskName, disk)
|
||||||
|
if err == nil {
|
||||||
|
err = f.WaitForCompletionRef(ctx, azcli.PollClient())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepCreateNewDisk.Run: error: %+v", err)
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"error creating new disk '%s': %v", diskResourceID, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StepCreateNewDisk) Cleanup(state multistep.StateBag) {
|
||||||
|
if !s.SkipCleanup {
|
||||||
|
azcli := state.Get("azureclient").(client.AzureClientSet)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
diskResourceID := state.Get("os_disk_resource_id").(string)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Waiting for disk %q detach to complete", diskResourceID))
|
||||||
|
err := NewDiskAttacher(azcli).WaitForDetach(context.Background(), diskResourceID)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Deleting disk %q", diskResourceID))
|
||||||
|
|
||||||
|
f, err := azcli.DisksClient().Delete(context.TODO(), s.ResourceGroup, s.DiskName)
|
||||||
|
if err == nil {
|
||||||
|
err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepCreateNewDisk.Cleanup: error: %+v", err)
|
||||||
|
ui.Error(fmt.Sprintf("error deleting new disk '%s': %v.", diskResourceID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
builder/azure/chroot/step_create_new_disk_test.go
Normal file
147
builder/azure/chroot/step_create_new_disk_test.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStepCreateNewDisk_Run(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
SubscriptionID string
|
||||||
|
ResourceGroup string
|
||||||
|
DiskName string
|
||||||
|
DiskSizeGB int32
|
||||||
|
DiskStorageAccountType string
|
||||||
|
HyperVGeneration string
|
||||||
|
Location string
|
||||||
|
PlatformImage *client.PlatformImage
|
||||||
|
SourceDiskResourceID string
|
||||||
|
|
||||||
|
expectedPutDiskBody string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want multistep.StepAction
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "HappyPathDiskSource",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "SubscriptionID",
|
||||||
|
ResourceGroup: "ResourceGroupName",
|
||||||
|
DiskName: "TemporaryOSDiskName",
|
||||||
|
DiskSizeGB: 42,
|
||||||
|
DiskStorageAccountType: string(compute.PremiumLRS),
|
||||||
|
HyperVGeneration: string(compute.V1),
|
||||||
|
Location: "westus",
|
||||||
|
SourceDiskResourceID: "SourceDisk",
|
||||||
|
|
||||||
|
expectedPutDiskBody: `
|
||||||
|
{
|
||||||
|
"location": "westus",
|
||||||
|
"properties": {
|
||||||
|
"osType": "Linux",
|
||||||
|
"hyperVGeneration": "V1",
|
||||||
|
"creationData": {
|
||||||
|
"createOption": "Copy",
|
||||||
|
"sourceResourceId": "SourceDisk"
|
||||||
|
},
|
||||||
|
"diskSizeGB": 42
|
||||||
|
},
|
||||||
|
"sku": {
|
||||||
|
"name": "Premium_LRS"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionContinue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HappyPathDiskSource",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "SubscriptionID",
|
||||||
|
ResourceGroup: "ResourceGroupName",
|
||||||
|
DiskName: "TemporaryOSDiskName",
|
||||||
|
DiskStorageAccountType: string(compute.StandardLRS),
|
||||||
|
HyperVGeneration: string(compute.V1),
|
||||||
|
Location: "westus",
|
||||||
|
PlatformImage: &client.PlatformImage{
|
||||||
|
Publisher: "Microsoft",
|
||||||
|
Offer: "Windows",
|
||||||
|
Sku: "2016-DataCenter",
|
||||||
|
Version: "2016.1.4",
|
||||||
|
},
|
||||||
|
|
||||||
|
expectedPutDiskBody: `
|
||||||
|
{
|
||||||
|
"location": "westus",
|
||||||
|
"properties": {
|
||||||
|
"osType": "Linux",
|
||||||
|
"hyperVGeneration": "V1",
|
||||||
|
"creationData": {
|
||||||
|
"createOption":"FromImage",
|
||||||
|
"imageReference": {
|
||||||
|
"id":"/subscriptions/SubscriptionID/providers/Microsoft.Compute/locations/westus/publishers/Microsoft/artifacttypes/vmimage/offers/Windows/skus/2016-DataCenter/versions/2016.1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sku": {
|
||||||
|
"name": "Standard_LRS"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionContinue,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := StepCreateNewDisk{
|
||||||
|
SubscriptionID: tt.fields.SubscriptionID,
|
||||||
|
ResourceGroup: tt.fields.ResourceGroup,
|
||||||
|
DiskName: tt.fields.DiskName,
|
||||||
|
DiskSizeGB: tt.fields.DiskSizeGB,
|
||||||
|
DiskStorageAccountType: tt.fields.DiskStorageAccountType,
|
||||||
|
HyperVGeneration: tt.fields.HyperVGeneration,
|
||||||
|
Location: tt.fields.Location,
|
||||||
|
PlatformImage: tt.fields.PlatformImage,
|
||||||
|
SourceDiskResourceID: tt.fields.SourceDiskResourceID,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedPutDiskBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.fields.expectedPutDiskBody, "")
|
||||||
|
|
||||||
|
m := compute.NewDisksClient("subscriptionId")
|
||||||
|
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
if r.Method != "PUT" {
|
||||||
|
t.Fatal("Expected only a PUT disk call")
|
||||||
|
}
|
||||||
|
b, _ := ioutil.ReadAll(r.Body)
|
||||||
|
if string(b) != expectedPutDiskBody {
|
||||||
|
t.Fatalf("expected body to be %q, but got %q", expectedPutDiskBody, string(b))
|
||||||
|
}
|
||||||
|
return &http.Response{
|
||||||
|
Request: r,
|
||||||
|
StatusCode: 200,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("azureclient", &client.AzureClientSetMock{
|
||||||
|
DisksClientMock: m,
|
||||||
|
})
|
||||||
|
state.Put("ui", packer.TestUi(t))
|
||||||
|
|
||||||
|
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("StepCreateNewDisk.Run() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
132
builder/azure/chroot/step_mount_device.go
Normal file
132
builder/azure/chroot/step_mount_device.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
// mostly borrowed from ./builder/amazon/chroot/step_mount_device.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/chroot"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ multistep.Step = &StepMountDevice{}
|
||||||
|
|
||||||
|
type StepMountDevice struct {
|
||||||
|
MountOptions []string
|
||||||
|
MountPartition string
|
||||||
|
MountPath string
|
||||||
|
|
||||||
|
mountPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
device := state.Get("device").(string)
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
wrappedCommand := state.Get("wrappedCommand").(chroot.CommandWrapper)
|
||||||
|
|
||||||
|
ictx := config.ctx
|
||||||
|
|
||||||
|
ictx.Data = &struct{ Device string }{Device: filepath.Base(device)}
|
||||||
|
mountPath, err := interpolate.Render(s.MountPath, &ictx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error preparing mount directory: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPath, err = filepath.Abs(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error preparing mount directory: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Mount path: %s", mountPath)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
||||||
|
err := fmt.Errorf("error creating mount directory: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceMount := fmt.Sprintf("%s%s", device, s.MountPartition)
|
||||||
|
|
||||||
|
state.Put("deviceMount", deviceMount)
|
||||||
|
|
||||||
|
ui.Say("Mounting the root device...")
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// build mount options from mount_options config, useful for nouuid options
|
||||||
|
// or other specific device type settings for mount
|
||||||
|
opts := ""
|
||||||
|
if len(s.MountOptions) > 0 {
|
||||||
|
opts = "-o " + strings.Join(s.MountOptions, " -o ")
|
||||||
|
}
|
||||||
|
mountCommand, err := wrappedCommand(
|
||||||
|
fmt.Sprintf("mount %s %s %s", opts, deviceMount, mountPath))
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error creating mount command: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] (step mount) mount command is %s", mountCommand)
|
||||||
|
cmd := chroot.ShellCommand(mountCommand)
|
||||||
|
cmd.Stderr = stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
err := fmt.Errorf(
|
||||||
|
"error mounting root volume: %s\nStderr: %s", err, stderr.String())
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the mount path so we remember to unmount it later
|
||||||
|
s.mountPath = mountPath
|
||||||
|
state.Put("mount_path", s.mountPath)
|
||||||
|
state.Put("mount_device_cleanup", s)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMountDevice) Cleanup(state multistep.StateBag) {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
if err := s.CleanupFunc(state); err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error {
|
||||||
|
if s.mountPath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
wrappedCommand := state.Get("wrappedCommand").(chroot.CommandWrapper)
|
||||||
|
|
||||||
|
ui.Say("Unmounting the root device...")
|
||||||
|
unmountCommand, err := wrappedCommand(fmt.Sprintf("umount %s", s.mountPath))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating unmount command: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := chroot.ShellCommand(unmountCommand)
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("error unmounting root device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mountPath = ""
|
||||||
|
return nil
|
||||||
|
}
|
81
builder/azure/chroot/step_verify_source_disk.go
Normal file
81
builder/azure/chroot/step_verify_source_disk.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepVerifySourceDisk struct {
|
||||||
|
SubscriptionID string
|
||||||
|
SourceDiskResourceID string
|
||||||
|
Location string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
azcli := state.Get("azureclient").(client.AzureClientSet)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
ui.Say("Checking source disk location")
|
||||||
|
resource, err := azure.ParseResourceID(s.SourceDiskResourceID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StepVerifySourceDisk.Run: error: %+v", err)
|
||||||
|
err := fmt.Errorf("Could not parse resource id %q: %s", s.SourceDiskResourceID, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(resource.SubscriptionID, s.SubscriptionID) {
|
||||||
|
err := fmt.Errorf("Source disk resource %q is in a different subscription than this VM (%q). "+
|
||||||
|
"Packer does not know how to handle that.",
|
||||||
|
s.SourceDiskResourceID, s.SubscriptionID)
|
||||||
|
log.Printf("StepVerifySourceDisk.Run: error: %+v", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(strings.EqualFold(resource.Provider, "Microsoft.Compute") && strings.EqualFold(resource.ResourceType, "disks")) {
|
||||||
|
err := fmt.Errorf("Resource ID %q is not a managed disk resource", s.SourceDiskResourceID)
|
||||||
|
log.Printf("StepVerifySourceDisk.Run: error: %+v", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
disk, err := azcli.DisksClient().Get(ctx,
|
||||||
|
resource.ResourceGroup, resource.ResourceName)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Unable to retrieve disk (%q): %s", s.SourceDiskResourceID, err)
|
||||||
|
log.Printf("StepVerifySourceDisk.Run: error: %+v", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
location := to.String(disk.Location)
|
||||||
|
if !strings.EqualFold(location, s.Location) {
|
||||||
|
err := fmt.Errorf("Source disk resource %q is in a different location (%q) than this VM (%q). "+
|
||||||
|
"Packer does not know how to handle that.",
|
||||||
|
s.SourceDiskResourceID,
|
||||||
|
location,
|
||||||
|
s.Location)
|
||||||
|
log.Printf("StepVerifySourceDisk.Run: error: %+v", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StepVerifySourceDisk) Cleanup(state multistep.StateBag) {}
|
168
builder/azure/chroot/step_verify_source_disk_test.go
Normal file
168
builder/azure/chroot/step_verify_source_disk_test.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_StepVerifySourceDisk_Run(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
SubscriptionID string
|
||||||
|
SourceDiskResourceID string
|
||||||
|
Location string
|
||||||
|
|
||||||
|
GetDiskResponseCode int
|
||||||
|
GetDiskResponseBody string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
state multistep.StateBag
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want multistep.StepAction
|
||||||
|
errormatch string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "HappyPath",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "subid1",
|
||||||
|
SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1",
|
||||||
|
Location: "westus2",
|
||||||
|
|
||||||
|
GetDiskResponseCode: 200,
|
||||||
|
GetDiskResponseBody: `{"location":"westus2"}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionContinue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NotAResourceID",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "subid1",
|
||||||
|
SourceDiskResourceID: "/other",
|
||||||
|
Location: "westus2",
|
||||||
|
|
||||||
|
GetDiskResponseCode: 200,
|
||||||
|
GetDiskResponseBody: `{"location":"westus2"}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
errormatch: "Could not parse resource id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DiskNotFound",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "subid1",
|
||||||
|
SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1",
|
||||||
|
Location: "westus2",
|
||||||
|
|
||||||
|
GetDiskResponseCode: 404,
|
||||||
|
GetDiskResponseBody: `{}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
errormatch: "Unable to retrieve",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NotADisk",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "subid1",
|
||||||
|
SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/images/image1",
|
||||||
|
Location: "westus2",
|
||||||
|
|
||||||
|
GetDiskResponseCode: 404,
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
errormatch: "not a managed disk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OtherSubscription",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "subid1",
|
||||||
|
SourceDiskResourceID: "/subscriptions/subid2/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1",
|
||||||
|
Location: "westus2",
|
||||||
|
|
||||||
|
GetDiskResponseCode: 200,
|
||||||
|
GetDiskResponseBody: `{"location":"westus2"}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
errormatch: "different subscription",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OtherLocation",
|
||||||
|
fields: fields{
|
||||||
|
SubscriptionID: "subid1",
|
||||||
|
SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1",
|
||||||
|
Location: "eastus",
|
||||||
|
|
||||||
|
GetDiskResponseCode: 200,
|
||||||
|
GetDiskResponseBody: `{"location":"westus2"}`,
|
||||||
|
},
|
||||||
|
want: multistep.ActionHalt,
|
||||||
|
errormatch: "different location",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := StepVerifySourceDisk{
|
||||||
|
SubscriptionID: tt.fields.SubscriptionID,
|
||||||
|
SourceDiskResourceID: tt.fields.SourceDiskResourceID,
|
||||||
|
Location: tt.fields.Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
m := compute.NewDisksClient("subscriptionId")
|
||||||
|
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
Request: r,
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(tt.fields.GetDiskResponseBody)),
|
||||||
|
StatusCode: tt.fields.GetDiskResponseCode,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
errorBuffer := &strings.Builder{}
|
||||||
|
ui := &packer.BasicUi{
|
||||||
|
Reader: strings.NewReader(""),
|
||||||
|
Writer: ioutil.Discard,
|
||||||
|
ErrorWriter: errorBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("azureclient", &client.AzureClientSetMock{
|
||||||
|
DisksClientMock: m,
|
||||||
|
})
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
got := s.Run(context.TODO(), state)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("StepVerifySourceDisk.Run() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.errormatch != "" {
|
||||||
|
if !regexp.MustCompile(tt.errormatch).MatchString(errorBuffer.String()) {
|
||||||
|
t.Errorf("Expected the error output (%q) to match %q", errorBuffer.String(), tt.errormatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if got == multistep.ActionHalt {
|
||||||
|
if _, ok := state.GetOk("error"); !ok {
|
||||||
|
t.Fatal("Expected 'error' to be set in statebag after failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type uiThatRemebersErrors struct {
|
||||||
|
packer.Ui
|
||||||
|
LastError string
|
||||||
|
}
|
37
builder/azure/chroot/template_funcs.go
Normal file
37
builder/azure/chroot/template_funcs.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package chroot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateVMMetadataTemplateFunc returns a template function that retrieves VM metadata. VM metadata is retrieved only once and reused for all executions of the function.
|
||||||
|
func CreateVMMetadataTemplateFunc() func(string) (string, error) {
|
||||||
|
var data *client.ComputeInfo
|
||||||
|
var dataErr error
|
||||||
|
once := sync.Once{}
|
||||||
|
return func(key string) (string, error) {
|
||||||
|
once.Do(func() {
|
||||||
|
data, dataErr = client.DefaultMetadataClient.GetComputeInfo()
|
||||||
|
})
|
||||||
|
if dataErr != nil {
|
||||||
|
return "", dataErr
|
||||||
|
}
|
||||||
|
switch key {
|
||||||
|
case "name":
|
||||||
|
return data.Name, nil
|
||||||
|
case "subscription_id":
|
||||||
|
return data.SubscriptionID, nil
|
||||||
|
case "resource_group":
|
||||||
|
return data.ResourceGroupName, nil
|
||||||
|
case "location":
|
||||||
|
return data.Location, nil
|
||||||
|
case "resource_id":
|
||||||
|
return data.ResourceID(), nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unknown metadata key: %s (supported: name, subscription_id, resource_group, location, resource_id)", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
103
builder/azure/common/artifact.go
Normal file
103
builder/azure/common/artifact.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
"github.com/hashicorp/packer/builder/azure/common/client"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Artifact is an artifact implementation that contains built Managed Images or Disks.
|
||||||
|
type Artifact struct {
|
||||||
|
// Array of the Azure resource IDs that were created.
|
||||||
|
Resources []string
|
||||||
|
|
||||||
|
// BuilderId is the unique ID for the builder that created this AMI
|
||||||
|
BuilderIdValue string
|
||||||
|
|
||||||
|
// Azure client for performing API stuff.
|
||||||
|
AzureClientSet client.AzureClientSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) BuilderId() string {
|
||||||
|
return a.BuilderIdValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Artifact) Files() []string {
|
||||||
|
// We have no files
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Id() string {
|
||||||
|
parts := make([]string, 0, len(a.Resources))
|
||||||
|
for _, resource := range a.Resources {
|
||||||
|
parts = append(parts, strings.ToLower(resource))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(parts)
|
||||||
|
return strings.Join(parts, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) String() string {
|
||||||
|
parts := make([]string, 0, len(a.Resources))
|
||||||
|
for _, resource := range a.Resources {
|
||||||
|
parts = append(parts, strings.ToLower(resource))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(parts)
|
||||||
|
return fmt.Sprintf("Azure resources created:\n%s\n", strings.Join(parts, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) State(name string) interface{} {
|
||||||
|
switch name {
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Destroy() error {
|
||||||
|
errs := make([]error, 0)
|
||||||
|
|
||||||
|
for _, resource := range a.Resources {
|
||||||
|
log.Printf("Deleting resource %s", resource)
|
||||||
|
|
||||||
|
id, err := azure.ParseResourceID(resource)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to parse resource id (%s): %v", resource, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
restype := strings.ToLower(fmt.Sprintf("%s/%s", id.Provider, id.ResourceType))
|
||||||
|
|
||||||
|
switch restype {
|
||||||
|
case "microsoft.compute/images":
|
||||||
|
res, err := a.AzureClientSet.ImagesClient().Delete(ctx, id.ResourceGroup, id.ResourceName)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Unable to initiate deletion of resource (%s): %v", resource, err))
|
||||||
|
} else {
|
||||||
|
err := res.WaitForCompletionRef(ctx, a.AzureClientSet.PollClient())
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Unable to complete deletion of resource (%s): %v", resource, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
errs = append(errs, fmt.Errorf("Don't know how to delete resources of type %s (%s)", resource, restype))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
if len(errs) == 1 {
|
||||||
|
return errs[0]
|
||||||
|
} else {
|
||||||
|
return &packer.MultiError{Errors: errs}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
90
builder/azure/common/client/azure_client_set.go
Normal file
90
builder/azure/common/client/azure_client_set.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute/computeapi"
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AzureClientSet interface {
|
||||||
|
MetadataClient() MetadataClientAPI
|
||||||
|
|
||||||
|
DisksClient() computeapi.DisksClientAPI
|
||||||
|
ImagesClient() computeapi.ImagesClientAPI
|
||||||
|
VirtualMachinesClient() computeapi.VirtualMachinesClientAPI
|
||||||
|
VirtualMachineImagesClient() VirtualMachineImagesClientAPI
|
||||||
|
|
||||||
|
PollClient() autorest.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var subscriptionPathRegex = regexp.MustCompile(`/subscriptions/([[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12})`)
|
||||||
|
|
||||||
|
var _ AzureClientSet = &azureClientSet{}
|
||||||
|
|
||||||
|
type azureClientSet struct {
|
||||||
|
sender autorest.Sender
|
||||||
|
authorizer autorest.Authorizer
|
||||||
|
subscriptionID string
|
||||||
|
PollingDelay time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(c Config, say func(string)) (AzureClientSet, error) {
|
||||||
|
token, err := c.GetServicePrincipalToken(say, c.CloudEnvironment().ResourceManagerEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &azureClientSet{
|
||||||
|
authorizer: autorest.NewBearerAuthorizer(token),
|
||||||
|
subscriptionID: c.SubscriptionID,
|
||||||
|
sender: http.DefaultClient,
|
||||||
|
PollingDelay: time.Second,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) configureAutorestClient(c *autorest.Client) {
|
||||||
|
c.Authorizer = s.authorizer
|
||||||
|
c.Sender = s.sender
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) MetadataClient() MetadataClientAPI {
|
||||||
|
return metadataClient{s.sender}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) DisksClient() computeapi.DisksClientAPI {
|
||||||
|
c := compute.NewDisksClient(s.subscriptionID)
|
||||||
|
s.configureAutorestClient(&c.Client)
|
||||||
|
c.PollingDelay = s.PollingDelay
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) ImagesClient() computeapi.ImagesClientAPI {
|
||||||
|
c := compute.NewImagesClient(s.subscriptionID)
|
||||||
|
s.configureAutorestClient(&c.Client)
|
||||||
|
c.PollingDelay = s.PollingDelay
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) VirtualMachinesClient() computeapi.VirtualMachinesClientAPI {
|
||||||
|
c := compute.NewVirtualMachinesClient(s.subscriptionID)
|
||||||
|
s.configureAutorestClient(&c.Client)
|
||||||
|
c.PollingDelay = s.PollingDelay
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) VirtualMachineImagesClient() VirtualMachineImagesClientAPI {
|
||||||
|
c := compute.NewVirtualMachineImagesClient(s.subscriptionID)
|
||||||
|
s.configureAutorestClient(&c.Client)
|
||||||
|
c.PollingDelay = s.PollingDelay
|
||||||
|
return virtualMachineImagesClientAPI{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s azureClientSet) PollClient() autorest.Client {
|
||||||
|
c := autorest.NewClientWithUserAgent("Packer-Azure-ClientSet")
|
||||||
|
s.configureAutorestClient(&c)
|
||||||
|
c.PollingDelay = time.Second / 3
|
||||||
|
return c
|
||||||
|
}
|
46
builder/azure/common/client/azure_client_set_mock.go
Normal file
46
builder/azure/common/client/azure_client_set_mock.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute/computeapi"
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AzureClientSetMock provides a generic mock for AzureClientSet
|
||||||
|
type AzureClientSetMock struct {
|
||||||
|
DisksClientMock computeapi.DisksClientAPI
|
||||||
|
ImagesClientMock computeapi.ImagesClientAPI
|
||||||
|
VirtualMachineImagesClientMock VirtualMachineImagesClientAPI
|
||||||
|
VirtualMachinesClientMock computeapi.VirtualMachinesClientAPI
|
||||||
|
PollClientMock autorest.Client
|
||||||
|
MetadataClientMock MetadataClientAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisksClient returns a DisksClientAPI
|
||||||
|
func (m *AzureClientSetMock) DisksClient() computeapi.DisksClientAPI {
|
||||||
|
return m.DisksClientMock
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagesClient returns a ImagesClientAPI
|
||||||
|
func (m *AzureClientSetMock) ImagesClient() computeapi.ImagesClientAPI {
|
||||||
|
return m.ImagesClientMock
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualMachineImagesClient returns a VirtualMachineImagesClientAPI
|
||||||
|
func (m *AzureClientSetMock) VirtualMachineImagesClient() VirtualMachineImagesClientAPI {
|
||||||
|
return m.VirtualMachineImagesClientMock
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualMachinesClient returns a VirtualMachinesClientAPI
|
||||||
|
func (m *AzureClientSetMock) VirtualMachinesClient() computeapi.VirtualMachinesClientAPI {
|
||||||
|
return m.VirtualMachinesClientMock
|
||||||
|
}
|
||||||
|
|
||||||
|
// PollClient returns an autorest Client that can be used for polling async requests
|
||||||
|
func (m *AzureClientSetMock) PollClient() autorest.Client {
|
||||||
|
return m.PollClientMock
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataClient returns a MetadataClientAPI
|
||||||
|
func (m *AzureClientSetMock) MetadataClient() MetadataClientAPI {
|
||||||
|
return m.MetadataClientMock
|
||||||
|
}
|
@ -4,7 +4,6 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -28,7 +27,7 @@ type Config struct {
|
|||||||
// USGovernment. Defaults to Public. Long forms such as
|
// USGovernment. Defaults to Public. Long forms such as
|
||||||
// USGovernmentCloud and AzureUSGovernmentCloud are also supported.
|
// USGovernmentCloud and AzureUSGovernmentCloud are also supported.
|
||||||
CloudEnvironmentName string `mapstructure:"cloud_environment_name" required:"false"`
|
CloudEnvironmentName string `mapstructure:"cloud_environment_name" required:"false"`
|
||||||
CloudEnvironment *azure.Environment
|
cloudEnvironment *azure.Environment
|
||||||
|
|
||||||
// Authentication fields
|
// Authentication fields
|
||||||
|
|
||||||
@ -73,6 +72,10 @@ func (c *Config) SetDefaultValues() error {
|
|||||||
return c.setCloudEnvironment()
|
return c.setCloudEnvironment()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) CloudEnvironment() *azure.Environment {
|
||||||
|
return c.cloudEnvironment
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) setCloudEnvironment() error {
|
func (c *Config) setCloudEnvironment() error {
|
||||||
lookup := map[string]string{
|
lookup := map[string]string{
|
||||||
"CHINA": "AzureChinaCloud",
|
"CHINA": "AzureChinaCloud",
|
||||||
@ -103,7 +106,7 @@ func (c *Config) setCloudEnvironment() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
env, err := azure.EnvironmentFromName(envName)
|
env, err := azure.EnvironmentFromName(envName)
|
||||||
c.CloudEnvironment = &env
|
c.cloudEnvironment = &env
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,60 +201,64 @@ func (c Config) UseMSI() bool {
|
|||||||
c.TenantID == ""
|
c.TenantID == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) GetServicePrincipalTokens(
|
func (c Config) GetServicePrincipalTokens(say func(string)) (
|
||||||
say func(string)) (
|
|
||||||
servicePrincipalToken *adal.ServicePrincipalToken,
|
servicePrincipalToken *adal.ServicePrincipalToken,
|
||||||
servicePrincipalTokenVault *adal.ServicePrincipalToken,
|
servicePrincipalTokenVault *adal.ServicePrincipalToken,
|
||||||
err error) {
|
err error) {
|
||||||
|
|
||||||
tenantID := c.TenantID
|
servicePrincipalToken, err = c.GetServicePrincipalToken(say,
|
||||||
|
c.CloudEnvironment().ResourceManagerEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
servicePrincipalTokenVault, err = c.GetServicePrincipalToken(say,
|
||||||
|
strings.TrimRight(c.CloudEnvironment().KeyVaultEndpoint, "/"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return servicePrincipalToken, servicePrincipalTokenVault, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) GetServicePrincipalToken(
|
||||||
|
say func(string), forResource string) (
|
||||||
|
servicePrincipalToken *adal.ServicePrincipalToken,
|
||||||
|
err error) {
|
||||||
|
|
||||||
var auth oAuthTokenProvider
|
var auth oAuthTokenProvider
|
||||||
switch c.authType {
|
switch c.authType {
|
||||||
case authTypeDeviceLogin:
|
case authTypeDeviceLogin:
|
||||||
say("Getting tokens using device flow")
|
say("Getting tokens using device flow")
|
||||||
auth = NewDeviceFlowOAuthTokenProvider(*c.CloudEnvironment, say, tenantID)
|
auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID)
|
||||||
case authTypeMSI:
|
case authTypeMSI:
|
||||||
say("Getting tokens using Managed Identity for Azure")
|
say("Getting tokens using Managed Identity for Azure")
|
||||||
auth = NewMSIOAuthTokenProvider(*c.CloudEnvironment)
|
auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment)
|
||||||
case authTypeClientSecret:
|
case authTypeClientSecret:
|
||||||
say("Getting tokens using client secret")
|
say("Getting tokens using client secret")
|
||||||
auth = NewSecretOAuthTokenProvider(*c.CloudEnvironment, c.ClientID, c.ClientSecret, tenantID)
|
auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID)
|
||||||
case authTypeClientCert:
|
case authTypeClientCert:
|
||||||
say("Getting tokens using client certificate")
|
say("Getting tokens using client certificate")
|
||||||
auth, err = NewCertOAuthTokenProvider(*c.CloudEnvironment, c.ClientID, c.ClientCertPath, tenantID)
|
auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, c.TenantID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case authTypeClientBearerJWT:
|
case authTypeClientBearerJWT:
|
||||||
say("Getting tokens using client bearer JWT")
|
say("Getting tokens using client bearer JWT")
|
||||||
auth = NewJWTOAuthTokenProvider(*c.CloudEnvironment, c.ClientID, c.ClientJWT, tenantID)
|
auth = NewJWTOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientJWT, c.TenantID)
|
||||||
default:
|
default:
|
||||||
panic("authType not set, call FillParameters, or set explicitly")
|
panic("authType not set, call FillParameters, or set explicitly")
|
||||||
}
|
}
|
||||||
|
|
||||||
servicePrincipalToken, err = auth.getServicePrincipalToken()
|
servicePrincipalToken, err = auth.getServicePrincipalTokenWithResource(forResource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = servicePrincipalToken.EnsureFresh()
|
err = servicePrincipalToken.EnsureFresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
servicePrincipalTokenVault, err = auth.getServicePrincipalTokenWithResource(
|
return servicePrincipalToken, nil
|
||||||
strings.TrimRight(c.CloudEnvironment.KeyVaultEndpoint, "/"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = servicePrincipalTokenVault.EnsureFresh()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return servicePrincipalToken, servicePrincipalTokenVault, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillParameters capture the user intent from the supplied parameter set in authType, retrieves the TenantID and CloudEnvironment if not specified.
|
// FillParameters capture the user intent from the supplied parameter set in authType, retrieves the TenantID and CloudEnvironment if not specified.
|
||||||
@ -280,7 +287,7 @@ func (c *Config) FillParameters() error {
|
|||||||
c.SubscriptionID = subscriptionID
|
c.SubscriptionID = subscriptionID
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.CloudEnvironment == nil {
|
if c.cloudEnvironment == nil {
|
||||||
err := c.setCloudEnvironment()
|
err := c.setCloudEnvironment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -288,7 +295,7 @@ func (c *Config) FillParameters() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.TenantID == "" {
|
if c.TenantID == "" {
|
||||||
tenantID, err := findTenantID(*c.CloudEnvironment, c.SubscriptionID)
|
tenantID, err := findTenantID(*c.cloudEnvironment, c.SubscriptionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -299,4 +306,4 @@ func (c *Config) FillParameters() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow override for unit tests
|
// allow override for unit tests
|
||||||
var findTenantID = common.FindTenantID
|
var findTenantID = FindTenantID
|
||||||
|
@ -133,7 +133,7 @@ func Test_ClientConfig_DeviceLogin(t *testing.T) {
|
|||||||
getEnvOrSkip(t, "AZURE_DEVICE_LOGIN")
|
getEnvOrSkip(t, "AZURE_DEVICE_LOGIN")
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
|
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
|
||||||
CloudEnvironment: getCloud(),
|
cloudEnvironment: getCloud(),
|
||||||
}
|
}
|
||||||
assertValid(t, cfg)
|
assertValid(t, cfg)
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ func Test_ClientConfig_ClientPassword(t *testing.T) {
|
|||||||
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
|
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
|
||||||
ClientSecret: getEnvOrSkip(t, "AZURE_CLIENTSECRET"),
|
ClientSecret: getEnvOrSkip(t, "AZURE_CLIENTSECRET"),
|
||||||
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
|
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
|
||||||
CloudEnvironment: getCloud(),
|
cloudEnvironment: getCloud(),
|
||||||
}
|
}
|
||||||
assertValid(t, cfg)
|
assertValid(t, cfg)
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ func Test_ClientConfig_ClientCert(t *testing.T) {
|
|||||||
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
|
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
|
||||||
ClientCertPath: getEnvOrSkip(t, "AZURE_CLIENTCERT"),
|
ClientCertPath: getEnvOrSkip(t, "AZURE_CLIENTCERT"),
|
||||||
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
|
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
|
||||||
CloudEnvironment: getCloud(),
|
cloudEnvironment: getCloud(),
|
||||||
}
|
}
|
||||||
assertValid(t, cfg)
|
assertValid(t, cfg)
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ func Test_ClientConfig_ClientJWT(t *testing.T) {
|
|||||||
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
|
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
|
||||||
ClientJWT: getEnvOrSkip(t, "AZURE_CLIENTJWT"),
|
ClientJWT: getEnvOrSkip(t, "AZURE_CLIENTJWT"),
|
||||||
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
|
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
|
||||||
CloudEnvironment: getCloud(),
|
cloudEnvironment: getCloud(),
|
||||||
}
|
}
|
||||||
assertValid(t, cfg)
|
assertValid(t, cfg)
|
||||||
|
|
||||||
|
8
builder/azure/common/client/detect_azure.go
Normal file
8
builder/azure/common/client/detect_azure.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
// IsAzure returns true if Packer is running on Azure (currently only works on Linux)
|
||||||
|
func IsAzure() bool {
|
||||||
|
return false
|
||||||
|
}
|
23
builder/azure/common/client/detect_azure_linux.go
Normal file
23
builder/azure/common/client/detect_azure_linux.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
smbiosAssetTagFile = "/sys/class/dmi/id/chassis_asset_tag"
|
||||||
|
azureAssetTag = []byte("7783-7084-3265-9085-8269-3286-77\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsAzure returns true if Packer is running on Azure
|
||||||
|
func IsAzure() bool {
|
||||||
|
return isAzureAssetTag(smbiosAssetTagFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAzureAssetTag(filename string) bool {
|
||||||
|
if d, err := ioutil.ReadFile(filename); err == nil {
|
||||||
|
return bytes.Compare(d, azureAssetTag) == 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
29
builder/azure/common/client/detect_azure_linux_test.go
Normal file
29
builder/azure/common/client/detect_azure_linux_test.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsAzure(t *testing.T) {
|
||||||
|
f, err := ioutil.TempFile("", "TestIsAzure*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
f.Seek(0, 0)
|
||||||
|
f.Truncate(0)
|
||||||
|
f.Write([]byte("not the azure assettag"))
|
||||||
|
|
||||||
|
assert.False(t, isAzureAssetTag(f.Name()), "asset tag is not Azure's")
|
||||||
|
|
||||||
|
f.Seek(0, 0)
|
||||||
|
f.Truncate(0)
|
||||||
|
f.Write(azureAssetTag)
|
||||||
|
|
||||||
|
assert.True(t, isAzureAssetTag(f.Name()), "asset tag is Azure's")
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package common
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
81
builder/azure/common/client/metadata.go
Normal file
81
builder/azure/common/client/metadata.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultMetadataClient is the default instance metadata client for Azure. Replace this variable for testing purposes only
|
||||||
|
var DefaultMetadataClient = NewMetadataClient()
|
||||||
|
|
||||||
|
// MetadataClient holds methods that Packer uses to get information about the current VM
|
||||||
|
type MetadataClientAPI interface {
|
||||||
|
GetComputeInfo() (*ComputeInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComputeInfo struct {
|
||||||
|
Name string
|
||||||
|
ResourceGroupName string
|
||||||
|
SubscriptionID string
|
||||||
|
Location string
|
||||||
|
}
|
||||||
|
|
||||||
|
// metadataClient implements MetadataClient
|
||||||
|
type metadataClient struct {
|
||||||
|
autorest.Sender
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ MetadataClientAPI = metadataClient{}
|
||||||
|
|
||||||
|
const imdsURL = "http://169.254.169.254/metadata/instance?api-version=2017-08-01"
|
||||||
|
|
||||||
|
// VMResourceID returns the resource ID of the current VM
|
||||||
|
func (client metadataClient) GetComputeInfo() (*ComputeInfo, error) {
|
||||||
|
req, err := autorest.CreatePreparer(
|
||||||
|
autorest.AsGet(),
|
||||||
|
autorest.WithHeader("Metadata", "true"),
|
||||||
|
autorest.WithBaseURL(imdsURL),
|
||||||
|
).Prepare((&http.Request{}))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := autorest.SendWithSender(client, req,
|
||||||
|
autorest.DoRetryForDuration(1*time.Minute, 5*time.Second))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vminfo struct {
|
||||||
|
ComputeInfo `json:"compute"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err = autorest.Respond(
|
||||||
|
res,
|
||||||
|
azure.WithErrorUnlessStatusCode(http.StatusOK),
|
||||||
|
autorest.ByUnmarshallingJSON(&vminfo),
|
||||||
|
autorest.ByClosing())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &vminfo.ComputeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci ComputeInfo) ResourceID() string {
|
||||||
|
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s",
|
||||||
|
ci.SubscriptionID,
|
||||||
|
ci.ResourceGroupName,
|
||||||
|
ci.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetadataClient creates a new instance metadata client
|
||||||
|
func NewMetadataClient() MetadataClientAPI {
|
||||||
|
return metadataClient{
|
||||||
|
Sender: autorest.CreateSender(),
|
||||||
|
}
|
||||||
|
}
|
32
builder/azure/common/client/metadata_test.go
Normal file
32
builder/azure/common/client/metadata_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_MetadataReturnsComputeInfo(t *testing.T) {
|
||||||
|
if !IsAzure() {
|
||||||
|
t.Skipf("Not running on Azure, skipping live IMDS test")
|
||||||
|
}
|
||||||
|
mdc := NewMetadataClient()
|
||||||
|
info, err := mdc.GetComputeInfo()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
vm, err := azure.ParseResourceID(fmt.Sprintf(
|
||||||
|
"/subscriptions/%s"+
|
||||||
|
"/resourceGroups/%s"+
|
||||||
|
"/providers/Microsoft.Compute"+
|
||||||
|
"/virtualMachines/%s",
|
||||||
|
info.SubscriptionID,
|
||||||
|
info.ResourceGroupName,
|
||||||
|
info.Name))
|
||||||
|
assert.Nil(t, err, "%q is not parsable as an Azure resource info", info)
|
||||||
|
|
||||||
|
assert.Regexp(t, "^[0-9a-fA-F-]{36}$", vm.SubscriptionID)
|
||||||
|
t.Logf("VM: %+v", vm)
|
||||||
|
}
|
57
builder/azure/common/client/platform_image.go
Normal file
57
builder/azure/common/client/platform_image.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute/computeapi"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var platformImageRegex = regexp.MustCompile(`^[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+$`)
|
||||||
|
|
||||||
|
type VirtualMachineImagesClientAPI interface {
|
||||||
|
computeapi.VirtualMachineImagesClientAPI
|
||||||
|
// extensions
|
||||||
|
GetLatest(ctx context.Context, publisher, offer, sku, location string) (*compute.VirtualMachineImageResource, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ VirtualMachineImagesClientAPI = virtualMachineImagesClientAPI{}
|
||||||
|
|
||||||
|
type virtualMachineImagesClientAPI struct {
|
||||||
|
computeapi.VirtualMachineImagesClientAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePlatformImageURN(urn string) (image *PlatformImage, err error) {
|
||||||
|
if !platformImageRegex.Match([]byte(urn)) {
|
||||||
|
return nil, fmt.Errorf("%q is not a valid platform image specifier", urn)
|
||||||
|
}
|
||||||
|
parts := strings.Split(urn, ":")
|
||||||
|
return &PlatformImage{parts[0], parts[1], parts[2], parts[3]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c virtualMachineImagesClientAPI) GetLatest(ctx context.Context, publisher, offer, sku, location string) (*compute.VirtualMachineImageResource, error) {
|
||||||
|
result, err := c.List(ctx, location, publisher, offer, sku, "", to.Int32Ptr(1), "name desc")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if result.Value == nil || len(*result.Value) == 0 {
|
||||||
|
return nil, fmt.Errorf("%s:%s:%s:latest could not be found in location %s", publisher, offer, sku, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &(*result.Value)[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlatformImage struct {
|
||||||
|
Publisher, Offer, Sku, Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pi PlatformImage) URN() string {
|
||||||
|
return fmt.Sprintf("%s:%s:%s:%s",
|
||||||
|
pi.Publisher,
|
||||||
|
pi.Offer,
|
||||||
|
pi.Sku,
|
||||||
|
pi.Version)
|
||||||
|
}
|
30
builder/azure/common/client/platform_image_test.go
Normal file
30
builder/azure/common/client/platform_image_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_platformImageRegex(t *testing.T) {
|
||||||
|
for i, v := range []string{
|
||||||
|
"Publisher:Offer:Sku:Versions",
|
||||||
|
"Publisher:Offer-name:2.0_alpha:2.0.2019060122",
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("should_match_%d", i), func(t *testing.T) {
|
||||||
|
if !platformImageRegex.Match([]byte(v)) {
|
||||||
|
t.Fatalf("expected %q to match", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range []string{
|
||||||
|
"Publ isher:Offer:Sku:Versions",
|
||||||
|
"Publ/isher:Offer-name:2.0_alpha:2.0.2019060122",
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("should_not_match_%d", i), func(t *testing.T) {
|
||||||
|
if platformImageRegex.Match([]byte(v)) {
|
||||||
|
t.Fatalf("did not expected %q to match", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
30
builder/azure/common/client/testclient.go
Normal file
30
builder/azure/common/client/testclient.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTestClientSet(t *testing.T) (AzureClientSet, error) {
|
||||||
|
if os.Getenv("AZURE_INTEGRATION_TEST") == "" {
|
||||||
|
t.Skip("AZURE_INTEGRATION_TEST not set")
|
||||||
|
} else {
|
||||||
|
a, err := auth.NewAuthorizerFromEnvironment()
|
||||||
|
if err == nil {
|
||||||
|
cli := azureClientSet{}
|
||||||
|
cli.authorizer = a
|
||||||
|
cli.subscriptionID = os.Getenv("AZURE_SUBSCRIPTION_ID")
|
||||||
|
cli.PollingDelay = 0
|
||||||
|
cli.sender = http.DefaultClient
|
||||||
|
return cli, nil
|
||||||
|
} else {
|
||||||
|
t.Skipf("Could not create Azure client: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("Couldn't create client set")
|
||||||
|
}
|
@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/Azure/go-autorest/autorest/adal"
|
"github.com/Azure/go-autorest/autorest/adal"
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
packerAzureCommon "github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDeviceFlowOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider {
|
func NewDeviceFlowOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider {
|
||||||
@ -36,5 +35,5 @@ func (tp *deviceflowOauthTokenProvider) getServicePrincipalTokenWithResource(res
|
|||||||
tp.say(fmt.Sprintf("Getting token for %s", resource))
|
tp.say(fmt.Sprintf("Getting token for %s", resource))
|
||||||
}
|
}
|
||||||
|
|
||||||
return packerAzureCommon.Authenticate(tp.env, tp.tenantID, tp.say, resource)
|
return Authenticate(tp.env, tp.tenantID, tp.say, resource)
|
||||||
}
|
}
|
||||||
|
@ -91,10 +91,11 @@ type Properties struct {
|
|||||||
PublicIPAllocatedMethod *network.IPAllocationMethod `json:"publicIPAllocationMethod,omitempty"`
|
PublicIPAllocatedMethod *network.IPAllocationMethod `json:"publicIPAllocationMethod,omitempty"`
|
||||||
Sku *Sku `json:"sku,omitempty"`
|
Sku *Sku `json:"sku,omitempty"`
|
||||||
//StorageProfile3 *compute.StorageProfile `json:"storageProfile,omitempty"`
|
//StorageProfile3 *compute.StorageProfile `json:"storageProfile,omitempty"`
|
||||||
StorageProfile *StorageProfileUnion `json:"storageProfile,omitempty"`
|
StorageProfile *StorageProfileUnion `json:"storageProfile,omitempty"`
|
||||||
Subnets *[]network.Subnet `json:"subnets,omitempty"`
|
Subnets *[]network.Subnet `json:"subnets,omitempty"`
|
||||||
TenantId *string `json:"tenantId,omitempty"`
|
SecurityRules *[]network.SecurityRule `json:"securityRules,omitempty"`
|
||||||
Value *string `json:"value,omitempty"`
|
TenantId *string `json:"tenantId,omitempty"`
|
||||||
|
Value *string `json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessPolicies struct {
|
type AccessPolicies struct {
|
||||||
|
@ -3,9 +3,11 @@ package template
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network"
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,11 +15,12 @@ const (
|
|||||||
jsonPrefix = ""
|
jsonPrefix = ""
|
||||||
jsonIndent = " "
|
jsonIndent = " "
|
||||||
|
|
||||||
resourceKeyVaults = "Microsoft.KeyVault/vaults"
|
resourceKeyVaults = "Microsoft.KeyVault/vaults"
|
||||||
resourceNetworkInterfaces = "Microsoft.Network/networkInterfaces"
|
resourceNetworkInterfaces = "Microsoft.Network/networkInterfaces"
|
||||||
resourcePublicIPAddresses = "Microsoft.Network/publicIPAddresses"
|
resourcePublicIPAddresses = "Microsoft.Network/publicIPAddresses"
|
||||||
resourceVirtualMachine = "Microsoft.Compute/virtualMachines"
|
resourceVirtualMachine = "Microsoft.Compute/virtualMachines"
|
||||||
resourceVirtualNetworks = "Microsoft.Network/virtualNetworks"
|
resourceVirtualNetworks = "Microsoft.Network/virtualNetworks"
|
||||||
|
resourceNetworkSecurityGroups = "Microsoft.Network/networkSecurityGroups"
|
||||||
|
|
||||||
variableSshKeyPath = "sshKeyPath"
|
variableSshKeyPath = "sshKeyPath"
|
||||||
)
|
)
|
||||||
@ -309,6 +312,39 @@ func (s *TemplateBuilder) SetPrivateVirtualNetworkWithPublicIp(virtualNetworkRes
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TemplateBuilder) SetNetworkSecurityGroup(ipAddresses []string, port int) error {
|
||||||
|
nsgResource, dependency, resourceId := s.createNsgResource(ipAddresses, port)
|
||||||
|
if err := s.addResource(nsgResource); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vnetResource, err := s.getResourceByType(resourceVirtualNetworks)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.deleteResourceByType(resourceVirtualNetworks)
|
||||||
|
|
||||||
|
s.addResourceDependency(vnetResource, dependency)
|
||||||
|
|
||||||
|
if vnetResource.Properties == nil || vnetResource.Properties.Subnets == nil || len(*vnetResource.Properties.Subnets) != 1 {
|
||||||
|
return fmt.Errorf("template: could not find virtual network/subnet to add default network security group to")
|
||||||
|
}
|
||||||
|
subnet := ((*vnetResource.Properties.Subnets)[0])
|
||||||
|
if subnet.SubnetPropertiesFormat == nil {
|
||||||
|
subnet.SubnetPropertiesFormat = &network.SubnetPropertiesFormat{}
|
||||||
|
}
|
||||||
|
if subnet.SubnetPropertiesFormat.NetworkSecurityGroup != nil {
|
||||||
|
return fmt.Errorf("template: subnet already has an associated network security group")
|
||||||
|
}
|
||||||
|
subnet.SubnetPropertiesFormat.NetworkSecurityGroup = &network.SecurityGroup{
|
||||||
|
ID: to.StringPtr(resourceId),
|
||||||
|
}
|
||||||
|
|
||||||
|
s.addResource(vnetResource)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TemplateBuilder) SetTags(tags *map[string]*string) error {
|
func (s *TemplateBuilder) SetTags(tags *map[string]*string) error {
|
||||||
if tags == nil || len(*tags) == 0 {
|
if tags == nil || len(*tags) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@ -366,6 +402,18 @@ func (s *TemplateBuilder) toVariable(name string) string {
|
|||||||
return fmt.Sprintf("[variables('%s')]", name)
|
return fmt.Sprintf("[variables('%s')]", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TemplateBuilder) addResource(newResource *Resource) error {
|
||||||
|
for _, resource := range *s.template.Resources {
|
||||||
|
if *resource.Type == *newResource.Type {
|
||||||
|
return fmt.Errorf("template: found an existing resource of type %s", *resource.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resources := append(*s.template.Resources, *newResource)
|
||||||
|
s.template.Resources = &resources
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TemplateBuilder) deleteResourceByType(resourceType string) {
|
func (s *TemplateBuilder) deleteResourceByType(resourceType string) {
|
||||||
resources := make([]Resource, 0)
|
resources := make([]Resource, 0)
|
||||||
|
|
||||||
@ -379,6 +427,15 @@ func (s *TemplateBuilder) deleteResourceByType(resourceType string) {
|
|||||||
s.template.Resources = &resources
|
s.template.Resources = &resources
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TemplateBuilder) addResourceDependency(resource *Resource, dep string) {
|
||||||
|
if resource.DependsOn != nil {
|
||||||
|
deps := append(*resource.DependsOn, dep)
|
||||||
|
resource.DependsOn = &deps
|
||||||
|
} else {
|
||||||
|
resource.DependsOn = &[]string{dep}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate func(string) bool) {
|
func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate func(string) bool) {
|
||||||
deps := make([]string, 0)
|
deps := make([]string, 0)
|
||||||
|
|
||||||
@ -391,6 +448,38 @@ func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate
|
|||||||
*resource.DependsOn = deps
|
*resource.DependsOn = deps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TemplateBuilder) createNsgResource(srcIpAddresses []string, port int) (*Resource, string, string) {
|
||||||
|
resource := &Resource{
|
||||||
|
ApiVersion: to.StringPtr("[variables('networkSecurityGroupsApiVersion')]"),
|
||||||
|
Name: to.StringPtr("[parameters('nsgName')]"),
|
||||||
|
Type: to.StringPtr(resourceNetworkSecurityGroups),
|
||||||
|
Location: to.StringPtr("[variables('location')]"),
|
||||||
|
Properties: &Properties{
|
||||||
|
SecurityRules: &[]network.SecurityRule{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("AllowIPsToSshWinRMInbound"),
|
||||||
|
SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
|
||||||
|
Description: to.StringPtr("Allow inbound traffic from specified IP addresses"),
|
||||||
|
Protocol: network.SecurityRuleProtocolTCP,
|
||||||
|
Priority: to.Int32Ptr(100),
|
||||||
|
Access: network.SecurityRuleAccessAllow,
|
||||||
|
Direction: network.SecurityRuleDirectionInbound,
|
||||||
|
SourceAddressPrefixes: &srcIpAddresses,
|
||||||
|
SourcePortRange: to.StringPtr("*"),
|
||||||
|
DestinationAddressPrefix: to.StringPtr("VirtualNetwork"),
|
||||||
|
DestinationPortRange: to.StringPtr(strconv.Itoa(port)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency := fmt.Sprintf("[concat('%s/', parameters('nsgName'))]", resourceNetworkSecurityGroups)
|
||||||
|
resourceId := fmt.Sprintf("[resourceId('%s', parameters('nsgName'))]", resourceNetworkSecurityGroups)
|
||||||
|
|
||||||
|
return resource, dependency, resourceId
|
||||||
|
}
|
||||||
|
|
||||||
// See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates.
|
// See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates.
|
||||||
|
|
||||||
// Template to deploy a KeyVault.
|
// Template to deploy a KeyVault.
|
||||||
@ -496,6 +585,9 @@ const BasicTemplate = `{
|
|||||||
"virtualNetworkName": {
|
"virtualNetworkName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"vmSize": {
|
"vmSize": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -510,6 +602,7 @@ const BasicTemplate = `{
|
|||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
"virtualNetworksApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -160,6 +163,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -158,6 +161,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -120,6 +123,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -174,6 +177,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -197,6 +200,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"nicName": {
|
"nicName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"nsgName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"osDiskName": {
|
"osDiskName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -190,6 +193,7 @@
|
|||||||
"location": "[resourceGroup().location]",
|
"location": "[resourceGroup().location]",
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"networkSecurityGroupsApiVersion": "2019-04-01",
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user