Merge remote-tracking branch 'origin/master' into ansible_local_playbook_files_update

This commit is contained in:
localghost 2018-05-16 21:54:39 +02:00
commit 11f2b1ec24
905 changed files with 398474 additions and 39018 deletions

3
.gitignore vendored
View File

@ -10,6 +10,7 @@
test/.env
*~
*.received.*
*.swp
website/.bundle
website/vendor
@ -21,3 +22,5 @@ packer-test*.log
.idea/
*.iml
Thumbs.db
/packer.exe

View File

@ -8,6 +8,7 @@ language: go
go:
- 1.7.4
- 1.8.3
- 1.9
install:
- make deps
@ -21,5 +22,3 @@ branches:
matrix:
fast_finish: true
allow_failures:
- go: 1.4.3

View File

@ -1,20 +1,208 @@
## (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:
* 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/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]
* 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)
### BUG FIXES:
* builder/amazon: Fix bugs related to stop instance command. [GH-5029]
* communicator/ssh: Fix truncated stdout from remote ssh provisioner. [GH-5050]
* builder/amazon: Fix bugs related to stop instance command. [GH-4719]
* communicator/ssh: Fix ssh connection errors. [GH-5038]
* 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]

View File

@ -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.
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
example, you can add to your `.bash_profile`.

View File

@ -7,6 +7,7 @@ GITBRANCH:=$(shell git symbolic-ref --short HEAD 2>/dev/null)
GOFMT_FILES?=$$(find . -not -path "./vendor/*" -name "*.go")
GOOS=$(shell go env GOOS)
GOARCH=$(shell go env GOARCH)
GOPATH=$(shell go env GOPATH)
# Get the git commit
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; \
fi
@mkdir -p pkg/$(GOOS)_$(GOARCH)
@mkdir -p bin
@go install -ldflags '$(GOLDFLAGS)'
@cp $(GOPATH)/bin/packer bin
@cp $(GOPATH)/bin/packer bin/packer
@cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH)
fmt: ## Format Go code

View File

@ -34,6 +34,7 @@ comes out of the box with support for the following platforms:
* Hyper-V
* 1&1
* OpenStack
* Oracle Bare Metal Cloud Services
* Parallels
* ProfitBricks
* QEMU. Both KVM and Xen images.

View File

@ -22,7 +22,7 @@ func (c *AlicloudAccessConfig) Client() (*ecs.Client, error) {
if err := c.loadAndValidate(); err != nil {
return nil, err
}
client := ecs.NewClient(c.AlicloudAccessKey, c.AlicloudSecretKey)
client := ecs.NewECSClient(c.AlicloudAccessKey, c.AlicloudSecretKey, common.Region(c.AlicloudRegion))
client.SetBusinessInfo("Packer")
if _, err := client.DescribeRegions(); err != nil {
return nil, err

View File

@ -5,6 +5,7 @@ package ecs
import (
"log"
"fmt"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"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,
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
//DebugKeyPath: b.config.Com
RegionId: b.config.AlicloudRegion,
DebugKeyPath: fmt.Sprintf("ecs_%s.pem", b.config.PackerBuildName),
RegionId: b.config.AlicloudRegion,
},
}
if b.chooseNetworkType() == VpcNet {

View File

@ -7,6 +7,7 @@ import (
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"time"
)
type stepAttachKeyPar struct {
@ -21,7 +22,7 @@ func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
retry_times := 3
timeoutPoint := time.Now().Add(120 * time.Second)
for {
err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
@ -29,8 +30,8 @@ func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
e, _ := err.(*common.Error)
if (!(e.Code == "MissingParameter" || e.Code == "DependencyViolation.WindowsInstance" ||
e.Code == "InvalidKeyPairName.NotFound" || e.Code == "InvalidRegionId.NotFound")) &&
retry_times > 0 {
retry_times = retry_times - 1
time.Now().Before(timeoutPoint) {
time.Sleep(5 * time.Second)
continue
}
err := fmt.Errorf("Error attaching keypair %s to instance %s : %s",

View File

@ -121,12 +121,12 @@ func (s *stepConfigAlicloudSecurityGroup) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
message(state, "security group")
start := time.Now().Add(10 * time.Second)
timeoutPoint := time.Now().Add(120 * time.Second)
for {
if err := client.DeleteSecurityGroup(common.Region(s.RegionId), s.SecurityGroupId); err != nil {
e, _ := err.(*common.Error)
if e.Code == "DependencyViolation" && time.Now().Before(start) {
time.Sleep(1 * time.Second)
if e.Code == "DependencyViolation" && time.Now().Before(timeoutPoint) {
time.Sleep(5 * time.Second)
continue
}
ui.Error(fmt.Sprintf("Failed to delete security group, it may still be around: %s", err))

View File

@ -78,13 +78,13 @@ func (s *stepConfigAlicloudVPC) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
message(state, "VPC")
start := time.Now().Add(10 * time.Second)
timeoutPoint := time.Now().Add(60 * time.Second)
for {
if err := client.DeleteVpc(s.VpcId); err != nil {
e, _ := err.(*common.Error)
if (e.Code == "DependencyViolation.Instance" || e.Code == "DependencyViolation.RouteEntry" ||
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)
continue
}

View File

@ -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 {
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
}
state.Put("vswitchid", vswitchId)
@ -130,13 +130,13 @@ func (s *stepConfigAlicloudVSwitch) Cleanup(state multistep.StateBag) {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
message(state, "vSwitch")
start := time.Now().Add(10 * time.Second)
timeoutPoint := time.Now().Add(10 * time.Second)
for {
if err := client.DeleteVSwitch(s.VSwitchId); err != nil {
e, _ := err.(*common.Error)
if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" ||
e.Code == "DependencyViolation.HaVip" ||
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(start) {
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(timeoutPoint) {
time.Sleep(1 * time.Second)
continue
}

View File

@ -213,9 +213,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
if !b.config.FromScratch {
steps = append(steps,
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
AmiFilters: b.config.SourceAmiFilter,
SourceAmi: b.config.SourceAmi,
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
AmiFilters: b.config.SourceAmiFilter,
},
&StepCheckRootDevice{},
)
@ -245,17 +246,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepEarlyCleanup{},
&StepSnapshot{},
&awscommon.StepDeregisterAMI{
AccessConfig: &b.config.AccessConfig,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
Regions: b.config.AMIRegions,
},
&StepRegisterAMI{
RootVolumeSize: b.config.RootVolumeSize,
RootVolumeSize: b.config.RootVolumeSize,
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
},
&awscommon.StepCreateEncryptedAMICopy{
KeyID: b.config.AMIKmsKeyId,
EncryptBootVolume: b.config.AMIEncryptBootVolume,
Name: b.config.AMIName,
AMIMappings: b.config.AMIBlockDevices.AMIMappings,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,

View File

@ -4,7 +4,8 @@ package chroot
import (
"os"
"syscall"
"golang.org/x/sys/unix"
)
// See: http://linux.die.net/include/sys/file.h
@ -13,7 +14,7 @@ const LOCK_NB = 4
const LOCK_UN = 8
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 {
return err
}
@ -22,5 +23,5 @@ func lockFile(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)
}

View File

@ -12,7 +12,9 @@ import (
// StepRegisterAMI creates the AMI.
type StepRegisterAMI struct {
RootVolumeSize int64
RootVolumeSize int64
EnableAMIENASupport bool
EnableAMISriovNetSupport bool
}
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)
}
if config.AMIEnhancedNetworking {
if s.EnableAMISriovNetSupport {
// 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)
registerOpts.SriovNetSupport = aws.String("simple")
}
if s.EnableAMIENASupport {
// Set EnaSupport to true
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
registerOpts.EnaSupport = aws.Bool(true)

View File

@ -17,7 +17,8 @@ type AMIConfig struct {
AMIRegions []string `mapstructure:"ami_regions"`
AMISkipRegionValidation bool `mapstructure:"skip_region_validation"`
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"`
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`

View File

@ -51,7 +51,7 @@ func (a *Artifact) String() string {
}
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{} {

View File

@ -48,9 +48,9 @@ func TestArtifactState_atlasMetadata(t *testing.T) {
func TestArtifactString(t *testing.T) {
expected := `AMIs were created:
east: foo
west: bar`
west: bar
`
amis := make(map[string]string)
amis["east"] = "foo"

View File

@ -3,6 +3,7 @@ package common
import (
"errors"
"fmt"
"net"
"os"
"regexp"
"time"
@ -40,6 +41,7 @@ type RunConfig struct {
DisableStopInstance bool `mapstructure:"disable_stop_instance"`
SecurityGroupId string `mapstructure:"security_group_id"`
SecurityGroupIds []string `mapstructure:"security_group_ids"`
TemporarySGSourceCidr string `mapstructure:"temporary_security_group_source_cidr"`
SubnetId string `mapstructure:"subnet_id"`
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
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 == "" {
c.InstanceInitiatedShutdownBehavior = "stop"
} else if !reShutdownBehavior.MatchString(c.InstanceInitiatedShutdownBehavior) {

View File

@ -166,13 +166,17 @@ func ConvertToEC2Tags(tags map[string]string, region, sourceAmiId string, ctx in
SourceAMI: sourceAmiId,
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)
if err != nil {
return ec2Tags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
}
ec2Tags = append(ec2Tags, &ec2.Tag{
Key: aws.String(key),
Key: aws.String(interpolatedKey),
Value: aws.String(interpolatedValue),
})
}

View File

@ -10,59 +10,76 @@ import (
)
type StepDeregisterAMI struct {
AccessConfig *AccessConfig
ForceDeregister bool
ForceDeleteSnapshot bool
AMIName string
Regions []string
}
func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
regions := s.Regions
if len(regions) == 0 {
regions = append(regions, s.AccessConfig.RawRegion)
}
// Check for force deregister
if s.ForceDeregister {
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
Filters: []*ec2.Filter{{
Name: aws.String("name"),
Values: []*string{aws.String(s.AMIName)},
}}})
for _, region := range regions {
// get new connection for each region in which we need to deregister vms
session, err := s.AccessConfig.Session()
if err != nil {
return multistep.ActionHalt
}
if err != nil {
err := fmt.Errorf("Error describing AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
regionconn := ec2.New(session.Copy(&aws.Config{
Region: aws.String(region)},
))
// Deregister image(s) by name
for _, i := range resp.Images {
_, err := ec2conn.DeregisterImage(&ec2.DeregisterImageInput{
ImageId: i.ImageId,
})
resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
Filters: []*ec2.Filter{{
Name: aws.String("name"),
Values: []*string{aws.String(s.AMIName)},
}}})
if err != nil {
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
err := fmt.Errorf("Error describing AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
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 := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
SnapshotId: b.Ebs.SnapshotId,
})
// Deregister image(s) by name
for _, i := range resp.Images {
_, err := regionconn.DeregisterImage(&ec2.DeregisterImageInput{
ImageId: i.ImageId,
})
if err != nil {
err := fmt.Errorf("Error deleting existing snapshot: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
if err != nil {
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
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))
}
}
}

View File

@ -15,6 +15,7 @@ type StepCreateEncryptedAMICopy struct {
KeyID string
EncryptBootVolume bool
Name string
AMIMappings []BlockDevice
}
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")
snapshots := state.Get("snapshots").(map[string][]string)
OuterLoop:
for _, blockDevice := range unencImage.BlockDeviceMappings {
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{
SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
}

View File

@ -10,7 +10,8 @@ import (
)
type StepModifyEBSBackedInstance struct {
EnableEnhancedNetworking bool
EnableAMIENASupport bool
EnableAMISriovNetSupport bool
}
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)
ui := state.Get("ui").(packer.Ui)
if s.EnableEnhancedNetworking {
// 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)
// 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)
if s.EnableAMISriovNetSupport {
ui.Say("Enabling Enhanced Networking (SR-IOV)...")
simple := "simple"
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
@ -33,11 +34,13 @@ func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.St
ui.Error(err.Error())
return multistep.ActionHalt
}
}
// Set EnaSupport to true.
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
// Set EnaSupport to true.
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
if s.EnableAMIENASupport {
ui.Say("Enabling Enhanced Networking (ENA)...")
_, err = ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
InstanceId: instance.InstanceId,
EnaSupport: &ec2.AttributeBooleanValue{Value: aws.Bool(true)},
})

View File

@ -26,7 +26,7 @@ func (s *StepPreValidate) Run(state multistep.StateBag) multistep.StepAction {
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{
Filters: []*ec2.Filter{{
Name: aws.String("name"),

View File

@ -5,14 +5,10 @@ import (
"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"
@ -29,16 +25,14 @@ type StepRunSourceInstance struct {
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
instanceId string
}
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
}
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
if spotPrice == "" || spotPrice == "0" {
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,
}
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
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)
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
s.instanceId = instanceId
@ -278,45 +190,6 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
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 instance.PublicDnsName != nil && *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)
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...")

View File

@ -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())
}
}
}

View File

@ -6,7 +6,7 @@ import (
"time"
"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/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
@ -15,9 +15,10 @@ import (
)
type StepSecurityGroup struct {
CommConfig *communicator.Config
SecurityGroupIds []string
VpcId string
CommConfig *communicator.Config
SecurityGroupIds []string
VpcId string
TemporarySGSourceCidr string
createdGroupId string
}
@ -45,7 +46,9 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
port := s.CommConfig.Port()
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
@ -76,15 +79,15 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
IpProtocol: aws.String("tcp"),
FromPort: 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
// group isn't available immediately because AWS resources are eventually
// consistent.
ui.Say(fmt.Sprintf(
"Authorizing access to port %d on the temporary security group...",
port))
"Authorizing access to port %d from %s in the temporary security group...",
port, s.TemporarySGSourceCidr))
for i := 0; i < 5; i++ {
_, err = ec2conn.AuthorizeSecurityGroupIngress(req)
if err == nil {
@ -151,36 +154,42 @@ func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
}
func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeSecurityGroups",
Delay: 15,
ctx := aws.BackgroundContext()
w := request.Waiter{
Name: "DescribeSecurityGroups",
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
Acceptors: []request.WaiterAcceptor{
{
State: "success",
Matcher: "path",
State: request.SuccessWaiterState,
Matcher: request.PathWaiterMatch,
Argument: "length(SecurityGroups[]) > `0`",
Expected: true,
},
{
State: "retry",
Matcher: "error",
State: request.RetryWaiterState,
Matcher: request.ErrorWaiterMatch,
Argument: "",
Expected: "InvalidGroup.NotFound",
},
{
State: "retry",
Matcher: "error",
State: request.RetryWaiterState,
Matcher: request.ErrorWaiterMatch,
Argument: "",
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
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
return w.WaitWithContext(ctx)
}

View File

@ -17,9 +17,10 @@ import (
// Produces:
// source_image *ec2.Image - the source AMI info
type StepSourceAMIInfo struct {
SourceAmi string
EnhancedNetworking bool
AmiFilters AmiFilterOptions
SourceAmi string
EnableAMISriovNetSupport bool
EnableAMIENASupport bool
AmiFilters AmiFilterOptions
}
// 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.
// 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)
state.Put("error", err)
ui.Error(err.Error())

View File

@ -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...
}

View File

@ -108,34 +108,29 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
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,
},
&stepCleanupVolumes{
BlockDevices: b.config.BlockDevices,
},
&awscommon.StepRunSourceInstance{
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,
VolumeTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
}
} else {
instanceStep = &awscommon.StepRunSpotInstance{
Debug: b.config.PackerDebug,
ExpectedRootDevice: "ebs",
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,
BlockDevices: b.config.BlockDevices,
Tags: b.config.RunTags,
VolumeTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
}
}
// Build the steps
steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepTagEBSVolumes{
VolumeRunTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
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{
Debug: b.config.PackerDebug,
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,
},
&awscommon.StepModifyEBSBackedInstance{
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
},
&awscommon.StepDeregisterAMI{
AccessConfig: &b.config.AccessConfig,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
Regions: b.config.AMIRegions,
},
&stepCreateAMI{},
&awscommon.StepCreateEncryptedAMICopy{
KeyID: b.config.AMIKmsKeyId,
EncryptBootVolume: b.config.AMIEncryptBootVolume,
Name: b.config.AMIName,
AMIMappings: b.config.AMIBlockDevices.AMIMappings,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,

View File

@ -122,31 +122,29 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
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,
},
&awscommon.StepRunSourceInstance{
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,
VolumeTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
}
} else {
instanceStep = &awscommon.StepRunSpotInstance{
Debug: b.config.PackerDebug,
ExpectedRootDevice: "ebs",
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,
BlockDevices: b.config.BlockDevices,
Tags: b.config.RunTags,
VolumeTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
}
}
// Build the steps
steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepTagEBSVolumes{
VolumeRunTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
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{
Debug: b.config.PackerDebug,
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,
},
&awscommon.StepModifyEBSBackedInstance{
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
},
&StepSnapshotNewRootVolume{
NewRootMountPoint: b.config.RootDevice.SourceDeviceName,
},
&awscommon.StepDeregisterAMI{
AccessConfig: &b.config.AccessConfig,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
Regions: b.config.AMIRegions,
},
&StepRegisterAMI{
RootDevice: b.config.RootDevice,
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
RootDevice: b.config.RootDevice,
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
},
&awscommon.StepCreateEncryptedAMICopy{
KeyID: b.config.AMIKmsKeyId,

View File

@ -12,9 +12,11 @@ import (
// StepRegisterAMI creates the AMI.
type StepRegisterAMI struct {
RootDevice RootBlockDevice
BlockDevices []*ec2.BlockDeviceMapping
image *ec2.Image
RootDevice RootBlockDevice
BlockDevices []*ec2.BlockDeviceMapping
EnableAMIENASupport bool
EnableAMISriovNetSupport bool
image *ec2.Image
}
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...")
blockDevicesExcludingRoot := make([]*ec2.BlockDeviceMapping, 0, len(s.BlockDevices)-1)
for _, blockDevice := range s.BlockDevices {
if *blockDevice.DeviceName == s.RootDevice.SourceDeviceName {
continue
}
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, blockDevice)
}
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, s.RootDevice.createBlockDeviceMapping(snapshotId))
blockDevicesExcludingRoot := DeduplicateRootVolume(s.BlockDevices, s.RootDevice, snapshotId)
registerOpts := &ec2.RegisterImageInput{
Name: &config.AMIName,
@ -44,16 +37,16 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
BlockDeviceMappings: blockDevicesExcludingRoot,
}
if config.AMIEnhancedNetworking {
if s.EnableAMISriovNetSupport {
// 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)
registerOpts.SriovNetSupport = aws.String("simple")
}
if s.EnableAMIENASupport {
// Set EnaSupport to true
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
registerOpts.EnaSupport = aws.Bool(true)
}
registerResp, err := ec2conn.RegisterImage(registerOpts)
if err != nil {
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
@ -125,3 +118,18 @@ func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {
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
}

View File

@ -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")
}
}

View File

@ -23,8 +23,9 @@ type Config struct {
awscommon.AccessConfig `mapstructure:",squash"`
awscommon.RunConfig `mapstructure:",squash"`
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
AMIENASupport bool `mapstructure:"ena_support"`
AMISriovNetSupport bool `mapstructure:"sriov_support"`
launchBlockDevices awscommon.BlockDevices
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("ui", ui)
// Build the steps
steps := []multistep.Step{
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
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,
},
&awscommon.StepRunSourceInstance{
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,
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,
@ -134,15 +135,35 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
EbsOptimized: b.config.EbsOptimized,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.launchBlockDevices,
Tags: b.config.RunTags,
Ctx: b.config.ctx,
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{
VolumeMapping: b.config.VolumeMappings,
Ctx: b.config.ctx,
&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{
Debug: b.config.PackerDebug,
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,
},
&awscommon.StepModifyEBSBackedInstance{
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
},
}

View File

@ -193,6 +193,48 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("hook", hook)
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
steps := []multistep.Step{
&awscommon.StepPreValidate{
@ -200,9 +242,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
AmiFilters: b.config.SourceAmiFilter,
SourceAmi: b.config.SourceAmi,
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
AmiFilters: b.config.SourceAmiFilter,
},
&awscommon.StepKeyPair{
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,
SecurityGroupIds: b.config.SecurityGroupIds,
VpcId: b.config.VpcId,
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
},
&awscommon.StepRunSourceInstance{
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,
},
instanceStep,
&awscommon.StepGetPassword{
Debug: b.config.PackerDebug,
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,
},
&awscommon.StepDeregisterAMI{
AccessConfig: &b.config.AccessConfig,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
Regions: b.config.AMIRegions,
},
&StepRegisterAMI{
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
},
&StepRegisterAMI{},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,

View File

@ -10,7 +10,10 @@ import (
"github.com/mitchellh/multistep"
)
type StepRegisterAMI struct{}
type StepRegisterAMI struct {
EnableAMIENASupport bool
EnableAMISriovNetSupport bool
}
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
@ -29,12 +32,13 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
registerOpts.VirtualizationType = aws.String(config.AMIVirtType)
}
if config.AMIEnhancedNetworking {
if s.EnableAMISriovNetSupport {
// 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)
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
registerOpts.EnaSupport = aws.Bool(true)
}

View File

@ -15,6 +15,7 @@ type uploadCmdData struct {
ManifestPath string
Region string
SecretKey string
Token string
}
type StepUploadBundle struct {
@ -40,11 +41,13 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
secretKey := config.SecretKey
session, err := config.AccessConfig.Session()
accessConfig := session.Config
var token string
if err == nil && accessKey == "" && secretKey == "" {
credentials, err := accessConfig.Credentials.Get()
if err == nil {
accessKey = credentials.AccessKeyID
secretKey = credentials.SecretAccessKey
token = credentials.SessionToken
}
}
@ -55,6 +58,7 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
ManifestPath: manifestPath,
Region: region,
SecretKey: secretKey,
Token: token,
}
config.BundleUploadCommand, err = interpolate.Render(config.BundleUploadCommand, &config.ctx)
if err != nil {
@ -78,6 +82,12 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
}
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(
"Bundle upload failed. Please see the output above for more\n"+
"details on what went wrong."))

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (
@ -119,7 +116,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
}
endpointConnectType := PublicEndpoint
if b.isPrivateNetworkCommunication() {
if b.isPublicPrivateNetworkCommunication() && b.isPrivateNetworkCommunication() {
endpointConnectType = PublicEndpointInPrivateNetwork
} else if b.isPrivateNetworkCommunication() {
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 {
return b.config.VirtualNetworkName != ""
}

View File

@ -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
import (

View File

@ -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
type CaptureTemplateParameter struct {

View File

@ -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
import (

View File

@ -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
import (
@ -34,10 +31,11 @@ import (
)
const (
DefaultCloudEnvironmentName = "Public"
DefaultImageVersion = "latest"
DefaultUserName = "packer"
DefaultVMSize = "Standard_A1"
DefaultCloudEnvironmentName = "Public"
DefaultImageVersion = "latest"
DefaultUserName = "packer"
DefaultPrivateVirtualNetworkWithPublicIp = false
DefaultVMSize = "Standard_A1"
)
var (
@ -73,24 +71,27 @@ type Config struct {
Location string `mapstructure:"location"`
VMSize string `mapstructure:"vm_size"`
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
ManagedImageName string `mapstructure:"managed_image_name"`
manageImageLocation string
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
ManagedImageName string `mapstructure:"managed_image_name"`
ManagedImageStorageAccountType string `mapstructure:"managed_image_storage_account_type"`
managedImageStorageAccountType compute.StorageAccountTypes
manageImageLocation string
// Deployment
AzureTags map[string]*string `mapstructure:"azure_tags"`
ResourceGroupName string `mapstructure:"resource_group_name"`
StorageAccount string `mapstructure:"storage_account"`
TempComputeName string `mapstructure:"temp_compute_name"`
TempResourceGroupName string `mapstructure:"temp_resource_group_name"`
storageAccountBlobEndpoint string
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
cloudEnvironment *azure.Environment
VirtualNetworkName string `mapstructure:"virtual_network_name"`
VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"`
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
CustomDataFile string `mapstructure:"custom_data_file"`
customData string
AzureTags map[string]*string `mapstructure:"azure_tags"`
ResourceGroupName string `mapstructure:"resource_group_name"`
StorageAccount string `mapstructure:"storage_account"`
TempComputeName string `mapstructure:"temp_compute_name"`
TempResourceGroupName string `mapstructure:"temp_resource_group_name"`
storageAccountBlobEndpoint string
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
cloudEnvironment *azure.Environment
PrivateVirtualNetworkWithPublicIp bool `mapstructure:"private_virtual_network_with_public_ip"`
VirtualNetworkName string `mapstructure:"virtual_network_name"`
VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"`
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
CustomDataFile string `mapstructure:"custom_data_file"`
customData string
// OS
OSType string `mapstructure:"os_type"`
@ -403,6 +404,10 @@ func provideDefaultValues(c *Config) {
c.VMSize = DefaultVMSize
}
if c.ManagedImageStorageAccountType == "" {
c.managedImageStorageAccountType = compute.StandardLRS
}
if c.ImagePublisher != "" && c.ImageVersion == "" {
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"))
}
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.ImagePublisher == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified"))
@ -596,4 +605,13 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
} else {
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))
}
}

View File

@ -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
import (
@ -9,6 +6,7 @@ import (
"testing"
"time"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/packer"
)
@ -48,6 +46,10 @@ func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) {
if 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) {
@ -56,6 +58,7 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
builderValues["ssh_username"] = "override_username"
builderValues["vm_size"] = "override_vm_size"
builderValues["communicator"] = "ssh"
builderValues["managed_image_storage_account_type"] = "Premium_LRS"
c, _, err := newConfig(builderValues, getPackerConfiguration())
@ -64,23 +67,27 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
}
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" {
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" {
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" {
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" {
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) {
config := map[string]interface{}{
"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 {
m := make(map[string]string)
for _, v := range requiredConfigValues {

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (
@ -16,12 +13,14 @@ type EndpointType int
const (
PublicEndpoint EndpointType = iota
PrivateEndpoint
PublicEndpointInPrivateNetwork
)
var (
EndpointCommunicationText = map[EndpointType]string{
PublicEndpoint: "PublicEndpoint",
PrivateEndpoint: "PrivateEndpoint",
PublicEndpoint: "PublicEndpoint",
PrivateEndpoint: "PrivateEndpoint",
PublicEndpointInPrivateNetwork: "PublicEndpointInPrivateNetwork",
}
)
@ -46,6 +45,8 @@ func NewStepGetIPAddress(client *AzureClient, ui packer.Ui, endpoint EndpointTyp
step.get = step.getPrivateIP
case PublicEndpoint:
step.get = step.getPublicIP
case PublicEndpointInPrivateNetwork:
step.get = step.getPublicIPInPrivateNetwork
}
return step
@ -70,6 +71,11 @@ func (s *StepGetIPAddress) getPublicIP(resourceGroupName string, ipAddressName s
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 {
s.say("Getting the VM's IP address ...")

View File

@ -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
import (
@ -12,42 +9,50 @@ import (
)
func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) {
var testSubject = &StepGetIPAddress{
get: func(string, string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
endpoint: PublicEndpoint,
say: func(message string) {},
error: func(e error) {},
}
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
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)
if result != multistep.ActionHalt {
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
stateBag := createTestStateBagStepGetIPAddress()
if _, ok := stateBag.GetOk(constants.Error); ok == false {
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
var result = testSubject.Run(stateBag)
if result != multistep.ActionHalt {
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk(constants.Error); ok == false {
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
}
}
}
func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) {
var testSubject = &StepGetIPAddress{
get: func(string, string, string) (string, error) { return "", nil },
endpoint: PublicEndpoint,
say: func(message string) {},
error: func(e error) {},
}
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
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)
if result != multistep.ActionContinue {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
stateBag := createTestStateBagStepGetIPAddress()
if _, ok := stateBag.GetOk(constants.Error); ok == true {
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
var result = testSubject.Run(stateBag)
if result != multistep.ActionContinue {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk(constants.Error); ok == true {
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
}
}
}
@ -55,50 +60,53 @@ func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) {
var actualResourceGroupName string
var actualIPAddressName string
var actualNicName string
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
var testSubject = &StepGetIPAddress{
get: func(resourceGroupName string, ipAddressName string, nicName string) (string, error) {
actualResourceGroupName = resourceGroupName
actualIPAddressName = ipAddressName
actualNicName = nicName
for _, endpoint := range endpoints {
var testSubject = &StepGetIPAddress{
get: func(resourceGroupName string, ipAddressName string, nicName string) (string, error) {
actualResourceGroupName = resourceGroupName
actualIPAddressName = ipAddressName
actualNicName = nicName
return "127.0.0.1", nil
},
endpoint: PublicEndpoint,
say: func(message string) {},
error: func(e error) {},
}
return "127.0.0.1", nil
},
endpoint: endpoint,
say: func(message string) {},
error: func(e error) {},
}
stateBag := createTestStateBagStepGetIPAddress()
var result = testSubject.Run(stateBag)
stateBag := createTestStateBagStepGetIPAddress()
var result = testSubject.Run(stateBag)
if result != multistep.ActionContinue {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if result != multistep.ActionContinue {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
if actualIPAddressName != expectedIPAddressName {
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
}
if actualIPAddressName != expectedIPAddressName {
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
}
if actualResourceGroupName != expectedResourceGroupName {
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
}
if actualResourceGroupName != expectedResourceGroupName {
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
}
if actualNicName != expectedNicName {
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
}
if actualNicName != expectedNicName {
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
}
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
if !ok {
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
}
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
if !ok {
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
}
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)
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)
}
}
}

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -53,7 +53,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
if config.ImageUrl != "" {
builder.SetImageUrl(config.ImageUrl, osType)
} else if config.CustomManagedImageName != "" {
builder.SetManagedDiskUrl(config.customManagedImageID)
builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType)
} else if config.ManagedImageName != "" && config.ImagePublisher != "" {
imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s",
config.SubscriptionID,
@ -63,7 +63,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
config.ImageSku,
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 {
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)
}
if config.VirtualNetworkName != "" {
if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp {
builder.SetPrivateVirtualNetworWithPublicIp(
config.VirtualNetworkResourceGroupName,
config.VirtualNetworkName,
config.VirtualNetworkSubnetName)
} else if config.VirtualNetworkName != "" {
builder.SetVirtualNetwork(
config.VirtualNetworkResourceGroupName,
config.VirtualNetworkName,

View File

@ -129,6 +129,9 @@
"osDisk": {
"caching": "ReadWrite",
"createOption": "fromImage",
"managedDisk": {
"storageAccountType": "Standard_LRS"
},
"name": "osdisk",
"osType": "Linux"
}

View File

@ -132,6 +132,9 @@
"osDisk": {
"caching": "ReadWrite",
"createOption": "fromImage",
"managedDisk": {
"storageAccountType": "Standard_LRS"
},
"name": "osdisk",
"osType": "Linux"
}

View File

@ -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'))]"
}
}

View File

@ -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.
func TestKeyVaultDeployment00(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
// complete flags

View File

@ -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
// Target types

View File

@ -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
// removes overlap between the end of a and the start of b and

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import "fmt"

View File

@ -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
import (

View File

@ -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
import (

View File

@ -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
import "github.com/mitchellh/multistep"

View File

@ -45,6 +45,7 @@ type OSDiskUnion struct {
Caching compute.CachingTypes `json:"caching,omitempty"`
CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"`
DiskSizeGB *int32 `json:"diskSizeGB,omitempty"`
ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"`
}
// Union of the StorageProfile and ImageStorageProfile types.

View File

@ -101,7 +101,7 @@ func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string)
return nil
}
func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string) error {
func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType compute.StorageAccountTypes) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
@ -115,11 +115,14 @@ func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string) error {
profile.OsDisk.OsType = s.osType
profile.OsDisk.CreateOption = compute.FromImage
profile.OsDisk.Vhd = nil
profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{
StorageAccountType: storageAccountType,
}
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)
if err != nil {
return err
@ -137,6 +140,9 @@ func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer,
profile.OsDisk.OsType = s.osType
profile.OsDisk.CreateOption = compute.FromImage
profile.OsDisk.Vhd = nil
profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{
StorageAccountType: storageAccountType,
}
return nil
}
@ -219,6 +225,24 @@ func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtual
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 {
if tags == nil || len(*tags) == 0 {
return nil

View File

@ -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
// The intent of these types to facilitate interchange with Azure in the

View File

@ -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
import (

View File

@ -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
// should be removed.

View File

@ -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.

View File

@ -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