Merge remote-tracking branch 'upstream/master' into prestine

This commit is contained in:
Matthew Patton 2018-10-01 20:18:10 -04:00
commit 8a413cfe83
1395 changed files with 194681 additions and 32969 deletions

1
.gitignore vendored
View file

@ -4,6 +4,7 @@
/src
/website/.sass-cache
/website/build
/website/tmp
.DS_Store
.vagrant
.idea

View file

@ -6,8 +6,7 @@ sudo: false
language: go
go:
- 1.9.x
- 1.10.x
- 1.11.x
- master
install:

View file

@ -1,41 +1,114 @@
## 1.2.6 (Unreleased)
## 1.3.1 (September 13, 2018)
### IMPROVEMENTS:
* builder/amazon: automatically decode encoded authorization messages if
possible [GH-5415]
* builder:amazon: Optional cleanup of the authorized keys file [GH-6713]
### BUG FIXES:
* builder/amazon: fix bugs relating to spot instances provisioning [GH-6697]
[GH-6693]
* builder/openstack: fix ssh keypair not attached [GH-6701]
* core: progressbar: fix deadlock locking builds afer first display [GH-6698]
## 1.3.0 (September 11, 2018)
### IMPROVEMENTS:
* azure/arm: Retry cleanup of individual resources on error [GH-6644]
* builder/alicloud: Support source image coming from marketplace [GH-6588]
* builder/amazon-chroot: Add new `root_volume_type` option. [GH-6669]
* builder/amazon-chroot: If you have a PV source AMI, with the Amazon Chroot
builder, and the destination AMI is type HVM, you can now enable
ena_support, example: [GH-6670]
* builder/amazon-chroot: New feature `root_volume_tags` to tag the created
volumes. [GH-6504]
* builder/amazon: Create a random interim AMI name when encrypt_boot is true so
that ami name is not searchable. [GH-6657]
* builder/azure: Implement clean_image_name template engine. [GH-6558]
* builder/cloudstack: Add option to use a fixed port via public_port. [GH-6532]
* builder/digitalocean: Add support for tagging to instances [GH-6546]
* builder/googlecompute: Add new `min_cpu_platform` feature [GH-6607]
* builder/googlecompute: Update the list of public image projects that we
search, based on GCE documentation. [GH-6648]
* builder/lxc: Allow unplivileged LXC containers. [GH-6279]
* builder/oci: Add `metadata` feature to Packer config. [GH-6498]
* builder/openstack: Add support for getting config from clouds-public.yaml.
[GH-6595]
* builder/openstack: Add support for ports. [GH-6570]
* builder/openstack: Add support for getting config from clouds-public.yaml. [GH-6595]
* builder/openstack: Add support for source_image_filter. [GH-6490]
* builder/openstack: Migrate floating IP usage to Network v2 API from Compute
API. [GH-6373]
* builder/openstack: Support Block Storage volumes as boot volume. [GH-6596]
* builder/openstack: Migrate floating IP usage to Network v2 API from Compute API. [GH-6373]
* builder/oracle-oci: Add support for freeform tagging of OCI images [GH-6338]
* builder/qemu: add ssh agent support. [GH-6541]
* builder/qemu: New `use_backing_file` feature [GH-6249]
* builder/vmware-iso: Add support for disk compaction [GH-6411]
* builder/vmware-iso: Try to use ISO files uploaded to the datastore when
building remotely instead of uploading them freshly every time [GH-5165]
* command/validate: Warn users if config needs fixing. [GH-6423]
* core: Add a 'split' function to parse template variables. [GH-6357]
* core: Add a template function allowing users to read keys from consul
[GH-6577]
* core: Add a template function allowing users to read keys from vault
[GH-6533]
* core: Add progress-bar to download step. [GH-5851]
* core: Create a new root-level Packer template option, "sensitive-variables"
which allows users to list which variables they would like to have scrubbed
from the Packer logs. [GH-6610]
* core: Create new config options, "boot_keygroup_interval" and
"boot_key_interval" that can be set at the builder-level to supercede
PACKER_KEY_INTERVAL for the bootcommand. [GH-6616]
* core: Deduplicate ui and log lines that stream to terminal [GH-6611]
* core: Refactor and deduplicate ssh code across builders. This should be a no-
op but is a big win for maintainability. [GH-6621] [GH-6613]
* post-processor/compress: Add support for xz compression [GH-6534]
* post-processor/vagrant: Support for Docker images. [GH-6494]
* post-processor/vsphere: Add new `esxi_host` option. [GH-5366]
* postprocessor/vagrant: Add support for Azure. [GH-6576]
* provisioner/ansible: Add new "extra var", packer_http_addr. [GH-6501]
* provisioner/ansible: Enable {{.WinRMPassword}} template engine. [GH-6450]
* provisioner/shell-local: Create PACKER_HTTP_ADDR environment variable
[GH-6503]
### BUG FIXES:
* builder/amazon-ebssurrogate: Clean up volumes at end of build. [GH-6514]
* builder/amazon: Increase default waiter timeout for AWS
WaitUntilImageAvailable command [GH-6601]
* builder/amazon: Increase the MaxRetries in the Amazon client from the default
to 20, to work around users who regularly reach their requestlimit and are
being throttled. [GH-6641]
* builder/amazon: Properly apply environment overrides to our custom-written
waiters. [GH-6649]
* builder/azure: Generated password satisfies Azure password requirements
[GH-6480]
* builder/hyper-v: Buider no longer errors if skip_compaction isn't true when
skip_export is true, and compaction efficiency is improved [GH-6393]
* builder/lxc: Correctly pass "config" option to "lxc launch". [GH-6563]
* builder/lxc: Determine lxc root according to the running user [GH-6543]
* builder/lxc: Fix file copying for unprivileged LXC containers [GH-6544]
* builder/oracle-oci: Update OCI sdk, fixing validation bug that occurred when
RSA key was encrypted. [GH-6492]
* builder/vmware-iso: Fix crash caused by invalid datacenter url. [GH-6529]
* builder/vmware: Maintain original boot order during CreateVMX step for
vmware-iso builder [GH-6204]
* communicator/chroot: Fix quote escaping so that ansible provisioner works
properly. [GH-6635]
* core: Better error handling in downloader when connection error occurs.
[GH-6557]
* core: Fix broken pathing checks in checksum files. [GH-6525]
* provisioner/shell Create new template option allowing users to choose to
source env vars from a file rather than declaring them inline. This
resolves a bug that occurred when users had complex quoting in their
`execute_command`s [GH-6636]
* provisioner/shell-local: Windows inline scripts now default to being appended
with ".cmd", fixing a backwards incompatibility in v1.2.5 [GH-6626]
* provisioner/windows-restart: Provisioner now works when used in conjuction
with SSH communicator [GH-6606]
### BACKWARDS INCOMPATIBILITIES:
* builder/amazon: "owners" field on source_ami_filter is now required for
secuirty reasons. [GH-6585]
* builder/vmware-iso: validation will fail for templates using esxi that have the "disk_type_id" set to something other than "thin" or "" and that do not have "skip_compaction": true also set. Use `packer fix` to fix this. [GH-6411]
## 1.2.5 (July 16, 2018)

View file

@ -11,7 +11,7 @@ GOPATH=$(shell go env GOPATH)
# gofmt
UNFORMATTED_FILES=$(shell find . -not -path "./vendor/*" -name "*.go" | xargs gofmt -s -l)
EXECUTABLE_FILES=$(shell find . -type f -perm +111 | egrep -v '^\./(vendor/|\.git|bin/|scripts/|pkg/)' | egrep -v '.*(\.sh|\.bats)' | egrep -v './provisioner/ansible/test-fixtures/exit1')
EXECUTABLE_FILES=$(shell find . -type f -perm +111 | egrep -v '^\./(website/vendor|vendor/|\.git|bin/|scripts/|pkg/)' | egrep -v '.*(\.sh|\.bats|\.git)' | egrep -v './provisioner/ansible/test-fixtures/exit1')
# Get the git commit
GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
@ -50,7 +50,6 @@ deps:
@go get -u github.com/mna/pigeon
@go get github.com/kardianos/govendor
@go get golang.org/x/tools/cmd/goimports
@govendor sync
dev: deps ## Build and install a development build
@grep 'const VersionPrerelease = ""' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \

View file

@ -16,8 +16,55 @@ environment:
clone_folder: c:\gopath\src\github.com\hashicorp\packer
install:
- ps: |
# Installs golang on Windows.
#
# # Run script:
# .\install-go.ps1
#
# # Download and run script:
# $env:GOVERSION = '1.5.3'
# iex ((new-object net.webclient).DownloadString('SCRIPT_URL_HERE'))
$version = '1.11'
$downloadDir = $env:TEMP
$packageName = 'golang'
$url32 = 'https://storage.googleapis.com/golang/go' + $version + '.windows-386.zip'
$url64 = 'https://storage.googleapis.com/golang/go' + $version + '.windows-amd64.zip'
$goroot = "C:\go$version"
# Determine type of system
if ($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64") {
$url = $url64
} else {
$url = $url32
}
if (Test-Path "$goroot\bin\go") {
Write-Host "Go is installed to $goroot"
exit
}
echo "Downloading $url"
$zip = "$downloadDir\golang-$version.zip"
if (!(Test-Path "$zip")) {
$downloader = new-object System.Net.WebClient
$downloader.DownloadFile($url, $zip)
}
echo "Extracting $zip to $goroot"
if (Test-Path "$downloadDir\go") {
rm -Force -Recurse -Path "$downloadDir\go"
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory("$zip", $downloadDir)
mv "$downloadDir\go" $goroot
- set GO15VENDOREXPERIMENT=1
- set GOROOT=C:\go1.11
- set Path=%GOROOT%\bin;%PATH%
- echo %Path%
- echo %GOROOT%
- go version
- go env
- go get github.com/mitchellh/gox

View file

@ -67,7 +67,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AlicloudAccessKey, b.config.AlicloudSecretKey))
packer.LogSecretFilter.Set(b.config.AlicloudAccessKey, b.config.AlicloudSecretKey)
return nil, nil
}
@ -78,7 +78,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, err
}
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("config", &b.config)
state.Put("client", client)
state.Put("hook", hook)
state.Put("ui", ui)
@ -95,13 +95,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
SourceECSImageId: b.config.AlicloudSourceImage,
},
&stepConfigAlicloudKeyPair{
Debug: b.config.PackerDebug,
KeyPairName: b.config.SSHKeyPairName,
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
DebugKeyPath: fmt.Sprintf("ecs_%s.pem", b.config.PackerBuildName),
RegionId: b.config.AlicloudRegion,
Debug: b.config.PackerDebug,
Comm: &b.config.Comm,
DebugKeyPath: fmt.Sprintf("ecs_%s.pem", b.config.PackerBuildName),
RegionId: b.config.AlicloudRegion,
},
}
if b.chooseNetworkType() == VpcNet {
@ -141,10 +138,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
RegionId: b.config.AlicloudRegion,
InternetChargeType: b.config.InternetChargeType,
InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut,
SSHPrivateIp: b.config.SSHPrivateIp,
})
} else {
steps = append(steps, &stepConfigAlicloudPublicIP{
RegionId: b.config.AlicloudRegion,
RegionId: b.config.AlicloudRegion,
SSHPrivateIp: b.config.SSHPrivateIp,
})
}
steps = append(steps,
@ -156,14 +155,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Host: SSHHost(
client,
b.config.SSHPrivateIp),
SSHConfig: SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword),
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.RunConfig.Comm,
},
&stepStopAlicloudInstance{
ForceStop: b.config.ForceStopInstance,
ForceStop: b.config.ForceStopInstance,
DisableStop: b.config.DisableStopInstance,
},
&stepDeleteAlicloudImageSnapshots{
AlicloudImageForceDeleteSnapshots: b.config.AlicloudImageForceDeleteSnapshots,
@ -171,6 +171,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AlicloudImageName: b.config.AlicloudImageName,
},
&stepCreateAlicloudImage{},
&stepCreateTags{
Tags: b.config.AlicloudImageTags,
},
&stepRegionCopyAlicloudImage{
AlicloudImageDestinationRegions: b.config.AlicloudImageDestinationRegions,
AlicloudImageDestinationNames: b.config.AlicloudImageDestinationNames,
@ -232,7 +235,7 @@ func (b *Builder) isVpcSpecified() bool {
func (b *Builder) isUserDataNeeded() bool {
// Public key setup requires userdata
if b.config.RunConfig.Comm.SSHPrivateKey != "" {
if b.config.RunConfig.Comm.SSHPrivateKeyFile != "" {
return true
}
@ -240,5 +243,5 @@ func (b *Builder) isUserDataNeeded() bool {
}
func (b *Builder) isKeyPairNeeded() bool {
return b.config.SSHKeyPairName != "" || b.config.TemporaryKeyPairName != ""
return b.config.Comm.SSHKeyPairName != "" || b.config.Comm.SSHTemporaryKeyPairName != ""
}

View file

@ -113,6 +113,17 @@ func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
})
}
func TestBuilderAcc_imageTags(t *testing.T) {
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: testBuilderAccImageTags,
Check: checkImageTags(),
})
}
func checkSnapshotsDeleted(snapshotIds []string) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
// Verify the snapshots are gone
@ -207,6 +218,51 @@ func checkRegionCopy(regions []string) builderT.TestCheckFunc {
}
}
func checkImageTags() builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
if len(artifacts) > 1 {
return fmt.Errorf("more than 1 artifact")
}
// Get the actual *Artifact pointer so we can access the AMIs directly
artifactRaw := artifacts[0]
artifact, ok := artifactRaw.(*Artifact)
if !ok {
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
}
// describe the image, get block devices with a snapshot
client, _ := testAliyunClient()
tags, _, err := client.DescribeTags(
&ecs.DescribeTagsArgs{
RegionId: "cn-beijing",
ResourceType: ecs.TagResourceImage,
ResourceId: artifact.AlicloudImages["cn-beijing"],
})
if err != nil {
return fmt.Errorf("Error retrieving Image Attributes for ECS Image Artifact (%#v) "+
"in ECS Image Tags Test: %s", artifact, err)
}
failed := false
if len(tags) != 2 {
failed = true
}
if !failed {
for i := 0; i < len(tags); i++ {
if tags[i].TagKey == "TagKey1" && tags[i].TagValue != "TagValue1" {
failed = true
} else if tags[i].TagKey == "TagKey2" && tags[i].TagValue != "TagValue2" {
failed = true
} else if tags[i].TagKey != "TagKey1" && tags[i].TagKey != "TagKey2" {
failed = true
}
}
}
if failed {
return fmt.Errorf("tags is not correctly set %#v", tags)
}
return nil
}
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("ALICLOUD_ACCESS_KEY"); v == "" {
t.Fatal("ALICLOUD_ACCESS_KEY must be set for acceptance tests")
@ -236,8 +292,7 @@ const testBuilderAccBasic = `
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
"ssh_username": "ubuntu",
"source_image":"ubuntu_16_0402_64_20G_alibase_20180409.vhd",
"io_optimized":"true",
"ssh_username":"root",
"image_name": "packer-test_{{timestamp}}"
@ -306,6 +361,22 @@ const testBuilderAccSharing = `
}
`
const testBuilderAccImageTags = `
{ "builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_20G_alibase_20180409.vhd",
"ssh_username": "root",
"io_optimized":"true",
"image_name": "packer-test_{{timestamp}}",
"tags": {
"TagKey1": "TagValue1",
"TagKey2": "TagValue2"
}
}]
}`
func buildForceDeregisterConfig(val, name string) string {
return fmt.Sprintf(testBuilderAccForceDelete, val, name)
}

View file

@ -25,17 +25,18 @@ type AlicloudDiskDevices struct {
}
type AlicloudImageConfig struct {
AlicloudImageName string `mapstructure:"image_name"`
AlicloudImageVersion string `mapstructure:"image_version"`
AlicloudImageDescription string `mapstructure:"image_description"`
AlicloudImageShareAccounts []string `mapstructure:"image_share_account"`
AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"`
AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"`
AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"`
AlicloudImageForceDelete bool `mapstructure:"image_force_delete"`
AlicloudImageForceDeleteSnapshots bool `mapstructure:"image_force_delete_snapshots"`
AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"`
AlicloudImageSkipRegionValidation bool `mapstructure:"skip_region_validation"`
AlicloudImageName string `mapstructure:"image_name"`
AlicloudImageVersion string `mapstructure:"image_version"`
AlicloudImageDescription string `mapstructure:"image_description"`
AlicloudImageShareAccounts []string `mapstructure:"image_share_account"`
AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"`
AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"`
AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"`
AlicloudImageForceDelete bool `mapstructure:"image_force_delete"`
AlicloudImageForceDeleteSnapshots bool `mapstructure:"image_force_delete_snapshots"`
AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"`
AlicloudImageSkipRegionValidation bool `mapstructure:"skip_region_validation"`
AlicloudImageTags map[string]string `mapstructure:"tags"`
AlicloudDiskDevices `mapstructure:",squash"`
}

View file

@ -55,6 +55,24 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
}
func TestECSImageConfigPrepare_imageTags(t *testing.T) {
c := testAlicloudImageConfig()
c.AlicloudImageTags = map[string]string{
"TagKey1": "TagValue1",
"TagKey2": "TagValue2",
}
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if len(c.AlicloudImageTags) != 2 || c.AlicloudImageTags["TagKey1"] != "TagValue1" ||
c.AlicloudImageTags["TagKey2"] != "TagValue2" {
t.Fatalf("invalid value, expected: %s, actual: %s", map[string]string{
"TagKey1": "TagValue1",
"TagKey2": "TagValue2",
}, c.AlicloudImageTags)
}
}
func regionsToString() []string {
var regions []string
for _, region := range common.ValidRegions {

View file

@ -19,6 +19,7 @@ type RunConfig struct {
Description string `mapstructure:"description"`
AlicloudSourceImage string `mapstructure:"source_image"`
ForceStopInstance bool `mapstructure:"force_stop_instance"`
DisableStopInstance bool `mapstructure:"disable_stop_instance"`
SecurityGroupId string `mapstructure:"security_group_id"`
SecurityGroupName string `mapstructure:"security_group_name"`
UserData string `mapstructure:"user_data"`
@ -31,19 +32,17 @@ type RunConfig struct {
InstanceName string `mapstructure:"instance_name"`
InternetChargeType string `mapstructure:"internet_charge_type"`
InternetMaxBandwidthOut int `mapstructure:"internet_max_bandwidth_out"`
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
// Communicator settings
Comm communicator.Config `mapstructure:",squash"`
SSHKeyPairName string `mapstructure:"ssh_keypair_name"`
SSHPrivateIp bool `mapstructure:"ssh_private_ip"`
Comm communicator.Config `mapstructure:",squash"`
SSHPrivateIp bool `mapstructure:"ssh_private_ip"`
}
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
if c.SSHKeyPairName == "" && c.TemporaryKeyPairName == "" &&
c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" && c.Comm.WinRMPassword == "" {
if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" &&
c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" && c.Comm.WinRMPassword == "" {
c.TemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
// Validation

View file

@ -105,21 +105,72 @@ func TestRunConfigPrepare_UserDataFile(t *testing.T) {
func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
c := testConfig()
c.TemporaryKeyPairName = ""
c.Comm.SSHTemporaryKeyPairName = ""
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.TemporaryKeyPairName == "" {
if c.Comm.SSHTemporaryKeyPairName == "" {
t.Fatal("keypair name is empty")
}
c.TemporaryKeyPairName = "ssh-key-123"
c.Comm.SSHTemporaryKeyPairName = "ssh-key-123"
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.TemporaryKeyPairName != "ssh-key-123" {
if c.Comm.SSHTemporaryKeyPairName != "ssh-key-123" {
t.Fatal("keypair name does not match")
}
}
func TestRunConfigPrepare_SSHPrivateIp(t *testing.T) {
c := testConfig()
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.SSHPrivateIp != false {
t.Fatalf("invalid value, expected: %t, actul: %t", false, c.SSHPrivateIp)
}
c.SSHPrivateIp = true
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.SSHPrivateIp != true {
t.Fatalf("invalid value, expected: %t, actul: %t", true, c.SSHPrivateIp)
}
c.SSHPrivateIp = false
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.SSHPrivateIp != false {
t.Fatalf("invalid value, expected: %t, actul: %t", false, c.SSHPrivateIp)
}
}
func TestRunConfigPrepare_DisableStopInstance(t *testing.T) {
c := testConfig()
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.DisableStopInstance != false {
t.Fatalf("invalid value, expected: %t, actul: %t", false, c.DisableStopInstance)
}
c.DisableStopInstance = true
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.DisableStopInstance != true {
t.Fatalf("invalid value, expected: %t, actul: %t", true, c.DisableStopInstance)
}
c.DisableStopInstance = false
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.DisableStopInstance != false {
t.Fatalf("invalid value, expected: %t, actul: %t", false, c.DisableStopInstance)
}
}

View file

@ -1,15 +1,9 @@
package ecs
import (
"fmt"
"net"
"os"
"time"
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/hashicorp/packer/helper/multistep"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
var (
@ -27,57 +21,3 @@ func SSHHost(e alicloudSSHHelper, private bool) func(multistep.StateBag) (string
return ipAddress, nil
}
}
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the private key
// or password.
func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
if useAgent {
authSock := os.Getenv("SSH_AUTH_SOCK")
if authSock == "" {
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
}
sshAgent, err := net.Dial("unix", authSock)
if err != nil {
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
privateKey, hasKey := state.GetOk("privateKey")
if hasKey {
signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
} else {
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
}
}

View file

@ -16,15 +16,15 @@ type stepAttachKeyPair struct {
}
func (s *stepAttachKeyPair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
keyPairName := state.Get("keyPair").(string)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(*Config)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
timeoutPoint := time.Now().Add(120 * time.Second)
keyPairName := config.Comm.SSHKeyPairName
if keyPairName == "" {
return multistep.ActionContinue
}
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
timeoutPoint := time.Now().Add(120 * time.Second)
for {
err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
@ -51,14 +51,14 @@ func (s *stepAttachKeyPair) Run(_ context.Context, state multistep.StateBag) mul
}
func (s *stepAttachKeyPair) Cleanup(state multistep.StateBag) {
keyPairName := state.Get("keyPair").(string)
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
keyPairName := config.Comm.SSHKeyPairName
if keyPairName == "" {
return
}
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
err := client.DetachKeyPair(&ecs.DetachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})

View file

@ -16,7 +16,7 @@ type stepCheckAlicloudSourceImage struct {
func (s *stepCheckAlicloudSourceImage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
args := &ecs.DescribeImagesArgs{
RegionId: common.Region(config.AlicloudRegion),

View file

@ -16,12 +16,24 @@ type stepConfigAlicloudEIP struct {
InternetChargeType string
InternetMaxBandwidthOut int
allocatedId string
SSHPrivateIp bool
}
func (s *stepConfigAlicloudEIP) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
if s.SSHPrivateIp {
ipaddress := instance.VpcAttributes.PrivateIpAddress.IpAddress
if len(ipaddress) == 0 {
ui.Say("Failed to get private ip of instance")
return multistep.ActionHalt
}
state.Put("ipaddress", ipaddress[0])
return multistep.ActionContinue
}
ui.Say("Allocating eip")
ipaddress, allocateId, err := client.AllocateEipAddress(&ecs.AllocateEipAddressArgs{
RegionId: common.Region(s.RegionId), InternetChargeType: common.InternetChargeType(s.InternetChargeType),

View file

@ -9,18 +9,16 @@ import (
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepConfigAlicloudKeyPair struct {
Debug bool
SSHAgentAuth bool
DebugKeyPath string
TemporaryKeyPairName string
KeyPairName string
PrivateKeyFile string
RegionId string
Debug bool
Comm *communicator.Config
DebugKeyPath string
RegionId string
keyName string
}
@ -28,43 +26,41 @@ type stepConfigAlicloudKeyPair struct {
func (s *stepConfigAlicloudKeyPair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.PrivateKeyFile != "" {
if s.Comm.SSHPrivateKeyFile != "" {
ui.Say("Using existing SSH private key")
privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"Error loading configured private key file: %s", err))
return multistep.ActionHalt
}
state.Put("keyPair", s.KeyPairName)
state.Put("privateKey", string(privateKeyBytes))
s.Comm.SSHPrivateKey = privateKeyBytes
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName == "" {
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName == "" {
ui.Say("Using SSH Agent with key pair in source image")
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName))
state.Put("keyPair", s.KeyPairName)
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.Comm.SSHKeyPairName))
return multistep.ActionContinue
}
if s.TemporaryKeyPairName == "" {
if s.Comm.SSHTemporaryKeyPairName == "" {
ui.Say("Not using temporary keypair")
state.Put("keyPair", "")
s.Comm.SSHKeyPairName = ""
return multistep.ActionContinue
}
client := state.Get("client").(*ecs.Client)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.TemporaryKeyPairName))
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
keyResp, err := client.CreateKeyPair(&ecs.CreateKeyPairArgs{
KeyPairName: s.TemporaryKeyPairName,
KeyPairName: s.Comm.SSHTemporaryKeyPairName,
RegionId: common.Region(s.RegionId),
})
if err != nil {
@ -73,11 +69,11 @@ func (s *stepConfigAlicloudKeyPair) Run(_ context.Context, state multistep.State
}
// Set the keyname so we know to delete it later
s.keyName = s.TemporaryKeyPairName
s.keyName = s.Comm.SSHTemporaryKeyPairName
// Set some state data for use in future steps
state.Put("keyPair", s.keyName)
state.Put("privateKey", keyResp.PrivateKeyBody)
s.Comm.SSHKeyPairName = s.keyName
s.Comm.SSHPrivateKey = []byte(keyResp.PrivateKeyBody)
// If we're in debug mode, output the private key to the working
// directory.
@ -112,7 +108,7 @@ func (s *stepConfigAlicloudKeyPair) Cleanup(state multistep.StateBag) {
// If no key name is set, then we never created it, so just return
// If we used an SSH private key file, do not go about deleting
// keypairs
if s.PrivateKeyFile != "" || (s.KeyPairName == "" && s.keyName == "") {
if s.Comm.SSHPrivateKeyFile != "" || (s.Comm.SSHKeyPairName == "" && s.keyName == "") {
return
}

View file

@ -12,6 +12,7 @@ import (
type stepConfigAlicloudPublicIP struct {
publicIPAddress string
RegionId string
SSHPrivateIp bool
}
func (s *stepConfigAlicloudPublicIP) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
@ -19,6 +20,16 @@ func (s *stepConfigAlicloudPublicIP) Run(_ context.Context, state multistep.Stat
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
if s.SSHPrivateIp {
ipaddress := instance.InnerIpAddress.IpAddress
if len(ipaddress) == 0 {
ui.Say("Failed to get private ip of instance")
return multistep.ActionHalt
}
state.Put("ipaddress", ipaddress[0])
return multistep.ActionContinue
}
ipaddress, err := client.AllocatePublicIpAddress(instance.InstanceId)
if err != nil {
state.Put("error", err)

View file

@ -20,7 +20,7 @@ type stepConfigAlicloudVPC struct {
}
func (s *stepConfigAlicloudVPC) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)

View file

@ -24,7 +24,7 @@ func (s *stepConfigAlicloudVSwitch) Run(_ context.Context, state multistep.State
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
vpcId := state.Get("vpcid").(string)
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
if len(s.VSwitchId) != 0 {
vswitchs, _, err := client.DescribeVSwitches(&ecs.DescribeVSwitchesArgs{

View file

@ -15,7 +15,7 @@ type stepCreateAlicloudImage struct {
}
func (s *stepCreateAlicloudImage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
@ -85,7 +85,7 @@ func (s *stepCreateAlicloudImage) Cleanup(state multistep.StateBag) {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
ui.Say("Deleting the image because of cancellation or error...")
if err := client.DeleteImage(common.Region(config.AlicloudRegion), s.image.ImageId); err != nil {

View file

@ -28,7 +28,7 @@ type stepCreateAlicloudInstance struct {
func (s *stepCreateAlicloudInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
source_image := state.Get("source_image").(*ecs.ImageType)
network_type := state.Get("networktype").(InstanceNetWork)

View file

@ -0,0 +1,41 @@
package ecs
import (
"context"
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepCreateTags struct {
Tags map[string]string
}
func (s *stepCreateTags) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
imageId := state.Get("alicloudimage").(string)
if len(s.Tags) == 0 {
return multistep.ActionContinue
}
ui.Say(fmt.Sprintf("Adding tags(%s) to image: %s", s.Tags, imageId))
err := client.AddTags(&ecs.AddTagsArgs{
ResourceId: imageId,
ResourceType: ecs.TagResourceImage,
RegionId: common.Region(config.AlicloudRegion),
Tag: s.Tags,
})
if err != nil {
err := fmt.Errorf("Error Adding tags to image: %s", err)
state.Put("error", err)
ui.Say(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepCreateTags) Cleanup(state multistep.StateBag) {
// Nothing need to do, tags will be cleaned when the resource is cleaned
}

View file

@ -20,8 +20,8 @@ type stepDeleteAlicloudImageSnapshots struct {
func (s *stepDeleteAlicloudImageSnapshots) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(Config)
ui.Say("Deleting image snapshots.")
config := state.Get("config").(*Config)
// Check for force delete
if s.AlicloudImageForceDelete {
images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
@ -31,6 +31,9 @@ func (s *stepDeleteAlicloudImageSnapshots) Run(_ context.Context, state multiste
if len(images) < 1 {
return multistep.ActionContinue
}
ui.Say(fmt.Sprintf("Deleting duplicated image and snapshot: %s", s.AlicloudImageName))
for _, image := range images {
if image.ImageOwnerAlias != string(ecs.ImageOwnerSelf) {
log.Printf("You can only delete instances based on customized images %s ", image.ImageId)

View file

@ -14,7 +14,7 @@ type stepMountAlicloudDisk struct {
func (s *stepMountAlicloudDisk) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
alicloudDiskDevices := config.ECSImagesDiskMappings

View file

@ -23,7 +23,7 @@ func (s *stepPreValidate) Run(_ context.Context, state multistep.StateBag) multi
}
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
ui.Say("Prevalidating image name...")
images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
ImageName: s.AlicloudDestImageName,

View file

@ -24,7 +24,9 @@ func (s *stepRunAlicloudInstance) Run(_ context.Context, state multistep.StateBa
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Starting instance.")
ui.Say(fmt.Sprintf("Starting instance: %s", instance.InstanceId))
err = client.WaitForInstance(instance.InstanceId, ecs.Running, ALICLOUD_DEFAULT_TIMEOUT)
if err != nil {
err := fmt.Errorf("Timeout waiting for instance to start: %s", err)

View file

@ -10,7 +10,8 @@ import (
)
type stepStopAlicloudInstance struct {
ForceStop bool
ForceStop bool
DisableStop bool
}
func (s *stepStopAlicloudInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
@ -18,8 +19,9 @@ func (s *stepStopAlicloudInstance) Run(_ context.Context, state multistep.StateB
instance := state.Get("instance").(*ecs.InstanceAttributesType)
ui := state.Get("ui").(packer.Ui)
err := client.StopInstance(instance.InstanceId, s.ForceStop)
if err != nil {
if !s.DisableStop {
ui.Say(fmt.Sprintf("Stopping instance: %s", instance.InstanceId))
err := client.StopInstance(instance.InstanceId, s.ForceStop)
if err != nil {
err := fmt.Errorf("Error stopping alicloud instance: %s", err)
state.Put("error", err)
@ -28,7 +30,9 @@ func (s *stepStopAlicloudInstance) Run(_ context.Context, state multistep.StateB
}
}
err = client.WaitForInstance(instance.InstanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT)
ui.Say(fmt.Sprintf("Waiting instance stopped: %s", instance.InstanceId))
err := client.WaitForInstance(instance.InstanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT)
if err != nil {
err := fmt.Errorf("Error waiting for alicloud instance to stop: %s", err)
state.Put("error", err)

View file

@ -42,6 +42,7 @@ type Config struct {
PreMountCommands []string `mapstructure:"pre_mount_commands"`
RootDeviceName string `mapstructure:"root_device_name"`
RootVolumeSize int64 `mapstructure:"root_volume_size"`
RootVolumeType string `mapstructure:"root_volume_type"`
SourceAmi string `mapstructure:"source_ami"`
SourceAmiFilter awscommon.AmiFilterOptions `mapstructure:"source_ami_filter"`
RootVolumeTags awscommon.TagMap `mapstructure:"root_volume_tags"`
@ -176,7 +177,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warns, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
return warns, nil
}
@ -222,6 +223,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
AmiFilters: b.config.SourceAmiFilter,
AMIVirtType: b.config.AMIVirtType,
},
&StepCheckRootDevice{},
)
@ -231,6 +233,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepFlock{},
&StepPrepareDevice{},
&StepCreateVolume{
RootVolumeType: b.config.RootVolumeType,
RootVolumeSize: b.config.RootVolumeSize,
RootVolumeTags: b.config.RootVolumeTags,
Ctx: b.config.ctx,

View file

@ -11,6 +11,8 @@ func testConfig() map[string]interface{} {
"ami_name": "foo",
"source_ami": "foo",
"region": "us-east-1",
// region validation logic is checked in ami_config_test
"skip_region_validation": true,
}
}
@ -28,6 +30,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test good
config["ami_name"] = "foo"
config["skip_region_validation"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)

View file

@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
@ -23,8 +24,10 @@ type Communicator struct {
}
func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
// need extra escapes for the command since we're wrapping it in quotes
cmd.Command = strconv.Quote(cmd.Command)
command, err := c.CmdWrapper(
fmt.Sprintf("chroot %s /bin/sh -c \"%s\"", c.Chroot, cmd.Command))
fmt.Sprintf("chroot %s /bin/sh -c %s", c.Chroot, cmd.Command))
if err != nil {
return err
}

View file

@ -2,6 +2,7 @@ package chroot
import (
"context"
"errors"
"fmt"
"log"
@ -21,6 +22,7 @@ import (
type StepCreateVolume struct {
volumeId string
RootVolumeSize int64
RootVolumeType string
RootVolumeTags awscommon.TagMap
Ctx interpolate.Context
}
@ -53,11 +55,21 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu
var createVolume *ec2.CreateVolumeInput
if config.FromScratch {
rootVolumeType := ec2.VolumeTypeGp2
if s.RootVolumeType == "io1" {
err := errors.New("Cannot use io1 volume when building from scratch")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else if s.RootVolumeType != "" {
rootVolumeType = s.RootVolumeType
}
createVolume = &ec2.CreateVolumeInput{
AvailabilityZone: instance.Placement.AvailabilityZone,
Size: aws.Int64(s.RootVolumeSize),
VolumeType: aws.String(ec2.VolumeTypeGp2),
VolumeType: aws.String(rootVolumeType),
}
} else {
// Determine the root device snapshot
image := state.Get("source_image").(*ec2.Image)
@ -70,26 +82,13 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu
}
}
if rootDevice == nil {
err := fmt.Errorf("Couldn't find root device!")
ui.Say("Creating the root volume...")
createVolume, err = s.buildCreateVolumeInput(*instance.Placement.AvailabilityZone, rootDevice)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Creating the root volume...")
vs := *rootDevice.Ebs.VolumeSize
if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize {
vs = s.RootVolumeSize
}
createVolume = &ec2.CreateVolumeInput{
AvailabilityZone: instance.Placement.AvailabilityZone,
Size: aws.Int64(vs),
SnapshotId: rootDevice.Ebs.SnapshotId,
VolumeType: rootDevice.Ebs.VolumeType,
Iops: rootDevice.Ebs.Iops,
}
}
if len(tagSpecs) > 0 {
@ -137,3 +136,33 @@ func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
ui.Error(fmt.Sprintf("Error deleting EBS volume: %s", err))
}
}
func (s *StepCreateVolume) buildCreateVolumeInput(az string, rootDevice *ec2.BlockDeviceMapping) (*ec2.CreateVolumeInput, error) {
if rootDevice == nil {
return nil, fmt.Errorf("Couldn't find root device!")
}
createVolumeInput := &ec2.CreateVolumeInput{
AvailabilityZone: aws.String(az),
Size: rootDevice.Ebs.VolumeSize,
SnapshotId: rootDevice.Ebs.SnapshotId,
VolumeType: rootDevice.Ebs.VolumeType,
Iops: rootDevice.Ebs.Iops,
}
if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize {
createVolumeInput.Size = aws.Int64(s.RootVolumeSize)
}
if s.RootVolumeType == "" || s.RootVolumeType == *rootDevice.Ebs.VolumeType {
return createVolumeInput, nil
}
if s.RootVolumeType == "io1" {
return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", *rootDevice.Ebs.VolumeType)
}
createVolumeInput.VolumeType = aws.String(s.RootVolumeType)
// non io1 cannot set iops
createVolumeInput.Iops = nil
return createVolumeInput, nil
}

View file

@ -0,0 +1,73 @@
package chroot
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
"testing"
)
func buildTestRootDevice() *ec2.BlockDeviceMapping {
return &ec2.BlockDeviceMapping{
Ebs: &ec2.EbsBlockDevice{
VolumeSize: aws.Int64(10),
SnapshotId: aws.String("snap-1234"),
VolumeType: aws.String("gp2"),
},
}
}
func TestCreateVolume_Default(t *testing.T) {
stepCreateVolume := new(StepCreateVolume)
_, err := stepCreateVolume.buildCreateVolumeInput("test-az", buildTestRootDevice())
assert.NoError(t, err)
}
func TestCreateVolume_Shrink(t *testing.T) {
stepCreateVolume := StepCreateVolume{RootVolumeSize: 1}
testRootDevice := buildTestRootDevice()
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
assert.NoError(t, err)
// Ensure that the new value is equal to the size of the old root device
assert.Equal(t, *ret.Size, *testRootDevice.Ebs.VolumeSize)
}
func TestCreateVolume_Expand(t *testing.T) {
stepCreateVolume := StepCreateVolume{RootVolumeSize: 25}
testRootDevice := buildTestRootDevice()
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
assert.NoError(t, err)
// Ensure that the new value is equal to the size of the value passed in
assert.Equal(t, *ret.Size, stepCreateVolume.RootVolumeSize)
}
func TestCreateVolume_io1_to_io1(t *testing.T) {
stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"}
testRootDevice := buildTestRootDevice()
testRootDevice.Ebs.VolumeType = aws.String("io1")
testRootDevice.Ebs.Iops = aws.Int64(1000)
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
assert.NoError(t, err)
assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType)
assert.Equal(t, *ret.Iops, *testRootDevice.Ebs.Iops)
}
func TestCreateVolume_io1_to_gp2(t *testing.T) {
stepCreateVolume := StepCreateVolume{RootVolumeType: "gp2"}
testRootDevice := buildTestRootDevice()
testRootDevice.Ebs.VolumeType = aws.String("io1")
testRootDevice.Ebs.Iops = aws.Int64(1000)
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
assert.NoError(t, err)
assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType)
assert.Nil(t, ret.Iops)
}
func TestCreateVolume_gp2_to_io1(t *testing.T) {
stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"}
testRootDevice := buildTestRootDevice()
_, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
assert.Error(t, err)
}

View file

@ -19,6 +19,7 @@ import (
type AccessConfig struct {
AccessKey string `mapstructure:"access_key"`
CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2"`
DecodeAuthZMessages bool `mapstructure:"decode_authorization_messages"`
MFACode string `mapstructure:"mfa_code"`
ProfileName string `mapstructure:"profile"`
RawRegion string `mapstructure:"region"`
@ -43,6 +44,9 @@ func (c *AccessConfig) Session() (*session.Session, error) {
config.WithCredentials(staticCreds)
}
// default is 3, and when it was causing failures for users being throttled
config = config.WithMaxRetries(20)
if c.RawRegion != "" {
config = config.WithRegion(c.RawRegion)
} else if region := c.metadataRegion(); region != "" {
@ -88,6 +92,11 @@ func (c *AccessConfig) Session() (*session.Session, error) {
}
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
}
if c.DecodeAuthZMessages {
DecodeAuthZMessages(c.session)
}
return c.session, nil
}
@ -139,7 +148,8 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
}
if c.RawRegion != "" && !c.SkipValidation {
if valid := ValidateRegion(c.RawRegion); !valid {
ec2conn := getValidationSession()
if valid := ValidateRegion(c.RawRegion, ec2conn); !valid {
errs = append(errs, fmt.Errorf("Unknown region: %s", c.RawRegion))
}
}

View file

@ -13,33 +13,40 @@ func testAccessConfig() *AccessConfig {
func TestAccessConfigPrepare_Region(t *testing.T) {
c := testAccessConfig()
c.RawRegion = ""
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
mockConn := &mockEC2Client{}
c.RawRegion = "us-east-12"
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error")
valid := ValidateRegion(c.RawRegion, mockConn)
if valid {
t.Fatalf("should have region validation err: %s", c.RawRegion)
}
c.RawRegion = "us-east-1"
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
valid = ValidateRegion(c.RawRegion, mockConn)
if !valid {
t.Fatalf("shouldn't have region validation err: %s", c.RawRegion)
}
c.RawRegion = "custom"
if err := c.Prepare(nil); err == nil {
t.Fatalf("should have err")
valid = ValidateRegion(c.RawRegion, mockConn)
if valid {
t.Fatalf("should have region validation err: %s", c.RawRegion)
}
c.RawRegion = "custom"
c.SkipValidation = true
// testing whole prepare func here; this is checking that validation is
// skipped, so we don't need a mock connection
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.SkipValidation = false
c.SkipValidation = false
c.RawRegion = ""
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
}
func TestAccessConfigPrepare_RegionRestricted(t *testing.T) {

View file

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/hashicorp/packer/template/interpolate"
)
@ -56,44 +57,8 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
}
}
if len(c.AMIRegions) > 0 {
regionSet := make(map[string]struct{})
regions := make([]string, 0, len(c.AMIRegions))
for _, region := range c.AMIRegions {
// If we already saw the region, then don't look again
if _, ok := regionSet[region]; ok {
continue
}
// Mark that we saw the region
regionSet[region] = struct{}{}
if !c.AMISkipRegionValidation {
// Verify the region is real
if valid := ValidateRegion(region); !valid {
errs = append(errs, fmt.Errorf("Unknown region: %s", region))
}
}
// Make sure that if we have region_kms_key_ids defined,
// the regions in ami_regions are also in region_kms_key_ids
if len(c.AMIRegionKMSKeyIDs) > 0 {
if _, ok := c.AMIRegionKMSKeyIDs[region]; !ok {
errs = append(errs, fmt.Errorf("Region %s is in ami_regions but not in region_kms_key_ids", region))
}
}
if (accessConfig != nil) && (region == accessConfig.RawRegion) {
// make sure we don't try to copy to the region we originally
// create the AMI in.
log.Printf("Cannot copy AMI to AWS session region '%s', deleting it from `ami_regions`.", region)
continue
}
regions = append(regions, region)
}
c.AMIRegions = regions
}
ec2conn := getValidationSession()
errs = c.prepareRegions(ec2conn, accessConfig, errs)
if len(c.AMIUsers) > 0 && c.AMIEncryptBootVolume {
errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume"))
@ -130,3 +95,45 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
return nil
}
func (c *AMIConfig) prepareRegions(ec2conn ec2iface.EC2API, accessConfig *AccessConfig, errs []error) []error {
if len(c.AMIRegions) > 0 {
regionSet := make(map[string]struct{})
regions := make([]string, 0, len(c.AMIRegions))
for _, region := range c.AMIRegions {
// If we already saw the region, then don't look again
if _, ok := regionSet[region]; ok {
continue
}
// Mark that we saw the region
regionSet[region] = struct{}{}
if !c.AMISkipRegionValidation {
// Verify the region is real
if valid := ValidateRegion(region, ec2conn); !valid {
errs = append(errs, fmt.Errorf("Unknown region: %s", region))
}
}
// Make sure that if we have region_kms_key_ids defined,
// the regions in ami_regions are also in region_kms_key_ids
if len(c.AMIRegionKMSKeyIDs) > 0 {
if _, ok := c.AMIRegionKMSKeyIDs[region]; !ok {
errs = append(errs, fmt.Errorf("Region %s is in ami_regions but not in region_kms_key_ids", region))
}
}
if (accessConfig != nil) && (region == accessConfig.RawRegion) {
// make sure we don't try to copy to the region we originally
// create the AMI in.
log.Printf("Cannot copy AMI to AWS session region '%s', deleting it from `ami_regions`.", region)
continue
}
regions = append(regions, region)
}
c.AMIRegions = regions
}
return errs
}

View file

@ -1,8 +1,13 @@
package common
import (
"fmt"
"reflect"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
)
func testAMIConfig() *AMIConfig {
@ -19,6 +24,7 @@ func getFakeAccessConfig(region string) *AccessConfig {
func TestAMIConfigPrepare_name(t *testing.T) {
c := testAMIConfig()
c.AMISkipRegionValidation = true
if err := c.Prepare(nil, nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
@ -29,26 +35,46 @@ func TestAMIConfigPrepare_name(t *testing.T) {
}
}
type mockEC2Client struct {
ec2iface.EC2API
}
func (m *mockEC2Client) DescribeRegions(*ec2.DescribeRegionsInput) (*ec2.DescribeRegionsOutput, error) {
return &ec2.DescribeRegionsOutput{
Regions: []*ec2.Region{
{RegionName: aws.String("us-east-1")},
{RegionName: aws.String("us-east-2")},
{RegionName: aws.String("us-west-1")},
},
}, nil
}
func TestAMIConfigPrepare_regions(t *testing.T) {
c := testAMIConfig()
c.AMIRegions = nil
if err := c.Prepare(nil, nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
c.AMISkipRegionValidation = true
var errs []error
mockConn := &mockEC2Client{}
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatalf("shouldn't have err: %#v", errs)
}
c.AMIRegions = listEC2Regions()
if err := c.Prepare(nil, nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
c.AMISkipRegionValidation = false
c.AMIRegions = listEC2Regions(mockConn)
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatalf("shouldn't have err: %#v", errs)
}
c.AMIRegions = []string{"foo"}
if err := c.Prepare(nil, nil); err == nil {
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) == 0 {
t.Fatal("should have error")
}
errs = errs[:0]
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-1"}
if err := c.Prepare(nil, nil); err != nil {
t.Fatalf("bad: %s", err)
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatalf("bad: %s", errs[0])
}
expected := []string{"us-east-1", "us-west-1"}
@ -58,7 +84,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
c.AMIRegions = []string{"custom"}
c.AMISkipRegionValidation = true
if err := c.Prepare(nil, nil); err != nil {
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatal("shouldn't have error")
}
c.AMISkipRegionValidation = false
@ -69,8 +95,8 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
"us-west-1": "789-012-3456",
"us-east-2": "456-789-0123",
}
if err := c.Prepare(nil, nil); err != nil {
t.Fatal("shouldn't have error")
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatal(fmt.Sprintf("shouldn't have error: %s", errs[0]))
}
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
@ -79,7 +105,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
"us-west-1": "789-012-3456",
"us-east-2": "",
}
if err := c.Prepare(nil, nil); err != nil {
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatal("should have passed; we are able to use default KMS key if not sharing")
}
@ -90,7 +116,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
"us-west-1": "789-012-3456",
"us-east-2": "",
}
if err := c.Prepare(nil, nil); err == nil {
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatal("should have an error b/c can't use default KMS key if sharing")
}
@ -100,7 +126,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
"us-west-1": "789-012-3456",
"us-east-2": "456-789-0123",
}
if err := c.Prepare(nil, nil); err == nil {
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatal("should have error b/c theres a region in the key map that isn't in ami_regions")
}
@ -109,9 +135,12 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
"us-east-1": "123-456-7890",
"us-west-1": "789-012-3456",
}
c.AMISkipRegionValidation = true
if err := c.Prepare(nil, nil); err == nil {
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
}
c.AMISkipRegionValidation = false
c.SnapshotUsers = []string{"foo", "bar"}
c.AMIKmsKeyId = "123-abc-456"
@ -121,7 +150,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
"us-east-1": "123-456-7890",
"us-west-1": "",
}
if err := c.Prepare(nil, nil); err == nil {
if errs = c.prepareRegions(mockConn, nil, errs); len(errs) > 0 {
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
}
@ -129,7 +158,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
accessConf := getFakeAccessConfig("us-east-1")
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
c.AMIRegionKMSKeyIDs = nil
if err := c.Prepare(accessConf, nil); err != nil {
if errs = c.prepareRegions(mockConn, accessConf, errs); len(errs) > 0 {
t.Fatal("should allow user to have the raw region in ami_regions")
}

View file

@ -0,0 +1,58 @@
package common
import (
"fmt"
"log"
"regexp"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
)
var encodedFailureMessagePattern = regexp.MustCompile(`(?i)(.*) Encoded authorization failure message: ([\w-]+) ?( .*)?`)
type stsDecoder interface {
DecodeAuthorizationMessage(input *sts.DecodeAuthorizationMessageInput) (*sts.DecodeAuthorizationMessageOutput, error)
}
// decodeError replaces encoded authorization messages with the
// decoded results
func decodeAWSError(decoder stsDecoder, err error) error {
groups := encodedFailureMessagePattern.FindStringSubmatch(err.Error())
if groups != nil && len(groups) > 1 {
result, decodeErr := decoder.DecodeAuthorizationMessage(&sts.DecodeAuthorizationMessageInput{
EncodedMessage: aws.String(groups[2]),
})
if decodeErr == nil {
msg := aws.StringValue(result.DecodedMessage)
return fmt.Errorf("%s Authorization failure message: '%s'%s", groups[1], msg, groups[3])
}
log.Printf("[WARN] Attempted to decode authorization message, but received: %v", decodeErr)
}
return err
}
// DecodeAuthZMessages enables automatic decoding of any
// encoded authorization messages
func DecodeAuthZMessages(sess *session.Session) {
azd := &authZMessageDecoder{
Decoder: sts.New(sess),
}
sess.Handlers.UnmarshalError.AfterEachFn = azd.afterEachFn
}
type authZMessageDecoder struct {
Decoder stsDecoder
}
func (a *authZMessageDecoder) afterEachFn(item request.HandlerListRunItem) bool {
if err, ok := item.Request.Error.(awserr.Error); ok && err.Code() == "UnauthorizedOperation" {
item.Request.Error = decodeAWSError(a.Decoder, err)
}
return true
}

View file

@ -0,0 +1,70 @@
package common
import (
"fmt"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/aws-sdk-go/aws/awserr"
)
type mockSTS struct {
}
func (m *mockSTS) DecodeAuthorizationMessage(input *sts.DecodeAuthorizationMessageInput) (*sts.DecodeAuthorizationMessageOutput, error) {
return &sts.DecodeAuthorizationMessageOutput{
DecodedMessage: aws.String(`{
"allowed": false,
"explicitDeny": true,
"matchedStatements": {}
}`),
}, nil
}
func TestErrorsParsing_RequestFailure(t *testing.T) {
ae := awserr.New("UnauthorizedOperation",
`You are not authorized to perform this operation. Encoded authorization failure message: D9Q7oicjOMr9l2CC-NPP1FiZXK9Ijia1k-3l0siBFCcrK3oSuMFMkBIO5TNj0HdXE-WfwnAcdycFOohfKroNO6toPJEns8RFVfy_M_IjNGmrEFJ6E62pnmBW0OLrMsXxR9FQE4gB4gJzSM0AD6cV6S3FOfqYzWBRX-sQdOT4HryGkFNRoFBr9Xbp-tRwiadwkbdHdfnV9fbRkXmnwCdULml16NBSofC4ZPepLMKmIB5rKjwk-m179UUh2XA-J5no0si6XcRo5GbHQB5QfCIwSHL4vsro2wLZUd16-8OWKyr3tVlTbQe0ERZskqRqRQ5E28QuiBCVV6XstUyo-T4lBSr75Fgnyr3wCO-dS3b_5Ns3WzA2JD4E2AJOAStXIU8IH5YuKkAg7C-dJMuBMPpmKCBEXhNoHDwCyOo5PsV3xMlc0jSb0qYGpfst_TDDtejcZfn7NssUjxVq9qkdH-OXz2gPoQB-hX8ycmZCL5UZwKc3TCLUr7TGnudHjmnMrE9cUo-yTCWfyHPLprhiYhTCKW18EikJ0O1EKI3FJ_b4F19_jFBPARjSwQc7Ut6MNCVzrPdZGYSF6acj5gPaxdy9uSkVQwWXK7Pd5MFP7EBDE1_DgYbzodgwDO2PXeVFUbSLBHKWo_ebZS9ZX2nYPcGss_sYaly0ZVSIJXp7G58B5BoFVhvVH6jYnF9XiAOjMltuP_ycu1pQP1lki500RY3baLvfeYeAsB38XZHKEgWZzq7Fei-uh89q0cjJTmlVyrfRU3q6`,
fmt.Errorf("You can't do it!!"))
rf := awserr.NewRequestFailure(ae, 400, "abc-def-123-456")
result := decodeAWSError(&mockSTS{}, rf)
if result == nil {
t.Error("Expected resulting error")
}
if !strings.Contains(result.Error(), "Authorization failure message:") {
t.Error("Expected authorization failure message")
}
}
func TestErrorsParsing_NonAuthorizationFailure(t *testing.T) {
ae := awserr.New("BadRequest",
`You did something wrong. Try again`,
fmt.Errorf("Request was no good."))
rf := awserr.NewRequestFailure(ae, 400, "abc-def-123-456")
result := decodeAWSError(&mockSTS{}, rf)
if result == nil {
t.Error("Expected resulting error")
}
if result != rf {
t.Error("Expected original error to be returned unchanged")
}
}
func TestErrorsParsing_NonAWSError(t *testing.T) {
err := fmt.Errorf("Random error occurred")
result := decodeAWSError(&mockSTS{}, err)
if result == nil {
t.Error("Expected resulting error")
}
if result != err {
t.Error("Expected original error to be returned unchanged")
}
}

View file

@ -1,35 +1,34 @@
package common
// aws ec2 describe-regions --query 'Regions[].{Name:RegionName}' --output text | sed 's/\(.*\)/"\1",/' | sort
func listEC2Regions() []string {
return []string{
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
// not part of autogenerated list
"us-gov-west-1",
"cn-north-1",
"cn-northwest-1",
import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
)
func getValidationSession() *ec2.EC2 {
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
ec2conn := ec2.New(sess)
return ec2conn
}
func listEC2Regions(ec2conn ec2iface.EC2API) []string {
var regions []string
resultRegions, _ := ec2conn.DescribeRegions(nil)
for _, region := range resultRegions.Regions {
regions = append(regions, *region.RegionName)
}
return regions
}
// ValidateRegion returns true if the supplied region is a valid AWS
// region and false if it's not.
func ValidateRegion(region string) bool {
for _, valid := range listEC2Regions() {
func ValidateRegion(region string, ec2conn ec2iface.EC2API) bool {
for _, valid := range listEC2Regions(ec2conn) {
if region == valid {
return true
}

View file

@ -34,6 +34,7 @@ func (d *AmiFilterOptions) NoOwner() bool {
type RunConfig struct {
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
AvailabilityZone string `mapstructure:"availability_zone"`
BlockDurationMinutes int64 `mapstructure:"block_duration_minutes"`
DisableStopInstance bool `mapstructure:"disable_stop_instance"`
EbsOptimized bool `mapstructure:"ebs_optimized"`
EnableT2Unlimited bool `mapstructure:"enable_t2_unlimited"`
@ -57,9 +58,7 @@ type RunConfig struct {
WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout"`
// Communicator settings
Comm communicator.Config `mapstructure:",squash"`
SSHKeyPairName string `mapstructure:"ssh_keypair_name"`
SSHInterface string `mapstructure:"ssh_interface"`
Comm communicator.Config `mapstructure:",squash"`
}
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
@ -67,10 +66,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
// ssh_private_key_file, then create a temporary one, but only if the
// temporary_key_pair_name has not been provided and we are not using
// ssh_password.
if c.SSHKeyPairName == "" && c.TemporaryKeyPairName == "" &&
c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" {
if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" &&
c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" {
c.TemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
if c.WindowsPasswordTimeout == 0 {
@ -85,18 +84,18 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
errs := c.Comm.Prepare(ctx)
// Validating ssh_interface
if c.SSHInterface != "public_ip" &&
c.SSHInterface != "private_ip" &&
c.SSHInterface != "public_dns" &&
c.SSHInterface != "private_dns" &&
c.SSHInterface != "" {
errs = append(errs, fmt.Errorf("Unknown interface type: %s", c.SSHInterface))
if c.Comm.SSHInterface != "public_ip" &&
c.Comm.SSHInterface != "private_ip" &&
c.Comm.SSHInterface != "public_dns" &&
c.Comm.SSHInterface != "private_dns" &&
c.Comm.SSHInterface != "" {
errs = append(errs, fmt.Errorf("Unknown interface type: %s", c.Comm.SSHInterface))
}
if c.SSHKeyPairName != "" {
if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKey == "" {
if c.Comm.SSHKeyPairName != "" {
if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKeyFile == "" {
errs = append(errs, fmt.Errorf("ssh_private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name."))
} else if c.Comm.SSHPrivateKey == "" && !c.Comm.SSHAgentAuth {
} else if c.Comm.SSHPrivateKeyFile == "" && !c.Comm.SSHAgentAuth {
errs = append(errs, fmt.Errorf("ssh_private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified."))
}
}
@ -113,6 +112,11 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
errs = append(errs, fmt.Errorf("An instance_type must be specified"))
}
if c.BlockDurationMinutes%60 != 0 {
errs = append(errs, fmt.Errorf(
"block_duration_minutes must be multiple of 60"))
}
if c.SpotPrice == "auto" {
if c.SpotPriceAutoProduct == "" {
errs = append(errs, fmt.Errorf(

View file

@ -206,27 +206,27 @@ func TestRunConfigPrepare_UserDataFile(t *testing.T) {
func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
c := testConfig()
c.TemporaryKeyPairName = ""
c.Comm.SSHTemporaryKeyPairName = ""
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.TemporaryKeyPairName == "" {
if c.Comm.SSHTemporaryKeyPairName == "" {
t.Fatal("keypair name is empty")
}
// Match prefix and UUID, e.g. "packer_5790d491-a0b8-c84c-c9d2-2aea55086550".
r := regexp.MustCompile(`\Apacker_(?:(?i)[a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12}?)\z`)
if !r.MatchString(c.TemporaryKeyPairName) {
if !r.MatchString(c.Comm.SSHTemporaryKeyPairName) {
t.Fatal("keypair name is not valid")
}
c.TemporaryKeyPairName = "ssh-key-123"
c.Comm.SSHTemporaryKeyPairName = "ssh-key-123"
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.TemporaryKeyPairName != "ssh-key-123" {
if c.Comm.SSHTemporaryKeyPairName != "ssh-key-123" {
t.Fatal("keypair name does not match")
}
}

View file

@ -3,15 +3,10 @@ package common
import (
"errors"
"fmt"
"net"
"os"
"time"
"github.com/aws/aws-sdk-go/service/ec2"
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/hashicorp/packer/helper/multistep"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
type ec2Describer interface {
@ -85,56 +80,3 @@ func SSHHost(e ec2Describer, sshInterface string) func(multistep.StateBag) (stri
return "", errors.New("couldn't determine address for instance")
}
}
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the private key
// or password.
func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
if useAgent {
authSock := os.Getenv("SSH_AUTH_SOCK")
if authSock == "" {
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
}
sshAgent, err := net.Dial("unix", authSock)
if err != nil {
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
privateKey, hasKey := state.GetOk("privateKey")
if hasKey {
signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
} else {
return &ssh.ClientConfig{
User: username,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
}}, nil
}
}
}

View file

@ -40,10 +40,16 @@ func WaitUntilAMIAvailable(ctx aws.Context, conn *ec2.EC2, imageId string) error
ImageIds: []*string{&imageId},
}
waitOpts := getWaiterOptions()
if len(waitOpts) == 0 {
// Bump this default to 30 minutes because the aws default
// of ten minutes doesn't work for some of our long-running copies.
waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(120))
}
err := conn.WaitUntilImageAvailableWithContext(
ctx,
&imageInput,
getWaiterOptions()...)
waitOpts...)
return err
}
@ -163,6 +169,8 @@ func WaitForVolumeToBeAttached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeV
return req, nil
},
}
w.ApplyOptions(opts...)
return w.WaitWithContext(ctx)
}
@ -192,6 +200,8 @@ func WaitForVolumeToBeDetached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeV
return req, nil
},
}
w.ApplyOptions(opts...)
return w.WaitWithContext(ctx)
}
@ -221,6 +231,8 @@ func WaitForImageToBeImported(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeIm
return req, nil
},
}
w.ApplyOptions(opts...)
return w.WaitWithContext(ctx)
}
@ -275,11 +287,11 @@ func getOverride(varInfo envInfo) envInfo {
return varInfo
}
func getEnvOverrides() overridableWaitVars {
// Load env vars from environment, and use them to override defaults
// Load env vars from environment.
envValues := overridableWaitVars{
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
envInfo{"AWS_POLL_DELAY_SECONDS", 0, false},
envInfo{"AWS_MAX_ATTEMPTS", 0, false},
envInfo{"AWS_TIMEOUT_SECONDS", 300, false},
envInfo{"AWS_TIMEOUT_SECONDS", 0, false},
}
envValues.awsMaxAttempts = getOverride(envValues.awsMaxAttempts)

View file

@ -95,7 +95,9 @@ WaitLoop:
"Password (since debug is enabled): %s", s.Comm.WinRMPassword))
}
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName)
packer.LogSecretFilter.Set(s.Comm.WinRMPassword)
return multistep.ActionContinue
}
@ -107,7 +109,7 @@ func (s *StepGetPassword) Cleanup(multistep.StateBag) {
func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) {
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
privateKey := state.Get("privateKey").(string)
privateKey := s.Comm.SSHPrivateKey
for {
select {

View file

@ -8,17 +8,15 @@ import (
"runtime"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepKeyPair struct {
Debug bool
SSHAgentAuth bool
DebugKeyPath string
TemporaryKeyPairName string
KeyPairName string
PrivateKeyFile string
Debug bool
Comm *communicator.Config
DebugKeyPath string
doCleanup bool
}
@ -26,43 +24,41 @@ type StepKeyPair struct {
func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.PrivateKeyFile != "" {
if s.Comm.SSHPrivateKeyFile != "" {
ui.Say("Using existing SSH private key")
privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"Error loading configured private key file: %s", err))
return multistep.ActionHalt
}
state.Put("keyPair", s.KeyPairName)
state.Put("privateKey", string(privateKeyBytes))
s.Comm.SSHPrivateKey = privateKeyBytes
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName == "" {
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName == "" {
ui.Say("Using SSH Agent with key pair in Source AMI")
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName))
state.Put("keyPair", s.KeyPairName)
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.Comm.SSHKeyPairName))
return multistep.ActionContinue
}
if s.TemporaryKeyPairName == "" {
if s.Comm.SSHTemporaryKeyPairName == "" {
ui.Say("Not using temporary keypair")
state.Put("keyPair", "")
s.Comm.SSHKeyPairName = ""
return multistep.ActionContinue
}
ec2conn := state.Get("ec2").(*ec2.EC2)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.TemporaryKeyPairName))
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
keyResp, err := ec2conn.CreateKeyPair(&ec2.CreateKeyPairInput{
KeyName: &s.TemporaryKeyPairName})
KeyName: &s.Comm.SSHTemporaryKeyPairName})
if err != nil {
state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
return multistep.ActionHalt
@ -70,9 +66,9 @@ func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep
s.doCleanup = true
// Set some state data for use in future steps
state.Put("keyPair", s.TemporaryKeyPairName)
state.Put("privateKey", *keyResp.KeyMaterial)
// Set some data for use in future steps
s.Comm.SSHKeyPairName = s.Comm.SSHTemporaryKeyPairName
s.Comm.SSHPrivateKey = []byte(*keyResp.KeyMaterial)
// If we're in debug mode, output the private key to the working
// directory.
@ -113,10 +109,10 @@ func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
// Remove the keypair
ui.Say("Deleting temporary keypair...")
_, err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: &s.TemporaryKeyPairName})
_, err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: &s.Comm.SSHTemporaryKeyPairName})
if err != nil {
ui.Error(fmt.Sprintf(
"Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName))
"Error cleaning up keypair. Please delete the key manually: %s", s.Comm.SSHTemporaryKeyPairName))
}
// Also remove the physical key if we're debugging.

View file

@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
retry "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -21,6 +22,7 @@ type StepRunSourceInstance struct {
AssociatePublicIpAddress bool
AvailabilityZone string
BlockDevices BlockDevices
Comm *communicator.Config
Ctx interpolate.Context
Debug bool
EbsOptimized bool
@ -42,10 +44,7 @@ type StepRunSourceInstance struct {
func (s *StepRunSourceInstance) Run(ctx context.Context, 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)
@ -150,8 +149,8 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
volTags.Report(ui)
}
if keyName != "" {
runOpts.KeyName = &keyName
if s.Comm.SSHKeyPairName != "" {
runOpts.KeyName = &s.Comm.SSHKeyPairName
}
if s.SubnetId != "" && s.AssociatePublicIpAddress {

View file

@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
retry "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -23,7 +24,9 @@ type StepRunSpotInstance struct {
AssociatePublicIpAddress bool
AvailabilityZone string
BlockDevices BlockDevices
BlockDurationMinutes int64
Debug bool
Comm *communicator.Config
EbsOptimized bool
ExpectedRootDevice string
IamInstanceProfile string
@ -46,10 +49,6 @@ type StepRunSpotInstance struct {
func (s *StepRunSpotInstance) Run(ctx context.Context, 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)
@ -184,14 +183,18 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
runOpts.SecurityGroupIds = securityGroupIds
}
if keyName != "" {
runOpts.KeyName = &keyName
if s.Comm.SSHKeyPairName != "" {
runOpts.KeyName = &s.Comm.SSHKeyPairName
}
spotInstanceInput := &ec2.RequestSpotInstancesInput{
LaunchSpecification: runOpts,
SpotPrice: &spotPrice,
}
if s.BlockDurationMinutes != 0 {
spotInstanceInput.BlockDurationMinutes = &s.BlockDurationMinutes
}
runSpotResp, err := ec2conn.RequestSpotInstances(&ec2.RequestSpotInstancesInput{
SpotPrice: &spotPrice,
LaunchSpecification: runOpts,
})
runSpotResp, err := ec2conn.RequestSpotInstances(spotInstanceInput)
if err != nil {
err := fmt.Errorf("Error launching source spot instance: %s", err)
state.Put("error", err)

View file

@ -21,6 +21,7 @@ type StepSourceAMIInfo struct {
SourceAmi string
EnableAMISriovNetSupport bool
EnableAMIENASupport bool
AMIVirtType string
AmiFilters AmiFilterOptions
}
@ -105,11 +106,13 @@ func (s *StepSourceAMIInfo) Run(_ context.Context, state multistep.StateBag) mul
// Enhanced Networking can only be enabled on HVM AMIs.
// See http://goo.gl/icuXh5
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())
return multistep.ActionHalt
if s.EnableAMIENASupport || s.EnableAMISriovNetSupport {
err = s.canEnableEnhancedNetworking(image)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
state.Put("source_image", image)
@ -117,3 +120,16 @@ func (s *StepSourceAMIInfo) Run(_ context.Context, state multistep.StateBag) mul
}
func (s *StepSourceAMIInfo) Cleanup(multistep.StateBag) {}
func (s *StepSourceAMIInfo) canEnableEnhancedNetworking(image *ec2.Image) error {
if s.AMIVirtType == "hvm" {
return nil
}
if s.AMIVirtType != "" {
return fmt.Errorf("Cannot enable enhanced networking, AMIVirtType '%s' is not HVM", s.AMIVirtType)
}
if *image.VirtualizationType != "hvm" {
return fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
}
return nil
}

View file

@ -0,0 +1,42 @@
package common
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
"testing"
)
func TestStepSourceAmiInfo_PVImage(t *testing.T) {
err := new(StepSourceAMIInfo).canEnableEnhancedNetworking(&ec2.Image{
VirtualizationType: aws.String("paravirtual"),
})
assert.Error(t, err)
}
func TestStepSourceAmiInfo_HVMImage(t *testing.T) {
err := new(StepSourceAMIInfo).canEnableEnhancedNetworking(&ec2.Image{
VirtualizationType: aws.String("hvm"),
})
assert.NoError(t, err)
}
func TestStepSourceAmiInfo_PVImageWithAMIVirtPV(t *testing.T) {
stepSourceAMIInfo := StepSourceAMIInfo{
AMIVirtType: "paravirtual",
}
err := stepSourceAMIInfo.canEnableEnhancedNetworking(&ec2.Image{
VirtualizationType: aws.String("paravirtual"),
})
assert.Error(t, err)
}
func TestStepSourceAmiInfo_PVImageWithAMIVirtHVM(t *testing.T) {
stepSourceAMIInfo := StepSourceAMIInfo{
AMIVirtType: "hvm",
}
err := stepSourceAMIInfo.canEnableEnhancedNetworking(&ec2.Image{
VirtualizationType: aws.String("paravirtual"),
})
assert.NoError(t, err)
}

View file

@ -81,7 +81,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
return nil, nil
}
@ -112,7 +112,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("config", &b.config)
state.Put("ec2", ec2conn)
state.Put("awsSession", session)
state.Put("hook", hook)
@ -125,7 +125,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
BlockDurationMinutes: b.config.BlockDurationMinutes,
Ctx: b.config.ctx,
Comm: &b.config.RunConfig.Comm,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
ExpectedRootDevice: "ebs",
@ -147,6 +149,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
Comm: &b.config.RunConfig.Comm,
Ctx: b.config.ctx,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
@ -176,19 +179,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
AmiFilters: b.config.SourceAmiFilter,
AMIVirtType: b.config.AMIVirtType,
},
&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,
Debug: b.config.PackerDebug,
Comm: &b.config.RunConfig.Comm,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
},
&awscommon.StepSecurityGroup{
SecurityGroupIds: b.config.SecurityGroupIds,
CommConfig: &b.config.RunConfig.Comm,
VpcId: b.config.VpcId,
SecurityGroupIds: b.config.SecurityGroupIds,
CommConfig: &b.config.RunConfig.Comm,
VpcId: b.config.VpcId,
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
},
&awscommon.StepCleanupVolumes{
@ -205,13 +206,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Config: &b.config.RunConfig.Comm,
Host: awscommon.SSHHost(
ec2conn,
b.config.SSHInterface),
SSHConfig: awscommon.SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword),
b.config.Comm.SSHInterface),
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.RunConfig.Comm,
},
&awscommon.StepStopEBSBackedInstance{
Skip: b.config.IsSpotInstance(),
DisableStopInstance: b.config.DisableStopInstance,
@ -260,7 +261,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Run!
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)

View file

@ -47,6 +47,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test good
config["ami_name"] = "foo"
config["skip_region_validation"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
@ -99,6 +100,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) {
// Test good
config["shutdown_behavior"] = "terminate"
config["skip_region_validation"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)

View file

@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common/random"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -16,16 +17,23 @@ type stepCreateAMI struct {
}
func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
config := state.Get("config").(*Config)
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
// Create the image
ui.Say(fmt.Sprintf("Creating the AMI: %s", config.AMIName))
amiName := config.AMIName
if config.AMIEncryptBootVolume {
// to avoid having a temporary unencrypted
// image named config.AMIName
amiName = random.AlphaNum(7)
}
ui.Say(fmt.Sprintf("Creating unencrypted AMI %s from instance %s", amiName, *instance.InstanceId))
createOpts := &ec2.CreateImageInput{
InstanceId: instance.InstanceId,
Name: &config.AMIName,
Name: &amiName,
BlockDeviceMappings: config.BlockDevices.BuildAMIDevices(),
}

View file

@ -96,7 +96,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
return nil, nil
}
@ -139,7 +139,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
BlockDurationMinutes: b.config.BlockDurationMinutes,
Ctx: b.config.ctx,
Comm: &b.config.RunConfig.Comm,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
ExpectedRootDevice: "ebs",
@ -161,6 +163,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
Comm: &b.config.RunConfig.Comm,
Ctx: b.config.ctx,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
@ -193,19 +196,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
AmiFilters: b.config.SourceAmiFilter,
AMIVirtType: b.config.AMIVirtType,
},
&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,
Debug: b.config.PackerDebug,
Comm: &b.config.RunConfig.Comm,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
},
&awscommon.StepSecurityGroup{
SecurityGroupIds: b.config.SecurityGroupIds,
CommConfig: &b.config.RunConfig.Comm,
VpcId: b.config.VpcId,
SecurityGroupIds: b.config.SecurityGroupIds,
CommConfig: &b.config.RunConfig.Comm,
VpcId: b.config.VpcId,
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
},
&awscommon.StepCleanupVolumes{
@ -222,13 +223,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Config: &b.config.RunConfig.Comm,
Host: awscommon.SSHHost(
ec2conn,
b.config.SSHInterface),
SSHConfig: awscommon.SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword),
b.config.Comm.SSHInterface),
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.RunConfig.Comm,
},
&awscommon.StepStopEBSBackedInstance{
Skip: b.config.IsSpotInstance(),
DisableStopInstance: b.config.DisableStopInstance,

View file

@ -81,7 +81,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
return nil, nil
}
@ -111,7 +111,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("config", &b.config)
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
@ -123,7 +123,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.launchBlockDevices,
BlockDurationMinutes: b.config.BlockDurationMinutes,
Ctx: b.config.ctx,
Comm: &b.config.RunConfig.Comm,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
ExpectedRootDevice: "ebs",
@ -144,6 +146,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.launchBlockDevices,
Comm: &b.config.RunConfig.Comm,
Ctx: b.config.ctx,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
@ -170,17 +173,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
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,
Debug: b.config.PackerDebug,
Comm: &b.config.RunConfig.Comm,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
},
&awscommon.StepSecurityGroup{
SecurityGroupIds: b.config.SecurityGroupIds,
CommConfig: &b.config.RunConfig.Comm,
VpcId: b.config.VpcId,
SecurityGroupIds: b.config.SecurityGroupIds,
CommConfig: &b.config.RunConfig.Comm,
VpcId: b.config.VpcId,
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
},
instanceStep,
@ -198,13 +198,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Config: &b.config.RunConfig.Comm,
Host: awscommon.SSHHost(
ec2conn,
b.config.SSHInterface),
SSHConfig: awscommon.SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword),
b.config.Comm.SSHInterface),
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.RunConfig.Comm,
},
&awscommon.StepStopEBSBackedInstance{
Skip: b.config.IsSpotInstance(),
DisableStopInstance: b.config.DisableStopInstance,

View file

@ -61,6 +61,7 @@ func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) {
// Test good
config["shutdown_behavior"] = "terminate"
config["skip_region_validation"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)

View file

@ -166,8 +166,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
return nil, nil
}
@ -210,7 +209,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
BlockDurationMinutes: b.config.BlockDurationMinutes,
Ctx: b.config.ctx,
Comm: &b.config.RunConfig.Comm,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
IamInstanceProfile: b.config.IamInstanceProfile,
@ -229,6 +230,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
Comm: &b.config.RunConfig.Comm,
Ctx: b.config.ctx,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
@ -255,19 +257,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
EnableAMIENASupport: b.config.AMIENASupport,
AmiFilters: b.config.SourceAmiFilter,
AMIVirtType: b.config.AMIVirtType,
},
&awscommon.StepKeyPair{
Debug: b.config.PackerDebug,
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
KeyPairName: b.config.SSHKeyPairName,
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
Debug: b.config.PackerDebug,
Comm: &b.config.RunConfig.Comm,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
},
&awscommon.StepSecurityGroup{
CommConfig: &b.config.RunConfig.Comm,
SecurityGroupIds: b.config.SecurityGroupIds,
VpcId: b.config.VpcId,
CommConfig: &b.config.RunConfig.Comm,
SecurityGroupIds: b.config.SecurityGroupIds,
VpcId: b.config.VpcId,
TemporarySGSourceCidr: b.config.TemporarySGSourceCidr,
},
instanceStep,
@ -281,13 +281,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Config: &b.config.RunConfig.Comm,
Host: awscommon.SSHHost(
ec2conn,
b.config.SSHInterface),
SSHConfig: awscommon.SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword),
b.config.Comm.SSHInterface),
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.RunConfig.Comm,
},
&StepUploadX509Cert{},
&StepBundleVolume{
Debug: b.config.PackerDebug,

View file

@ -41,6 +41,8 @@ func TestBuilder_ImplementsBuilder(t *testing.T) {
func TestBuilderPrepare_AccountId(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()
@ -84,6 +86,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test good
config["ami_name"] = "foo"
config["skip_region_validation"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
@ -118,6 +121,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
func TestBuilderPrepare_BundleDestination(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()
@ -138,6 +142,7 @@ func TestBuilderPrepare_BundleDestination(t *testing.T) {
func TestBuilderPrepare_BundlePrefix(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()
@ -174,6 +179,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
func TestBuilderPrepare_S3Bucket(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()
@ -199,6 +205,7 @@ func TestBuilderPrepare_S3Bucket(t *testing.T) {
func TestBuilderPrepare_X509CertPath(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()
@ -240,6 +247,7 @@ func TestBuilderPrepare_X509CertPath(t *testing.T) {
func TestBuilderPrepare_X509KeyPath(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()
@ -281,6 +289,7 @@ func TestBuilderPrepare_X509KeyPath(t *testing.T) {
func TestBuilderPrepare_X509UploadPath(t *testing.T) {
b := &Builder{}
config, tempfile := testConfig()
config["skip_region_validation"] = true
defer os.Remove(tempfile.Name())
defer tempfile.Close()

View file

@ -174,9 +174,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&communicator.StepConnectSSH{
Config: &b.config.Comm,
Host: lin.SSHHost,
SSHConfig: lin.SSHConfig(b.config.UserName),
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
&packerCommon.StepProvision{},
&packerCommon.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
NewStepGetOSDisk(azureClient, ui),
NewStepGetAdditionalDisks(azureClient, ui),
NewStepPowerOffCompute(azureClient, ui),
@ -229,7 +232,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ui.Message(fmt.Sprintf("temp admin user: '%s'", b.config.UserName))
ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password))
if b.config.sshPrivateKey != "" {
if len(b.config.Comm.SSHPrivateKey) != 0 {
debugKeyPath := fmt.Sprintf("%s-%s.pem", b.config.PackerBuildName, b.config.tmpComputeName)
ui.Message(fmt.Sprintf("temp ssh key: %s", debugKeyPath))
@ -282,7 +285,7 @@ func (b *Builder) writeSSHPrivateKey(ui packer.Ui, debugKeyPath string) {
defer f.Close()
// Write the key out
if _, err := f.Write([]byte(b.config.sshPrivateKey)); err != nil {
if _, err := f.Write(b.config.Comm.SSHPrivateKey); err != nil {
ui.Say(fmt.Sprintf("Error saving debug key: %s", err))
return
}
@ -333,7 +336,6 @@ func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, resou
func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey)
stateBag.Put(constants.PrivateKey, b.config.sshPrivateKey)
stateBag.Put(constants.ArmTags, b.config.AzureTags)
stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName)

View file

@ -15,7 +15,6 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) {
var expectedStateBagKeys = []string{
constants.AuthorizedKey,
constants.PrivateKey,
constants.ArmTags,
constants.ArmComputeName,

View file

@ -144,7 +144,6 @@ type Config struct {
// Authentication with the VM via SSH
sshAuthorizedKey string
sshPrivateKey string
// Authentication with the VM via WinRM
winrmCertificate string
@ -315,8 +314,8 @@ func setSshValues(c *Config) error {
c.Comm.SSHTimeout = 20 * time.Minute
}
if c.Comm.SSHPrivateKey != "" {
privateKeyBytes, err := ioutil.ReadFile(c.Comm.SSHPrivateKey)
if c.Comm.SSHPrivateKeyFile != "" {
privateKeyBytes, err := ioutil.ReadFile(c.Comm.SSHPrivateKeyFile)
if err != nil {
return err
}
@ -330,7 +329,7 @@ func setSshValues(c *Config) error {
publicKey.Type(),
base64.StdEncoding.EncodeToString(publicKey.Marshal()),
time.Now().Format(time.RFC3339))
c.sshPrivateKey = string(privateKeyBytes)
c.Comm.SSHPrivateKey = privateKeyBytes
} else {
sshKeyPair, err := NewOpenSshKeyPair()
@ -339,7 +338,7 @@ func setSshValues(c *Config) error {
}
c.sshAuthorizedKey = sshKeyPair.AuthorizedKey()
c.sshPrivateKey = sshKeyPair.PrivateKey()
c.Comm.SSHPrivateKey = sshKeyPair.PrivateKey()
}
return nil
@ -363,6 +362,7 @@ func setRuntimeValues(c *Config) {
c.tmpAdminPassword = tempName.AdminPassword
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", c.tmpAdminPassword, c.PackerConfig.PackerBuildName)
packer.LogSecretFilter.Set(c.tmpAdminPassword)
c.tmpCertificatePassword = tempName.CertificatePassword
if c.TempComputeName == "" {
@ -475,7 +475,7 @@ func assertTagProperties(c *Config, errs *packer.MultiError) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 512 character limit", k, len(k)))
}
if len(*v) > 256 {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", v, len(*v)))
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", *v, len(*v)))
}
}
}

View file

@ -586,12 +586,12 @@ func TestConfigShouldAcceptTags(t *testing.T) {
value := c.AzureTags["tag01"]
if *value != "value01" {
t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", value)
t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", *value)
}
value = c.AzureTags["tag02"]
if *value != "value02" {
t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", value)
t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", *value)
}
}
@ -799,7 +799,7 @@ func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) {
func TestConfigShouldRejectCustomAndImageUrlForManagedImageBuild(t *testing.T) {
config := map[string]interface{}{
"image_url": "ignore",
"image_url": "ignore",
"custom_managed_image_resource_group_name": "ignore",
"custom_managed_image_name": "ignore",
"location": "ignore",

View file

@ -49,11 +49,11 @@ func (s *OpenSshKeyPair) AuthorizedKey() string {
time.Now().Format(time.RFC3339))
}
func (s *OpenSshKeyPair) PrivateKey() string {
privateKey := string(pem.EncodeToMemory(&pem.Block{
func (s *OpenSshKeyPair) PrivateKey() []byte {
privateKey := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(s.privateKey),
}))
})
return privateKey
}

View file

@ -24,7 +24,7 @@ type resourceResolver struct {
func newResourceResolver(client *AzureClient) *resourceResolver {
return &resourceResolver{
client: client,
client: client,
findVirtualNetworkResourceGroup: findVirtualNetworkResourceGroup,
findVirtualNetworkSubnet: findVirtualNetworkSubnet,
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/hashicorp/packer/builder/azure/common/constants"
retry "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -93,11 +94,19 @@ func (s *StepDeleteResourceGroup) deleteDeploymentResources(ctx context.Context,
resourceType,
resourceName))
err := deleteResource(ctx, s.client,
resourceType,
resourceName,
resourceGroupName)
s.reportIfError(err, resourceName)
err := retry.Retry(10, 600, 10, func(attempt uint) (bool, error) {
err := deleteResource(ctx, s.client,
resourceType,
resourceName,
resourceGroupName)
if err != nil {
s.reportIfError(err, resourceName)
return false, nil
}
return true, nil
})
if err = deploymentOperations.Next(); err != nil {
return err
}

View file

@ -5,6 +5,7 @@ import (
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepSaveWinRMPassword struct {
@ -15,6 +16,7 @@ type StepSaveWinRMPassword struct {
func (s *StepSaveWinRMPassword) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Password, s.BuildName)
packer.LogSecretFilter.Set(s.Password)
return multistep.ActionContinue
}

View file

@ -261,10 +261,10 @@ growpart:
// Ensure the VM template is correct when building from a custom managed image.
func TestVirtualMachineDeployment08(t *testing.T) {
config := map[string]interface{}{
"location": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
"location": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
"custom_managed_image_resource_group_name": "CustomManagedImageResourceGroupName",
"custom_managed_image_name": "CustomManagedImageName",
"managed_image_name": "ManagedImageName",

View file

@ -4,17 +4,7 @@ import (
"fmt"
"strings"
"github.com/hashicorp/packer/builder/azure/common"
)
const (
TempNameAlphabet = "0123456789bcdfghjklmnpqrstvwxyz"
numbers = "0123456789"
lowerCase = "abcdefghijklmnopqrstuvwxyz"
upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
TempPasswordAlphabet = numbers + lowerCase + upperCase
"github.com/hashicorp/packer/common/random"
)
type TempName struct {
@ -34,7 +24,7 @@ type TempName struct {
func NewTempName() *TempName {
tempName := &TempName{}
suffix := common.RandomString(TempNameAlphabet, 10)
suffix := random.AlphaNumLower(10)
tempName.ComputeName = fmt.Sprintf("pkrvm%s", suffix)
tempName.DeploymentName = fmt.Sprintf("pkrdp%s", suffix)
tempName.KeyVaultName = fmt.Sprintf("pkrkv%s", suffix)
@ -46,7 +36,7 @@ func NewTempName() *TempName {
tempName.ResourceGroupName = fmt.Sprintf("packer-Resource-Group-%s", suffix)
tempName.AdminPassword = generatePassword()
tempName.CertificatePassword = common.RandomString(TempPasswordAlphabet, 32)
tempName.CertificatePassword = random.AlphaNum(32)
return tempName
}
@ -60,16 +50,16 @@ func NewTempName() *TempName {
func generatePassword() string {
var s string
for i := 0; i < 100; i++ {
s := common.RandomString(TempPasswordAlphabet, 32)
if !strings.ContainsAny(s, numbers) {
s := random.AlphaNum(32)
if !strings.ContainsAny(s, random.PossibleNumbers) {
continue
}
if !strings.ContainsAny(s, lowerCase) {
if !strings.ContainsAny(s, random.PossibleLowerCase) {
continue
}
if !strings.ContainsAny(s, upperCase) {
if !strings.ContainsAny(s, random.PossibleUpperCase) {
continue
}

View file

@ -3,6 +3,8 @@ package arm
import (
"strings"
"testing"
"github.com/hashicorp/packer/common/random"
)
func TestTempNameShouldCreatePrefixedRandomNames(t *testing.T) {
@ -44,14 +46,14 @@ func TestTempNameShouldCreatePrefixedRandomNames(t *testing.T) {
func TestTempAdminPassword(t *testing.T) {
tempName := NewTempName()
if !strings.ContainsAny(tempName.AdminPassword, numbers) {
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", numbers)
if !strings.ContainsAny(tempName.AdminPassword, random.PossibleNumbers) {
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleNumbers)
}
if !strings.ContainsAny(tempName.AdminPassword, lowerCase) {
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", lowerCase)
if !strings.ContainsAny(tempName.AdminPassword, random.PossibleLowerCase) {
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleLowerCase)
}
if !strings.ContainsAny(tempName.AdminPassword, upperCase) {
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", upperCase)
if !strings.ContainsAny(tempName.AdminPassword, random.PossibleUpperCase) {
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleUpperCase)
}
}

View file

@ -5,7 +5,6 @@ const (
AuthorizedKey string = "authorizedKey"
Certificate string = "certificate"
Error string = "error"
PrivateKey string = "privateKey"
SSHHost string = "sshHost"
Thumbprint string = "thumbprint"
Ui string = "ui"

View file

@ -1,36 +1,11 @@
package lin
import (
"fmt"
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/helper/multistep"
"golang.org/x/crypto/ssh"
)
func SSHHost(state multistep.StateBag) (string, error) {
host := state.Get(constants.SSHHost).(string)
return host, nil
}
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the generated
// private key.
func SSHConfig(username string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
privateKey := state.Get(constants.PrivateKey).(string)
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
}

View file

@ -1,112 +0,0 @@
package lin
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"time"
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepCreateCert struct {
TmpServiceName string
}
func (s *StepCreateCert) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating temporary certificate...")
err := s.createCert(state)
if err != nil {
err = fmt.Errorf("Error creating temporary certificate: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepCreateCert) Cleanup(state multistep.StateBag) {}
func (s *StepCreateCert) createCert(state multistep.StateBag) error {
log.Println("createCert: Generating RSA key pair...")
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
err = fmt.Errorf("Failed to Generate Private Key: %s", err)
return err
}
// ASN.1 DER encoded form
privkey := string(pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(priv),
}))
// Set the private key in the state bag for later
state.Put(constants.PrivateKey, privkey)
log.Printf("createCert: Private key:\n%s", privkey)
log.Println("createCert: Creating certificate...")
host := fmt.Sprintf("%s.cloudapp.net", s.TmpServiceName)
notBefore := time.Now()
notAfter := notBefore.Add(365 * 24 * time.Hour)
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
err = fmt.Errorf("Failed to Generate Serial Number: %v", err)
return err
}
template := x509.Certificate{
SerialNumber: serialNumber,
Issuer: pkix.Name{
CommonName: host,
},
Subject: pkix.Name{
CommonName: host,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
err = fmt.Errorf("Failed to Create Certificate: %s", err)
return err
}
cert := string(pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: derBytes,
}))
state.Put(constants.Certificate, cert)
log.Printf("createCert: Certificate:\n%s", cert)
h := sha1.New()
h.Write(derBytes)
thumbprint := fmt.Sprintf("%X", h.Sum(nil))
state.Put(constants.Thumbprint, thumbprint)
log.Printf("createCert: Thumbprint:\n%s", thumbprint)
return nil
}

View file

@ -1,56 +0,0 @@
package lin
import (
"bytes"
"context"
"fmt"
"log"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepGeneralizeOS struct {
Command string
}
func (s *StepGeneralizeOS) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
comm := state.Get("communicator").(packer.Communicator)
ui.Say("Executing OS generalization...")
var stdout, stderr bytes.Buffer
cmd := &packer.RemoteCmd{
Command: s.Command,
Stdout: &stdout,
Stderr: &stderr,
}
if err := comm.Start(cmd); err != nil {
err = fmt.Errorf("Failed executing OS generalization command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Wait for the command to run
cmd.Wait()
// If the command failed to run, notify the user in some way.
if cmd.ExitStatus != 0 {
state.Put("error", fmt.Errorf(
"OS generalization has non-zero exit status.\n\nStdout: %s\n\nStderr: %s",
stdout.String(), stderr.String()))
return multistep.ActionHalt
}
log.Printf("OS generalization stdout: %s", stdout.String())
log.Printf("OS generalization stderr: %s", stderr.String())
return multistep.ActionContinue
}
func (s *StepGeneralizeOS) Cleanup(state multistep.StateBag) {
// do nothing
}

View file

@ -1,45 +0,0 @@
package common
import (
"math/rand"
"os"
"time"
)
var pwSymbols = []string{
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"0123456789",
}
var rnd = rand.New(rand.NewSource(time.Now().UnixNano() + int64(os.Getpid())))
func RandomString(chooseFrom string, length int) (randomString string) {
cflen := len(chooseFrom)
for i := 0; i < length; i++ {
randomString += string(chooseFrom[rnd.Intn(cflen)])
}
return
}
func RandomPassword() (password string) {
pwlen := 15
batchsize := pwlen / len(pwSymbols)
pw := make([]byte, 0, pwlen)
// choose character set
for c := 0; len(pw) < pwlen; c++ {
s := RandomString(pwSymbols[c%len(pwSymbols)], rnd.Intn(batchsize-1)+1)
pw = append(pw, []byte(s)...)
}
// truncate
pw = pw[:pwlen]
// permute
for c := 0; c < pwlen-1; c++ {
i := rnd.Intn(pwlen-c) + c
x := pw[c]
pw[c] = pw[i]
pw[i] = x
}
return string(pw)
}

View file

@ -1,15 +0,0 @@
package common
import (
"testing"
)
func TestRandomPassword_generates_15char_passwords(t *testing.T) {
for i := 0; i < 100; i++ {
pw := RandomPassword()
t.Logf("pw: %v", pw)
if len(pw) != 15 {
t.Fatalf("len(pw)!=15, but %v: %v (%v)", len(pw), pw, i)
}
}
}

View file

@ -14,8 +14,8 @@ func TestTemplateParametersShouldHaveExpectedKeys(t *testing.T) {
DnsNameForPublicIP: &TemplateParameter{Value: "sentinel"},
OSDiskName: &TemplateParameter{Value: "sentinel"},
StorageAccountBlobEndpoint: &TemplateParameter{Value: "sentinel"},
VMName: &TemplateParameter{Value: "sentinel"},
VMSize: &TemplateParameter{Value: "sentinel"},
VMName: &TemplateParameter{Value: "sentinel"},
VMSize: &TemplateParameter{Value: "sentinel"},
}
bs, err := json.Marshal(params)
@ -55,8 +55,8 @@ func TestParameterValuesShouldBeSet(t *testing.T) {
DnsNameForPublicIP: &TemplateParameter{Value: "dnsnameforpublicip00"},
OSDiskName: &TemplateParameter{Value: "osdiskname00"},
StorageAccountBlobEndpoint: &TemplateParameter{Value: "storageaccountblobendpoint00"},
VMName: &TemplateParameter{Value: "vmname00"},
VMSize: &TemplateParameter{Value: "vmsize00"},
VMName: &TemplateParameter{Value: "vmname00"},
VMSize: &TemplateParameter{Value: "vmsize00"},
}
bs, err := json.Marshal(params)

View file

@ -64,12 +64,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
HTTPPortMax: b.config.HTTPPortMax,
},
&stepKeypair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("cs_%s.pem", b.config.PackerBuildName),
KeyPair: b.config.Keypair,
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
TemporaryKeyPairName: b.config.TemporaryKeypairName,
Debug: b.config.PackerDebug,
Comm: &b.config.Comm,
DebugKeyPath: fmt.Sprintf("cs_%s.pem", b.config.PackerBuildName),
},
&stepCreateSecurityGroup{},
&stepCreateInstance{
@ -78,16 +75,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
},
&stepSetupNetworking{},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: commHost,
SSHConfig: sshConfig(
b.config.Comm.SSHAgentAuth,
b.config.Comm.SSHUsername,
b.config.Comm.SSHPassword),
Config: &b.config.Comm,
Host: commHost,
SSHConfig: b.config.Comm.SSHConfigFunc(),
SSHPort: commPort,
WinRMPort: commPort,
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
&stepShutdownInstance{},
&stepCreateTemplate{},
}

View file

@ -34,10 +34,10 @@ type Config struct {
Expunge bool `mapstructure:"expunge"`
Hypervisor string `mapstructure:"hypervisor"`
InstanceName string `mapstructure:"instance_name"`
Keypair string `mapstructure:"keypair"`
Network string `mapstructure:"network"`
Project string `mapstructure:"project"`
PublicIPAddress string `mapstructure:"public_ip_address"`
PublicPort int `mapstructure:"public_port"`
SecurityGroups []string `mapstructure:"security_groups"`
ServiceOffering string `mapstructure:"service_offering"`
PreventFirewallChanges bool `mapstructure:"prevent_firewall_changes"`
@ -125,9 +125,9 @@ func NewConfig(raws ...interface{}) (*Config, error) {
// If we are not given an explicit keypair, ssh_password or ssh_private_key_file,
// then create a temporary one, but only if the temporary_keypair_name has not
// been provided.
if c.Keypair == "" && c.TemporaryKeypairName == "" &&
c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" {
c.TemporaryKeypairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" &&
c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" {
c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
// Process required parameters.

View file

@ -2,13 +2,8 @@ package cloudstack
import (
"fmt"
"net"
"os"
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/hashicorp/packer/helper/multistep"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
func commHost(state multistep.StateBag) (string, error) {
@ -28,55 +23,3 @@ func commPort(state multistep.StateBag) (int, error) {
return commPort, nil
}
func sshConfig(useAgent bool, username, password string) func(state multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
if useAgent {
authSock := os.Getenv("SSH_AUTH_SOCK")
if authSock == "" {
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
}
sshAgent, err := net.Dial("unix", authSock)
if err != nil {
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
privateKey, hasKey := state.GetOk("privateKey")
if hasKey {
signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
} else {
return &ssh.ClientConfig{
User: username,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
}}, nil
}
}
}

View file

@ -31,9 +31,13 @@ func (s *stepSetupNetworking) Run(_ context.Context, state multistep.StateBag) m
return multistep.ActionContinue
}
// Generate a random public port used to configure our port forward.
rand.Seed(time.Now().UnixNano())
s.publicPort = 50000 + rand.Intn(10000)
if config.PublicPort != 0 {
s.publicPort = config.PublicPort
} else {
// Generate a random public port used to configure our port forward.
rand.Seed(time.Now().UnixNano())
s.publicPort = 50000 + rand.Intn(10000)
}
state.Put("commPort", s.publicPort)
// Set the currently configured port to be the private port.

View file

@ -46,10 +46,9 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
p.SetName(config.InstanceName)
p.SetDisplayname("Created by Packer")
if keypair, ok := state.GetOk("keypair"); ok {
kp := keypair.(string)
ui.Message(fmt.Sprintf("Using keypair: %s", kp))
p.SetKeypair(kp)
if len(config.Comm.SSHKeyPairName) != 0 {
ui.Message(fmt.Sprintf("Using keypair: %s", config.Comm.SSHKeyPairName))
p.SetKeypair(config.Comm.SSHKeyPairName)
}
if securitygroups, ok := state.GetOk("security_groups"); ok {

View file

@ -7,59 +7,55 @@ import (
"os"
"runtime"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/xanzy/go-cloudstack/cloudstack"
)
type stepKeypair struct {
Debug bool
DebugKeyPath string
KeyPair string
PrivateKeyFile string
SSHAgentAuth bool
TemporaryKeyPairName string
Debug bool
Comm *communicator.Config
DebugKeyPath string
}
func (s *stepKeypair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.PrivateKeyFile != "" {
privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
if s.Comm.SSHPrivateKeyFile != "" {
privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"Error loading configured private key file: %s", err))
return multistep.ActionHalt
}
state.Put("keypair", s.KeyPair)
state.Put("privateKey", string(privateKeyBytes))
s.Comm.SSHPrivateKey = privateKeyBytes
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPair == "" {
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName == "" {
ui.Say("Using SSH Agent with keypair in Source image")
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPair != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing keypair %s", s.KeyPair))
state.Put("keypair", s.KeyPair)
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing keypair %s", s.Comm.SSHKeyPairName))
return multistep.ActionContinue
}
if s.TemporaryKeyPairName == "" {
if s.Comm.SSHTemporaryKeyPairName == "" {
ui.Say("Not using a keypair")
state.Put("keypair", "")
s.Comm.SSHKeyPairName = ""
return multistep.ActionContinue
}
client := state.Get("client").(*cloudstack.CloudStackClient)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.TemporaryKeyPairName))
ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.Comm.SSHTemporaryKeyPairName))
p := client.SSH.NewCreateSSHKeyPairParams(s.TemporaryKeyPairName)
p := client.SSH.NewCreateSSHKeyPairParams(s.Comm.SSHTemporaryKeyPairName)
cfg := state.Get("config").(*Config)
if cfg.Project != "" {
@ -81,7 +77,7 @@ func (s *stepKeypair) Run(_ context.Context, state multistep.StateBag) multistep
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.TemporaryKeyPairName))
ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
// If we're in debug mode, output the private key to the working directory.
if s.Debug {
@ -112,15 +108,15 @@ func (s *stepKeypair) Run(_ context.Context, state multistep.StateBag) multistep
}
}
// Set some state data for use in future steps
state.Put("keypair", s.TemporaryKeyPairName)
state.Put("privateKey", keypair.Privatekey)
// Set some data for use in future steps
s.Comm.SSHKeyPairName = s.Comm.SSHTemporaryKeyPairName
s.Comm.SSHPrivateKey = []byte(keypair.Privatekey)
return multistep.ActionContinue
}
func (s *stepKeypair) Cleanup(state multistep.StateBag) {
if s.TemporaryKeyPairName == "" {
if s.Comm.SSHTemporaryKeyPairName == "" {
return
}
@ -128,17 +124,17 @@ func (s *stepKeypair) Cleanup(state multistep.StateBag) {
client := state.Get("client").(*cloudstack.CloudStackClient)
cfg := state.Get("config").(*Config)
p := client.SSH.NewDeleteSSHKeyPairParams(s.TemporaryKeyPairName)
p := client.SSH.NewDeleteSSHKeyPairParams(s.Comm.SSHTemporaryKeyPairName)
if cfg.Project != "" {
p.SetProjectid(cfg.Project)
}
ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.TemporaryKeyPairName))
ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.Comm.SSHTemporaryKeyPairName))
_, err := client.SSH.DeleteSSHKeyPair(p)
if err != nil {
ui.Error(err.Error())
ui.Error(fmt.Sprintf(
"Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName))
"Error cleaning up keypair. Please delete the key manually: %s", s.Comm.SSHTemporaryKeyPairName))
}
}

View file

@ -71,7 +71,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Set up the state
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("config", &b.config)
state.Put("client", client)
state.Put("hook", hook)
state.Put("ui", ui)
@ -87,9 +87,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&communicator.StepConnect{
Config: &b.config.Comm,
Host: commHost,
SSHConfig: sshConfig,
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
new(common.StepProvision),
&common.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
new(stepShutdown),
new(stepPowerOff),
new(stepSnapshot),

View file

@ -138,6 +138,6 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
return nil, nil, errs
}
common.ScrubConfig(c, c.APIToken)
packer.LogSecretFilter.Set(c.APIToken)
return c, nil, nil
}

View file

@ -1,10 +1,6 @@
package digitalocean
import (
"fmt"
"golang.org/x/crypto/ssh"
"github.com/hashicorp/packer/helper/multistep"
)
@ -12,21 +8,3 @@ func commHost(state multistep.StateBag) (string, error) {
ipAddress := state.Get("droplet_ip").(string)
return ipAddress, nil
}
func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) {
config := state.Get("config").(Config)
privateKey := state.Get("privateKey").(string)
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: config.Comm.SSHUsername,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}

View file

@ -18,7 +18,7 @@ type stepCreateDroplet struct {
func (s *stepCreateDroplet) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*godo.Client)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(Config)
c := state.Get("config").(*Config)
sshKeyId := state.Get("ssh_key_id").(int)
// Create the droplet based on configuration

View file

@ -28,6 +28,7 @@ type stepCreateSSHKey struct {
func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*godo.Client)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(*Config)
ui.Say("Creating temporary ssh key for droplet...")
@ -41,8 +42,8 @@ func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) mult
Bytes: priv_der,
}
// Set the private key in the statebag for later
state.Put("privateKey", string(pem.EncodeToMemory(&priv_blk)))
// Set the private key in the config for later
c.Comm.SSHPrivateKey = pem.EncodeToMemory(&priv_blk)
// Marshal the public key into SSH compatible format
// TODO properly handle the public key error

View file

@ -14,7 +14,7 @@ type stepDropletInfo struct{}
func (s *stepDropletInfo) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*godo.Client)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(Config)
c := state.Get("config").(*Config)
dropletID := state.Get("droplet_id").(int)
ui.Say("Waiting for droplet to become active...")

View file

@ -14,7 +14,7 @@ type stepPowerOff struct{}
func (s *stepPowerOff) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*godo.Client)
c := state.Get("config").(Config)
c := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
dropletId := state.Get("droplet_id").(int)

View file

@ -15,7 +15,7 @@ type stepShutdown struct{}
func (s *stepShutdown) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*godo.Client)
c := state.Get("config").(Config)
c := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
dropletId := state.Get("droplet_id").(int)

View file

@ -17,7 +17,7 @@ type stepSnapshot struct{}
func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*godo.Client)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(Config)
c := state.Get("config").(*Config)
dropletId := state.Get("droplet_id").(int)
var snapshotRegions []string

View file

@ -48,12 +48,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&communicator.StepConnect{
Config: &b.config.Comm,
Host: commHost,
SSHConfig: sshConfig(&b.config.Comm),
SSHConfig: b.config.Comm.SSHConfigFunc(),
CustomConnect: map[string]multistep.Step{
"docker": &StepConnectDocker{},
},
},
&common.StepProvision{},
&common.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
}
if b.config.Discard {

View file

@ -1,13 +1,7 @@
package docker
import (
"fmt"
"io/ioutil"
"github.com/hashicorp/packer/communicator/ssh"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
gossh "golang.org/x/crypto/ssh"
)
func commHost(state multistep.StateBag) (string, error) {
@ -15,40 +9,3 @@ func commHost(state multistep.StateBag) (string, error) {
driver := state.Get("driver").(Driver)
return driver.IPAddress(containerId)
}
func sshConfig(comm *communicator.Config) func(state multistep.StateBag) (*gossh.ClientConfig, error) {
return func(state multistep.StateBag) (*gossh.ClientConfig, error) {
if comm.SSHPrivateKey != "" {
// key based auth
bytes, err := ioutil.ReadFile(comm.SSHPrivateKey)
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
privateKey := string(bytes)
signer, err := gossh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &gossh.ClientConfig{
User: comm.SSHUsername,
Auth: []gossh.AuthMethod{
gossh.PublicKeys(signer),
},
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
}, nil
} else {
// password based auth
return &gossh.ClientConfig{
User: comm.SSHUsername,
Auth: []gossh.AuthMethod{
gossh.Password(comm.SSHPassword),
gossh.KeyboardInteractive(
ssh.PasswordKeyboardInteractive(comm.SSHPassword)),
},
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
}, nil
}
}
}

View file

@ -68,8 +68,8 @@ func TestUploadDownload(t *testing.T) {
hooks[packer.HookProvision] = []packer.Hook{
&packer.ProvisionHook{
Provisioners: []*packer.HookedProvisioner{
{upload, nil, ""},
{download, nil, ""},
{Provisioner: upload, Config: nil, TypeName: ""},
{Provisioner: download, Config: nil, TypeName: ""},
},
},
}
@ -157,9 +157,9 @@ func TestLargeDownload(t *testing.T) {
hooks[packer.HookProvision] = []packer.Hook{
&packer.ProvisionHook{
Provisioners: []*packer.HookedProvisioner{
{shell, nil, ""},
{downloadCupcake, nil, ""},
{downloadBigcake, nil, ""},
{Provisioner: shell, Config: nil, TypeName: ""},
{Provisioner: downloadCupcake, Config: nil, TypeName: ""},
{Provisioner: downloadBigcake, Config: nil, TypeName: ""},
},
},
}
@ -266,10 +266,10 @@ func TestFixUploadOwner(t *testing.T) {
hooks[packer.HookProvision] = []packer.Hook{
&packer.ProvisionHook{
Provisioners: []*packer.HookedProvisioner{
{fileProvisioner, nil, ""},
{dirProvisioner, nil, ""},
{shellProvisioner, nil, ""},
{verifyProvisioner, nil, ""},
{Provisioner: fileProvisioner, Config: nil, TypeName: ""},
{Provisioner: dirProvisioner, Config: nil, TypeName: ""},
{Provisioner: shellProvisioner, Config: nil, TypeName: ""},
{Provisioner: verifyProvisioner, Config: nil, TypeName: ""},
},
},
}

View file

@ -51,9 +51,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
steps := []multistep.Step{
new(StepCheckExistingImage),
&StepCreateSSHKey{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("gce_%s.pem", b.config.PackerBuildName),
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("gce_%s.pem", b.config.PackerBuildName),
},
&StepCreateInstance{
Debug: b.config.PackerDebug,
@ -68,10 +67,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&communicator.StepConnect{
Config: &b.config.Comm,
Host: commHost,
SSHConfig: sshConfig,
SSHConfig: b.config.Comm.SSHConfigFunc(),
WinRMConfig: winrmConfig,
},
new(common.StepProvision),
&common.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
}
if _, exists := b.config.Metadata[StartupScriptKey]; exists || b.config.StartupScriptFile != "" {
steps = append(steps, new(StepWaitStartupScript))

View file

@ -42,6 +42,7 @@ type Config struct {
Labels map[string]string `mapstructure:"labels"`
MachineType string `mapstructure:"machine_type"`
Metadata map[string]string `mapstructure:"metadata"`
MinCpuPlatform string `mapstructure:"min_cpu_platform"`
Network string `mapstructure:"network"`
NetworkProjectId string `mapstructure:"network_project_id"`
OmitExternalIP bool `mapstructure:"omit_external_ip"`

View file

@ -69,6 +69,7 @@ type InstanceConfig struct {
Labels map[string]string
MachineType string
Metadata map[string]string
MinCpuPlatform string
Name string
Network string
NetworkProjectId string

View file

@ -168,7 +168,27 @@ func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) {
}
func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) {
projects := []string{d.projectId, "centos-cloud", "coreos-cloud", "cos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "ubuntu-os-cloud", "windows-cloud", "gce-nvme", "windows-sql-cloud", "rhel-sap-cloud"}
projects := []string{
d.projectId,
// Public projects, drawn from
// https://cloud.google.com/compute/docs/images
"centos-cloud",
"cos-cloud",
"coreos-cloud",
"debian-cloud",
"rhel-cloud",
"rhel-sap-cloud",
"suse-cloud",
"suse-sap-cloud",
"ubuntu-os-cloud",
"windows-cloud",
"windows-sql-cloud",
"gce-uefi-images",
"gce-nvme",
// misc
"google-containers",
"opensuse-cloud",
}
var errs error
for _, project := range projects {
image, err := d.GetImageFromProject(project, name, fromFamily)
@ -377,7 +397,8 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
Metadata: &compute.Metadata{
Items: metadata,
},
Name: c.Name,
MinCpuPlatform: c.MinCpuPlatform,
Name: c.Name,
NetworkInterfaces: []*compute.NetworkInterface{
{
AccessConfigs: []*compute.AccessConfig{accessconfig},

View file

@ -43,10 +43,11 @@ func TestProcesssPrivateKeyFile(t *testing.T) {
}
func TestProcessPrivateKeyFile_encrypted(t *testing.T) {
data := []byte("what")
// Encrypt the file
b, err := x509.EncryptPEMBlock(rand.Reader,
"RSA PRIVATE KEY",
[]byte("what"),
data,
[]byte("password"),
x509.PEMCipherAES128)
if err != nil {
@ -68,8 +69,16 @@ func TestProcessPrivateKeyFile_encrypted(t *testing.T) {
path := tf.Name()
// Should have an error with a bad password
if _, err := processPrivateKeyFile(path, "bad"); err == nil {
t.Fatal("should error")
if b, err := processPrivateKeyFile(path, "bad"); err == nil {
if string(b) == string(data) {
t.Fatal("should error & be different")
}
t.Logf(`Decrypt was successfull but the body was wrong.`)
// Because of deficiencies
// in the encrypted-PEM format, it's not always possible to detect an incorrect
// password. In these cases no error will be returned but the decrypted DER
// bytes will be random noise.
// https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/crypto/x509/pem_decrypt.go#L112-L114
}
if _, err := processPrivateKeyFile(path, "password"); err != nil {

View file

@ -1,32 +1,10 @@
package googlecompute
import (
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"golang.org/x/crypto/ssh"
)
func commHost(state multistep.StateBag) (string, error) {
ipAddress := state.Get("instance_ip").(string)
return ipAddress, nil
}
// sshConfig returns the ssh configuration.
func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) {
config := state.Get("config").(*Config)
privateKey := state.Get("ssh_private_key").(string)
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: config.Comm.SSHUsername,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}

View file

@ -76,7 +76,7 @@ func getImage(c *Config, d Driver) (*Image, error) {
func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
c := state.Get("config").(*Config)
d := state.Get("driver").(Driver)
sshPublicKey := state.Get("ssh_public_key").(string)
ui := state.Get("ui").(packer.Ui)
sourceImage, err := getImage(c, d)
@ -98,7 +98,7 @@ func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
var errCh <-chan error
var metadata map[string]string
metadata, err = c.createInstanceMetadata(sourceImage, sshPublicKey)
metadata, err = c.createInstanceMetadata(sourceImage, string(c.Comm.SSHPublicKey))
errCh, err = d.RunInstance(&InstanceConfig{
AcceleratorType: c.AcceleratorType,
AcceleratorCount: c.AcceleratorCount,
@ -111,6 +111,7 @@ func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
Labels: c.Labels,
MachineType: c.MachineType,
Metadata: metadata,
MinCpuPlatform: c.MinCpuPlatform,
Name: name,
Network: c.Network,
NetworkProjectId: c.NetworkProjectId,

Some files were not shown because too many files have changed in this diff Show more