From e99cd56b6ccb6eaebfe8ac85e0e1dd68b6d49c2e Mon Sep 17 00:00:00 2001 From: James Nugent Date: Sun, 5 Apr 2015 17:58:48 -0400 Subject: [PATCH 1/5] Migrate from mitchellh/goamz to awslabs/aws-sdk-go This commit moves the Amazon builders of Packer away from the Hashicorp fork of the goamz library to the official AWS SDK for Go, in order that third party plugins may depend on the more complete official library more easily. --- builder/amazon/chroot/builder.go | 11 +- builder/amazon/chroot/step_attach_volume.go | 21 ++- .../amazon/chroot/step_check_root_device.go | 4 +- builder/amazon/chroot/step_create_volume.go | 31 ++-- builder/amazon/chroot/step_instance_info.go | 11 +- builder/amazon/chroot/step_mount_device.go | 11 +- builder/amazon/chroot/step_register_ami.go | 35 ++-- .../amazon/chroot/step_register_ami_test.go | 44 ++--- builder/amazon/chroot/step_snapshot.go | 19 +- builder/amazon/common/access_config.go | 75 ++++++-- builder/amazon/common/ami_config.go | 3 +- builder/amazon/common/artifact.go | 17 +- builder/amazon/common/block_device.go | 41 ++-- builder/amazon/common/block_device_test.go | 27 +-- builder/amazon/common/regions.go | 16 ++ builder/amazon/common/ssh.go | 25 +-- builder/amazon/common/state.go | 37 ++-- builder/amazon/common/step_ami_region_copy.go | 38 ++-- builder/amazon/common/step_create_tags.go | 19 +- builder/amazon/common/step_key_pair.go | 15 +- .../common/step_modify_ami_attributes.go | 45 +++-- .../amazon/common/step_run_source_instance.go | 175 ++++++++++-------- builder/amazon/common/step_security_group.go | 39 ++-- builder/amazon/common/step_source_ami_info.go | 8 +- builder/amazon/ebs/builder.go | 11 +- builder/amazon/ebs/builder_test.go | 3 +- builder/amazon/ebs/step_create_ami.go | 27 ++- builder/amazon/ebs/step_modify_instance.go | 13 +- builder/amazon/ebs/step_stop_instance.go | 7 +- builder/amazon/instance/builder.go | 11 +- builder/amazon/instance/step_bundle_volume.go | 4 +- builder/amazon/instance/step_register_ami.go | 21 ++- builder/amazon/instance/step_upload_bundle.go | 2 +- 33 files changed, 507 insertions(+), 359 deletions(-) create mode 100644 builder/amazon/common/regions.go diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 832a26022..58dbc71fa 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -10,7 +10,7 @@ import ( "log" "runtime" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/common" @@ -153,17 +153,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, errors.New("The amazon-chroot builder only works on Linux environments.") } - region, err := b.config.Region() + config, err := b.config.Config() if err != nil { return nil, err } - auth, err := b.config.AccessConfig.Auth() - if err != nil { - return nil, err - } - - ec2conn := ec2.New(auth, region) + ec2conn := ec2.New(config) wrappedCommand := func(command string) (string, error) { return b.config.tpl.Process( diff --git a/builder/amazon/chroot/step_attach_volume.go b/builder/amazon/chroot/step_attach_volume.go index e67479550..2263defd0 100644 --- a/builder/amazon/chroot/step_attach_volume.go +++ b/builder/amazon/chroot/step_attach_volume.go @@ -3,11 +3,12 @@ package chroot import ( "errors" "fmt" - "github.com/mitchellh/goamz/ec2" + "strings" + + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" - "strings" ) // StepAttachVolume attaches the previously created volume to an @@ -32,7 +33,11 @@ func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction { attachVolume := strings.Replace(device, "/xvd", "/sd", 1) ui.Say(fmt.Sprintf("Attaching the root volume to %s", attachVolume)) - _, err := ec2conn.AttachVolume(volumeId, instance.InstanceId, attachVolume) + _, err := ec2conn.AttachVolume(&ec2.AttachVolumeInput{ + InstanceID: instance.InstanceID, + VolumeID: &volumeId, + Device: &attachVolume, + }) if err != nil { err := fmt.Errorf("Error attaching volume: %s", err) state.Put("error", err) @@ -50,7 +55,7 @@ func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction { StepState: state, Target: "attached", Refresh: func() (interface{}, string, error) { - resp, err := ec2conn.Volumes([]string{volumeId}, ec2.NewFilter()) + resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIDs: []*string{&volumeId}}) if err != nil { return nil, "", err } @@ -60,7 +65,7 @@ func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction { } a := resp.Volumes[0].Attachments[0] - return a, a.Status, nil + return a, *a.State, nil }, } @@ -92,7 +97,7 @@ func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error { ui := state.Get("ui").(packer.Ui) ui.Say("Detaching EBS volume...") - _, err := ec2conn.DetachVolume(s.volumeId) + _, err := ec2conn.DetachVolume(&ec2.DetachVolumeInput{VolumeID: &s.volumeId}) if err != nil { return fmt.Errorf("Error detaching EBS volume: %s", err) } @@ -105,14 +110,14 @@ func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error { StepState: state, Target: "detached", Refresh: func() (interface{}, string, error) { - resp, err := ec2conn.Volumes([]string{s.volumeId}, ec2.NewFilter()) + resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIDs: []*string{&s.volumeId}}) if err != nil { return nil, "", err } v := resp.Volumes[0] if len(v.Attachments) > 0 { - return v, v.Attachments[0].Status, nil + return v, *v.Attachments[0].State, nil } else { return v, "detached", nil } diff --git a/builder/amazon/chroot/step_check_root_device.go b/builder/amazon/chroot/step_check_root_device.go index da18599aa..49a83178a 100644 --- a/builder/amazon/chroot/step_check_root_device.go +++ b/builder/amazon/chroot/step_check_root_device.go @@ -3,7 +3,7 @@ package chroot import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -18,7 +18,7 @@ func (s *StepCheckRootDevice) Run(state multistep.StateBag) multistep.StepAction ui.Say("Checking the root device on source AMI...") // It must be EBS-backed otherwise the build won't work - if image.RootDeviceType != "ebs" { + if *image.RootDeviceType != "ebs" { err := fmt.Errorf("The root device of the source AMI must be EBS-backed.") state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/amazon/chroot/step_create_volume.go b/builder/amazon/chroot/step_create_volume.go index 881857e71..d1d12d65b 100644 --- a/builder/amazon/chroot/step_create_volume.go +++ b/builder/amazon/chroot/step_create_volume.go @@ -2,11 +2,12 @@ package chroot import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "log" + + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" - "log" ) // StepCreateVolume creates a new volume from the snapshot of the root @@ -25,11 +26,11 @@ func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) // Determine the root device snapshot - log.Printf("Searching for root device of the image (%s)", image.RootDeviceName) + log.Printf("Searching for root device of the image (%s)", *image.RootDeviceName) var rootDevice *ec2.BlockDeviceMapping - for _, device := range image.BlockDevices { + for _, device := range image.BlockDeviceMappings { if device.DeviceName == image.RootDeviceName { - rootDevice = &device + rootDevice = device break } } @@ -42,12 +43,12 @@ func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction { } ui.Say("Creating the root volume...") - createVolume := &ec2.CreateVolume{ - AvailZone: instance.AvailZone, - Size: rootDevice.VolumeSize, - SnapshotId: rootDevice.SnapshotId, - VolumeType: rootDevice.VolumeType, - IOPS: rootDevice.IOPS, + createVolume := &ec2.CreateVolumeInput{ + AvailabilityZone: instance.Placement.AvailabilityZone, + Size: rootDevice.EBS.VolumeSize, + SnapshotID: rootDevice.EBS.SnapshotID, + VolumeType: rootDevice.EBS.VolumeType, + IOPS: rootDevice.EBS.IOPS, } log.Printf("Create args: %#v", createVolume) @@ -60,7 +61,7 @@ func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction { } // Set the volume ID so we remember to delete it later - s.volumeId = createVolumeResp.VolumeId + s.volumeId = *createVolumeResp.VolumeID log.Printf("Volume ID: %s", s.volumeId) // Wait for the volume to become ready @@ -69,13 +70,13 @@ func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction { StepState: state, Target: "available", Refresh: func() (interface{}, string, error) { - resp, err := ec2conn.Volumes([]string{s.volumeId}, ec2.NewFilter()) + resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIDs: []*string{&s.volumeId}}) if err != nil { return nil, "", err } v := resp.Volumes[0] - return v, v.Status, nil + return v, *v.State, nil }, } @@ -100,7 +101,7 @@ func (s *StepCreateVolume) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) ui.Say("Deleting the created EBS volume...") - _, err := ec2conn.DeleteVolume(s.volumeId) + _, err := ec2conn.DeleteVolume(&ec2.DeleteVolumeInput{VolumeID: &s.volumeId}) if err != nil { ui.Error(fmt.Sprintf("Error deleting EBS volume: %s", err)) } diff --git a/builder/amazon/chroot/step_instance_info.go b/builder/amazon/chroot/step_instance_info.go index cb694cc07..23191c54d 100644 --- a/builder/amazon/chroot/step_instance_info.go +++ b/builder/amazon/chroot/step_instance_info.go @@ -2,11 +2,12 @@ package chroot import ( "fmt" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/goamz/ec2" + "log" + + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" - "log" ) // StepInstanceInfo verifies that this builder is running on an EC2 instance. @@ -18,7 +19,7 @@ func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction { // Get our own instance ID ui.Say("Gathering information about this EC2 instance...") - instanceIdBytes, err := aws.GetMetaData("instance-id") + instanceIdBytes, err := common.GetInstanceMetaData("instance-id") if err != nil { log.Printf("Error: %s", err) err := fmt.Errorf( @@ -33,7 +34,7 @@ func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction { log.Printf("Instance ID: %s", instanceId) // Query the entire instance metadata - instancesResp, err := ec2conn.Instances([]string{instanceId}, ec2.NewFilter()) + instancesResp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{InstanceIDs: []*string{&instanceId}}) if err != nil { err := fmt.Errorf("Error getting instance data: %s", err) state.Put("error", err) diff --git a/builder/amazon/chroot/step_mount_device.go b/builder/amazon/chroot/step_mount_device.go index 3c3d959c1..b8196f7fa 100644 --- a/builder/amazon/chroot/step_mount_device.go +++ b/builder/amazon/chroot/step_mount_device.go @@ -3,12 +3,13 @@ package chroot import ( "bytes" "fmt" - "github.com/mitchellh/goamz/ec2" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" "log" "os" "path/filepath" + + "github.com/awslabs/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" ) type mountPathData struct { @@ -59,9 +60,9 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - log.Printf("Source image virtualization type is: %s", image.VirtualizationType) + log.Printf("Source image virtualization type is: %s", *image.VirtualizationType) deviceMount := device - if image.VirtualizationType == "hvm" { + if *image.VirtualizationType == "hvm" { deviceMount = fmt.Sprintf("%s%d", device, 1) } state.Put("deviceMount", deviceMount) diff --git a/builder/amazon/chroot/step_register_ami.go b/builder/amazon/chroot/step_register_ami.go index 62e6a3ff0..88da65e9f 100644 --- a/builder/amazon/chroot/step_register_ami.go +++ b/builder/amazon/chroot/step_register_ami.go @@ -3,7 +3,8 @@ package chroot import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" @@ -20,11 +21,11 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") - blockDevices := make([]ec2.BlockDeviceMapping, len(image.BlockDevices)) - for i, device := range image.BlockDevices { + blockDevices := make([]*ec2.BlockDeviceMapping, len(image.BlockDeviceMappings)) + for i, device := range image.BlockDeviceMappings { newDevice := device if newDevice.DeviceName == image.RootDeviceName { - newDevice.SnapshotId = snapshotId + newDevice.EBS.SnapshotID = &snapshotId } blockDevices[i] = newDevice @@ -34,7 +35,7 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 if config.AMIEnhancedNetworking { - registerOpts.SriovNetSupport = "simple" + registerOpts.SRIOVNetSupport = aws.String("simple") } registerResp, err := ec2conn.RegisterImage(registerOpts) @@ -45,16 +46,16 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { } // Set the AMI ID in the state - ui.Say(fmt.Sprintf("AMI: %s", registerResp.ImageId)) + ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageID)) amis := make(map[string]string) - amis[ec2conn.Region.Name] = registerResp.ImageId + amis[ec2conn.Config.Region] = *registerResp.ImageID state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", - Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId), + Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *registerResp.ImageID), StepState: state, } @@ -71,18 +72,18 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {} -func buildRegisterOpts(config *Config, image *ec2.Image, blockDevices []ec2.BlockDeviceMapping) *ec2.RegisterImage { - registerOpts := &ec2.RegisterImage{ - Name: config.AMIName, - Architecture: image.Architecture, - RootDeviceName: image.RootDeviceName, - BlockDevices: blockDevices, - VirtType: config.AMIVirtType, +func buildRegisterOpts(config *Config, image *ec2.Image, blockDevices []*ec2.BlockDeviceMapping) *ec2.RegisterImageInput { + registerOpts := &ec2.RegisterImageInput{ + Name: &config.AMIName, + Architecture: image.Architecture, + RootDeviceName: image.RootDeviceName, + BlockDeviceMappings: blockDevices, + VirtualizationType: &config.AMIVirtType, } if config.AMIVirtType != "hvm" { - registerOpts.KernelId = image.KernelId - registerOpts.RamdiskId = image.RamdiskId + registerOpts.KernelID = image.KernelID + registerOpts.RAMDiskID = image.RAMDiskID } return registerOpts diff --git a/builder/amazon/chroot/step_register_ami_test.go b/builder/amazon/chroot/step_register_ami_test.go index 393b95c8b..9d44ba684 100644 --- a/builder/amazon/chroot/step_register_ami_test.go +++ b/builder/amazon/chroot/step_register_ami_test.go @@ -1,16 +1,18 @@ package chroot import ( - "github.com/mitchellh/goamz/ec2" "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" ) func testImage() ec2.Image { return ec2.Image{ - Id: "ami-abcd1234", - Name: "ami_test_name", - Architecture: "x86_64", - KernelId: "aki-abcd1234", + ImageID: aws.String("ami-abcd1234"), + Name: aws.String("ami_test_name"), + Architecture: aws.String("x86_64"), + KernelID: aws.String("aki-abcd1234"), } } @@ -22,23 +24,23 @@ func TestStepRegisterAmi_buildRegisterOpts_pv(t *testing.T) { image := testImage() - blockDevices := []ec2.BlockDeviceMapping{} + blockDevices := []*ec2.BlockDeviceMapping{} opts := buildRegisterOpts(&config, &image, blockDevices) expected := config.AMIVirtType - if opts.VirtType != expected { - t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, opts.VirtType) + if *opts.VirtualizationType != expected { + t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, *opts.VirtualizationType) } expected = config.AMIName - if opts.Name != expected { - t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, opts.Name) + if *opts.Name != expected { + t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, *opts.Name) } - expected = image.KernelId - if opts.KernelId != expected { - t.Fatalf("Unexpected KernelId value: expected %s got %s\n", expected, opts.KernelId) + expected = *image.KernelID + if *opts.KernelID != expected { + t.Fatalf("Unexpected KernelId value: expected %s got %s\n", expected, *opts.KernelID) } } @@ -51,23 +53,21 @@ func TestStepRegisterAmi_buildRegisterOpts_hvm(t *testing.T) { image := testImage() - blockDevices := []ec2.BlockDeviceMapping{} + blockDevices := []*ec2.BlockDeviceMapping{} opts := buildRegisterOpts(&config, &image, blockDevices) expected := config.AMIVirtType - if opts.VirtType != expected { - t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, opts.VirtType) + if *opts.VirtualizationType != expected { + t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, *opts.VirtualizationType) } expected = config.AMIName - if opts.Name != expected { - t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, opts.Name) + if *opts.Name != expected { + t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, *opts.Name) } - expected = "" - if opts.KernelId != expected { - t.Fatalf("Unexpected KernelId value: expected %s got %s\n", expected, opts.KernelId) + if opts.KernelID != nil { + t.Fatalf("Unexpected KernelId value: expected nil got %s\n", *opts.KernelID) } - } diff --git a/builder/amazon/chroot/step_snapshot.go b/builder/amazon/chroot/step_snapshot.go index cad4b782b..e798a3a3e 100644 --- a/builder/amazon/chroot/step_snapshot.go +++ b/builder/amazon/chroot/step_snapshot.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" @@ -25,9 +25,12 @@ func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction { volumeId := state.Get("volume_id").(string) ui.Say("Creating snapshot...") - createSnapResp, err := ec2conn.CreateSnapshot( - volumeId, - fmt.Sprintf("Packer: %s", time.Now().String())) + description := fmt.Sprintf("Packer: %s", time.Now().String()) + + createSnapResp, err := ec2conn.CreateSnapshot(&ec2.CreateSnapshotInput{ + VolumeID: &volumeId, + Description: &description, + }) if err != nil { err := fmt.Errorf("Error creating snapshot: %s", err) state.Put("error", err) @@ -36,7 +39,7 @@ func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction { } // Set the snapshot ID so we can delete it later - s.snapshotId = createSnapResp.Id + s.snapshotId = *createSnapResp.SnapshotID ui.Message(fmt.Sprintf("Snapshot ID: %s", s.snapshotId)) // Wait for the snapshot to be ready @@ -45,7 +48,7 @@ func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction { StepState: state, Target: "completed", Refresh: func() (interface{}, string, error) { - resp, err := ec2conn.Snapshots([]string{s.snapshotId}, ec2.NewFilter()) + resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{SnapshotIDs: []*string{&s.snapshotId}}) if err != nil { return nil, "", err } @@ -55,7 +58,7 @@ func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction { } s := resp.Snapshots[0] - return s, s.Status, nil + return s, *s.State, nil }, } @@ -83,7 +86,7 @@ func (s *StepSnapshot) Cleanup(state multistep.StateBag) { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) ui.Say("Removing snapshot since we cancelled or halted...") - _, err := ec2conn.DeleteSnapshots([]string{s.snapshotId}) + _, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{SnapshotID: &s.snapshotId}) if err != nil { ui.Error(fmt.Sprintf("Error: %s", err)) } diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index a9a60d7cf..fedaabd24 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -2,10 +2,13 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/packer/packer" + "io/ioutil" + "net/http" "strings" "unicode" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/mitchellh/packer/packer" ) // AccessConfig is for common configuration related to AWS access @@ -16,37 +19,48 @@ type AccessConfig struct { Token string `mapstructure:"token"` } -// Auth returns a valid aws.Auth object for access to AWS services, or -// an error if the authentication couldn't be resolved. -func (c *AccessConfig) Auth() (aws.Auth, error) { - auth, err := aws.GetAuth(c.AccessKey, c.SecretKey) - if err == nil { - // Store the accesskey and secret that we got... - c.AccessKey = auth.AccessKey - c.SecretKey = auth.SecretKey - c.Token = auth.Token +// Config returns a valid aws.Config object for access to AWS services, or +// an error if the authentication and region couldn't be resolved +func (c *AccessConfig) Config() (*aws.Config, error) { + credsProvider := aws.DetectCreds(c.AccessKey, c.SecretKey, c.Token) + + creds, err := credsProvider.Credentials() + if err != nil { + return nil, err } - if c.Token != "" { - auth.Token = c.Token + + c.AccessKey = creds.AccessKeyID + c.SecretKey = creds.SecretAccessKey + c.Token = creds.SessionToken + + region, err := c.Region() + if err != nil { + return nil, err } - return auth, err + return &aws.Config{ + Region: region, + Credentials: credsProvider, + }, nil } // Region returns the aws.Region object for access to AWS services, requesting // the region from the instance metadata if possible. -func (c *AccessConfig) Region() (aws.Region, error) { +func (c *AccessConfig) Region() (string, error) { if c.RawRegion != "" { - return aws.Regions[c.RawRegion], nil + if valid := ValidateRegion(c.RawRegion); valid == false { + return "", fmt.Errorf("Not a valid region: %s", c.RawRegion) + } + return c.RawRegion, nil } - md, err := aws.GetMetaData("placement/availability-zone") + md, err := GetInstanceMetaData("placement/availability-zone") if err != nil { - return aws.Region{}, err + return "", err } region := strings.TrimRightFunc(string(md), unicode.IsLetter) - return aws.Regions[region], nil + return region, nil } func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error { @@ -75,7 +89,7 @@ func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error { } if c.RawRegion != "" { - if _, ok := aws.Regions[c.RawRegion]; !ok { + if valid := ValidateRegion(c.RawRegion); valid == false { errs = append(errs, fmt.Errorf("Unknown region: %s", c.RawRegion)) } } @@ -86,3 +100,24 @@ func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error { return nil } + +func GetInstanceMetaData(path string) (contents []byte, err error) { + url := "http://169.254.169.254/latest/meta-data/" + path + + resp, err := http.Get(url) + if err != nil { + return + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url) + return + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + return []byte(body), err +} diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index 91c2d12d0..4dbcd03c4 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -3,7 +3,6 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/aws" "github.com/mitchellh/packer/packer" ) @@ -81,7 +80,7 @@ func (c *AMIConfig) Prepare(t *packer.ConfigTemplate) []error { regionSet[region] = struct{}{} // Verify the region is real - if _, ok := aws.Regions[region]; !ok { + if valid := ValidateRegion(region); valid == false { errs = append(errs, fmt.Errorf("Unknown region: %s", region)) continue } diff --git a/builder/amazon/common/artifact.go b/builder/amazon/common/artifact.go index 8d6265508..89f3d9fa2 100644 --- a/builder/amazon/common/artifact.go +++ b/builder/amazon/common/artifact.go @@ -6,8 +6,8 @@ import ( "sort" "strings" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/packer/packer" ) @@ -67,8 +67,17 @@ func (a *Artifact) Destroy() error { for region, imageId := range a.Amis { log.Printf("Deregistering image ID (%s) from region (%s)", imageId, region) - regionconn := ec2.New(a.Conn.Auth, aws.Regions[region]) - if _, err := regionconn.DeregisterImage(imageId); err != nil { + + regionConfig := &aws.Config{ + Credentials: a.Conn.Config.Credentials, + Region: region, + } + regionConn := ec2.New(regionConfig) + + input := &ec2.DeregisterImageInput{ + ImageID: &imageId, + } + if _, err := regionConn.DeregisterImage(input); err != nil { errors = append(errors, err) } diff --git a/builder/amazon/common/block_device.go b/builder/amazon/common/block_device.go index 9557cc579..de155b56a 100644 --- a/builder/amazon/common/block_device.go +++ b/builder/amazon/common/block_device.go @@ -3,7 +3,8 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/packer/packer" ) @@ -25,21 +26,29 @@ type BlockDevices struct { LaunchMappings []BlockDevice `mapstructure:"launch_block_device_mappings"` } -func buildBlockDevices(b []BlockDevice) []ec2.BlockDeviceMapping { - var blockDevices []ec2.BlockDeviceMapping +func buildBlockDevices(b []BlockDevice) []*ec2.BlockDeviceMapping { + var blockDevices []*ec2.BlockDeviceMapping for _, blockDevice := range b { - blockDevices = append(blockDevices, ec2.BlockDeviceMapping{ - DeviceName: blockDevice.DeviceName, - VirtualName: blockDevice.VirtualName, - SnapshotId: blockDevice.SnapshotId, - VolumeType: blockDevice.VolumeType, - VolumeSize: blockDevice.VolumeSize, - DeleteOnTermination: blockDevice.DeleteOnTermination, - IOPS: blockDevice.IOPS, - NoDevice: blockDevice.NoDevice, - Encrypted: blockDevice.Encrypted, - }) + ebsBlockDevice := &ec2.EBSBlockDevice{ + SnapshotID: &blockDevice.SnapshotId, + Encrypted: &blockDevice.Encrypted, + IOPS: &blockDevice.IOPS, + VolumeType: &blockDevice.VolumeType, + VolumeSize: &blockDevice.VolumeSize, + DeleteOnTermination: &blockDevice.DeleteOnTermination, + } + mapping := &ec2.BlockDeviceMapping{ + EBS: ebsBlockDevice, + DeviceName: &blockDevice.DeviceName, + VirtualName: &blockDevice.VirtualName, + } + + if blockDevice.NoDevice { + mapping.NoDevice = aws.String("") + } + + blockDevices = append(blockDevices, mapping) } return blockDevices } @@ -89,10 +98,10 @@ func (b *BlockDevices) Prepare(t *packer.ConfigTemplate) []error { return nil } -func (b *BlockDevices) BuildAMIDevices() []ec2.BlockDeviceMapping { +func (b *BlockDevices) BuildAMIDevices() []*ec2.BlockDeviceMapping { return buildBlockDevices(b.AMIMappings) } -func (b *BlockDevices) BuildLaunchDevices() []ec2.BlockDeviceMapping { +func (b *BlockDevices) BuildLaunchDevices() []*ec2.BlockDeviceMapping { return buildBlockDevices(b.LaunchMappings) } diff --git a/builder/amazon/common/block_device_test.go b/builder/amazon/common/block_device_test.go index 838b23aec..a4c1dbb79 100644 --- a/builder/amazon/common/block_device_test.go +++ b/builder/amazon/common/block_device_test.go @@ -1,9 +1,11 @@ package common import ( - "github.com/mitchellh/goamz/ec2" "reflect" "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" ) func TestBlockDevice(t *testing.T) { @@ -23,13 +25,16 @@ func TestBlockDevice(t *testing.T) { }, Result: &ec2.BlockDeviceMapping{ - DeviceName: "/dev/sdb", - VirtualName: "ephemeral0", - SnapshotId: "snap-1234", - VolumeType: "standard", - VolumeSize: 8, - DeleteOnTermination: true, - IOPS: 1000, + DeviceName: aws.String("/dev/sdb"), + VirtualName: aws.String("ephemeral0"), + EBS: &ec2.EBSBlockDevice{ + Encrypted: aws.Boolean(false), + SnapshotID: aws.String("snap-1234"), + VolumeType: aws.String("standard"), + VolumeSize: aws.Long(8), + DeleteOnTermination: aws.Boolean(true), + IOPS: aws.Long(1000), + }, }, }, } @@ -40,9 +45,9 @@ func TestBlockDevice(t *testing.T) { LaunchMappings: []BlockDevice{*tc.Config}, } - expected := []ec2.BlockDeviceMapping{*tc.Result} - - if !reflect.DeepEqual(expected, blockDevices.BuildAMIDevices()) { + expected := []*ec2.BlockDeviceMapping{tc.Result} + got := blockDevices.BuildAMIDevices() + if !reflect.DeepEqual(expected, got) { t.Fatalf("bad: %#v", expected) } diff --git a/builder/amazon/common/regions.go b/builder/amazon/common/regions.go new file mode 100644 index 000000000..4d3762465 --- /dev/null +++ b/builder/amazon/common/regions.go @@ -0,0 +1,16 @@ +package common + +// IsValidRegion returns true if the supplied region is a valid AWS +// region and false if it's not. +func ValidateRegion(region string) bool { + var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1", + "eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", + "sa-east-1", "cn-north-1", "us-gov-west-1"} + + for _, valid := range regions { + if region == valid { + return true + } + } + return false +} diff --git a/builder/amazon/common/ssh.go b/builder/amazon/common/ssh.go index f31437d89..965a26994 100644 --- a/builder/amazon/common/ssh.go +++ b/builder/amazon/common/ssh.go @@ -1,12 +1,13 @@ package common import ( - "code.google.com/p/go.crypto/ssh" "errors" "fmt" - "github.com/mitchellh/goamz/ec2" - "github.com/mitchellh/multistep" "time" + + "code.google.com/p/go.crypto/ssh" + "github.com/awslabs/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" ) // SSHAddress returns a function that can be given to the SSH communicator @@ -16,27 +17,29 @@ func SSHAddress(e *ec2.EC2, port int, private bool) func(multistep.StateBag) (st for j := 0; j < 2; j++ { var host string i := state.Get("instance").(*ec2.Instance) - if i.VpcId != "" { - if i.PublicIpAddress != "" && !private { - host = i.PublicIpAddress + if *i.VPCID != "" { + if *i.PublicIPAddress != "" && !private { + host = *i.PublicIPAddress } else { - host = i.PrivateIpAddress + host = *i.PrivateIPAddress } - } else if i.DNSName != "" { - host = i.DNSName + } else if *i.PublicDNSName != "" { + host = *i.PublicDNSName } if host != "" { return fmt.Sprintf("%s:%d", host, port), nil } - r, err := e.Instances([]string{i.InstanceId}, ec2.NewFilter()) + r, err := e.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIDs: []*string{i.InstanceID}, + }) if err != nil { return "", err } if len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 { - return "", fmt.Errorf("instance not found: %s", i.InstanceId) + return "", fmt.Errorf("instance not found: %s", *i.InstanceID) } state.Put("instance", &r.Reservations[0].Instances[0]) diff --git a/builder/amazon/common/state.go b/builder/amazon/common/state.go index 62e861d74..00a58be08 100644 --- a/builder/amazon/common/state.go +++ b/builder/amazon/common/state.go @@ -3,13 +3,15 @@ package common import ( "errors" "fmt" - "github.com/mitchellh/goamz/ec2" - "github.com/mitchellh/multistep" "log" "net" "os" "strconv" "time" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" ) // StateRefreshFunc is a function type used for StateChangeConf that is @@ -36,9 +38,11 @@ type StateChangeConf struct { // an AMI for state changes. func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.Images([]string{imageId}, ec2.NewFilter()) + resp, err := conn.DescribeImages(&ec2.DescribeImagesInput{ + ImageIDs: []*string{&imageId}, + }) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" { + if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidAMIID.NotFound" { // Set this to nil as if we didn't find anything. resp = nil } else if isTransientNetworkError(err) { @@ -57,7 +61,7 @@ func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc { } i := resp.Images[0] - return i, i.State, nil + return i, *i.State, nil } } @@ -65,9 +69,11 @@ func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc { // an EC2 instance. func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.Instances([]string{i.InstanceId}, ec2.NewFilter()) + resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIDs: []*string{i.InstanceID}, + }) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" { + if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" { // Set this to nil as if we didn't find anything. resp = nil } else if isTransientNetworkError(err) { @@ -85,8 +91,8 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc { return nil, "", nil } - i = &resp.Reservations[0].Instances[0] - return i, i.State.Name, nil + i = resp.Reservations[0].Instances[0] + return i, *i.State.Name, nil } } @@ -94,9 +100,12 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc { // a spot request for state changes. func SpotRequestStateRefreshFunc(conn *ec2.EC2, spotRequestId string) StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.DescribeSpotRequests([]string{spotRequestId}, ec2.NewFilter()) + resp, err := conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{ + SpotInstanceRequestIDs: []*string{&spotRequestId}, + }) + if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSpotInstanceRequestID.NotFound" { + if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidSpotInstanceRequestID.NotFound" { // Set this to nil as if we didn't find anything. resp = nil } else if isTransientNetworkError(err) { @@ -108,14 +117,14 @@ func SpotRequestStateRefreshFunc(conn *ec2.EC2, spotRequestId string) StateRefre } } - if resp == nil || len(resp.SpotRequestResults) == 0 { + if resp == nil || len(resp.SpotInstanceRequests) == 0 { // Sometimes AWS has consistency issues and doesn't see the // SpotRequest. Return an empty state. return nil, "", nil } - i := resp.SpotRequestResults[0] - return i, i.State, nil + i := resp.SpotInstanceRequests[0] + return i, *i.State, nil } } diff --git a/builder/amazon/common/step_ami_region_copy.go b/builder/amazon/common/step_ami_region_copy.go index 898eacc08..88ed1884f 100644 --- a/builder/amazon/common/step_ami_region_copy.go +++ b/builder/amazon/common/step_ami_region_copy.go @@ -2,11 +2,14 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/goamz/ec2" + + "sync" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "sync" ) type StepAMIRegionCopy struct { @@ -17,7 +20,7 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) amis := state.Get("amis").(map[string]string) - ami := amis[ec2conn.Region.Name] + ami := amis[ec2conn.Config.Region] if len(s.Regions) == 0 { return multistep.ActionContinue @@ -34,8 +37,7 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { go func(region string) { defer wg.Done() - id, err := amiRegionCopy(state, ec2conn.Auth, ami, - aws.Regions[region], ec2conn.Region) + id, err := amiRegionCopy(state, ec2conn.Config.Credentials, ami, region, ec2conn.Config.Region) lock.Lock() defer lock.Unlock() @@ -67,32 +69,36 @@ func (s *StepAMIRegionCopy) Cleanup(state multistep.StateBag) { // amiRegionCopy does a copy for the given AMI to the target region and // returns the resulting ID or error. -func amiRegionCopy(state multistep.StateBag, auth aws.Auth, imageId string, - target aws.Region, source aws.Region) (string, error) { +func amiRegionCopy(state multistep.StateBag, auth aws.CredentialsProvider, imageId string, + target string, source string) (string, error) { // Connect to the region where the AMI will be copied to - regionconn := ec2.New(auth, target) - resp, err := regionconn.CopyImage(&ec2.CopyImage{ - SourceRegion: source.Name, - SourceImageId: imageId, + config := &aws.Config{ + Credentials: auth, + Region: target, + } + regionconn := ec2.New(config) + resp, err := regionconn.CopyImage(&ec2.CopyImageInput{ + SourceRegion: &source, + SourceImageID: &imageId, }) if err != nil { return "", fmt.Errorf("Error Copying AMI (%s) to region (%s): %s", - imageId, target.Name, err) + imageId, target, err) } stateChange := StateChangeConf{ Pending: []string{"pending"}, Target: "available", - Refresh: AMIStateRefreshFunc(regionconn, resp.ImageId), + Refresh: AMIStateRefreshFunc(regionconn, *resp.ImageID), StepState: state, } if _, err := WaitForState(&stateChange); err != nil { return "", fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", - resp.ImageId, target.Name, err) + *resp.ImageID, target, err) } - return resp.ImageId, nil + return *resp.ImageID, nil } diff --git a/builder/amazon/common/step_create_tags.go b/builder/amazon/common/step_create_tags.go index a204ca321..fc17458ee 100644 --- a/builder/amazon/common/step_create_tags.go +++ b/builder/amazon/common/step_create_tags.go @@ -2,8 +2,9 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/goamz/ec2" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -21,14 +22,20 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction { for region, ami := range amis { ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami)) - var ec2Tags []ec2.Tag + var ec2Tags []*ec2.Tag for key, value := range s.Tags { ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value)) - ec2Tags = append(ec2Tags, ec2.Tag{key, value}) + ec2Tags = append(ec2Tags, &ec2.Tag{Key: &key, Value: &value}) } - regionconn := ec2.New(ec2conn.Auth, aws.Regions[region]) - _, err := regionconn.CreateTags([]string{ami}, ec2Tags) + regionconn := ec2.New(&aws.Config{ + Credentials: ec2conn.Config.Credentials, + Region: region, + }) + _, err := regionconn.CreateTags(&ec2.CreateTagsInput{ + Resources: []*string{&ami}, + Tags: ec2Tags, + }) if err != nil { err := fmt.Errorf("Error adding tags to AMI (%s): %s", ami, err) state.Put("error", err) diff --git a/builder/amazon/common/step_key_pair.go b/builder/amazon/common/step_key_pair.go index 3a7eb9f35..5082d7b26 100644 --- a/builder/amazon/common/step_key_pair.go +++ b/builder/amazon/common/step_key_pair.go @@ -2,12 +2,13 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/ec2" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" "io/ioutil" "os" "runtime" + + "github.com/awslabs/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" ) type StepKeyPair struct { @@ -39,7 +40,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.KeyPairName)) - keyResp, err := ec2conn.CreateKeyPair(s.KeyPairName) + keyResp, err := ec2conn.CreateKeyPair(&ec2.CreateKeyPairInput{KeyName: &s.KeyPairName}) if err != nil { state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err)) return multistep.ActionHalt @@ -50,7 +51,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { // Set some state data for use in future steps state.Put("keyPair", s.keyName) - state.Put("privateKey", keyResp.KeyMaterial) + state.Put("privateKey", *keyResp.KeyMaterial) // If we're in debug mode, output the private key to the working // directory. @@ -64,7 +65,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { defer f.Close() // Write the key out - if _, err := f.Write([]byte(keyResp.KeyMaterial)); err != nil { + if _, err := f.Write([]byte(*keyResp.KeyMaterial)); err != nil { state.Put("error", fmt.Errorf("Error saving debug key: %s", err)) return multistep.ActionHalt } @@ -91,7 +92,7 @@ func (s *StepKeyPair) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) ui.Say("Deleting temporary keypair...") - _, err := ec2conn.DeleteKeyPair(s.keyName) + _, err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: &s.keyName}) if err != nil { ui.Error(fmt.Sprintf( "Error cleaning up keypair. Please delete the key manually: %s", s.keyName)) diff --git a/builder/amazon/common/step_modify_ami_attributes.go b/builder/amazon/common/step_modify_ami_attributes.go index 533d4cfd9..0628109b5 100644 --- a/builder/amazon/common/step_modify_ami_attributes.go +++ b/builder/amazon/common/step_modify_ami_attributes.go @@ -2,8 +2,9 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/goamz/ec2" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -34,37 +35,53 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc // Construct the modify image attribute requests we're going to make. // We need to make each separately since the EC2 API only allows changing // one type at a kind currently. - options := make(map[string]*ec2.ModifyImageAttribute) + options := make(map[string]*ec2.ModifyImageAttributeInput) if s.Description != "" { - options["description"] = &ec2.ModifyImageAttribute{ - Description: s.Description, + options["description"] = &ec2.ModifyImageAttributeInput{ + Description: &ec2.AttributeValue{Value: &s.Description}, } } if len(s.Groups) > 0 { - options["groups"] = &ec2.ModifyImageAttribute{ - AddGroups: s.Groups, + groups := make([]*string, len(s.Groups)) + for i, g := range s.Groups { + groups[i] = &g + } + options["groups"] = &ec2.ModifyImageAttributeInput{ + UserGroups: groups, } } if len(s.Users) > 0 { - options["users"] = &ec2.ModifyImageAttribute{ - AddUsers: s.Users, + users := make([]*string, len(s.Users)) + for i, u := range s.Users { + users[i] = &u + } + options["users"] = &ec2.ModifyImageAttributeInput{ + UserIDs: users, } } if len(s.ProductCodes) > 0 { - options["product codes"] = &ec2.ModifyImageAttribute{ - ProductCodes: s.ProductCodes, + codes := make([]*string, len(s.ProductCodes)) + for i, c := range s.ProductCodes { + codes[i] = &c + } + options["product codes"] = &ec2.ModifyImageAttributeInput{ + ProductCodes: codes, } } for region, ami := range amis { ui.Say(fmt.Sprintf("Modifying attributes on AMI (%s)...", ami)) - regionconn := ec2.New(ec2conn.Auth, aws.Regions[region]) - for name, opts := range options { + regionconn := ec2.New(&aws.Config{ + Credentials: ec2conn.Config.Credentials, + Region: region, + }) + for name, input := range options { ui.Message(fmt.Sprintf("Modifying: %s", name)) - _, err := regionconn.ModifyImageAttribute(ami, opts) + input.ImageID = &ami + _, err := regionconn.ModifyImageAttribute(input) if err != nil { err := fmt.Errorf("Error modify AMI attributes: %s", err) state.Put("error", err) diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 50cedf6ea..644734619 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -7,7 +7,9 @@ import ( "strconv" "time" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -29,15 +31,20 @@ type StepRunSourceInstance struct { UserDataFile string instance *ec2.Instance - spotRequest *ec2.SpotRequestResult + spotRequest *ec2.SpotInstanceRequest } func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) keyName := state.Get("keyPair").(string) - securityGroupIds := state.Get("securityGroupIds").([]string) + tempSecurityGroupIds := state.Get("securityGroupIds").([]string) ui := state.Get("ui").(packer.Ui) + securityGroupIds := make([]*string, len(tempSecurityGroupIds)) + for i, sg := range tempSecurityGroupIds { + securityGroupIds[i] = &sg + } + userData := s.UserData if s.UserDataFile != "" { contents, err := ioutil.ReadFile(s.UserDataFile) @@ -49,13 +56,10 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi userData = string(contents) } - securityGroups := make([]ec2.SecurityGroup, len(securityGroupIds)) - for n, securityGroupId := range securityGroupIds { - securityGroups[n] = ec2.SecurityGroup{Id: securityGroupId} - } - ui.Say("Launching a source AWS instance...") - imageResp, err := ec2conn.Images([]string{s.SourceAMI}, ec2.NewFilter()) + imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ + ImageIDs: []*string{&s.SourceAMI}, + }) if err != nil { state.Put("error", fmt.Errorf("There was a problem with the source AMI: %s", err)) return multistep.ActionHalt @@ -66,11 +70,11 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi return multistep.ActionHalt } - if s.ExpectedRootDevice != "" && imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice { + if s.ExpectedRootDevice != "" && *imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice { state.Put("error", fmt.Errorf( "The provided source AMI has an invalid root device type.\n"+ "Expected '%s', got '%s'.", - s.ExpectedRootDevice, imageResp.Images[0].RootDeviceType)) + s.ExpectedRootDevice, *imageResp.Images[0].RootDeviceType)) return multistep.ActionHalt } @@ -82,11 +86,11 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi // Detect the spot price startTime := time.Now().Add(-1 * time.Hour) - resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistory{ - InstanceType: []string{s.InstanceType}, - ProductDescription: []string{s.SpotPriceProduct}, - AvailabilityZone: s.AvailabilityZone, - StartTime: startTime, + resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistoryInput{ + InstanceTypes: []*string{&s.InstanceType}, + ProductDescriptions: []*string{&s.SpotPriceProduct}, + AvailabilityZone: &s.AvailabilityZone, + StartTime: &startTime, }) if err != nil { err := fmt.Errorf("Error finding spot price: %s", err) @@ -96,9 +100,9 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi } var price float64 - for _, history := range resp.History { - log.Printf("[INFO] Candidate spot price: %s", history.SpotPrice) - current, err := strconv.ParseFloat(history.SpotPrice, 64) + for _, history := range resp.SpotPriceHistory { + log.Printf("[INFO] Candidate spot price: %s", *history.SpotPrice) + current, err := strconv.ParseFloat(*history.SpotPrice, 64) if err != nil { log.Printf("[ERR] Error parsing spot price: %s", err) continue @@ -120,20 +124,33 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi var instanceId string if spotPrice == "" { - runOpts := &ec2.RunInstances{ - KeyName: keyName, - ImageId: s.SourceAMI, - InstanceType: s.InstanceType, - UserData: []byte(userData), - MinCount: 0, - MaxCount: 0, - SecurityGroups: securityGroups, - IamInstanceProfile: s.IamInstanceProfile, - SubnetId: s.SubnetId, - AssociatePublicIpAddress: s.AssociatePublicIpAddress, - BlockDevices: s.BlockDevices.BuildLaunchDevices(), - AvailZone: s.AvailabilityZone, + runOpts := &ec2.RunInstancesInput{ + KeyName: &keyName, + ImageID: &s.SourceAMI, + InstanceType: &s.InstanceType, + UserData: &userData, + MaxCount: aws.Long(1), + MinCount: aws.Long(1), + IAMInstanceProfile: &ec2.IAMInstanceProfileSpecification{Name: &s.IamInstanceProfile}, + BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(), + Placement: &ec2.Placement{AvailabilityZone: &s.AvailabilityZone}, + } + + if s.SubnetId != "" && s.AssociatePublicIpAddress { + runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ + &ec2.InstanceNetworkInterfaceSpecification{ + DeviceIndex: aws.Long(0), + AssociatePublicIPAddress: &s.AssociatePublicIpAddress, + SubnetID: &s.SubnetId, + Groups: securityGroupIds, + DeleteOnTermination: aws.Boolean(true), + }, + } + } else { + runOpts.SubnetID = &s.SubnetId + runOpts.SecurityGroupIDs = securityGroupIds } + runResp, err := ec2conn.RunInstances(runOpts) if err != nil { err := fmt.Errorf("Error launching source instance: %s", err) @@ -141,26 +158,29 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi ui.Error(err.Error()) return multistep.ActionHalt } - instanceId = runResp.Instances[0].InstanceId + instanceId = *runResp.Instances[0].InstanceID } else { ui.Message(fmt.Sprintf( "Requesting spot instance '%s' for: %s", s.InstanceType, spotPrice)) - runOpts := &ec2.RequestSpotInstances{ - SpotPrice: spotPrice, - KeyName: keyName, - ImageId: s.SourceAMI, - InstanceType: s.InstanceType, - UserData: []byte(userData), - SecurityGroups: securityGroups, - IamInstanceProfile: s.IamInstanceProfile, - SubnetId: s.SubnetId, - AssociatePublicIpAddress: s.AssociatePublicIpAddress, - BlockDevices: s.BlockDevices.BuildLaunchDevices(), - AvailZone: s.AvailabilityZone, - } - runSpotResp, err := ec2conn.RequestSpotInstances(runOpts) + runSpotResp, err := ec2conn.RequestSpotInstances(&ec2.RequestSpotInstancesInput{ + SpotPrice: &spotPrice, + LaunchSpecification: &ec2.RequestSpotLaunchSpecification{ + KeyName: &keyName, + ImageID: &s.SourceAMI, + InstanceType: &s.InstanceType, + UserData: &userData, + SecurityGroupIDs: securityGroupIds, + IAMInstanceProfile: &ec2.IAMInstanceProfileSpecification{Name: &s.IamInstanceProfile}, + SubnetID: &s.SubnetId, + NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{ + &ec2.InstanceNetworkInterfaceSpecification{AssociatePublicIPAddress: &s.AssociatePublicIpAddress}, + }, + Placement: &ec2.SpotPlacement{AvailabilityZone: &s.AvailabilityZone}, + BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(), + }, + }) if err != nil { err := fmt.Errorf("Error launching source spot instance: %s", err) state.Put("error", err) @@ -168,44 +188,47 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi return multistep.ActionHalt } - s.spotRequest = &runSpotResp.SpotRequestResults[0] + s.spotRequest = runSpotResp.SpotInstanceRequests[0] - spotRequestId := s.spotRequest.SpotRequestId - ui.Message(fmt.Sprintf("Waiting for spot request (%s) to become active...", spotRequestId)) + spotRequestId := s.spotRequest.SpotInstanceRequestID + ui.Message(fmt.Sprintf("Waiting for spot request (%s) to become active...", *spotRequestId)) stateChange := StateChangeConf{ Pending: []string{"open"}, Target: "active", - Refresh: SpotRequestStateRefreshFunc(ec2conn, spotRequestId), + Refresh: SpotRequestStateRefreshFunc(ec2conn, *spotRequestId), StepState: state, } _, err = WaitForState(&stateChange) if err != nil { - err := fmt.Errorf("Error waiting for spot request (%s) to become ready: %s", spotRequestId, err) + err := fmt.Errorf("Error waiting for spot request (%s) to become ready: %s", *spotRequestId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - spotResp, err := ec2conn.DescribeSpotRequests([]string{spotRequestId}, nil) + + spotResp, err := ec2conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{ + SpotInstanceRequestIDs: []*string{spotRequestId}, + }) if err != nil { - err := fmt.Errorf("Error finding spot request (%s): %s", spotRequestId, err) + err := fmt.Errorf("Error finding spot request (%s): %s", *spotRequestId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - instanceId = spotResp.SpotRequestResults[0].InstanceId + instanceId = *spotResp.SpotInstanceRequests[0].InstanceID } - instanceResp, err := ec2conn.Instances([]string{instanceId}, nil) + instanceResp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{InstanceIDs: []*string{&instanceId}}) if err != nil { err := fmt.Errorf("Error finding source instance (%s): %s", instanceId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - s.instance = &instanceResp.Reservations[0].Instances[0] - ui.Message(fmt.Sprintf("Instance ID: %s", s.instance.InstanceId)) + s.instance = instanceResp.Reservations[0].Instances[0] + ui.Message(fmt.Sprintf("Instance ID: %s", *s.instance.InstanceID)) - ui.Say(fmt.Sprintf("Waiting for instance (%s) to become ready...", s.instance.InstanceId)) + ui.Say(fmt.Sprintf("Waiting for instance (%s) to become ready...", *s.instance.InstanceID)) stateChange := StateChangeConf{ Pending: []string{"pending"}, Target: "running", @@ -214,7 +237,7 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi } latestInstance, err := WaitForState(&stateChange) if err != nil { - err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", s.instance.InstanceId, err) + err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", *s.instance.InstanceID, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -222,29 +245,32 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi s.instance = latestInstance.(*ec2.Instance) - ec2Tags := make([]ec2.Tag, 1, len(s.Tags)+1) - ec2Tags[0] = ec2.Tag{"Name", "Packer Builder"} + ec2Tags := make([]*ec2.Tag, 1, len(s.Tags)+1) + ec2Tags[0] = &ec2.Tag{Key: aws.String("Name"), Value: aws.String("Packer Builder")} for k, v := range s.Tags { - ec2Tags = append(ec2Tags, ec2.Tag{k, v}) + ec2Tags = append(ec2Tags, &ec2.Tag{Key: &k, Value: &v}) } - _, err = ec2conn.CreateTags([]string{s.instance.InstanceId}, ec2Tags) + _, err = ec2conn.CreateTags(&ec2.CreateTagsInput{ + Tags: ec2Tags, + Resources: []*string{s.instance.InstanceID}, + }) if err != nil { ui.Message( fmt.Sprintf("Failed to tag a Name on the builder instance: %s", err)) } if s.Debug { - if s.instance.DNSName != "" { - ui.Message(fmt.Sprintf("Public DNS: %s", s.instance.DNSName)) + if *s.instance.PublicDNSName != "" { + ui.Message(fmt.Sprintf("Public DNS: %s", *s.instance.PublicDNSName)) } - if s.instance.PublicIpAddress != "" { - ui.Message(fmt.Sprintf("Public IP: %s", s.instance.PublicIpAddress)) + if *s.instance.PublicIPAddress != "" { + ui.Message(fmt.Sprintf("Public IP: %s", *s.instance.PublicIPAddress)) } - if s.instance.PrivateIpAddress != "" { - ui.Message(fmt.Sprintf("Private IP: %s", s.instance.PrivateIpAddress)) + if *s.instance.PrivateIPAddress != "" { + ui.Message(fmt.Sprintf("Private IP: %s", *s.instance.PrivateIPAddress)) } } @@ -261,13 +287,16 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) { // Cancel the spot request if it exists if s.spotRequest != nil { ui.Say("Cancelling the spot request...") - if _, err := ec2conn.CancelSpotRequests([]string{s.spotRequest.SpotRequestId}); err != nil { + input := &ec2.CancelSpotInstanceRequestsInput{ + SpotInstanceRequestIDs: []*string{s.spotRequest.InstanceID}, + } + if _, err := ec2conn.CancelSpotInstanceRequests(input); err != nil { ui.Error(fmt.Sprintf("Error cancelling the spot request, may still be around: %s", err)) return } stateChange := StateChangeConf{ Pending: []string{"active", "open"}, - Refresh: SpotRequestStateRefreshFunc(ec2conn, s.spotRequest.SpotRequestId), + Refresh: SpotRequestStateRefreshFunc(ec2conn, *s.spotRequest.SpotInstanceRequestID), Target: "cancelled", } @@ -279,7 +308,7 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) { if s.instance != nil { ui.Say("Terminating the source AWS instance...") - if _, err := ec2conn.TerminateInstances([]string{s.instance.InstanceId}); err != nil { + if _, err := ec2conn.TerminateInstances(&ec2.TerminateInstancesInput{InstanceIDs: []*string{s.instance.InstanceID}}); err != nil { ui.Error(fmt.Sprintf("Error terminating instance, may still be around: %s", err)) return } diff --git a/builder/amazon/common/step_security_group.go b/builder/amazon/common/step_security_group.go index 1d95619fe..356c4a752 100644 --- a/builder/amazon/common/step_security_group.go +++ b/builder/amazon/common/step_security_group.go @@ -2,12 +2,14 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "log" + "time" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/common/uuid" "github.com/mitchellh/packer/packer" - "log" - "time" ) type StepSecurityGroup struct { @@ -36,10 +38,10 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction { ui.Say("Creating temporary security group for this instance...") groupName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID()) log.Printf("Temporary group name: %s", groupName) - group := ec2.SecurityGroup{ - Name: groupName, - Description: "Temporary group for Packer", - VpcId: s.VpcId, + group := &ec2.CreateSecurityGroupInput{ + GroupName: &groupName, + Description: aws.String("Temporary group for Packer"), + VPCID: &s.VpcId, } groupResp, err := ec2conn.CreateSecurityGroup(group) if err != nil { @@ -48,16 +50,15 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction { } // Set the group ID so we can delete it later - s.createdGroupId = groupResp.Id - - // Authorize the SSH access - perms := []ec2.IPPerm{ - ec2.IPPerm{ - Protocol: "tcp", - FromPort: s.SSHPort, - ToPort: s.SSHPort, - SourceIPs: []string{"0.0.0.0/0"}, - }, + s.createdGroupId = *groupResp.GroupID + + // Authorize the SSH access for the security group + req := &ec2.AuthorizeSecurityGroupIngressInput{ + GroupID: groupResp.GroupID, + IPProtocol: aws.String("tcp"), + FromPort: aws.Long(int64(s.SSHPort)), + ToPort: aws.Long(int64(s.SSHPort)), + CIDRIP: aws.String("0.0.0.0/0"), } // We loop and retry this a few times because sometimes the security @@ -65,7 +66,7 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction { // consistent. ui.Say("Authorizing SSH access on the temporary security group...") for i := 0; i < 5; i++ { - _, err = ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms) + _, err = ec2conn.AuthorizeSecurityGroupIngress(req) if err == nil { break } @@ -99,7 +100,7 @@ func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) { var err error for i := 0; i < 5; i++ { - _, err = ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: s.createdGroupId}) + _, err = ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupID: &s.createdGroupId}) if err == nil { break } diff --git a/builder/amazon/common/step_source_ami_info.go b/builder/amazon/common/step_source_ami_info.go index c9f72123d..b0c941fda 100644 --- a/builder/amazon/common/step_source_ami_info.go +++ b/builder/amazon/common/step_source_ami_info.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -23,7 +23,7 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Inspecting the source AMI...") - imageResp, err := ec2conn.Images([]string{s.SourceAmi}, ec2.NewFilter()) + imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIDs: []*string{&s.SourceAmi}}) if err != nil { err := fmt.Errorf("Error querying AMI: %s", err) state.Put("error", err) @@ -38,11 +38,11 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - image := &imageResp.Images[0] + image := imageResp.Images[0] // Enhanced Networking (SriovNetSupport) can only be enabled on HVM AMIs. // See http://goo.gl/icuXh5 - if s.EnhancedNetworking && image.VirtualizationType != "hvm" { + if s.EnhancedNetworking && *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()) diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 889cc7b60..619ad0ff6 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -9,7 +9,7 @@ import ( "fmt" "log" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/common" @@ -63,17 +63,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - region, err := b.config.Region() + config, err := b.config.Config() if err != nil { return nil, err } - auth, err := b.config.AccessConfig.Auth() - if err != nil { - return nil, err - } - - ec2conn := ec2.New(auth, region) + ec2conn := ec2.New(config) // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) diff --git a/builder/amazon/ebs/builder_test.go b/builder/amazon/ebs/builder_test.go index 6f777afa9..664c751fa 100644 --- a/builder/amazon/ebs/builder_test.go +++ b/builder/amazon/ebs/builder_test.go @@ -1,8 +1,9 @@ package ebs import ( - "github.com/mitchellh/packer/packer" "testing" + + "github.com/mitchellh/packer/packer" ) func testConfig() map[string]interface{} { diff --git a/builder/amazon/ebs/step_create_ami.go b/builder/amazon/ebs/step_create_ami.go index f380ea0b1..8b86f1d25 100644 --- a/builder/amazon/ebs/step_create_ami.go +++ b/builder/amazon/ebs/step_create_ami.go @@ -2,7 +2,8 @@ package ebs import ( "fmt" - "github.com/mitchellh/goamz/ec2" + + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" @@ -20,10 +21,10 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction { // Create the image ui.Say(fmt.Sprintf("Creating the AMI: %s", config.AMIName)) - createOpts := &ec2.CreateImage{ - InstanceId: instance.InstanceId, - Name: config.AMIName, - BlockDevices: config.BlockDevices.BuildAMIDevices(), + createOpts := &ec2.CreateImageInput{ + InstanceID: instance.InstanceID, + Name: &config.AMIName, + BlockDeviceMappings: config.BlockDevices.BuildAMIDevices(), } createResp, err := ec2conn.CreateImage(createOpts) @@ -35,16 +36,16 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction { } // Set the AMI ID in the state - ui.Message(fmt.Sprintf("AMI: %s", createResp.ImageId)) + ui.Message(fmt.Sprintf("AMI: %s", *createResp.ImageID)) amis := make(map[string]string) - amis[ec2conn.Region.Name] = createResp.ImageId + amis[ec2conn.Config.Region] = *createResp.ImageID state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", - Refresh: awscommon.AMIStateRefreshFunc(ec2conn, createResp.ImageId), + Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *createResp.ImageID), StepState: state, } @@ -56,14 +57,14 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - imagesResp, err := ec2conn.Images([]string{createResp.ImageId}, nil) + imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIDs: []*string{createResp.ImageID}}) if err != nil { err := fmt.Errorf("Error searching for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - s.image = &imagesResp.Images[0] + s.image = imagesResp.Images[0] return multistep.ActionContinue } @@ -83,11 +84,9 @@ func (s *stepCreateAMI) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) ui.Say("Deregistering the AMI because cancelation or error...") - if resp, err := ec2conn.DeregisterImage(s.image.Id); err != nil { + deregisterOpts := &ec2.DeregisterImageInput{ImageID: s.image.ImageID} + if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil { ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err)) return - } else if resp.Return == false { - ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %t", resp.Return)) - return } } diff --git a/builder/amazon/ebs/step_modify_instance.go b/builder/amazon/ebs/step_modify_instance.go index 21c5e7de9..9fe0b40ce 100644 --- a/builder/amazon/ebs/step_modify_instance.go +++ b/builder/amazon/ebs/step_modify_instance.go @@ -3,7 +3,7 @@ package ebs import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -19,12 +19,13 @@ func (s *stepModifyInstance) Run(state multistep.StateBag) multistep.StepAction // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 if config.AMIEnhancedNetworking { ui.Say("Enabling Enhanced Networking...") - _, err := ec2conn.ModifyInstance( - instance.InstanceId, - &ec2.ModifyInstance{SriovNetSupport: true}, - ) + simple := "simple" + _, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{ + InstanceID: instance.InstanceID, + SRIOVNetSupport: &ec2.AttributeValue{Value: &simple}, + }) if err != nil { - err := fmt.Errorf("Error enabling Enhanced Networking on %s: %s", instance.InstanceId, err) + err := fmt.Errorf("Error enabling Enhanced Networking on %s: %s", *instance.InstanceID, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt diff --git a/builder/amazon/ebs/step_stop_instance.go b/builder/amazon/ebs/step_stop_instance.go index 09c19bddb..c01de8fdc 100644 --- a/builder/amazon/ebs/step_stop_instance.go +++ b/builder/amazon/ebs/step_stop_instance.go @@ -2,7 +2,8 @@ package ebs import ( "fmt" - "github.com/mitchellh/goamz/ec2" + + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" @@ -24,7 +25,9 @@ func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction { // Stop the instance so we can create an AMI from it ui.Say("Stopping the source instance...") - _, err := ec2conn.StopInstances(instance.InstanceId) + _, err := ec2conn.StopInstances(&ec2.StopInstancesInput{ + InstanceIDs: []*string{instance.InstanceID}, + }) if err != nil { err := fmt.Errorf("Error stopping instance: %s", err) state.Put("error", err) diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 63b8442ac..69806c3db 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -9,7 +9,7 @@ import ( "os" "strings" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/common" @@ -168,17 +168,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - region, err := b.config.Region() + config, err := b.config.Config() if err != nil { return nil, err } - auth, err := b.config.AccessConfig.Auth() - if err != nil { - return nil, err - } - - ec2conn := ec2.New(auth, region) + ec2conn := ec2.New(config) // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) diff --git a/builder/amazon/instance/step_bundle_volume.go b/builder/amazon/instance/step_bundle_volume.go index 736e1adb2..362f949f0 100644 --- a/builder/amazon/instance/step_bundle_volume.go +++ b/builder/amazon/instance/step_bundle_volume.go @@ -3,7 +3,7 @@ package instance import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -34,7 +34,7 @@ func (s *StepBundleVolume) Run(state multistep.StateBag) multistep.StepAction { var err error config.BundleVolCommand, err = config.tpl.Process(config.BundleVolCommand, bundleCmdData{ AccountId: config.AccountId, - Architecture: instance.Architecture, + Architecture: *instance.Architecture, CertPath: x509RemoteCertPath, Destination: config.BundleDestination, KeyPath: x509RemoteKeyPath, diff --git a/builder/amazon/instance/step_register_ami.go b/builder/amazon/instance/step_register_ami.go index 07040f417..349c7f856 100644 --- a/builder/amazon/instance/step_register_ami.go +++ b/builder/amazon/instance/step_register_ami.go @@ -3,7 +3,7 @@ package instance import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" awscommon "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" @@ -18,16 +18,17 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") - registerOpts := &ec2.RegisterImage{ - ImageLocation: manifestPath, - Name: config.AMIName, - BlockDevices: config.BlockDevices.BuildAMIDevices(), - VirtType: config.AMIVirtType, + registerOpts := &ec2.RegisterImageInput{ + ImageLocation: &manifestPath, + Name: &config.AMIName, + BlockDeviceMappings: config.BlockDevices.BuildAMIDevices(), + VirtualizationType: &config.AMIVirtType, } // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 if config.AMIEnhancedNetworking { - registerOpts.SriovNetSupport = "simple" + simple := "simple" + registerOpts.SRIOVNetSupport = &simple } registerResp, err := ec2conn.RegisterImage(registerOpts) @@ -38,16 +39,16 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { } // Set the AMI ID in the state - ui.Say(fmt.Sprintf("AMI: %s", registerResp.ImageId)) + ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageID)) amis := make(map[string]string) - amis[ec2conn.Region.Name] = registerResp.ImageId + amis[ec2conn.Config.Region] = *registerResp.ImageID state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", - Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId), + Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *registerResp.ImageID), StepState: state, } diff --git a/builder/amazon/instance/step_upload_bundle.go b/builder/amazon/instance/step_upload_bundle.go index dbf6a0c29..4b146498c 100644 --- a/builder/amazon/instance/step_upload_bundle.go +++ b/builder/amazon/instance/step_upload_bundle.go @@ -40,7 +40,7 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction { BucketName: config.S3Bucket, BundleDirectory: config.BundleDestination, ManifestPath: manifestPath, - Region: region.Name, + Region: region, SecretKey: config.SecretKey, }) if err != nil { From 33b4f5cc0a577ee8ac351e855e2713298f10c054 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 15 Apr 2015 11:49:29 -0700 Subject: [PATCH 2/5] Check for EBS being nil before assigning it --- builder/amazon/chroot/step_register_ami.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/builder/amazon/chroot/step_register_ami.go b/builder/amazon/chroot/step_register_ami.go index 88da65e9f..25b87592a 100644 --- a/builder/amazon/chroot/step_register_ami.go +++ b/builder/amazon/chroot/step_register_ami.go @@ -25,7 +25,11 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { for i, device := range image.BlockDeviceMappings { newDevice := device if newDevice.DeviceName == image.RootDeviceName { - newDevice.EBS.SnapshotID = &snapshotId + if newDevice.EBS != nil { + newDevice.EBS.SnapshotID = &snapshotId + } else { + newDevice.EBS = &ec2.EBSBlockDevice{SnapshotID: &snapshotId} + } } blockDevices[i] = newDevice From 65a9347b1ead3084596a076f9a3de4e6dc44f49b Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 15 Apr 2015 12:13:06 -0700 Subject: [PATCH 3/5] Fix potential nil pointer errors in ported code This commit adds extra nil checks for some pointers which were not necessary when using goamz --- builder/amazon/common/ssh.go | 6 +++--- builder/amazon/common/step_run_source_instance.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/amazon/common/ssh.go b/builder/amazon/common/ssh.go index 965a26994..25c71a243 100644 --- a/builder/amazon/common/ssh.go +++ b/builder/amazon/common/ssh.go @@ -17,13 +17,13 @@ func SSHAddress(e *ec2.EC2, port int, private bool) func(multistep.StateBag) (st for j := 0; j < 2; j++ { var host string i := state.Get("instance").(*ec2.Instance) - if *i.VPCID != "" { - if *i.PublicIPAddress != "" && !private { + if i.VPCID != nil && *i.VPCID != "" { + if i.PublicIPAddress != nil && *i.PublicIPAddress != "" && !private { host = *i.PublicIPAddress } else { host = *i.PrivateIPAddress } - } else if *i.PublicDNSName != "" { + } else if i.PublicDNSName != nil && *i.PublicDNSName != "" { host = *i.PublicDNSName } diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 644734619..702267c97 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -261,15 +261,15 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi } if s.Debug { - if *s.instance.PublicDNSName != "" { + if s.instance.PublicDNSName != nil && *s.instance.PublicDNSName != "" { ui.Message(fmt.Sprintf("Public DNS: %s", *s.instance.PublicDNSName)) } - if *s.instance.PublicIPAddress != "" { + if s.instance.PublicIPAddress != nil && *s.instance.PublicIPAddress != "" { ui.Message(fmt.Sprintf("Public IP: %s", *s.instance.PublicIPAddress)) } - if *s.instance.PrivateIPAddress != "" { + if s.instance.PrivateIPAddress != nil && *s.instance.PrivateIPAddress != "" { ui.Message(fmt.Sprintf("Private IP: %s", *s.instance.PrivateIPAddress)) } } From 6a3bf9d87e563b6960586427d7cfd0d7cb0baa17 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 15 Apr 2015 12:18:33 -0700 Subject: [PATCH 4/5] Remove Go 1.2 from the Travis build --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b0de812ea..d67d52bfa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false language: go go: - - 1.2 - 1.3 - 1.4 - tip From b78b119a11a67736afd5c301eee7fcbc0e96929a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 28 May 2015 08:31:22 -0700 Subject: [PATCH 5/5] amazon/*: fix merge issues with lib switch --- builder/amazon/common/access_config.go | 24 ++++++++++--------- builder/amazon/common/block_device.go | 2 -- builder/amazon/common/step_ami_region_copy.go | 18 +++++++------- builder/amazon/ebs/builder.go | 3 ++- builder/amazon/instance/builder.go | 3 ++- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index a5403a3fd..16d5041f3 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -8,6 +8,7 @@ import ( "unicode" "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/aws/credentials" "github.com/mitchellh/packer/template/interpolate" ) @@ -22,16 +23,16 @@ type AccessConfig struct { // Config returns a valid aws.Config object for access to AWS services, or // an error if the authentication and region couldn't be resolved func (c *AccessConfig) Config() (*aws.Config, error) { - credsProvider := aws.DetectCreds(c.AccessKey, c.SecretKey, c.Token) - - creds, err := credsProvider.Credentials() - if err != nil { - return nil, err - } - - c.AccessKey = creds.AccessKeyID - c.SecretKey = creds.SecretAccessKey - c.Token = creds.SessionToken + creds := credentials.NewChainCredentials([]credentials.Provider{ + &credentials.StaticProvider{Value: credentials.Value{ + AccessKeyID: c.AccessKey, + SecretAccessKey: c.SecretKey, + SessionToken: c.Token, + }}, + &credentials.EnvProvider{}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, + &credentials.EC2RoleProvider{}, + }) region, err := c.Region() if err != nil { @@ -40,7 +41,8 @@ func (c *AccessConfig) Config() (*aws.Config, error) { return &aws.Config{ Region: region, - Credentials: credsProvider, + Credentials: creds, + MaxRetries: 11, }, nil } diff --git a/builder/amazon/common/block_device.go b/builder/amazon/common/block_device.go index f86feff1c..9bd8344a3 100644 --- a/builder/amazon/common/block_device.go +++ b/builder/amazon/common/block_device.go @@ -1,8 +1,6 @@ package common import ( - "fmt" - "github.com/awslabs/aws-sdk-go/aws" "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/packer/template/interpolate" diff --git a/builder/amazon/common/step_ami_region_copy.go b/builder/amazon/common/step_ami_region_copy.go index 88ed1884f..33d67c655 100644 --- a/builder/amazon/common/step_ami_region_copy.go +++ b/builder/amazon/common/step_ami_region_copy.go @@ -5,7 +5,6 @@ import ( "sync" - "github.com/awslabs/aws-sdk-go/aws" "github.com/awslabs/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" @@ -13,7 +12,8 @@ import ( ) type StepAMIRegionCopy struct { - Regions []string + AccessConfig *AccessConfig + Regions []string } func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { @@ -37,7 +37,7 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { go func(region string) { defer wg.Done() - id, err := amiRegionCopy(state, ec2conn.Config.Credentials, ami, region, ec2conn.Config.Region) + id, err := amiRegionCopy(state, s.AccessConfig, ami, region, ec2conn.Config.Region) lock.Lock() defer lock.Unlock() @@ -69,15 +69,17 @@ func (s *StepAMIRegionCopy) Cleanup(state multistep.StateBag) { // amiRegionCopy does a copy for the given AMI to the target region and // returns the resulting ID or error. -func amiRegionCopy(state multistep.StateBag, auth aws.CredentialsProvider, imageId string, +func amiRegionCopy(state multistep.StateBag, config *AccessConfig, imageId string, target string, source string) (string, error) { // Connect to the region where the AMI will be copied to - config := &aws.Config{ - Credentials: auth, - Region: target, + awsConfig, err := config.Config() + if err != nil { + return "", err } - regionconn := ec2.New(config) + awsConfig.Region = target + + regionconn := ec2.New(awsConfig) resp, err := regionconn.CopyImage(&ec2.CopyImageInput{ SourceRegion: &source, SourceImageID: &imageId, diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index a2d393bd5..e4f8eb0b2 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -121,7 +121,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &stepModifyInstance{}, &stepCreateAMI{}, &awscommon.StepAMIRegionCopy{ - Regions: b.config.AMIRegions, + AccessConfig: &b.config.AccessConfig, + Regions: b.config.AMIRegions, }, &awscommon.StepModifyAMIAttributes{ Description: b.config.AMIDescription, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 66351aab0..bffd1a97a 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -203,7 +203,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &StepRegisterAMI{}, &awscommon.StepAMIRegionCopy{ - Regions: b.config.AMIRegions, + AccessConfig: &b.config.AccessConfig, + Regions: b.config.AMIRegions, }, &awscommon.StepModifyAMIAttributes{ Description: b.config.AMIDescription,