Merge remote-tracking branch 'origin/master' into ansible_local_playbook_files_update
This commit is contained in:
commit
11f2b1ec24
|
@ -10,6 +10,7 @@
|
||||||
test/.env
|
test/.env
|
||||||
*~
|
*~
|
||||||
*.received.*
|
*.received.*
|
||||||
|
*.swp
|
||||||
|
|
||||||
website/.bundle
|
website/.bundle
|
||||||
website/vendor
|
website/vendor
|
||||||
|
@ -21,3 +22,5 @@ packer-test*.log
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
|
Thumbs.db
|
||||||
|
/packer.exe
|
|
@ -8,6 +8,7 @@ language: go
|
||||||
go:
|
go:
|
||||||
- 1.7.4
|
- 1.7.4
|
||||||
- 1.8.3
|
- 1.8.3
|
||||||
|
- 1.9
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- make deps
|
- make deps
|
||||||
|
@ -21,5 +22,3 @@ branches:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
allow_failures:
|
|
||||||
- go: 1.4.3
|
|
||||||
|
|
198
CHANGELOG.md
198
CHANGELOG.md
|
@ -1,20 +1,208 @@
|
||||||
## (UNRELEASED)
|
## (UNRELEASED)
|
||||||
|
|
||||||
### IMRPOVEMENTS:
|
## 1.1.0 (October 13, 2017)
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
|
||||||
|
* **New builder:** `hyperv-vmcx` for building images from existing VMs.
|
||||||
|
[GH-4944] [GH-5444]
|
||||||
|
* builder/amazon-instance: Add `.Token` as a variable in the
|
||||||
|
`BundleUploadCommand` template. [GH-5288]
|
||||||
|
* builder/amazon: Add `temporary_security_group_source_cidr` option to control
|
||||||
|
ingress to source instances. [GH-5384]
|
||||||
|
* builder/amazon: Output AMI Name during prevalidation. [GH-5389]
|
||||||
|
* builder/amazon: Support template functions in tag keys. [GH-5381]
|
||||||
|
* builder/amazon: Tag volumes on creation instead of as a separate step.
|
||||||
|
[GH-5417]
|
||||||
|
* builder/docker: Add option to set `--user` flag when running `exec`.
|
||||||
|
[GH-5406]
|
||||||
|
* builder/docker: Set file owner to container user when uploading. Can be
|
||||||
|
disabled by setting `fix_upload_owner` to `false`. [GH-5422]
|
||||||
|
* builder/googlecompute: Support setting labels on the resulting image.
|
||||||
|
[GH-5356]
|
||||||
|
* builder/hyper-v: Add `vhd_temp_path` option to control where the VHD resides
|
||||||
|
while it's being provisioned. [GH-5206]
|
||||||
|
* builder/hyper-v: Allow vhd or vhdx source images instead of just ISO.
|
||||||
|
[GH-4944] [GH-5444]
|
||||||
|
* builder/hyper-v: Disable automatic checkpoints. [GH-5374]
|
||||||
|
* builder/virtualbox-ovf: Add `keep_registered` option. [GH-5336]
|
||||||
|
* builder/vmware: Add `disable_vnc` option to prevent VNC connections from
|
||||||
|
being made. [GH-5436]
|
||||||
|
* core: Releases will now be built for ppc64le.
|
||||||
|
* post-processor/vagrant: When building from a builder/hyper-v artifact, link
|
||||||
|
instead of copy when available. [GH-5207]
|
||||||
|
|
||||||
* builder/googlecompute: Allow using URL's for network and subnetwork. [GH-5035]
|
|
||||||
|
|
||||||
### BUG FIXES:
|
### BUG FIXES:
|
||||||
|
|
||||||
|
* builder/cloudstack: Fix panic if build is aborted. [GH-5388]
|
||||||
|
* builder/hyper-v: Respect `enable_dynamic_memory` flag. [GH-5363]
|
||||||
|
* builder/puppet-masterless: Make sure directories created with sudo are
|
||||||
|
writable by the packer user. [GH-5351]
|
||||||
|
* provisioner/chef-solo: Fix issue installing chef-solo on Windows. [GH-5357]
|
||||||
|
* provisioner/powershell: Fix issue setting environment variables by writing
|
||||||
|
them to a file, instead of the command line. [GH-5345]
|
||||||
|
* provisioner/powershell: Fix issue where powershell scripts could hang.
|
||||||
|
[GH-5082]
|
||||||
|
* provisioner/powershell: Fix Powershell progress stream leak to stderr for
|
||||||
|
normal and elevated commands. [GH-5365]
|
||||||
|
* provisioner/puppet-masterless: Fix bug where `puppet_bin_dir` wasn't being
|
||||||
|
respected. [GH-5340]
|
||||||
|
* provisioner/puppet: Fix setting facter vars on Windows. [GH-5341]
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.0 (September 12, 2017)
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
|
||||||
|
* builder/alicloud: Update alicloud go sdk and enable multi sites support for
|
||||||
|
alicloud [GH-5219]
|
||||||
|
* builder/amazon: Upgrade aws-sdk-go to 1.10.14, add tags at instance run time.
|
||||||
|
[GH-5196]
|
||||||
|
* builder/azure: Add object_id to windows_custom_image.json. [GH-5285]
|
||||||
|
* builder/azure: Add support for storage account for managed images. [GH-5244]
|
||||||
|
* builder/azure: Update pkcs12 package. [GH-5301]
|
||||||
|
* builder/cloudstack: Add support for Security Groups. [GH-5175]
|
||||||
|
* builder/docker: Uploading files and directories now use docker cp. [GH-5273]
|
||||||
|
[GH-5333]
|
||||||
|
* builder/googlecompute: Add `labels` option for labeling launched instances.
|
||||||
|
[GH-5308]
|
||||||
|
* builder/googlecompute: Add support for accelerator api. [GH-5137]
|
||||||
|
* builder/profitbricks: added support for Cloud API v4. [GH-5233]
|
||||||
|
* builder/vmware-esxi: Remote builds now respect `output_directory` [GH-4592]
|
||||||
|
* builder/vmware: Set artifact ID to `VMName`. [GH-5187]
|
||||||
|
* core: Build solaris binary by default. [GH-5268] [GH-5248]
|
||||||
|
* core: Remove LGPL dependencies. [GH-5262]
|
||||||
|
* provisioner/puppet: Add `guest_os_type` option to add support for Windows.
|
||||||
|
[GH-5252]
|
||||||
|
* provisioner/salt-masterless: Also use sudo to clean up if we used sudo to
|
||||||
|
install. [GH-5240]
|
||||||
|
|
||||||
|
### BACKWARDS INCOMPATIBILITIES:
|
||||||
|
|
||||||
|
* builder/amazon: Changes way that AMI artifacts are printed out after build,
|
||||||
|
aligning them to builder. Could affect output parsing. [GH-5281]
|
||||||
|
* builder/amazon: Split `enhanced_networking` into `sriov_support` and
|
||||||
|
`ena_support` to support finer grained control. Use `packer fix
|
||||||
|
<template.json>` to automatically update your template to use `ena_support`
|
||||||
|
where previously there was only `enhanced_networking`. Make sure to also
|
||||||
|
add `sriov_support` if you need that feature, and to ensure `ena_support`
|
||||||
|
is what you intended to be in your template. [GH-5284]
|
||||||
|
* builder/cloudstack: Setup temporary SSH keypair; backwards incompatible in
|
||||||
|
the uncommon case that the source image allowed SSH auth with password but
|
||||||
|
not with keypair. [GH-5174]
|
||||||
|
* communicator/ssh: Renamed `ssh_disable_agent` to
|
||||||
|
`ssh_disable_agent_forwarding`. Need to run fixer on packer configs that
|
||||||
|
use `ssh_disable_agent`. [GH-5024]
|
||||||
|
* communicator: Preserve left-sided white-space in remote command output. Make
|
||||||
|
sure any scripts that parse this output can handle the new whitespace
|
||||||
|
before upgrading. [GH-5167]
|
||||||
|
* provisioner/shell: Set default for `ExpectDisconnect` to `false`. If your
|
||||||
|
script causes the connection to be reset, you should set this to `true` to
|
||||||
|
prevent errors. [GH-5283]
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
|
||||||
|
* builder/amazon: `force_deregister` works in all regions, not just original
|
||||||
|
region. [GH-5250]
|
||||||
|
* builder/docker: Directory uploads now use the same semantics as the rest of
|
||||||
|
the communicators. [GH-5333]
|
||||||
|
* builder/vmware: Fix timestamp in default VMName. [GH-5274]
|
||||||
|
* builder/winrm: WinRM now waits to make sure commands can run successfully
|
||||||
|
before considering itself connected. [GH-5300]
|
||||||
|
* core: Fix issue where some builders wouldn't respect `-on-error` behavior.
|
||||||
|
[GH-5297]
|
||||||
|
* provisioner/windows-restart: The first powershell provisioner after a restart
|
||||||
|
now works. [GH-5272]
|
||||||
|
|
||||||
|
### FEATURES:
|
||||||
|
|
||||||
|
* **New builder**: Oracle Cloud Infrastructure (OCI) builder for creating
|
||||||
|
custom images. [GH-4554]
|
||||||
|
* **New builder:** `lxc` for building lxc images. [GH-3523]
|
||||||
|
* **New builder:** `lxd` for building lxd images. [GH-3625]
|
||||||
|
* **New post-processor**: vSphere Template post-processor to be used with
|
||||||
|
vmware-iso builder enabling user to mark a VM as a template. [GH-5114]
|
||||||
|
|
||||||
|
## 1.0.4 (August 11, 2017)
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
|
||||||
|
* builder/alicloud: Increase polling timeout. [GH-5148]
|
||||||
|
* builder/azure: Add `private_virtual_network_with_public_ip` option to
|
||||||
|
optionally obtain a public IP. [GH-5222]
|
||||||
|
* builder/googlecompute: use a more portable method of obtaining zone.
|
||||||
|
[GH-5192]
|
||||||
|
* builder/hyperv: Properly interpolate user variables in template. [GH-5184]
|
||||||
|
* builder/parallels: Remove soon to be removed --vmtype flag in createvm.
|
||||||
|
[GH-5172]
|
||||||
|
* contrib: add json files to zsh completion. [GH-5195]
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
|
||||||
|
* builder/amazon: Don't delete snapshots we didn't create. [GH-5211]
|
||||||
|
* builder/amazon: fix builds when using the null communicator. [GH-5217]
|
||||||
|
* builder/docker: Correctly handle case when uploading an empty directory.
|
||||||
|
[GH-5234]
|
||||||
|
* command/push: Don't push variables if they are unspecified. Reverts to
|
||||||
|
behavior in 1.0.1. [GH-5235]
|
||||||
|
* command/push: fix handling of symlinks. [GH-5226]
|
||||||
|
* core: Strip query parameters from ISO URLs when checking against a checksum
|
||||||
|
file. [GH-5181]
|
||||||
|
* provisioner/ansible-remote: Fix issue where packer could hang communicating
|
||||||
|
with ansible-remote. [GH-5146]
|
||||||
|
|
||||||
|
## 1.0.3 (July 17, 2017)
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
* builder/azure: Update to latest Azure SDK, enabling support for managed
|
||||||
|
disks. [GH-4511]
|
||||||
|
* builder/cloudstack: Add default cidr_list [ 0.0.0.0/0 ]. [GH-5125]
|
||||||
|
* builder/cloudstack: Add support for ssh_agent_auth. [GH-5130]
|
||||||
|
* builder/cloudstack: Add support for using a HTTP server. [GH-5017]
|
||||||
|
* builder/cloudstack: Allow reading api_url, api_key, and secret_key from env
|
||||||
|
vars. [GH-5124]
|
||||||
|
* builder/cloudstack: Make expunge optional and improve logging output.
|
||||||
|
[GH-5099]
|
||||||
|
* builder/googlecompute: Allow using URL's for network and subnetwork.
|
||||||
|
[GH-5035]
|
||||||
|
* builder/hyperv: Add support for floppy_dirs with hyperv-iso builder.
|
||||||
|
* builder/hyperv: Add support for override of system %temp% path.
|
||||||
|
* core: Experimental Android ARM support. [GH-5111]
|
||||||
|
* post-processor/atlas: Disallow packer push of vagrant.box artifacts to atlas.
|
||||||
|
[GH-4780]
|
||||||
|
* postprocessor/atlas: Disallow pushing vagrant.box artifacts now that Vagrant
|
||||||
|
cloud is live. [GH-4780]
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
* builder/amazon: Fix panic that happens if ami_block_device_mappings is empty.
|
||||||
|
[GH-5059]
|
||||||
* builder/azure: Write private SSH to file in debug mode. [GH-5070] [GH-5074]
|
* builder/azure: Write private SSH to file in debug mode. [GH-5070] [GH-5074]
|
||||||
* builder/parallels: Skip missing paths when looking for unnecessary files. [GH-5058]
|
* builder/cloudstack: Properly report back errors. [GH-5103] [GH-5123]
|
||||||
|
* builder/docker: Fix windows filepath in docker-toolbox call [GH-4887]
|
||||||
|
* builder/docker: Fix windows filepath in docker-toolbox call. [GH-4887]
|
||||||
|
* builder/hyperv: Use SID to verify membersip in Admin group, fixing for non-
|
||||||
|
english users. [GH-5022]
|
||||||
|
* builder/hyperv: Verify membership in the group Hyper-V Administrators by SID
|
||||||
|
not name. [GH-5022]
|
||||||
|
* builder/openstack: Update gophercloud version, fixing builds > 1 hr long.
|
||||||
|
[GH-5046]
|
||||||
|
* builder/parallels: Skip missing paths when looking for unnecessary files.
|
||||||
|
[GH-5058]
|
||||||
* builder/vmware-esxi: Fix VNC port discovery default timeout. [GH-5051]
|
* builder/vmware-esxi: Fix VNC port discovery default timeout. [GH-5051]
|
||||||
|
* communicator/ssh: Add ProvisionerTypes to communicator tests, resolving panic
|
||||||
|
[GH-5116]
|
||||||
|
* communicator/ssh: Resolve race condition that sometimes truncates ssh
|
||||||
|
provisioner stdout [GH-4719]
|
||||||
|
* post-processor/checksum: Fix interpolation of "output". [GH-5112]
|
||||||
|
* push: Push vars in packer config, not just those set from command line and in
|
||||||
|
var-file. [GH-5101]
|
||||||
|
|
||||||
## 1.0.2 (June 21, 2017)
|
## 1.0.2 (June 21, 2017)
|
||||||
|
|
||||||
### BUG FIXES:
|
### BUG FIXES:
|
||||||
|
* communicator/ssh: Fix truncated stdout from remote ssh provisioner. [GH-5050]
|
||||||
* builder/amazon: Fix bugs related to stop instance command. [GH-5029]
|
* builder/amazon: Fix bugs related to stop instance command. [GH-4719]
|
||||||
* communicator/ssh: Fix ssh connection errors. [GH-5038]
|
* communicator/ssh: Fix ssh connection errors. [GH-5038]
|
||||||
* core: Remove logging that shouldn't be there when running commands. [GH-5042]
|
* core: Remove logging that shouldn't be there when running commands. [GH-5042]
|
||||||
* provisioner/shell: Fix bug where scripts were being run under `sh`. [GH-5043]
|
* provisioner/shell: Fix bug where scripts were being run under `sh`. [GH-5043]
|
||||||
|
|
|
@ -54,7 +54,7 @@ If you have never worked with Go before, you will have to complete the
|
||||||
following steps in order to be able to compile and test Packer. These instructions target POSIX-like environments (Mac OS X, Linux, Cygwin, etc.) so you may need to adjust them for Windows or other shells.
|
following steps in order to be able to compile and test Packer. These instructions target POSIX-like environments (Mac OS X, Linux, Cygwin, etc.) so you may need to adjust them for Windows or other shells.
|
||||||
|
|
||||||
1. [Download](https://golang.org/dl) and install Go. The instructions below
|
1. [Download](https://golang.org/dl) and install Go. The instructions below
|
||||||
are for go 1.6. Earlier versions of Go are no longer supported.
|
are for go 1.7. Earlier versions of Go are no longer supported.
|
||||||
|
|
||||||
2. Set and export the `GOPATH` environment variable and update your `PATH`. For
|
2. Set and export the `GOPATH` environment variable and update your `PATH`. For
|
||||||
example, you can add to your `.bash_profile`.
|
example, you can add to your `.bash_profile`.
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -7,6 +7,7 @@ GITBRANCH:=$(shell git symbolic-ref --short HEAD 2>/dev/null)
|
||||||
GOFMT_FILES?=$$(find . -not -path "./vendor/*" -name "*.go")
|
GOFMT_FILES?=$$(find . -not -path "./vendor/*" -name "*.go")
|
||||||
GOOS=$(shell go env GOOS)
|
GOOS=$(shell go env GOOS)
|
||||||
GOARCH=$(shell go env GOARCH)
|
GOARCH=$(shell go env GOARCH)
|
||||||
|
GOPATH=$(shell go env GOPATH)
|
||||||
|
|
||||||
# Get the git commit
|
# Get the git commit
|
||||||
GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
|
GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
|
||||||
|
@ -50,8 +51,9 @@ dev: deps ## Build and install a development build
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
@mkdir -p pkg/$(GOOS)_$(GOARCH)
|
@mkdir -p pkg/$(GOOS)_$(GOARCH)
|
||||||
|
@mkdir -p bin
|
||||||
@go install -ldflags '$(GOLDFLAGS)'
|
@go install -ldflags '$(GOLDFLAGS)'
|
||||||
@cp $(GOPATH)/bin/packer bin
|
@cp $(GOPATH)/bin/packer bin/packer
|
||||||
@cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH)
|
@cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH)
|
||||||
|
|
||||||
fmt: ## Format Go code
|
fmt: ## Format Go code
|
||||||
|
|
|
@ -34,6 +34,7 @@ comes out of the box with support for the following platforms:
|
||||||
* Hyper-V
|
* Hyper-V
|
||||||
* 1&1
|
* 1&1
|
||||||
* OpenStack
|
* OpenStack
|
||||||
|
* Oracle Bare Metal Cloud Services
|
||||||
* Parallels
|
* Parallels
|
||||||
* ProfitBricks
|
* ProfitBricks
|
||||||
* QEMU. Both KVM and Xen images.
|
* QEMU. Both KVM and Xen images.
|
||||||
|
|
|
@ -22,7 +22,7 @@ func (c *AlicloudAccessConfig) Client() (*ecs.Client, error) {
|
||||||
if err := c.loadAndValidate(); err != nil {
|
if err := c.loadAndValidate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := ecs.NewClient(c.AlicloudAccessKey, c.AlicloudSecretKey)
|
client := ecs.NewECSClient(c.AlicloudAccessKey, c.AlicloudSecretKey, common.Region(c.AlicloudRegion))
|
||||||
client.SetBusinessInfo("Packer")
|
client.SetBusinessInfo("Packer")
|
||||||
if _, err := client.DescribeRegions(); err != nil {
|
if _, err := client.DescribeRegions(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -5,6 +5,7 @@ package ecs
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
"github.com/hashicorp/packer/common"
|
"github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
|
@ -98,8 +99,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
|
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
|
||||||
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
||||||
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
||||||
//DebugKeyPath: b.config.Com
|
DebugKeyPath: fmt.Sprintf("ecs_%s.pem", b.config.PackerBuildName),
|
||||||
RegionId: b.config.AlicloudRegion,
|
RegionId: b.config.AlicloudRegion,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if b.chooseNetworkType() == VpcNet {
|
if b.chooseNetworkType() == VpcNet {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/denverdino/aliyungo/ecs"
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepAttachKeyPar struct {
|
type stepAttachKeyPar struct {
|
||||||
|
@ -21,7 +22,7 @@ func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
client := state.Get("client").(*ecs.Client)
|
client := state.Get("client").(*ecs.Client)
|
||||||
config := state.Get("config").(Config)
|
config := state.Get("config").(Config)
|
||||||
instance := state.Get("instance").(*ecs.InstanceAttributesType)
|
instance := state.Get("instance").(*ecs.InstanceAttributesType)
|
||||||
retry_times := 3
|
timeoutPoint := time.Now().Add(120 * time.Second)
|
||||||
for {
|
for {
|
||||||
err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
|
err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
|
||||||
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
|
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
|
||||||
|
@ -29,8 +30,8 @@ func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
e, _ := err.(*common.Error)
|
e, _ := err.(*common.Error)
|
||||||
if (!(e.Code == "MissingParameter" || e.Code == "DependencyViolation.WindowsInstance" ||
|
if (!(e.Code == "MissingParameter" || e.Code == "DependencyViolation.WindowsInstance" ||
|
||||||
e.Code == "InvalidKeyPairName.NotFound" || e.Code == "InvalidRegionId.NotFound")) &&
|
e.Code == "InvalidKeyPairName.NotFound" || e.Code == "InvalidRegionId.NotFound")) &&
|
||||||
retry_times > 0 {
|
time.Now().Before(timeoutPoint) {
|
||||||
retry_times = retry_times - 1
|
time.Sleep(5 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err := fmt.Errorf("Error attaching keypair %s to instance %s : %s",
|
err := fmt.Errorf("Error attaching keypair %s to instance %s : %s",
|
||||||
|
|
|
@ -121,12 +121,12 @@ func (s *stepConfigAlicloudSecurityGroup) Cleanup(state multistep.StateBag) {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
message(state, "security group")
|
message(state, "security group")
|
||||||
start := time.Now().Add(10 * time.Second)
|
timeoutPoint := time.Now().Add(120 * time.Second)
|
||||||
for {
|
for {
|
||||||
if err := client.DeleteSecurityGroup(common.Region(s.RegionId), s.SecurityGroupId); err != nil {
|
if err := client.DeleteSecurityGroup(common.Region(s.RegionId), s.SecurityGroupId); err != nil {
|
||||||
e, _ := err.(*common.Error)
|
e, _ := err.(*common.Error)
|
||||||
if e.Code == "DependencyViolation" && time.Now().Before(start) {
|
if e.Code == "DependencyViolation" && time.Now().Before(timeoutPoint) {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ui.Error(fmt.Sprintf("Failed to delete security group, it may still be around: %s", err))
|
ui.Error(fmt.Sprintf("Failed to delete security group, it may still be around: %s", err))
|
||||||
|
|
|
@ -78,13 +78,13 @@ func (s *stepConfigAlicloudVPC) Cleanup(state multistep.StateBag) {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
message(state, "VPC")
|
message(state, "VPC")
|
||||||
start := time.Now().Add(10 * time.Second)
|
timeoutPoint := time.Now().Add(60 * time.Second)
|
||||||
for {
|
for {
|
||||||
if err := client.DeleteVpc(s.VpcId); err != nil {
|
if err := client.DeleteVpc(s.VpcId); err != nil {
|
||||||
e, _ := err.(*common.Error)
|
e, _ := err.(*common.Error)
|
||||||
if (e.Code == "DependencyViolation.Instance" || e.Code == "DependencyViolation.RouteEntry" ||
|
if (e.Code == "DependencyViolation.Instance" || e.Code == "DependencyViolation.RouteEntry" ||
|
||||||
e.Code == "DependencyViolation.VSwitch" ||
|
e.Code == "DependencyViolation.VSwitch" ||
|
||||||
e.Code == "DependencyViolation.SecurityGroup") && time.Now().Before(start) {
|
e.Code == "DependencyViolation.SecurityGroup") && time.Now().Before(timeoutPoint) {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ func (s *stepConfigAlicloudVSwitch) Run(state multistep.StateBag) multistep.Step
|
||||||
}
|
}
|
||||||
if err := client.WaitForVSwitchAvailable(vpcId, s.VSwitchId, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
|
if err := client.WaitForVSwitchAvailable(vpcId, s.VSwitchId, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(fmt.Sprintf("Timeout waiting for vswitch to become avaiable: %v", err))
|
ui.Error(fmt.Sprintf("Timeout waiting for vswitch to become available: %v", err))
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
state.Put("vswitchid", vswitchId)
|
state.Put("vswitchid", vswitchId)
|
||||||
|
@ -130,13 +130,13 @@ func (s *stepConfigAlicloudVSwitch) Cleanup(state multistep.StateBag) {
|
||||||
client := state.Get("client").(*ecs.Client)
|
client := state.Get("client").(*ecs.Client)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
message(state, "vSwitch")
|
message(state, "vSwitch")
|
||||||
start := time.Now().Add(10 * time.Second)
|
timeoutPoint := time.Now().Add(10 * time.Second)
|
||||||
for {
|
for {
|
||||||
if err := client.DeleteVSwitch(s.VSwitchId); err != nil {
|
if err := client.DeleteVSwitch(s.VSwitchId); err != nil {
|
||||||
e, _ := err.(*common.Error)
|
e, _ := err.(*common.Error)
|
||||||
if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" ||
|
if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" ||
|
||||||
e.Code == "DependencyViolation.HaVip" ||
|
e.Code == "DependencyViolation.HaVip" ||
|
||||||
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(start) {
|
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(timeoutPoint) {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,9 +213,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
if !b.config.FromScratch {
|
if !b.config.FromScratch {
|
||||||
steps = append(steps,
|
steps = append(steps,
|
||||||
&awscommon.StepSourceAMIInfo{
|
&awscommon.StepSourceAMIInfo{
|
||||||
SourceAmi: b.config.SourceAmi,
|
SourceAmi: b.config.SourceAmi,
|
||||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
AmiFilters: b.config.SourceAmiFilter,
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AmiFilters: b.config.SourceAmiFilter,
|
||||||
},
|
},
|
||||||
&StepCheckRootDevice{},
|
&StepCheckRootDevice{},
|
||||||
)
|
)
|
||||||
|
@ -245,17 +246,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
&StepEarlyCleanup{},
|
&StepEarlyCleanup{},
|
||||||
&StepSnapshot{},
|
&StepSnapshot{},
|
||||||
&awscommon.StepDeregisterAMI{
|
&awscommon.StepDeregisterAMI{
|
||||||
|
AccessConfig: &b.config.AccessConfig,
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||||
AMIName: b.config.AMIName,
|
AMIName: b.config.AMIName,
|
||||||
|
Regions: b.config.AMIRegions,
|
||||||
},
|
},
|
||||||
&StepRegisterAMI{
|
&StepRegisterAMI{
|
||||||
RootVolumeSize: b.config.RootVolumeSize,
|
RootVolumeSize: b.config.RootVolumeSize,
|
||||||
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
},
|
},
|
||||||
&awscommon.StepCreateEncryptedAMICopy{
|
&awscommon.StepCreateEncryptedAMICopy{
|
||||||
KeyID: b.config.AMIKmsKeyId,
|
KeyID: b.config.AMIKmsKeyId,
|
||||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||||
Name: b.config.AMIName,
|
Name: b.config.AMIName,
|
||||||
|
AMIMappings: b.config.AMIBlockDevices.AMIMappings,
|
||||||
},
|
},
|
||||||
&awscommon.StepAMIRegionCopy{
|
&awscommon.StepAMIRegionCopy{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
|
|
|
@ -4,7 +4,8 @@ package chroot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// See: http://linux.die.net/include/sys/file.h
|
// See: http://linux.die.net/include/sys/file.h
|
||||||
|
@ -13,7 +14,7 @@ const LOCK_NB = 4
|
||||||
const LOCK_UN = 8
|
const LOCK_UN = 8
|
||||||
|
|
||||||
func lockFile(f *os.File) error {
|
func lockFile(f *os.File) error {
|
||||||
err := syscall.Flock(int(f.Fd()), LOCK_EX)
|
err := unix.Flock(int(f.Fd()), LOCK_EX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -22,5 +23,5 @@ func lockFile(f *os.File) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func unlockFile(f *os.File) error {
|
func unlockFile(f *os.File) error {
|
||||||
return syscall.Flock(int(f.Fd()), LOCK_UN)
|
return unix.Flock(int(f.Fd()), LOCK_UN)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@ import (
|
||||||
|
|
||||||
// StepRegisterAMI creates the AMI.
|
// StepRegisterAMI creates the AMI.
|
||||||
type StepRegisterAMI struct {
|
type StepRegisterAMI struct {
|
||||||
RootVolumeSize int64
|
RootVolumeSize int64
|
||||||
|
EnableAMIENASupport bool
|
||||||
|
EnableAMISriovNetSupport bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -75,11 +77,12 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
registerOpts = buildRegisterOpts(config, image, newMappings)
|
registerOpts = buildRegisterOpts(config, image, newMappings)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.AMIEnhancedNetworking {
|
if s.EnableAMISriovNetSupport {
|
||||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||||
registerOpts.SriovNetSupport = aws.String("simple")
|
registerOpts.SriovNetSupport = aws.String("simple")
|
||||||
|
}
|
||||||
|
if s.EnableAMIENASupport {
|
||||||
// Set EnaSupport to true
|
// Set EnaSupport to true
|
||||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||||
registerOpts.EnaSupport = aws.Bool(true)
|
registerOpts.EnaSupport = aws.Bool(true)
|
||||||
|
|
|
@ -17,7 +17,8 @@ type AMIConfig struct {
|
||||||
AMIRegions []string `mapstructure:"ami_regions"`
|
AMIRegions []string `mapstructure:"ami_regions"`
|
||||||
AMISkipRegionValidation bool `mapstructure:"skip_region_validation"`
|
AMISkipRegionValidation bool `mapstructure:"skip_region_validation"`
|
||||||
AMITags map[string]string `mapstructure:"tags"`
|
AMITags map[string]string `mapstructure:"tags"`
|
||||||
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
AMIENASupport bool `mapstructure:"ena_support"`
|
||||||
|
AMISriovNetSupport bool `mapstructure:"sriov_support"`
|
||||||
AMIForceDeregister bool `mapstructure:"force_deregister"`
|
AMIForceDeregister bool `mapstructure:"force_deregister"`
|
||||||
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
|
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
|
||||||
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
|
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (a *Artifact) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(amiStrings)
|
sort.Strings(amiStrings)
|
||||||
return fmt.Sprintf("AMIs were created:\n\n%s", strings.Join(amiStrings, "\n"))
|
return fmt.Sprintf("AMIs were created:\n%s\n", strings.Join(amiStrings, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) State(name string) interface{} {
|
func (a *Artifact) State(name string) interface{} {
|
||||||
|
|
|
@ -48,9 +48,9 @@ func TestArtifactState_atlasMetadata(t *testing.T) {
|
||||||
|
|
||||||
func TestArtifactString(t *testing.T) {
|
func TestArtifactString(t *testing.T) {
|
||||||
expected := `AMIs were created:
|
expected := `AMIs were created:
|
||||||
|
|
||||||
east: foo
|
east: foo
|
||||||
west: bar`
|
west: bar
|
||||||
|
`
|
||||||
|
|
||||||
amis := make(map[string]string)
|
amis := make(map[string]string)
|
||||||
amis["east"] = "foo"
|
amis["east"] = "foo"
|
||||||
|
|
|
@ -3,6 +3,7 @@ package common
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
@ -40,6 +41,7 @@ type RunConfig struct {
|
||||||
DisableStopInstance bool `mapstructure:"disable_stop_instance"`
|
DisableStopInstance bool `mapstructure:"disable_stop_instance"`
|
||||||
SecurityGroupId string `mapstructure:"security_group_id"`
|
SecurityGroupId string `mapstructure:"security_group_id"`
|
||||||
SecurityGroupIds []string `mapstructure:"security_group_ids"`
|
SecurityGroupIds []string `mapstructure:"security_group_ids"`
|
||||||
|
TemporarySGSourceCidr string `mapstructure:"temporary_security_group_source_cidr"`
|
||||||
SubnetId string `mapstructure:"subnet_id"`
|
SubnetId string `mapstructure:"subnet_id"`
|
||||||
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
|
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
|
||||||
UserData string `mapstructure:"user_data"`
|
UserData string `mapstructure:"user_data"`
|
||||||
|
@ -115,6 +117,14 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.TemporarySGSourceCidr == "" {
|
||||||
|
c.TemporarySGSourceCidr = "0.0.0.0/0"
|
||||||
|
} else {
|
||||||
|
if _, _, err := net.ParseCIDR(c.TemporarySGSourceCidr); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Error parsing temporary_security_group_source_cidr: %s", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c.InstanceInitiatedShutdownBehavior == "" {
|
if c.InstanceInitiatedShutdownBehavior == "" {
|
||||||
c.InstanceInitiatedShutdownBehavior = "stop"
|
c.InstanceInitiatedShutdownBehavior = "stop"
|
||||||
} else if !reShutdownBehavior.MatchString(c.InstanceInitiatedShutdownBehavior) {
|
} else if !reShutdownBehavior.MatchString(c.InstanceInitiatedShutdownBehavior) {
|
||||||
|
|
|
@ -166,13 +166,17 @@ func ConvertToEC2Tags(tags map[string]string, region, sourceAmiId string, ctx in
|
||||||
SourceAMI: sourceAmiId,
|
SourceAMI: sourceAmiId,
|
||||||
BuildRegion: region,
|
BuildRegion: region,
|
||||||
}
|
}
|
||||||
|
interpolatedKey, err := interpolate.Render(key, &ctx)
|
||||||
|
if err != nil {
|
||||||
|
return ec2Tags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
|
||||||
|
}
|
||||||
interpolatedValue, err := interpolate.Render(value, &ctx)
|
interpolatedValue, err := interpolate.Render(value, &ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ec2Tags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
|
return ec2Tags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ec2Tags = append(ec2Tags, &ec2.Tag{
|
ec2Tags = append(ec2Tags, &ec2.Tag{
|
||||||
Key: aws.String(key),
|
Key: aws.String(interpolatedKey),
|
||||||
Value: aws.String(interpolatedValue),
|
Value: aws.String(interpolatedValue),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,59 +10,76 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type StepDeregisterAMI struct {
|
type StepDeregisterAMI struct {
|
||||||
|
AccessConfig *AccessConfig
|
||||||
ForceDeregister bool
|
ForceDeregister bool
|
||||||
ForceDeleteSnapshot bool
|
ForceDeleteSnapshot bool
|
||||||
AMIName string
|
AMIName string
|
||||||
|
Regions []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
regions := s.Regions
|
||||||
|
if len(regions) == 0 {
|
||||||
|
regions = append(regions, s.AccessConfig.RawRegion)
|
||||||
|
}
|
||||||
|
|
||||||
// Check for force deregister
|
// Check for force deregister
|
||||||
if s.ForceDeregister {
|
if s.ForceDeregister {
|
||||||
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
for _, region := range regions {
|
||||||
Filters: []*ec2.Filter{{
|
// get new connection for each region in which we need to deregister vms
|
||||||
Name: aws.String("name"),
|
session, err := s.AccessConfig.Session()
|
||||||
Values: []*string{aws.String(s.AMIName)},
|
if err != nil {
|
||||||
}}})
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
regionconn := ec2.New(session.Copy(&aws.Config{
|
||||||
err := fmt.Errorf("Error describing AMI: %s", err)
|
Region: aws.String(region)},
|
||||||
state.Put("error", err)
|
))
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deregister image(s) by name
|
resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
|
||||||
for _, i := range resp.Images {
|
Filters: []*ec2.Filter{{
|
||||||
_, err := ec2conn.DeregisterImage(&ec2.DeregisterImageInput{
|
Name: aws.String("name"),
|
||||||
ImageId: i.ImageId,
|
Values: []*string{aws.String(s.AMIName)},
|
||||||
})
|
}}})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
|
err := fmt.Errorf("Error describing AMI: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
ui.Say(fmt.Sprintf("Deregistered AMI %s, id: %s", s.AMIName, *i.ImageId))
|
|
||||||
|
|
||||||
// Delete snapshot(s) by image
|
// Deregister image(s) by name
|
||||||
if s.ForceDeleteSnapshot {
|
for _, i := range resp.Images {
|
||||||
for _, b := range i.BlockDeviceMappings {
|
_, err := regionconn.DeregisterImage(&ec2.DeregisterImageInput{
|
||||||
if b.Ebs != nil && aws.StringValue(b.Ebs.SnapshotId) != "" {
|
ImageId: i.ImageId,
|
||||||
_, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
|
})
|
||||||
SnapshotId: b.Ebs.SnapshotId,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error deleting existing snapshot: %s", err)
|
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
ui.Say(fmt.Sprintf("Deregistered AMI %s, id: %s", s.AMIName, *i.ImageId))
|
||||||
|
|
||||||
|
// Delete snapshot(s) by image
|
||||||
|
if s.ForceDeleteSnapshot {
|
||||||
|
for _, b := range i.BlockDeviceMappings {
|
||||||
|
if b.Ebs != nil && aws.StringValue(b.Ebs.SnapshotId) != "" {
|
||||||
|
_, err := regionconn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
|
||||||
|
SnapshotId: b.Ebs.SnapshotId,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error deleting existing snapshot: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
ui.Say(fmt.Sprintf("Deleted snapshot: %s", *b.Ebs.SnapshotId))
|
||||||
}
|
}
|
||||||
ui.Say(fmt.Sprintf("Deleted snapshot: %s", *b.Ebs.SnapshotId))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ type StepCreateEncryptedAMICopy struct {
|
||||||
KeyID string
|
KeyID string
|
||||||
EncryptBootVolume bool
|
EncryptBootVolume bool
|
||||||
Name string
|
Name string
|
||||||
|
AMIMappings []BlockDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -116,9 +117,18 @@ func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste
|
||||||
ui.Say("Deleting unencrypted snapshots")
|
ui.Say("Deleting unencrypted snapshots")
|
||||||
snapshots := state.Get("snapshots").(map[string][]string)
|
snapshots := state.Get("snapshots").(map[string][]string)
|
||||||
|
|
||||||
|
OuterLoop:
|
||||||
for _, blockDevice := range unencImage.BlockDeviceMappings {
|
for _, blockDevice := range unencImage.BlockDeviceMappings {
|
||||||
if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
|
if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
|
||||||
ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
|
// If this packer run didn't create it, then don't delete it
|
||||||
|
for _, origDevice := range s.AMIMappings {
|
||||||
|
if origDevice.SnapshotId == *blockDevice.Ebs.SnapshotId {
|
||||||
|
ui.Message(fmt.Sprintf("Keeping Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
|
||||||
|
continue OuterLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("Deleting Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
|
||||||
deleteSnapOpts := &ec2.DeleteSnapshotInput{
|
deleteSnapOpts := &ec2.DeleteSnapshotInput{
|
||||||
SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
|
SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type StepModifyEBSBackedInstance struct {
|
type StepModifyEBSBackedInstance struct {
|
||||||
EnableEnhancedNetworking bool
|
EnableAMIENASupport bool
|
||||||
|
EnableAMISriovNetSupport bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -18,9 +19,9 @@ func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.St
|
||||||
instance := state.Get("instance").(*ec2.Instance)
|
instance := state.Get("instance").(*ec2.Instance)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
if s.EnableEnhancedNetworking {
|
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
if s.EnableAMISriovNetSupport {
|
||||||
ui.Say("Enabling Enhanced Networking (SR-IOV)...")
|
ui.Say("Enabling Enhanced Networking (SR-IOV)...")
|
||||||
simple := "simple"
|
simple := "simple"
|
||||||
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||||
|
@ -33,11 +34,13 @@ func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.St
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set EnaSupport to true.
|
// Set EnaSupport to true.
|
||||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||||
|
if s.EnableAMIENASupport {
|
||||||
ui.Say("Enabling Enhanced Networking (ENA)...")
|
ui.Say("Enabling Enhanced Networking (ENA)...")
|
||||||
_, err = ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||||
InstanceId: instance.InstanceId,
|
InstanceId: instance.InstanceId,
|
||||||
EnaSupport: &ec2.AttributeBooleanValue{Value: aws.Bool(true)},
|
EnaSupport: &ec2.AttributeBooleanValue{Value: aws.Bool(true)},
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (s *StepPreValidate) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
|
||||||
ui.Say("Prevalidating AMI Name...")
|
ui.Say(fmt.Sprintf("Prevalidating AMI Name: %s", s.DestAmiName))
|
||||||
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
||||||
Filters: []*ec2.Filter{{
|
Filters: []*ec2.Filter{{
|
||||||
Name: aws.String("name"),
|
Name: aws.String("name"),
|
||||||
|
|
|
@ -5,14 +5,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
|
||||||
retry "github.com/hashicorp/packer/common"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
|
@ -29,16 +25,14 @@ type StepRunSourceInstance struct {
|
||||||
InstanceInitiatedShutdownBehavior string
|
InstanceInitiatedShutdownBehavior string
|
||||||
InstanceType string
|
InstanceType string
|
||||||
SourceAMI string
|
SourceAMI string
|
||||||
SpotPrice string
|
|
||||||
SpotPriceProduct string
|
|
||||||
SubnetId string
|
SubnetId string
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
|
VolumeTags map[string]string
|
||||||
UserData string
|
UserData string
|
||||||
UserDataFile string
|
UserDataFile string
|
||||||
Ctx interpolate.Context
|
Ctx interpolate.Context
|
||||||
|
|
||||||
instanceId string
|
instanceId string
|
||||||
spotRequest *ec2.SpotInstanceRequest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -83,180 +77,98 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
spotPrice := s.SpotPrice
|
|
||||||
availabilityZone := s.AvailabilityZone
|
|
||||||
if spotPrice == "auto" {
|
|
||||||
ui.Message(fmt.Sprintf(
|
|
||||||
"Finding spot price for %s %s...",
|
|
||||||
s.SpotPriceProduct, s.InstanceType))
|
|
||||||
|
|
||||||
// Detect the spot price
|
|
||||||
startTime := time.Now().Add(-1 * time.Hour)
|
|
||||||
resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistoryInput{
|
|
||||||
InstanceTypes: []*string{&s.InstanceType},
|
|
||||||
ProductDescriptions: []*string{&s.SpotPriceProduct},
|
|
||||||
AvailabilityZone: &s.AvailabilityZone,
|
|
||||||
StartTime: &startTime,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error finding spot price: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
var price float64
|
|
||||||
for _, history := range resp.SpotPriceHistory {
|
|
||||||
log.Printf("[INFO] Candidate spot price: %s", *history.SpotPrice)
|
|
||||||
current, err := strconv.ParseFloat(*history.SpotPrice, 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERR] Error parsing spot price: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if price == 0 || current < price {
|
|
||||||
price = current
|
|
||||||
if s.AvailabilityZone == "" {
|
|
||||||
availabilityZone = *history.AvailabilityZone
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if price == 0 {
|
|
||||||
err := fmt.Errorf("No candidate spot prices found!")
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
} else {
|
|
||||||
// Add 0.5 cents to minimum spot bid to ensure capacity will be available
|
|
||||||
// Avoids price-too-low error in active markets which can fluctuate
|
|
||||||
price = price + 0.005
|
|
||||||
}
|
|
||||||
|
|
||||||
spotPrice = strconv.FormatFloat(price, 'f', -1, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
var instanceId string
|
var instanceId string
|
||||||
|
|
||||||
if spotPrice == "" || spotPrice == "0" {
|
ui.Say("Adding tags to source instance")
|
||||||
runOpts := &ec2.RunInstancesInput{
|
if _, exists := s.Tags["Name"]; !exists {
|
||||||
ImageId: &s.SourceAMI,
|
s.Tags["Name"] = "Packer Builder"
|
||||||
InstanceType: &s.InstanceType,
|
|
||||||
UserData: &userData,
|
|
||||||
MaxCount: aws.Int64(1),
|
|
||||||
MinCount: aws.Int64(1),
|
|
||||||
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile},
|
|
||||||
BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
|
|
||||||
Placement: &ec2.Placement{AvailabilityZone: &s.AvailabilityZone},
|
|
||||||
EbsOptimized: &s.EbsOptimized,
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyName != "" {
|
|
||||||
runOpts.KeyName = &keyName
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SubnetId != "" && s.AssociatePublicIpAddress {
|
|
||||||
runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
|
|
||||||
{
|
|
||||||
DeviceIndex: aws.Int64(0),
|
|
||||||
AssociatePublicIpAddress: &s.AssociatePublicIpAddress,
|
|
||||||
SubnetId: &s.SubnetId,
|
|
||||||
Groups: securityGroupIds,
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
runOpts.SubnetId = &s.SubnetId
|
|
||||||
runOpts.SecurityGroupIds = securityGroupIds
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.ExpectedRootDevice == "ebs" {
|
|
||||||
runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior
|
|
||||||
}
|
|
||||||
|
|
||||||
runResp, err := ec2conn.RunInstances(runOpts)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error launching source instance: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
instanceId = *runResp.Instances[0].InstanceId
|
|
||||||
} else {
|
|
||||||
ui.Message(fmt.Sprintf(
|
|
||||||
"Requesting spot instance '%s' for: %s",
|
|
||||||
s.InstanceType, spotPrice))
|
|
||||||
|
|
||||||
runOpts := &ec2.RequestSpotLaunchSpecification{
|
|
||||||
ImageId: &s.SourceAMI,
|
|
||||||
InstanceType: &s.InstanceType,
|
|
||||||
UserData: &userData,
|
|
||||||
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile},
|
|
||||||
Placement: &ec2.SpotPlacement{
|
|
||||||
AvailabilityZone: &availabilityZone,
|
|
||||||
},
|
|
||||||
BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
|
|
||||||
EbsOptimized: &s.EbsOptimized,
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SubnetId != "" && s.AssociatePublicIpAddress {
|
|
||||||
runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
|
|
||||||
{
|
|
||||||
DeviceIndex: aws.Int64(0),
|
|
||||||
AssociatePublicIpAddress: &s.AssociatePublicIpAddress,
|
|
||||||
SubnetId: &s.SubnetId,
|
|
||||||
Groups: securityGroupIds,
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
runOpts.SubnetId = &s.SubnetId
|
|
||||||
runOpts.SecurityGroupIds = securityGroupIds
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyName != "" {
|
|
||||||
runOpts.KeyName = &keyName
|
|
||||||
}
|
|
||||||
|
|
||||||
runSpotResp, err := ec2conn.RequestSpotInstances(&ec2.RequestSpotInstancesInput{
|
|
||||||
SpotPrice: &spotPrice,
|
|
||||||
LaunchSpecification: runOpts,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error launching source spot instance: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
s.spotRequest = runSpotResp.SpotInstanceRequests[0]
|
|
||||||
|
|
||||||
spotRequestId := s.spotRequest.SpotInstanceRequestId
|
|
||||||
ui.Message(fmt.Sprintf("Waiting for spot request (%s) to become active...", *spotRequestId))
|
|
||||||
stateChange := StateChangeConf{
|
|
||||||
Pending: []string{"open"},
|
|
||||||
Target: "active",
|
|
||||||
Refresh: SpotRequestStateRefreshFunc(ec2conn, *spotRequestId),
|
|
||||||
StepState: state,
|
|
||||||
}
|
|
||||||
_, err = WaitForState(&stateChange)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error waiting for spot request (%s) to become ready: %s", *spotRequestId, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
spotResp, err := ec2conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{
|
|
||||||
SpotInstanceRequestIds: []*string{spotRequestId},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error finding spot request (%s): %s", *spotRequestId, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
instanceId = *spotResp.SpotInstanceRequests[0].InstanceId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ec2Tags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
ReportTags(ui, ec2Tags)
|
||||||
|
|
||||||
|
volTags, err := ConvertToEC2Tags(s.VolumeTags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error tagging volumes: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
runOpts := &ec2.RunInstancesInput{
|
||||||
|
ImageId: &s.SourceAMI,
|
||||||
|
InstanceType: &s.InstanceType,
|
||||||
|
UserData: &userData,
|
||||||
|
MaxCount: aws.Int64(1),
|
||||||
|
MinCount: aws.Int64(1),
|
||||||
|
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile},
|
||||||
|
BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
|
||||||
|
Placement: &ec2.Placement{AvailabilityZone: &s.AvailabilityZone},
|
||||||
|
EbsOptimized: &s.EbsOptimized,
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagSpecs []*ec2.TagSpecification
|
||||||
|
|
||||||
|
if len(ec2Tags) > 0 {
|
||||||
|
runTags := &ec2.TagSpecification{
|
||||||
|
ResourceType: aws.String("instance"),
|
||||||
|
Tags: ec2Tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
tagSpecs = append(tagSpecs, runTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(volTags) > 0 {
|
||||||
|
runVolTags := &ec2.TagSpecification{
|
||||||
|
ResourceType: aws.String("volume"),
|
||||||
|
Tags: volTags,
|
||||||
|
}
|
||||||
|
|
||||||
|
tagSpecs = append(tagSpecs, runVolTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tagSpecs) > 0 {
|
||||||
|
runOpts.SetTagSpecifications(tagSpecs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyName != "" {
|
||||||
|
runOpts.KeyName = &keyName
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.SubnetId != "" && s.AssociatePublicIpAddress {
|
||||||
|
runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
|
||||||
|
{
|
||||||
|
DeviceIndex: aws.Int64(0),
|
||||||
|
AssociatePublicIpAddress: &s.AssociatePublicIpAddress,
|
||||||
|
SubnetId: &s.SubnetId,
|
||||||
|
Groups: securityGroupIds,
|
||||||
|
DeleteOnTermination: aws.Bool(true),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runOpts.SubnetId = &s.SubnetId
|
||||||
|
runOpts.SecurityGroupIds = securityGroupIds
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ExpectedRootDevice == "ebs" {
|
||||||
|
runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior
|
||||||
|
}
|
||||||
|
|
||||||
|
runResp, err := ec2conn.RunInstances(runOpts)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error launching source instance: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
instanceId = *runResp.Instances[0].InstanceId
|
||||||
|
|
||||||
// Set the instance ID so that the cleanup works properly
|
// Set the instance ID so that the cleanup works properly
|
||||||
s.instanceId = instanceId
|
s.instanceId = instanceId
|
||||||
|
|
||||||
|
@ -278,45 +190,6 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||||
|
|
||||||
instance := latestInstance.(*ec2.Instance)
|
instance := latestInstance.(*ec2.Instance)
|
||||||
|
|
||||||
ui.Say("Adding tags to source instance")
|
|
||||||
if _, exists := s.Tags["Name"]; !exists {
|
|
||||||
s.Tags["Name"] = "Packer Builder"
|
|
||||||
}
|
|
||||||
|
|
||||||
ec2Tags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error tagging source instance: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
ReportTags(ui, ec2Tags)
|
|
||||||
|
|
||||||
// Retry creating tags for about 2.5 minutes
|
|
||||||
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
|
|
||||||
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
|
|
||||||
Tags: ec2Tags,
|
|
||||||
Resources: []*string{instance.InstanceId},
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if awsErr, ok := err.(awserr.Error); ok {
|
|
||||||
if awsErr.Code() == "InvalidInstanceID.NotFound" {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, err
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error tagging source instance: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Debug {
|
if s.Debug {
|
||||||
if instance.PublicDnsName != nil && *instance.PublicDnsName != "" {
|
if instance.PublicDnsName != nil && *instance.PublicDnsName != "" {
|
||||||
ui.Message(fmt.Sprintf("Public DNS: %s", *instance.PublicDnsName))
|
ui.Message(fmt.Sprintf("Public DNS: %s", *instance.PublicDnsName))
|
||||||
|
@ -341,29 +214,6 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
|
||||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
// Cancel the spot request if it exists
|
|
||||||
if s.spotRequest != nil {
|
|
||||||
ui.Say("Cancelling the spot request...")
|
|
||||||
input := &ec2.CancelSpotInstanceRequestsInput{
|
|
||||||
SpotInstanceRequestIds: []*string{s.spotRequest.SpotInstanceRequestId},
|
|
||||||
}
|
|
||||||
if _, err := ec2conn.CancelSpotInstanceRequests(input); err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("Error cancelling the spot request, may still be around: %s", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stateChange := StateChangeConf{
|
|
||||||
Pending: []string{"active", "open"},
|
|
||||||
Refresh: SpotRequestStateRefreshFunc(ec2conn, *s.spotRequest.SpotInstanceRequestId),
|
|
||||||
Target: "cancelled",
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := WaitForState(&stateChange)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terminate the source instance if it exists
|
// Terminate the source instance if it exists
|
||||||
if s.instanceId != "" {
|
if s.instanceId != "" {
|
||||||
ui.Say("Terminating the source AWS instance...")
|
ui.Say("Terminating the source AWS instance...")
|
||||||
|
|
|
@ -0,0 +1,372 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
|
||||||
|
retry "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepRunSpotInstance struct {
|
||||||
|
AssociatePublicIpAddress bool
|
||||||
|
AvailabilityZone string
|
||||||
|
BlockDevices BlockDevices
|
||||||
|
Debug bool
|
||||||
|
EbsOptimized bool
|
||||||
|
ExpectedRootDevice string
|
||||||
|
IamInstanceProfile string
|
||||||
|
InstanceInitiatedShutdownBehavior string
|
||||||
|
InstanceType string
|
||||||
|
SourceAMI string
|
||||||
|
SpotPrice string
|
||||||
|
SpotPriceProduct string
|
||||||
|
SubnetId string
|
||||||
|
Tags map[string]string
|
||||||
|
VolumeTags map[string]string
|
||||||
|
UserData string
|
||||||
|
UserDataFile string
|
||||||
|
Ctx interpolate.Context
|
||||||
|
|
||||||
|
instanceId string
|
||||||
|
spotRequest *ec2.SpotInstanceRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRunSpotInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
var keyName string
|
||||||
|
if name, ok := state.GetOk("keyPair"); ok {
|
||||||
|
keyName = name.(string)
|
||||||
|
}
|
||||||
|
securityGroupIds := aws.StringSlice(state.Get("securityGroupIds").([]string))
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
userData := s.UserData
|
||||||
|
if s.UserDataFile != "" {
|
||||||
|
contents, err := ioutil.ReadFile(s.UserDataFile)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("Problem reading user data file: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
userData = string(contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if it is encoded already, and if not, encode it
|
||||||
|
if _, err := base64.StdEncoding.DecodeString(userData); err != nil {
|
||||||
|
log.Printf("[DEBUG] base64 encoding user data...")
|
||||||
|
userData = base64.StdEncoding.EncodeToString([]byte(userData))
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Launching a source AWS instance...")
|
||||||
|
image, ok := state.Get("source_image").(*ec2.Image)
|
||||||
|
if !ok {
|
||||||
|
state.Put("error", fmt.Errorf("source_image type assertion failed"))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
s.SourceAMI = *image.ImageId
|
||||||
|
|
||||||
|
if s.ExpectedRootDevice != "" && *image.RootDeviceType != s.ExpectedRootDevice {
|
||||||
|
state.Put("error", fmt.Errorf(
|
||||||
|
"The provided source AMI has an invalid root device type.\n"+
|
||||||
|
"Expected '%s', got '%s'.",
|
||||||
|
s.ExpectedRootDevice, *image.RootDeviceType))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
spotPrice := s.SpotPrice
|
||||||
|
availabilityZone := s.AvailabilityZone
|
||||||
|
if spotPrice == "auto" {
|
||||||
|
ui.Message(fmt.Sprintf(
|
||||||
|
"Finding spot price for %s %s...",
|
||||||
|
s.SpotPriceProduct, s.InstanceType))
|
||||||
|
|
||||||
|
// Detect the spot price
|
||||||
|
startTime := time.Now().Add(-1 * time.Hour)
|
||||||
|
resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistoryInput{
|
||||||
|
InstanceTypes: []*string{&s.InstanceType},
|
||||||
|
ProductDescriptions: []*string{&s.SpotPriceProduct},
|
||||||
|
AvailabilityZone: &s.AvailabilityZone,
|
||||||
|
StartTime: &startTime,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error finding spot price: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
var price float64
|
||||||
|
for _, history := range resp.SpotPriceHistory {
|
||||||
|
log.Printf("[INFO] Candidate spot price: %s", *history.SpotPrice)
|
||||||
|
current, err := strconv.ParseFloat(*history.SpotPrice, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERR] Error parsing spot price: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if price == 0 || current < price {
|
||||||
|
price = current
|
||||||
|
if s.AvailabilityZone == "" {
|
||||||
|
availabilityZone = *history.AvailabilityZone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if price == 0 {
|
||||||
|
err := fmt.Errorf("No candidate spot prices found!")
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
} else {
|
||||||
|
// Add 0.5 cents to minimum spot bid to ensure capacity will be available
|
||||||
|
// Avoids price-too-low error in active markets which can fluctuate
|
||||||
|
price = price + 0.005
|
||||||
|
}
|
||||||
|
|
||||||
|
spotPrice = strconv.FormatFloat(price, 'f', -1, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var instanceId string
|
||||||
|
|
||||||
|
ui.Say("Adding tags to source instance")
|
||||||
|
if _, exists := s.Tags["Name"]; !exists {
|
||||||
|
s.Tags["Name"] = "Packer Builder"
|
||||||
|
}
|
||||||
|
|
||||||
|
ec2Tags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
ReportTags(ui, ec2Tags)
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf(
|
||||||
|
"Requesting spot instance '%s' for: %s",
|
||||||
|
s.InstanceType, spotPrice))
|
||||||
|
|
||||||
|
runOpts := &ec2.RequestSpotLaunchSpecification{
|
||||||
|
ImageId: &s.SourceAMI,
|
||||||
|
InstanceType: &s.InstanceType,
|
||||||
|
UserData: &userData,
|
||||||
|
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile},
|
||||||
|
Placement: &ec2.SpotPlacement{
|
||||||
|
AvailabilityZone: &availabilityZone,
|
||||||
|
},
|
||||||
|
BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
|
||||||
|
EbsOptimized: &s.EbsOptimized,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.SubnetId != "" && s.AssociatePublicIpAddress {
|
||||||
|
runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
|
||||||
|
{
|
||||||
|
DeviceIndex: aws.Int64(0),
|
||||||
|
AssociatePublicIpAddress: &s.AssociatePublicIpAddress,
|
||||||
|
SubnetId: &s.SubnetId,
|
||||||
|
Groups: securityGroupIds,
|
||||||
|
DeleteOnTermination: aws.Bool(true),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runOpts.SubnetId = &s.SubnetId
|
||||||
|
runOpts.SecurityGroupIds = securityGroupIds
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyName != "" {
|
||||||
|
runOpts.KeyName = &keyName
|
||||||
|
}
|
||||||
|
|
||||||
|
runSpotResp, err := ec2conn.RequestSpotInstances(&ec2.RequestSpotInstancesInput{
|
||||||
|
SpotPrice: &spotPrice,
|
||||||
|
LaunchSpecification: runOpts,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error launching source spot instance: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.spotRequest = runSpotResp.SpotInstanceRequests[0]
|
||||||
|
|
||||||
|
spotRequestId := s.spotRequest.SpotInstanceRequestId
|
||||||
|
ui.Message(fmt.Sprintf("Waiting for spot request (%s) to become active...", *spotRequestId))
|
||||||
|
stateChange := StateChangeConf{
|
||||||
|
Pending: []string{"open"},
|
||||||
|
Target: "active",
|
||||||
|
Refresh: SpotRequestStateRefreshFunc(ec2conn, *spotRequestId),
|
||||||
|
StepState: state,
|
||||||
|
}
|
||||||
|
_, err = WaitForState(&stateChange)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error waiting for spot request (%s) to become ready: %s", *spotRequestId, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
spotResp, err := ec2conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{
|
||||||
|
SpotInstanceRequestIds: []*string{spotRequestId},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error finding spot request (%s): %s", *spotRequestId, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
instanceId = *spotResp.SpotInstanceRequests[0].InstanceId
|
||||||
|
|
||||||
|
// Set the instance ID so that the cleanup works properly
|
||||||
|
s.instanceId = instanceId
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("Instance ID: %s", instanceId))
|
||||||
|
ui.Say(fmt.Sprintf("Waiting for instance (%v) to become ready...", instanceId))
|
||||||
|
stateChangeSpot := StateChangeConf{
|
||||||
|
Pending: []string{"pending"},
|
||||||
|
Target: "running",
|
||||||
|
Refresh: InstanceStateRefreshFunc(ec2conn, instanceId),
|
||||||
|
StepState: state,
|
||||||
|
}
|
||||||
|
latestInstance, err := WaitForState(&stateChangeSpot)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := latestInstance.(*ec2.Instance)
|
||||||
|
|
||||||
|
// Retry creating tags for about 2.5 minutes
|
||||||
|
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
|
||||||
|
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
|
||||||
|
Tags: ec2Tags,
|
||||||
|
Resources: []*string{instance.InstanceId},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if awsErr.Code() == "InvalidInstanceID.NotFound" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeIds := make([]*string, 0)
|
||||||
|
for _, v := range instance.BlockDeviceMappings {
|
||||||
|
if ebs := v.Ebs; ebs != nil {
|
||||||
|
volumeIds = append(volumeIds, ebs.VolumeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(volumeIds) > 0 {
|
||||||
|
ui.Say("Adding tags to source EBS Volumes")
|
||||||
|
tags, err := ConvertToEC2Tags(s.VolumeTags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportTags(ui, tags)
|
||||||
|
|
||||||
|
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
|
||||||
|
Resources: volumeIds,
|
||||||
|
Tags: tags,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Debug {
|
||||||
|
if instance.PublicDnsName != nil && *instance.PublicDnsName != "" {
|
||||||
|
ui.Message(fmt.Sprintf("Public DNS: %s", *instance.PublicDnsName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if instance.PublicIpAddress != nil && *instance.PublicIpAddress != "" {
|
||||||
|
ui.Message(fmt.Sprintf("Public IP: %s", *instance.PublicIpAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
if instance.PrivateIpAddress != nil && *instance.PrivateIpAddress != "" {
|
||||||
|
ui.Message(fmt.Sprintf("Private IP: %s", *instance.PrivateIpAddress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Put("instance", instance)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRunSpotInstance) Cleanup(state multistep.StateBag) {
|
||||||
|
|
||||||
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
// Cancel the spot request if it exists
|
||||||
|
if s.spotRequest != nil {
|
||||||
|
ui.Say("Cancelling the spot request...")
|
||||||
|
input := &ec2.CancelSpotInstanceRequestsInput{
|
||||||
|
SpotInstanceRequestIds: []*string{s.spotRequest.SpotInstanceRequestId},
|
||||||
|
}
|
||||||
|
if _, err := ec2conn.CancelSpotInstanceRequests(input); err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error cancelling the spot request, may still be around: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stateChange := StateChangeConf{
|
||||||
|
Pending: []string{"active", "open"},
|
||||||
|
Refresh: SpotRequestStateRefreshFunc(ec2conn, *s.spotRequest.SpotInstanceRequestId),
|
||||||
|
Target: "cancelled",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := WaitForState(&stateChange)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate the source instance if it exists
|
||||||
|
if s.instanceId != "" {
|
||||||
|
ui.Say("Terminating the source AWS instance...")
|
||||||
|
if _, err := ec2conn.TerminateInstances(&ec2.TerminateInstancesInput{InstanceIds: []*string{&s.instanceId}}); err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error terminating instance, may still be around: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stateChange := StateChangeConf{
|
||||||
|
Pending: []string{"pending", "running", "shutting-down", "stopped", "stopping"},
|
||||||
|
Refresh: InstanceStateRefreshFunc(ec2conn, s.instanceId),
|
||||||
|
Target: "terminated",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := WaitForState(&stateChange)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/private/waiter"
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/hashicorp/packer/common/uuid"
|
"github.com/hashicorp/packer/common/uuid"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
@ -15,9 +15,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type StepSecurityGroup struct {
|
type StepSecurityGroup struct {
|
||||||
CommConfig *communicator.Config
|
CommConfig *communicator.Config
|
||||||
SecurityGroupIds []string
|
SecurityGroupIds []string
|
||||||
VpcId string
|
VpcId string
|
||||||
|
TemporarySGSourceCidr string
|
||||||
|
|
||||||
createdGroupId string
|
createdGroupId string
|
||||||
}
|
}
|
||||||
|
@ -45,7 +46,9 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
port := s.CommConfig.Port()
|
port := s.CommConfig.Port()
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
panic("port must be set to a non-zero value.")
|
if s.CommConfig.Type != "none" {
|
||||||
|
panic("port must be set to a non-zero value.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the group
|
// Create the group
|
||||||
|
@ -76,15 +79,15 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
IpProtocol: aws.String("tcp"),
|
IpProtocol: aws.String("tcp"),
|
||||||
FromPort: aws.Int64(int64(port)),
|
FromPort: aws.Int64(int64(port)),
|
||||||
ToPort: aws.Int64(int64(port)),
|
ToPort: aws.Int64(int64(port)),
|
||||||
CidrIp: aws.String("0.0.0.0/0"),
|
CidrIp: aws.String(s.TemporarySGSourceCidr),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We loop and retry this a few times because sometimes the security
|
// We loop and retry this a few times because sometimes the security
|
||||||
// group isn't available immediately because AWS resources are eventually
|
// group isn't available immediately because AWS resources are eventually
|
||||||
// consistent.
|
// consistent.
|
||||||
ui.Say(fmt.Sprintf(
|
ui.Say(fmt.Sprintf(
|
||||||
"Authorizing access to port %d on the temporary security group...",
|
"Authorizing access to port %d from %s in the temporary security group...",
|
||||||
port))
|
port, s.TemporarySGSourceCidr))
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
_, err = ec2conn.AuthorizeSecurityGroupIngress(req)
|
_, err = ec2conn.AuthorizeSecurityGroupIngress(req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -151,36 +154,42 @@ func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
|
func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
|
||||||
waiterCfg := waiter.Config{
|
ctx := aws.BackgroundContext()
|
||||||
Operation: "DescribeSecurityGroups",
|
w := request.Waiter{
|
||||||
Delay: 15,
|
Name: "DescribeSecurityGroups",
|
||||||
MaxAttempts: 40,
|
MaxAttempts: 40,
|
||||||
Acceptors: []waiter.WaitAcceptor{
|
Acceptors: []request.WaiterAcceptor{
|
||||||
{
|
{
|
||||||
State: "success",
|
State: request.SuccessWaiterState,
|
||||||
Matcher: "path",
|
Matcher: request.PathWaiterMatch,
|
||||||
Argument: "length(SecurityGroups[]) > `0`",
|
Argument: "length(SecurityGroups[]) > `0`",
|
||||||
Expected: true,
|
Expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
State: "retry",
|
State: request.RetryWaiterState,
|
||||||
Matcher: "error",
|
Matcher: request.ErrorWaiterMatch,
|
||||||
Argument: "",
|
Argument: "",
|
||||||
Expected: "InvalidGroup.NotFound",
|
Expected: "InvalidGroup.NotFound",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
State: "retry",
|
State: request.RetryWaiterState,
|
||||||
Matcher: "error",
|
Matcher: request.ErrorWaiterMatch,
|
||||||
Argument: "",
|
Argument: "",
|
||||||
Expected: "InvalidSecurityGroupID.NotFound",
|
Expected: "InvalidSecurityGroupID.NotFound",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Logger: c.Config.Logger,
|
||||||
|
NewRequest: func(opts []request.Option) (*request.Request, error) {
|
||||||
|
var inCpy *ec2.DescribeSecurityGroupsInput
|
||||||
|
if input != nil {
|
||||||
|
tmp := *input
|
||||||
|
inCpy = &tmp
|
||||||
|
}
|
||||||
|
req, _ := c.DescribeSecurityGroupsRequest(inCpy)
|
||||||
|
req.SetContext(ctx)
|
||||||
|
req.ApplyOptions(opts...)
|
||||||
|
return req, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
return w.WaitWithContext(ctx)
|
||||||
w := waiter.Waiter{
|
|
||||||
Client: c,
|
|
||||||
Input: input,
|
|
||||||
Config: waiterCfg,
|
|
||||||
}
|
|
||||||
return w.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ import (
|
||||||
// Produces:
|
// Produces:
|
||||||
// source_image *ec2.Image - the source AMI info
|
// source_image *ec2.Image - the source AMI info
|
||||||
type StepSourceAMIInfo struct {
|
type StepSourceAMIInfo struct {
|
||||||
SourceAmi string
|
SourceAmi string
|
||||||
EnhancedNetworking bool
|
EnableAMISriovNetSupport bool
|
||||||
AmiFilters AmiFilterOptions
|
EnableAMIENASupport bool
|
||||||
|
AmiFilters AmiFilterOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a slice of AMI filter options from the filters provided.
|
// Build a slice of AMI filter options from the filters provided.
|
||||||
|
@ -103,7 +104,7 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
// Enhanced Networking can only be enabled on HVM AMIs.
|
// Enhanced Networking can only be enabled on HVM AMIs.
|
||||||
// See http://goo.gl/icuXh5
|
// See http://goo.gl/icuXh5
|
||||||
if s.EnhancedNetworking && *image.VirtualizationType != "hvm" {
|
if (s.EnableAMIENASupport || s.EnableAMISriovNetSupport) && *image.VirtualizationType != "hvm" {
|
||||||
err := fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
|
err := fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
|
||||||
"github.com/mitchellh/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepTagEBSVolumes struct {
|
|
||||||
VolumeRunTags map[string]string
|
|
||||||
Ctx interpolate.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
|
|
||||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
|
||||||
instance := state.Get("instance").(*ec2.Instance)
|
|
||||||
sourceAMI := state.Get("source_image").(*ec2.Image)
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
|
||||||
|
|
||||||
if len(s.VolumeRunTags) == 0 {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeIds := make([]*string, 0)
|
|
||||||
for _, v := range instance.BlockDeviceMappings {
|
|
||||||
if ebs := v.Ebs; ebs != nil {
|
|
||||||
volumeIds = append(volumeIds, ebs.VolumeId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(volumeIds) == 0 {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say("Adding tags to source EBS Volumes")
|
|
||||||
tags, err := ConvertToEC2Tags(s.VolumeRunTags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
ReportTags(ui, tags)
|
|
||||||
|
|
||||||
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
|
|
||||||
Resources: volumeIds,
|
|
||||||
Tags: tags,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepTagEBSVolumes) Cleanup(state multistep.StateBag) {
|
|
||||||
// No cleanup...
|
|
||||||
}
|
|
|
@ -108,34 +108,29 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Build the steps
|
var instanceStep multistep.Step
|
||||||
steps := []multistep.Step{
|
|
||||||
&awscommon.StepPreValidate{
|
if b.config.SpotPrice == "" || b.config.SpotPrice == "0" {
|
||||||
DestAmiName: b.config.AMIName,
|
instanceStep = &awscommon.StepRunSourceInstance{
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
ExpectedRootDevice: "ebs",
|
||||||
&awscommon.StepSourceAMIInfo{
|
InstanceType: b.config.InstanceType,
|
||||||
SourceAmi: b.config.SourceAmi,
|
UserData: b.config.UserData,
|
||||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
UserDataFile: b.config.UserDataFile,
|
||||||
AmiFilters: b.config.SourceAmiFilter,
|
SourceAMI: b.config.SourceAmi,
|
||||||
},
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
&awscommon.StepKeyPair{
|
SubnetId: b.config.SubnetId,
|
||||||
Debug: b.config.PackerDebug,
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
KeyPairName: b.config.SSHKeyPairName,
|
BlockDevices: b.config.BlockDevices,
|
||||||
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
Tags: b.config.RunTags,
|
||||||
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
},
|
Ctx: b.config.ctx,
|
||||||
&awscommon.StepSecurityGroup{
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
SecurityGroupIds: b.config.SecurityGroupIds,
|
}
|
||||||
CommConfig: &b.config.RunConfig.Comm,
|
} else {
|
||||||
VpcId: b.config.VpcId,
|
instanceStep = &awscommon.StepRunSpotInstance{
|
||||||
},
|
|
||||||
&stepCleanupVolumes{
|
|
||||||
BlockDevices: b.config.BlockDevices,
|
|
||||||
},
|
|
||||||
&awscommon.StepRunSourceInstance{
|
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
|
@ -151,13 +146,42 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
AvailabilityZone: b.config.AvailabilityZone,
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
BlockDevices: b.config.BlockDevices,
|
BlockDevices: b.config.BlockDevices,
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
Ctx: b.config.ctx,
|
Ctx: b.config.ctx,
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the steps
|
||||||
|
steps := []multistep.Step{
|
||||||
|
&awscommon.StepPreValidate{
|
||||||
|
DestAmiName: b.config.AMIName,
|
||||||
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
},
|
},
|
||||||
&awscommon.StepTagEBSVolumes{
|
&awscommon.StepSourceAMIInfo{
|
||||||
VolumeRunTags: b.config.VolumeRunTags,
|
SourceAmi: b.config.SourceAmi,
|
||||||
Ctx: b.config.ctx,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AmiFilters: b.config.SourceAmiFilter,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepKeyPair{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
||||||
|
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
||||||
|
KeyPairName: b.config.SSHKeyPairName,
|
||||||
|
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
||||||
|
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
||||||
|
},
|
||||||
|
&awscommon.StepSecurityGroup{
|
||||||
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
|
VpcId: b.config.VpcId,
|
||||||
|
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
|
||||||
|
},
|
||||||
|
&stepCleanupVolumes{
|
||||||
|
BlockDevices: b.config.BlockDevices,
|
||||||
|
},
|
||||||
|
instanceStep,
|
||||||
&awscommon.StepGetPassword{
|
&awscommon.StepGetPassword{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
Comm: &b.config.RunConfig.Comm,
|
Comm: &b.config.RunConfig.Comm,
|
||||||
|
@ -179,18 +203,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
DisableStopInstance: b.config.DisableStopInstance,
|
DisableStopInstance: b.config.DisableStopInstance,
|
||||||
},
|
},
|
||||||
&awscommon.StepModifyEBSBackedInstance{
|
&awscommon.StepModifyEBSBackedInstance{
|
||||||
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
},
|
},
|
||||||
&awscommon.StepDeregisterAMI{
|
&awscommon.StepDeregisterAMI{
|
||||||
|
AccessConfig: &b.config.AccessConfig,
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||||
AMIName: b.config.AMIName,
|
AMIName: b.config.AMIName,
|
||||||
|
Regions: b.config.AMIRegions,
|
||||||
},
|
},
|
||||||
&stepCreateAMI{},
|
&stepCreateAMI{},
|
||||||
&awscommon.StepCreateEncryptedAMICopy{
|
&awscommon.StepCreateEncryptedAMICopy{
|
||||||
KeyID: b.config.AMIKmsKeyId,
|
KeyID: b.config.AMIKmsKeyId,
|
||||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||||
Name: b.config.AMIName,
|
Name: b.config.AMIName,
|
||||||
|
AMIMappings: b.config.AMIBlockDevices.AMIMappings,
|
||||||
},
|
},
|
||||||
&awscommon.StepAMIRegionCopy{
|
&awscommon.StepAMIRegionCopy{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
|
|
|
@ -122,31 +122,29 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Build the steps
|
var instanceStep multistep.Step
|
||||||
steps := []multistep.Step{
|
|
||||||
&awscommon.StepPreValidate{
|
if b.config.SpotPrice == "" || b.config.SpotPrice == "0" {
|
||||||
DestAmiName: b.config.AMIName,
|
instanceStep = &awscommon.StepRunSourceInstance{
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
ExpectedRootDevice: "ebs",
|
||||||
&awscommon.StepSourceAMIInfo{
|
InstanceType: b.config.InstanceType,
|
||||||
SourceAmi: b.config.SourceAmi,
|
UserData: b.config.UserData,
|
||||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
UserDataFile: b.config.UserDataFile,
|
||||||
AmiFilters: b.config.SourceAmiFilter,
|
SourceAMI: b.config.SourceAmi,
|
||||||
},
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
&awscommon.StepKeyPair{
|
SubnetId: b.config.SubnetId,
|
||||||
Debug: b.config.PackerDebug,
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
KeyPairName: b.config.SSHKeyPairName,
|
BlockDevices: b.config.BlockDevices,
|
||||||
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
Tags: b.config.RunTags,
|
||||||
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
},
|
Ctx: b.config.ctx,
|
||||||
&awscommon.StepSecurityGroup{
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
SecurityGroupIds: b.config.SecurityGroupIds,
|
}
|
||||||
CommConfig: &b.config.RunConfig.Comm,
|
} else {
|
||||||
VpcId: b.config.VpcId,
|
instanceStep = &awscommon.StepRunSpotInstance{
|
||||||
},
|
|
||||||
&awscommon.StepRunSourceInstance{
|
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
|
@ -162,12 +160,39 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
AvailabilityZone: b.config.AvailabilityZone,
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
BlockDevices: b.config.BlockDevices,
|
BlockDevices: b.config.BlockDevices,
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
|
VolumeTags: b.config.VolumeRunTags,
|
||||||
|
Ctx: b.config.ctx,
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the steps
|
||||||
|
steps := []multistep.Step{
|
||||||
|
&awscommon.StepPreValidate{
|
||||||
|
DestAmiName: b.config.AMIName,
|
||||||
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
},
|
},
|
||||||
&awscommon.StepTagEBSVolumes{
|
&awscommon.StepSourceAMIInfo{
|
||||||
VolumeRunTags: b.config.VolumeRunTags,
|
SourceAmi: b.config.SourceAmi,
|
||||||
Ctx: b.config.ctx,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AmiFilters: b.config.SourceAmiFilter,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepKeyPair{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
||||||
|
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
||||||
|
KeyPairName: b.config.SSHKeyPairName,
|
||||||
|
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
||||||
|
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
||||||
|
},
|
||||||
|
&awscommon.StepSecurityGroup{
|
||||||
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
|
VpcId: b.config.VpcId,
|
||||||
|
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
|
||||||
|
},
|
||||||
|
instanceStep,
|
||||||
&awscommon.StepGetPassword{
|
&awscommon.StepGetPassword{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
Comm: &b.config.RunConfig.Comm,
|
Comm: &b.config.RunConfig.Comm,
|
||||||
|
@ -189,19 +214,24 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
DisableStopInstance: b.config.DisableStopInstance,
|
DisableStopInstance: b.config.DisableStopInstance,
|
||||||
},
|
},
|
||||||
&awscommon.StepModifyEBSBackedInstance{
|
&awscommon.StepModifyEBSBackedInstance{
|
||||||
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
},
|
},
|
||||||
&StepSnapshotNewRootVolume{
|
&StepSnapshotNewRootVolume{
|
||||||
NewRootMountPoint: b.config.RootDevice.SourceDeviceName,
|
NewRootMountPoint: b.config.RootDevice.SourceDeviceName,
|
||||||
},
|
},
|
||||||
&awscommon.StepDeregisterAMI{
|
&awscommon.StepDeregisterAMI{
|
||||||
|
AccessConfig: &b.config.AccessConfig,
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||||
AMIName: b.config.AMIName,
|
AMIName: b.config.AMIName,
|
||||||
|
Regions: b.config.AMIRegions,
|
||||||
},
|
},
|
||||||
&StepRegisterAMI{
|
&StepRegisterAMI{
|
||||||
RootDevice: b.config.RootDevice,
|
RootDevice: b.config.RootDevice,
|
||||||
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
|
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
|
||||||
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
},
|
},
|
||||||
&awscommon.StepCreateEncryptedAMICopy{
|
&awscommon.StepCreateEncryptedAMICopy{
|
||||||
KeyID: b.config.AMIKmsKeyId,
|
KeyID: b.config.AMIKmsKeyId,
|
||||||
|
|
|
@ -12,9 +12,11 @@ import (
|
||||||
|
|
||||||
// StepRegisterAMI creates the AMI.
|
// StepRegisterAMI creates the AMI.
|
||||||
type StepRegisterAMI struct {
|
type StepRegisterAMI struct {
|
||||||
RootDevice RootBlockDevice
|
RootDevice RootBlockDevice
|
||||||
BlockDevices []*ec2.BlockDeviceMapping
|
BlockDevices []*ec2.BlockDeviceMapping
|
||||||
image *ec2.Image
|
EnableAMIENASupport bool
|
||||||
|
EnableAMISriovNetSupport bool
|
||||||
|
image *ec2.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -25,16 +27,7 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
ui.Say("Registering the AMI...")
|
ui.Say("Registering the AMI...")
|
||||||
|
|
||||||
blockDevicesExcludingRoot := make([]*ec2.BlockDeviceMapping, 0, len(s.BlockDevices)-1)
|
blockDevicesExcludingRoot := DeduplicateRootVolume(s.BlockDevices, s.RootDevice, snapshotId)
|
||||||
for _, blockDevice := range s.BlockDevices {
|
|
||||||
if *blockDevice.DeviceName == s.RootDevice.SourceDeviceName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, blockDevice)
|
|
||||||
}
|
|
||||||
|
|
||||||
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, s.RootDevice.createBlockDeviceMapping(snapshotId))
|
|
||||||
|
|
||||||
registerOpts := &ec2.RegisterImageInput{
|
registerOpts := &ec2.RegisterImageInput{
|
||||||
Name: &config.AMIName,
|
Name: &config.AMIName,
|
||||||
|
@ -44,16 +37,16 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
BlockDeviceMappings: blockDevicesExcludingRoot,
|
BlockDeviceMappings: blockDevicesExcludingRoot,
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.AMIEnhancedNetworking {
|
if s.EnableAMISriovNetSupport {
|
||||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||||
registerOpts.SriovNetSupport = aws.String("simple")
|
registerOpts.SriovNetSupport = aws.String("simple")
|
||||||
|
}
|
||||||
|
if s.EnableAMIENASupport {
|
||||||
// Set EnaSupport to true
|
// Set EnaSupport to true
|
||||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||||
registerOpts.EnaSupport = aws.Bool(true)
|
registerOpts.EnaSupport = aws.Bool(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerResp, err := ec2conn.RegisterImage(registerOpts)
|
registerResp, err := ec2conn.RegisterImage(registerOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
|
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
|
||||||
|
@ -125,3 +118,18 @@ func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeduplicateRootVolume(BlockDevices []*ec2.BlockDeviceMapping, RootDevice RootBlockDevice, snapshotId string) []*ec2.BlockDeviceMapping {
|
||||||
|
// Defensive coding to make sure we only add the root volume once
|
||||||
|
blockDevicesExcludingRoot := make([]*ec2.BlockDeviceMapping, 0, len(BlockDevices))
|
||||||
|
for _, blockDevice := range BlockDevices {
|
||||||
|
if *blockDevice.DeviceName == RootDevice.SourceDeviceName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, blockDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, RootDevice.createBlockDeviceMapping(snapshotId))
|
||||||
|
return blockDevicesExcludingRoot
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package ebssurrogate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetStringPointer() *string {
|
||||||
|
tmp := "/dev/name"
|
||||||
|
return &tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTestDevice() *ec2.BlockDeviceMapping {
|
||||||
|
TestDev := ec2.BlockDeviceMapping{
|
||||||
|
DeviceName: GetStringPointer(),
|
||||||
|
}
|
||||||
|
return &TestDev
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepRegisterAmi_DeduplicateRootVolume(t *testing.T) {
|
||||||
|
TestRootDevice := RootBlockDevice{}
|
||||||
|
TestRootDevice.SourceDeviceName = "/dev/name"
|
||||||
|
|
||||||
|
blockDevices := []*ec2.BlockDeviceMapping{}
|
||||||
|
blockDevicesExcludingRoot := DeduplicateRootVolume(blockDevices, TestRootDevice, "12342351")
|
||||||
|
if len(blockDevicesExcludingRoot) != 1 {
|
||||||
|
t.Fatalf("Unexpected length of block devices list")
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBlockDevice := GetTestDevice()
|
||||||
|
blockDevices = append(blockDevices, TestBlockDevice)
|
||||||
|
blockDevicesExcludingRoot = DeduplicateRootVolume(blockDevices, TestRootDevice, "12342351")
|
||||||
|
if len(blockDevicesExcludingRoot) != 1 {
|
||||||
|
t.Fatalf("Unexpected length of block devices list")
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,8 +23,9 @@ type Config struct {
|
||||||
awscommon.AccessConfig `mapstructure:",squash"`
|
awscommon.AccessConfig `mapstructure:",squash"`
|
||||||
awscommon.RunConfig `mapstructure:",squash"`
|
awscommon.RunConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
|
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
|
||||||
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
AMIENASupport bool `mapstructure:"ena_support"`
|
||||||
|
AMISriovNetSupport bool `mapstructure:"sriov_support"`
|
||||||
|
|
||||||
launchBlockDevices awscommon.BlockDevices
|
launchBlockDevices awscommon.BlockDevices
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
|
@ -100,27 +101,27 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Build the steps
|
var instanceStep multistep.Step
|
||||||
steps := []multistep.Step{
|
|
||||||
&awscommon.StepSourceAMIInfo{
|
if b.config.SpotPrice == "" || b.config.SpotPrice == "0" {
|
||||||
SourceAmi: b.config.SourceAmi,
|
instanceStep = &awscommon.StepRunSourceInstance{
|
||||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
Debug: b.config.PackerDebug,
|
||||||
AmiFilters: b.config.SourceAmiFilter,
|
ExpectedRootDevice: "ebs",
|
||||||
},
|
InstanceType: b.config.InstanceType,
|
||||||
&awscommon.StepKeyPair{
|
UserData: b.config.UserData,
|
||||||
Debug: b.config.PackerDebug,
|
UserDataFile: b.config.UserDataFile,
|
||||||
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
SourceAMI: b.config.SourceAmi,
|
||||||
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
KeyPairName: b.config.SSHKeyPairName,
|
SubnetId: b.config.SubnetId,
|
||||||
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
},
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
&awscommon.StepSecurityGroup{
|
Tags: b.config.RunTags,
|
||||||
SecurityGroupIds: b.config.SecurityGroupIds,
|
Ctx: b.config.ctx,
|
||||||
CommConfig: &b.config.RunConfig.Comm,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
VpcId: b.config.VpcId,
|
}
|
||||||
},
|
} else {
|
||||||
&awscommon.StepRunSourceInstance{
|
instanceStep = &awscommon.StepRunSpotInstance{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
ExpectedRootDevice: "ebs",
|
ExpectedRootDevice: "ebs",
|
||||||
SpotPrice: b.config.SpotPrice,
|
SpotPrice: b.config.SpotPrice,
|
||||||
|
@ -134,15 +135,35 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
AvailabilityZone: b.config.AvailabilityZone,
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
BlockDevices: b.config.launchBlockDevices,
|
|
||||||
Tags: b.config.RunTags,
|
Tags: b.config.RunTags,
|
||||||
Ctx: b.config.ctx,
|
Ctx: b.config.ctx,
|
||||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the steps
|
||||||
|
steps := []multistep.Step{
|
||||||
|
&awscommon.StepSourceAMIInfo{
|
||||||
|
SourceAmi: b.config.SourceAmi,
|
||||||
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AmiFilters: b.config.SourceAmiFilter,
|
||||||
},
|
},
|
||||||
&stepTagEBSVolumes{
|
&awscommon.StepKeyPair{
|
||||||
VolumeMapping: b.config.VolumeMappings,
|
Debug: b.config.PackerDebug,
|
||||||
Ctx: b.config.ctx,
|
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
||||||
|
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
||||||
|
KeyPairName: b.config.SSHKeyPairName,
|
||||||
|
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
||||||
|
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
||||||
},
|
},
|
||||||
|
&awscommon.StepSecurityGroup{
|
||||||
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
|
VpcId: b.config.VpcId,
|
||||||
|
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
|
||||||
|
},
|
||||||
|
instanceStep,
|
||||||
&awscommon.StepGetPassword{
|
&awscommon.StepGetPassword{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
Comm: &b.config.RunConfig.Comm,
|
Comm: &b.config.RunConfig.Comm,
|
||||||
|
@ -164,7 +185,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
DisableStopInstance: b.config.DisableStopInstance,
|
DisableStopInstance: b.config.DisableStopInstance,
|
||||||
},
|
},
|
||||||
&awscommon.StepModifyEBSBackedInstance{
|
&awscommon.StepModifyEBSBackedInstance{
|
||||||
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,48 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
var instanceStep multistep.Step
|
||||||
|
|
||||||
|
if b.config.SpotPrice == "" || b.config.SpotPrice == "0" {
|
||||||
|
instanceStep = &awscommon.StepRunSourceInstance{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
ExpectedRootDevice: "ebs",
|
||||||
|
InstanceType: b.config.InstanceType,
|
||||||
|
UserData: b.config.UserData,
|
||||||
|
UserDataFile: b.config.UserDataFile,
|
||||||
|
SourceAMI: b.config.SourceAmi,
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
SubnetId: b.config.SubnetId,
|
||||||
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
|
BlockDevices: b.config.BlockDevices,
|
||||||
|
Tags: b.config.RunTags,
|
||||||
|
Ctx: b.config.ctx,
|
||||||
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instanceStep = &awscommon.StepRunSpotInstance{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
ExpectedRootDevice: "ebs",
|
||||||
|
SpotPrice: b.config.SpotPrice,
|
||||||
|
SpotPriceProduct: b.config.SpotPriceAutoProduct,
|
||||||
|
InstanceType: b.config.InstanceType,
|
||||||
|
UserData: b.config.UserData,
|
||||||
|
UserDataFile: b.config.UserDataFile,
|
||||||
|
SourceAMI: b.config.SourceAmi,
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
SubnetId: b.config.SubnetId,
|
||||||
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
|
BlockDevices: b.config.BlockDevices,
|
||||||
|
Tags: b.config.RunTags,
|
||||||
|
Ctx: b.config.ctx,
|
||||||
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build the steps
|
// Build the steps
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
&awscommon.StepPreValidate{
|
&awscommon.StepPreValidate{
|
||||||
|
@ -200,9 +242,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
},
|
},
|
||||||
&awscommon.StepSourceAMIInfo{
|
&awscommon.StepSourceAMIInfo{
|
||||||
SourceAmi: b.config.SourceAmi,
|
SourceAmi: b.config.SourceAmi,
|
||||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
AmiFilters: b.config.SourceAmiFilter,
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
|
AmiFilters: b.config.SourceAmiFilter,
|
||||||
},
|
},
|
||||||
&awscommon.StepKeyPair{
|
&awscommon.StepKeyPair{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
|
@ -216,24 +259,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
CommConfig: &b.config.RunConfig.Comm,
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
SecurityGroupIds: b.config.SecurityGroupIds,
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
VpcId: b.config.VpcId,
|
VpcId: b.config.VpcId,
|
||||||
|
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
|
||||||
},
|
},
|
||||||
&awscommon.StepRunSourceInstance{
|
instanceStep,
|
||||||
Debug: b.config.PackerDebug,
|
|
||||||
SpotPrice: b.config.SpotPrice,
|
|
||||||
SpotPriceProduct: b.config.SpotPriceAutoProduct,
|
|
||||||
InstanceType: b.config.InstanceType,
|
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
||||||
UserData: b.config.UserData,
|
|
||||||
UserDataFile: b.config.UserDataFile,
|
|
||||||
SourceAMI: b.config.SourceAmi,
|
|
||||||
SubnetId: b.config.SubnetId,
|
|
||||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
|
||||||
EbsOptimized: b.config.EbsOptimized,
|
|
||||||
AvailabilityZone: b.config.AvailabilityZone,
|
|
||||||
BlockDevices: b.config.BlockDevices,
|
|
||||||
Tags: b.config.RunTags,
|
|
||||||
Ctx: b.config.ctx,
|
|
||||||
},
|
|
||||||
&awscommon.StepGetPassword{
|
&awscommon.StepGetPassword{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
Comm: &b.config.RunConfig.Comm,
|
Comm: &b.config.RunConfig.Comm,
|
||||||
|
@ -258,11 +286,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
},
|
||||||
&awscommon.StepDeregisterAMI{
|
&awscommon.StepDeregisterAMI{
|
||||||
|
AccessConfig: &b.config.AccessConfig,
|
||||||
ForceDeregister: b.config.AMIForceDeregister,
|
ForceDeregister: b.config.AMIForceDeregister,
|
||||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||||
AMIName: b.config.AMIName,
|
AMIName: b.config.AMIName,
|
||||||
|
Regions: b.config.AMIRegions,
|
||||||
|
},
|
||||||
|
&StepRegisterAMI{
|
||||||
|
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||||
|
EnableAMIENASupport: b.config.AMIENASupport,
|
||||||
},
|
},
|
||||||
&StepRegisterAMI{},
|
|
||||||
&awscommon.StepAMIRegionCopy{
|
&awscommon.StepAMIRegionCopy{
|
||||||
AccessConfig: &b.config.AccessConfig,
|
AccessConfig: &b.config.AccessConfig,
|
||||||
Regions: b.config.AMIRegions,
|
Regions: b.config.AMIRegions,
|
||||||
|
|
|
@ -10,7 +10,10 @@ import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StepRegisterAMI struct{}
|
type StepRegisterAMI struct {
|
||||||
|
EnableAMIENASupport bool
|
||||||
|
EnableAMISriovNetSupport bool
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
config := state.Get("config").(*Config)
|
||||||
|
@ -29,12 +32,13 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
registerOpts.VirtualizationType = aws.String(config.AMIVirtType)
|
registerOpts.VirtualizationType = aws.String(config.AMIVirtType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.AMIEnhancedNetworking {
|
if s.EnableAMISriovNetSupport {
|
||||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||||
registerOpts.SriovNetSupport = aws.String("simple")
|
registerOpts.SriovNetSupport = aws.String("simple")
|
||||||
|
}
|
||||||
// Set EnaSupport to true.
|
if s.EnableAMIENASupport {
|
||||||
|
// Set EnaSupport to true
|
||||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||||
registerOpts.EnaSupport = aws.Bool(true)
|
registerOpts.EnaSupport = aws.Bool(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ type uploadCmdData struct {
|
||||||
ManifestPath string
|
ManifestPath string
|
||||||
Region string
|
Region string
|
||||||
SecretKey string
|
SecretKey string
|
||||||
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
type StepUploadBundle struct {
|
type StepUploadBundle struct {
|
||||||
|
@ -40,11 +41,13 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
secretKey := config.SecretKey
|
secretKey := config.SecretKey
|
||||||
session, err := config.AccessConfig.Session()
|
session, err := config.AccessConfig.Session()
|
||||||
accessConfig := session.Config
|
accessConfig := session.Config
|
||||||
|
var token string
|
||||||
if err == nil && accessKey == "" && secretKey == "" {
|
if err == nil && accessKey == "" && secretKey == "" {
|
||||||
credentials, err := accessConfig.Credentials.Get()
|
credentials, err := accessConfig.Credentials.Get()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
accessKey = credentials.AccessKeyID
|
accessKey = credentials.AccessKeyID
|
||||||
secretKey = credentials.SecretAccessKey
|
secretKey = credentials.SecretAccessKey
|
||||||
|
token = credentials.SessionToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ManifestPath: manifestPath,
|
ManifestPath: manifestPath,
|
||||||
Region: region,
|
Region: region,
|
||||||
SecretKey: secretKey,
|
SecretKey: secretKey,
|
||||||
|
Token: token,
|
||||||
}
|
}
|
||||||
config.BundleUploadCommand, err = interpolate.Render(config.BundleUploadCommand, &config.ctx)
|
config.BundleUploadCommand, err = interpolate.Render(config.BundleUploadCommand, &config.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -78,6 +82,12 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.ExitStatus != 0 {
|
if cmd.ExitStatus != 0 {
|
||||||
|
if cmd.ExitStatus == 3 {
|
||||||
|
ui.Error(fmt.Sprintf("Please check that the bucket `%s` "+
|
||||||
|
"does not exist, or exists and is writable. This error "+
|
||||||
|
"indicates that the bucket may be owned by somebody else.",
|
||||||
|
config.S3Bucket))
|
||||||
|
}
|
||||||
state.Put("error", fmt.Errorf(
|
state.Put("error", fmt.Errorf(
|
||||||
"Bundle upload failed. Please see the output above for more\n"+
|
"Bundle upload failed. Please see the output above for more\n"+
|
||||||
"details on what went wrong."))
|
"details on what went wrong."))
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -119,7 +116,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
endpointConnectType := PublicEndpoint
|
endpointConnectType := PublicEndpoint
|
||||||
if b.isPrivateNetworkCommunication() {
|
if b.isPublicPrivateNetworkCommunication() && b.isPrivateNetworkCommunication() {
|
||||||
|
endpointConnectType = PublicEndpointInPrivateNetwork
|
||||||
|
} else if b.isPrivateNetworkCommunication() {
|
||||||
endpointConnectType = PrivateEndpoint
|
endpointConnectType = PrivateEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +244,10 @@ func (b *Builder) writeSSHPrivateKey(ui packer.Ui, debugKeyPath string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) isPublicPrivateNetworkCommunication() bool {
|
||||||
|
return DefaultPrivateVirtualNetworkWithPublicIp != b.config.PrivateVirtualNetworkWithPublicIp
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) isPrivateNetworkCommunication() bool {
|
func (b *Builder) isPrivateNetworkCommunication() bool {
|
||||||
return b.config.VirtualNetworkName != ""
|
return b.config.VirtualNetworkName != ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
type CaptureTemplateParameter struct {
|
type CaptureTemplateParameter struct {
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -34,10 +31,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultCloudEnvironmentName = "Public"
|
DefaultCloudEnvironmentName = "Public"
|
||||||
DefaultImageVersion = "latest"
|
DefaultImageVersion = "latest"
|
||||||
DefaultUserName = "packer"
|
DefaultUserName = "packer"
|
||||||
DefaultVMSize = "Standard_A1"
|
DefaultPrivateVirtualNetworkWithPublicIp = false
|
||||||
|
DefaultVMSize = "Standard_A1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -73,24 +71,27 @@ type Config struct {
|
||||||
Location string `mapstructure:"location"`
|
Location string `mapstructure:"location"`
|
||||||
VMSize string `mapstructure:"vm_size"`
|
VMSize string `mapstructure:"vm_size"`
|
||||||
|
|
||||||
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
|
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
|
||||||
ManagedImageName string `mapstructure:"managed_image_name"`
|
ManagedImageName string `mapstructure:"managed_image_name"`
|
||||||
manageImageLocation string
|
ManagedImageStorageAccountType string `mapstructure:"managed_image_storage_account_type"`
|
||||||
|
managedImageStorageAccountType compute.StorageAccountTypes
|
||||||
|
manageImageLocation string
|
||||||
|
|
||||||
// Deployment
|
// Deployment
|
||||||
AzureTags map[string]*string `mapstructure:"azure_tags"`
|
AzureTags map[string]*string `mapstructure:"azure_tags"`
|
||||||
ResourceGroupName string `mapstructure:"resource_group_name"`
|
ResourceGroupName string `mapstructure:"resource_group_name"`
|
||||||
StorageAccount string `mapstructure:"storage_account"`
|
StorageAccount string `mapstructure:"storage_account"`
|
||||||
TempComputeName string `mapstructure:"temp_compute_name"`
|
TempComputeName string `mapstructure:"temp_compute_name"`
|
||||||
TempResourceGroupName string `mapstructure:"temp_resource_group_name"`
|
TempResourceGroupName string `mapstructure:"temp_resource_group_name"`
|
||||||
storageAccountBlobEndpoint string
|
storageAccountBlobEndpoint string
|
||||||
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
|
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
|
||||||
cloudEnvironment *azure.Environment
|
cloudEnvironment *azure.Environment
|
||||||
VirtualNetworkName string `mapstructure:"virtual_network_name"`
|
PrivateVirtualNetworkWithPublicIp bool `mapstructure:"private_virtual_network_with_public_ip"`
|
||||||
VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"`
|
VirtualNetworkName string `mapstructure:"virtual_network_name"`
|
||||||
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
|
VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"`
|
||||||
CustomDataFile string `mapstructure:"custom_data_file"`
|
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
|
||||||
customData string
|
CustomDataFile string `mapstructure:"custom_data_file"`
|
||||||
|
customData string
|
||||||
|
|
||||||
// OS
|
// OS
|
||||||
OSType string `mapstructure:"os_type"`
|
OSType string `mapstructure:"os_type"`
|
||||||
|
@ -403,6 +404,10 @@ func provideDefaultValues(c *Config) {
|
||||||
c.VMSize = DefaultVMSize
|
c.VMSize = DefaultVMSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.ManagedImageStorageAccountType == "" {
|
||||||
|
c.managedImageStorageAccountType = compute.StandardLRS
|
||||||
|
}
|
||||||
|
|
||||||
if c.ImagePublisher != "" && c.ImageVersion == "" {
|
if c.ImagePublisher != "" && c.ImageVersion == "" {
|
||||||
c.ImageVersion = DefaultImageVersion
|
c.ImageVersion = DefaultImageVersion
|
||||||
}
|
}
|
||||||
|
@ -526,6 +531,10 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku) or a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name"))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku) or a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isImageUrl && c.ManagedImageResourceGroupName != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD."))
|
||||||
|
}
|
||||||
|
|
||||||
if c.ImageUrl == "" && c.CustomManagedImageName == "" {
|
if c.ImageUrl == "" && c.CustomManagedImageName == "" {
|
||||||
if c.ImagePublisher == "" {
|
if c.ImagePublisher == "" {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified"))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified"))
|
||||||
|
@ -596,4 +605,13 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
||||||
} else {
|
} else {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The os_type %q is invalid", c.OSType))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The os_type %q is invalid", c.OSType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch c.ManagedImageStorageAccountType {
|
||||||
|
case "", string(compute.StandardLRS):
|
||||||
|
c.managedImageStorageAccountType = compute.StandardLRS
|
||||||
|
case string(compute.PremiumLRS):
|
||||||
|
c.managedImageStorageAccountType = compute.PremiumLRS
|
||||||
|
default:
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -9,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/arm/compute"
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
@ -48,6 +46,10 @@ func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) {
|
||||||
if c.ObjectID != "" {
|
if c.ObjectID != "" {
|
||||||
t.Errorf("Expected 'ObjectID' to be nil, but it was '%s'!", c.ObjectID)
|
t.Errorf("Expected 'ObjectID' to be nil, but it was '%s'!", c.ObjectID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.managedImageStorageAccountType == "" {
|
||||||
|
t.Errorf("Expected 'managedImageStorageAccountType' to be populated, but it was empty!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
||||||
|
@ -56,6 +58,7 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
||||||
builderValues["ssh_username"] = "override_username"
|
builderValues["ssh_username"] = "override_username"
|
||||||
builderValues["vm_size"] = "override_vm_size"
|
builderValues["vm_size"] = "override_vm_size"
|
||||||
builderValues["communicator"] = "ssh"
|
builderValues["communicator"] = "ssh"
|
||||||
|
builderValues["managed_image_storage_account_type"] = "Premium_LRS"
|
||||||
|
|
||||||
c, _, err := newConfig(builderValues, getPackerConfiguration())
|
c, _, err := newConfig(builderValues, getPackerConfiguration())
|
||||||
|
|
||||||
|
@ -64,23 +67,27 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Password != "override_password" {
|
if c.Password != "override_password" {
|
||||||
t.Errorf("Expected 'Password' to be set to 'override_password', but found '%s'!", c.Password)
|
t.Errorf("Expected 'Password' to be set to 'override_password', but found %q!", c.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Comm.SSHPassword != "override_password" {
|
if c.Comm.SSHPassword != "override_password" {
|
||||||
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found '%s'!", c.Comm.SSHPassword)
|
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found %q!", c.Comm.SSHPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.UserName != "override_username" {
|
if c.UserName != "override_username" {
|
||||||
t.Errorf("Expected 'UserName' to be set to 'override_username', but found '%s'!", c.UserName)
|
t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Comm.SSHUsername != "override_username" {
|
if c.Comm.SSHUsername != "override_username" {
|
||||||
t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found '%s'!", c.Comm.SSHUsername)
|
t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found %q!", c.Comm.SSHUsername)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.VMSize != "override_vm_size" {
|
if c.VMSize != "override_vm_size" {
|
||||||
t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found '%s'!", c.VMSize)
|
t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found %q!", c.VMSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.managedImageStorageAccountType != compute.PremiumLRS {
|
||||||
|
t.Errorf("Expected 'managed_image_storage_account_type' to be set to 'Premium_LRS', but found %q!", c.managedImageStorageAccountType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,6 +789,26 @@ func TestConfigShouldRejectVhdAndManagedImageOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user specified a build of a VHD, but started with a managed image it should be rejected.
|
||||||
|
func TestConfigShouldRejectManagedImageSourceAndVhdOutput(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"image_url": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"communicator": "none",
|
||||||
|
"managed_image_resource_group_name": "ignore",
|
||||||
|
"managed_image_name": "ignore",
|
||||||
|
|
||||||
|
// Does not matter for this test case, just pick one.
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected config to reject VHD and Managed Image build")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) {
|
func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) {
|
||||||
config := map[string]interface{}{
|
config := map[string]interface{}{
|
||||||
"custom_managed_image_resource_group_name": "ignore",
|
"custom_managed_image_resource_group_name": "ignore",
|
||||||
|
@ -826,6 +853,52 @@ func TestConfigShouldRejectCustomAndImageUrlForManagedImageBuild(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigShouldRejectMalformedManageImageStorageAccountTypes(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"custom_managed_image_resource_group_name": "ignore",
|
||||||
|
"custom_managed_image_name": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"communicator": "none",
|
||||||
|
"managed_image_resource_group_name": "ignore",
|
||||||
|
"managed_image_name": "ignore",
|
||||||
|
"managed_image_storage_account_type": "--invalid--",
|
||||||
|
|
||||||
|
// Does not matter for this test case, just pick one.
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected config to reject custom and platform input for a managed image build")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"custom_managed_image_resource_group_name": "ignore",
|
||||||
|
"custom_managed_image_name": "ignore",
|
||||||
|
"location": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"communicator": "none",
|
||||||
|
"managed_image_resource_group_name": "ignore",
|
||||||
|
"managed_image_name": "ignore",
|
||||||
|
|
||||||
|
// Does not matter for this test case, just pick one.
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_account_types := []string{"Premium_LRS", "Standard_LRS"}
|
||||||
|
|
||||||
|
for _, x := range storage_account_types {
|
||||||
|
config["managed_image_storage_account_type"] = x
|
||||||
|
_, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected config to accept a managed_image_storage_account_type of %q", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getArmBuilderConfiguration() map[string]string {
|
func getArmBuilderConfiguration() map[string]string {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
for _, v := range requiredConfigValues {
|
for _, v := range requiredConfigValues {
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -16,12 +13,14 @@ type EndpointType int
|
||||||
const (
|
const (
|
||||||
PublicEndpoint EndpointType = iota
|
PublicEndpoint EndpointType = iota
|
||||||
PrivateEndpoint
|
PrivateEndpoint
|
||||||
|
PublicEndpointInPrivateNetwork
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
EndpointCommunicationText = map[EndpointType]string{
|
EndpointCommunicationText = map[EndpointType]string{
|
||||||
PublicEndpoint: "PublicEndpoint",
|
PublicEndpoint: "PublicEndpoint",
|
||||||
PrivateEndpoint: "PrivateEndpoint",
|
PrivateEndpoint: "PrivateEndpoint",
|
||||||
|
PublicEndpointInPrivateNetwork: "PublicEndpointInPrivateNetwork",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,6 +45,8 @@ func NewStepGetIPAddress(client *AzureClient, ui packer.Ui, endpoint EndpointTyp
|
||||||
step.get = step.getPrivateIP
|
step.get = step.getPrivateIP
|
||||||
case PublicEndpoint:
|
case PublicEndpoint:
|
||||||
step.get = step.getPublicIP
|
step.get = step.getPublicIP
|
||||||
|
case PublicEndpointInPrivateNetwork:
|
||||||
|
step.get = step.getPublicIPInPrivateNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
return step
|
return step
|
||||||
|
@ -70,6 +71,11 @@ func (s *StepGetIPAddress) getPublicIP(resourceGroupName string, ipAddressName s
|
||||||
return *resp.IPAddress, nil
|
return *resp.IPAddress, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StepGetIPAddress) getPublicIPInPrivateNetwork(resourceGroupName string, ipAddressName string, interfaceName string) (string, error) {
|
||||||
|
s.getPrivateIP(resourceGroupName, ipAddressName, interfaceName)
|
||||||
|
return s.getPublicIP(resourceGroupName, ipAddressName, interfaceName)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StepGetIPAddress) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepGetIPAddress) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
s.say("Getting the VM's IP address ...")
|
s.say("Getting the VM's IP address ...")
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -12,42 +9,50 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) {
|
func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) {
|
||||||
var testSubject = &StepGetIPAddress{
|
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
||||||
get: func(string, string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
|
||||||
endpoint: PublicEndpoint,
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetIPAddress()
|
for _, endpoint := range endpoints {
|
||||||
|
var testSubject = &StepGetIPAddress{
|
||||||
|
get: func(string, string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
||||||
|
endpoint: endpoint,
|
||||||
|
say: func(message string) {},
|
||||||
|
error: func(e error) {},
|
||||||
|
}
|
||||||
|
|
||||||
var result = testSubject.Run(stateBag)
|
stateBag := createTestStateBagStepGetIPAddress()
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
var result = testSubject.Run(stateBag)
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
if result != multistep.ActionHalt {
|
||||||
|
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
||||||
|
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) {
|
func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) {
|
||||||
var testSubject = &StepGetIPAddress{
|
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
||||||
get: func(string, string, string) (string, error) { return "", nil },
|
|
||||||
endpoint: PublicEndpoint,
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetIPAddress()
|
for _, endpoint := range endpoints {
|
||||||
|
var testSubject = &StepGetIPAddress{
|
||||||
|
get: func(string, string, string) (string, error) { return "", nil },
|
||||||
|
endpoint: endpoint,
|
||||||
|
say: func(message string) {},
|
||||||
|
error: func(e error) {},
|
||||||
|
}
|
||||||
|
|
||||||
var result = testSubject.Run(stateBag)
|
stateBag := createTestStateBagStepGetIPAddress()
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
var result = testSubject.Run(stateBag)
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
if result != multistep.ActionContinue {
|
||||||
|
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||||
|
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,50 +60,53 @@ func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
||||||
var actualResourceGroupName string
|
var actualResourceGroupName string
|
||||||
var actualIPAddressName string
|
var actualIPAddressName string
|
||||||
var actualNicName string
|
var actualNicName string
|
||||||
|
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
||||||
|
|
||||||
var testSubject = &StepGetIPAddress{
|
for _, endpoint := range endpoints {
|
||||||
get: func(resourceGroupName string, ipAddressName string, nicName string) (string, error) {
|
var testSubject = &StepGetIPAddress{
|
||||||
actualResourceGroupName = resourceGroupName
|
get: func(resourceGroupName string, ipAddressName string, nicName string) (string, error) {
|
||||||
actualIPAddressName = ipAddressName
|
actualResourceGroupName = resourceGroupName
|
||||||
actualNicName = nicName
|
actualIPAddressName = ipAddressName
|
||||||
|
actualNicName = nicName
|
||||||
|
|
||||||
return "127.0.0.1", nil
|
return "127.0.0.1", nil
|
||||||
},
|
},
|
||||||
endpoint: PublicEndpoint,
|
endpoint: endpoint,
|
||||||
say: func(message string) {},
|
say: func(message string) {},
|
||||||
error: func(e error) {},
|
error: func(e error) {},
|
||||||
}
|
}
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetIPAddress()
|
stateBag := createTestStateBagStepGetIPAddress()
|
||||||
var result = testSubject.Run(stateBag)
|
var result = testSubject.Run(stateBag)
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
if result != multistep.ActionContinue {
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
||||||
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
|
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
|
||||||
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
|
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
|
||||||
|
|
||||||
if actualIPAddressName != expectedIPAddressName {
|
if actualIPAddressName != expectedIPAddressName {
|
||||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
|
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
if actualResourceGroupName != expectedResourceGroupName {
|
||||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if actualNicName != expectedNicName {
|
if actualNicName != expectedNicName {
|
||||||
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
|
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
|
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
|
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expectedIPAddress != "127.0.0.1" {
|
if expectedIPAddress != "127.0.0.1" {
|
||||||
t.Fatalf("Expected the value of stateBag[%s] to be '127.0.0.1', but got '%s'.", constants.SSHHost, expectedIPAddress)
|
t.Fatalf("Expected the value of stateBag[%s] to be '127.0.0.1', but got '%s'.", constants.SSHHost, expectedIPAddress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -53,7 +53,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||||
if config.ImageUrl != "" {
|
if config.ImageUrl != "" {
|
||||||
builder.SetImageUrl(config.ImageUrl, osType)
|
builder.SetImageUrl(config.ImageUrl, osType)
|
||||||
} else if config.CustomManagedImageName != "" {
|
} else if config.CustomManagedImageName != "" {
|
||||||
builder.SetManagedDiskUrl(config.customManagedImageID)
|
builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType)
|
||||||
} else if config.ManagedImageName != "" && config.ImagePublisher != "" {
|
} else if config.ManagedImageName != "" && config.ImagePublisher != "" {
|
||||||
imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s",
|
imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s",
|
||||||
config.SubscriptionID,
|
config.SubscriptionID,
|
||||||
|
@ -63,7 +63,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||||
config.ImageSku,
|
config.ImageSku,
|
||||||
config.ImageVersion)
|
config.ImageVersion)
|
||||||
|
|
||||||
builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID)
|
builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType)
|
||||||
} else {
|
} else {
|
||||||
builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion)
|
builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,12 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
||||||
builder.SetCustomData(config.customData)
|
builder.SetCustomData(config.customData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.VirtualNetworkName != "" {
|
if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp {
|
||||||
|
builder.SetPrivateVirtualNetworWithPublicIp(
|
||||||
|
config.VirtualNetworkResourceGroupName,
|
||||||
|
config.VirtualNetworkName,
|
||||||
|
config.VirtualNetworkSubnetName)
|
||||||
|
} else if config.VirtualNetworkName != "" {
|
||||||
builder.SetVirtualNetwork(
|
builder.SetVirtualNetwork(
|
||||||
config.VirtualNetworkResourceGroupName,
|
config.VirtualNetworkResourceGroupName,
|
||||||
config.VirtualNetworkName,
|
config.VirtualNetworkName,
|
||||||
|
|
|
@ -129,6 +129,9 @@
|
||||||
"osDisk": {
|
"osDisk": {
|
||||||
"caching": "ReadWrite",
|
"caching": "ReadWrite",
|
||||||
"createOption": "fromImage",
|
"createOption": "fromImage",
|
||||||
|
"managedDisk": {
|
||||||
|
"storageAccountType": "Standard_LRS"
|
||||||
|
},
|
||||||
"name": "osdisk",
|
"name": "osdisk",
|
||||||
"osType": "Linux"
|
"osType": "Linux"
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,9 @@
|
||||||
"osDisk": {
|
"osDisk": {
|
||||||
"caching": "ReadWrite",
|
"caching": "ReadWrite",
|
||||||
"createOption": "fromImage",
|
"createOption": "fromImage",
|
||||||
|
"managedDisk": {
|
||||||
|
"storageAccountType": "Standard_LRS"
|
||||||
|
},
|
||||||
"name": "osdisk",
|
"name": "osdisk",
|
||||||
"osType": "Linux"
|
"osType": "Linux"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
{
|
||||||
|
"$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"
|
||||||
|
},
|
||||||
|
"osDiskName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"storageAccountBlobEndpoint": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vmName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vmSize": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[variables('publicIPAddressName')]",
|
||||||
|
"properties": {
|
||||||
|
"dnsSettings": {
|
||||||
|
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
||||||
|
},
|
||||||
|
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Network/publicIPAddresses"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||||
|
],
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[variables('nicName')]",
|
||||||
|
"properties": {
|
||||||
|
"ipConfigurations": [
|
||||||
|
{
|
||||||
|
"name": "ipconfig",
|
||||||
|
"properties": {
|
||||||
|
"privateIPAllocationMethod": "Dynamic",
|
||||||
|
"publicIPAddress": {
|
||||||
|
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||||
|
},
|
||||||
|
"subnet": {
|
||||||
|
"id": "[variables('subnetRef')]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Network/networkInterfaces"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "[variables('apiVersion')]",
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||||
|
],
|
||||||
|
"location": "[variables('location')]",
|
||||||
|
"name": "[parameters('vmName')]",
|
||||||
|
"properties": {
|
||||||
|
"diagnosticsProfile": {
|
||||||
|
"bootDiagnostics": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hardwareProfile": {
|
||||||
|
"vmSize": "[parameters('vmSize')]"
|
||||||
|
},
|
||||||
|
"networkProfile": {
|
||||||
|
"networkInterfaces": [
|
||||||
|
{
|
||||||
|
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"osProfile": {
|
||||||
|
"adminPassword": "[parameters('adminPassword')]",
|
||||||
|
"adminUsername": "[parameters('adminUsername')]",
|
||||||
|
"computerName": "[parameters('vmName')]",
|
||||||
|
"linuxConfiguration": {
|
||||||
|
"ssh": {
|
||||||
|
"publicKeys": [
|
||||||
|
{
|
||||||
|
"keyData": "",
|
||||||
|
"path": "[variables('sshKeyPath')]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"storageProfile": {
|
||||||
|
"imageReference": {
|
||||||
|
"offer": "--image-offer--",
|
||||||
|
"publisher": "--image-publisher--",
|
||||||
|
"sku": "--image-sku--",
|
||||||
|
"version": "--version--"
|
||||||
|
},
|
||||||
|
"osDisk": {
|
||||||
|
"caching": "ReadWrite",
|
||||||
|
"createOption": "fromImage",
|
||||||
|
"managedDisk": {
|
||||||
|
"storageAccountType": "Standard_LRS"
|
||||||
|
},
|
||||||
|
"name": "osdisk",
|
||||||
|
"osType": "Linux"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "Microsoft.Compute/virtualMachines"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"addressPrefix": "10.0.0.0/16",
|
||||||
|
"apiVersion": "2017-03-30",
|
||||||
|
"location": "[resourceGroup().location]",
|
||||||
|
"managedDiskApiVersion": "2017-03-30",
|
||||||
|
"networkInterfacesApiVersion": "2017-04-01",
|
||||||
|
"nicName": "packerNic",
|
||||||
|
"publicIPAddressApiVersion": "2017-04-01",
|
||||||
|
"publicIPAddressName": "packerPublicIP",
|
||||||
|
"publicIPAddressType": "Dynamic",
|
||||||
|
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||||
|
"subnetAddressPrefix": "10.0.0.0/24",
|
||||||
|
"subnetName": "--virtual_network_subnet_name--",
|
||||||
|
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
||||||
|
"virtualNetworkName": "--virtual_network_name--",
|
||||||
|
"virtualNetworkResourceGroup": "--virtual_network_resource_group_name--",
|
||||||
|
"virtualNetworksApiVersion": "2017-04-01",
|
||||||
|
"vmStorageAccountContainerName": "images",
|
||||||
|
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
||||||
|
}
|
||||||
|
}
|
|
@ -318,6 +318,43 @@ func TestVirtualMachineDeployment09(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the VM template is correct when building with PublicIp and connect to Private Network
|
||||||
|
func TestVirtualMachineDeployment10(t *testing.T) {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"location": "ignore",
|
||||||
|
"subscription_id": "ignore",
|
||||||
|
"os_type": constants.Target_Linux,
|
||||||
|
"communicator": "none",
|
||||||
|
"image_publisher": "--image-publisher--",
|
||||||
|
"image_offer": "--image-offer--",
|
||||||
|
"image_sku": "--image-sku--",
|
||||||
|
"image_version": "--version--",
|
||||||
|
|
||||||
|
"virtual_network_resource_group_name": "--virtual_network_resource_group_name--",
|
||||||
|
"virtual_network_name": "--virtual_network_name--",
|
||||||
|
"virtual_network_subnet_name": "--virtual_network_subnet_name--",
|
||||||
|
"private_virtual_network_with_public_ip": true,
|
||||||
|
|
||||||
|
"managed_image_name": "ManagedImageName",
|
||||||
|
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
||||||
|
}
|
||||||
|
|
||||||
|
c, _, err := newConfig(config, getPackerConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package constants
|
package constants
|
||||||
|
|
||||||
// complete flags
|
// complete flags
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package constants
|
package constants
|
||||||
|
|
||||||
// Target types
|
// Target types
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
// removes overlap between the end of a and the start of b and
|
// removes overlap between the end of a and the start of b and
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package lin
|
package lin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package lin
|
package lin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package lin
|
package lin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package logutil
|
package logutil
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import "github.com/mitchellh/multistep"
|
import "github.com/mitchellh/multistep"
|
||||||
|
|
|
@ -45,6 +45,7 @@ type OSDiskUnion struct {
|
||||||
Caching compute.CachingTypes `json:"caching,omitempty"`
|
Caching compute.CachingTypes `json:"caching,omitempty"`
|
||||||
CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"`
|
CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"`
|
||||||
DiskSizeGB *int32 `json:"diskSizeGB,omitempty"`
|
DiskSizeGB *int32 `json:"diskSizeGB,omitempty"`
|
||||||
|
ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union of the StorageProfile and ImageStorageProfile types.
|
// Union of the StorageProfile and ImageStorageProfile types.
|
||||||
|
|
|
@ -101,7 +101,7 @@ func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string) error {
|
func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType compute.StorageAccountTypes) error {
|
||||||
resource, err := s.getResourceByType(resourceVirtualMachine)
|
resource, err := s.getResourceByType(resourceVirtualMachine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -115,11 +115,14 @@ func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string) error {
|
||||||
profile.OsDisk.OsType = s.osType
|
profile.OsDisk.OsType = s.osType
|
||||||
profile.OsDisk.CreateOption = compute.FromImage
|
profile.OsDisk.CreateOption = compute.FromImage
|
||||||
profile.OsDisk.Vhd = nil
|
profile.OsDisk.Vhd = nil
|
||||||
|
profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{
|
||||||
|
StorageAccountType: storageAccountType,
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string) error {
|
func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string, storageAccountType compute.StorageAccountTypes) error {
|
||||||
resource, err := s.getResourceByType(resourceVirtualMachine)
|
resource, err := s.getResourceByType(resourceVirtualMachine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -137,6 +140,9 @@ func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer,
|
||||||
profile.OsDisk.OsType = s.osType
|
profile.OsDisk.OsType = s.osType
|
||||||
profile.OsDisk.CreateOption = compute.FromImage
|
profile.OsDisk.CreateOption = compute.FromImage
|
||||||
profile.OsDisk.Vhd = nil
|
profile.OsDisk.Vhd = nil
|
||||||
|
profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{
|
||||||
|
StorageAccountType: storageAccountType,
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -219,6 +225,24 @@ func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtual
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TemplateBuilder) SetPrivateVirtualNetworWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error {
|
||||||
|
s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup)
|
||||||
|
s.setVariable("virtualNetworkName", virtualNetworkName)
|
||||||
|
s.setVariable("subnetName", subnetName)
|
||||||
|
|
||||||
|
s.deleteResourceByType(resourceVirtualNetworks)
|
||||||
|
resource, err := s.getResourceByType(resourceNetworkInterfaces)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.deleteResourceDependency(resource, func(s string) bool {
|
||||||
|
return strings.Contains(s, "Microsoft.Network/virtualNetworks")
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package template
|
package template
|
||||||
|
|
||||||
// The intent of these types to facilitate interchange with Azure in the
|
// The intent of these types to facilitate interchange with Azure in the
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
package template
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
|
|
||||||
|
|
||||||
// NOTE: vault APIs do not yet exist in the SDK, but once they do this code
|
// NOTE: vault APIs do not yet exist in the SDK, but once they do this code
|
||||||
// should be removed.
|
// should be removed.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,9 +0,0 @@
|
||||||
This is a fork of the from the original PKCS#12 parsing code
|
|
||||||
published in the Azure repository [go-pkcs12](https://github.com/Azure/go-pkcs12).
|
|
||||||
This fork adds serializing a x509 certificate and private key as PKCS#12 binary blob
|
|
||||||
(aka .PFX file). Due to the specific nature of this code it was not accepted for
|
|
||||||
inclusion in the official Go crypto repository.
|
|
||||||
|
|
||||||
The methods used for decoding PKCS#12 have been moved to the test files to further
|
|
||||||
discourage the use of this library for decoding. Please use the official
|
|
||||||
[pkcs12](https://godoc.org/golang.org/x/crypto/pkcs12) library for decode support.
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue