Files
Packer-Cn/builder/amazon/common/step_run_source_instance.go
T

311 lines
9.0 KiB
Go
Raw Normal View History

package common
import (
2018-01-17 22:49:03 -08:00
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
2015-06-03 17:13:52 -04:00
"github.com/aws/aws-sdk-go/aws"
2018-02-02 20:16:23 -08:00
"github.com/aws/aws-sdk-go/aws/awserr"
2015-06-03 17:13:52 -04:00
"github.com/aws/aws-sdk-go/service/ec2"
2018-02-02 20:16:23 -08:00
retry "github.com/hashicorp/packer/common"
2018-08-29 11:23:59 +02:00
"github.com/hashicorp/packer/helper/communicator"
2018-01-19 16:18:44 -08:00
"github.com/hashicorp/packer/helper/multistep"
2017-04-04 13:39:01 -07:00
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
type StepRunSourceInstance struct {
2016-05-20 19:54:45 +10:00
AssociatePublicIpAddress bool
AvailabilityZone string
BlockDevices BlockDevices
2018-08-29 11:23:59 +02:00
Comm *communicator.Config
2018-02-02 20:16:23 -08:00
Ctx interpolate.Context
2016-05-20 19:54:45 +10:00
Debug bool
EbsOptimized bool
EnableT2Unlimited bool
2016-05-20 19:54:45 +10:00
ExpectedRootDevice string
IamInstanceProfile string
InstanceInitiatedShutdownBehavior string
InstanceType string
2018-02-02 20:16:23 -08:00
IsRestricted bool
2016-05-20 19:54:45 +10:00
SourceAMI string
SubnetId string
2018-02-02 20:16:23 -08:00
Tags TagMap
2016-05-20 19:54:45 +10:00
UserData string
UserDataFile string
2018-02-02 20:16:23 -08:00
VolumeTags TagMap
instanceId string
}
2018-04-23 12:57:04 -07:00
func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
2013-08-31 12:58:55 -07:00
ec2conn := state.Get("ec2").(*ec2.EC2)
2018-08-29 11:23:59 +02:00
securityGroupIds := aws.StringSlice(state.Get("securityGroupIds").([]string))
2013-08-31 12:58:55 -07:00
ui := state.Get("ui").(packer.Ui)
userData := s.UserData
if s.UserDataFile != "" {
contents, err := ioutil.ReadFile(s.UserDataFile)
if err != nil {
2013-08-31 12:58:55 -07:00
state.Put("error", fmt.Errorf("Problem reading user data file: %s", err))
return multistep.ActionHalt
}
userData = string(contents)
}
// Test if it is encoded already, and if not, encode it
if _, err := base64.StdEncoding.DecodeString(userData); err != nil {
log.Printf("[DEBUG] base64 encoding user data...")
userData = base64.StdEncoding.EncodeToString([]byte(userData))
}
ui.Say("Launching a source AWS instance...")
2016-08-20 18:58:36 +00:00
image, ok := state.Get("source_image").(*ec2.Image)
if !ok {
state.Put("error", fmt.Errorf("source_image type assertion failed"))
2013-07-12 07:01:23 +09:00
return multistep.ActionHalt
}
2016-08-20 18:58:36 +00:00
s.SourceAMI = *image.ImageId
2013-07-31 15:29:03 -07:00
2016-08-20 18:58:36 +00:00
if s.ExpectedRootDevice != "" && *image.RootDeviceType != s.ExpectedRootDevice {
2013-08-31 12:58:55 -07:00
state.Put("error", fmt.Errorf(
"The provided source AMI has an invalid root device type.\n"+
"Expected '%s', got '%s'.",
2016-08-21 00:07:58 +00:00
s.ExpectedRootDevice, *image.RootDeviceType))
2013-07-12 07:01:23 +09:00
return multistep.ActionHalt
}
var instanceId string
2017-08-11 12:31:05 -07:00
ui.Say("Adding tags to source instance")
if _, exists := s.Tags["Name"]; !exists {
s.Tags["Name"] = "Packer Builder"
}
2018-03-30 10:47:11 +02:00
ec2Tags, err := s.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
if err != nil {
err := fmt.Errorf("Error tagging source instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
2017-08-02 09:29:47 -07:00
2018-03-30 10:47:11 +02:00
volTags, err := s.VolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
2017-10-03 00:05:40 +02:00
if err != nil {
err := fmt.Errorf("Error tagging volumes: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
runOpts := &ec2.RunInstancesInput{
ImageId: &s.SourceAMI,
InstanceType: &s.InstanceType,
UserData: &userData,
MaxCount: aws.Int64(1),
MinCount: aws.Int64(1),
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: &s.IamInstanceProfile},
BlockDeviceMappings: s.BlockDevices.BuildLaunchDevices(),
Placement: &ec2.Placement{AvailabilityZone: &s.AvailabilityZone},
EbsOptimized: &s.EbsOptimized,
}
if s.EnableT2Unlimited {
creditOption := "unlimited"
runOpts.CreditSpecification = &ec2.CreditSpecificationRequest{CpuCredits: &creditOption}
}
2018-02-02 20:16:23 -08:00
// Collect tags for tagging on resource creation
var tagSpecs []*ec2.TagSpecification
if len(ec2Tags) > 0 {
runTags := &ec2.TagSpecification{
ResourceType: aws.String("instance"),
Tags: ec2Tags,
}
tagSpecs = append(tagSpecs, runTags)
}
if len(volTags) > 0 {
runVolTags := &ec2.TagSpecification{
ResourceType: aws.String("volume"),
Tags: volTags,
}
tagSpecs = append(tagSpecs, runVolTags)
}
2018-02-02 20:16:23 -08:00
// If our region supports it, set tag specifications
if len(tagSpecs) > 0 && !s.IsRestricted {
runOpts.SetTagSpecifications(tagSpecs)
2018-02-02 20:16:23 -08:00
ec2Tags.Report(ui)
volTags.Report(ui)
}
2018-08-29 11:23:59 +02:00
if s.Comm.SSHKeyPairName != "" {
runOpts.KeyName = &s.Comm.SSHKeyPairName
}
if s.SubnetId != "" && s.AssociatePublicIpAddress {
runOpts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
{
DeviceIndex: aws.Int64(0),
AssociatePublicIpAddress: &s.AssociatePublicIpAddress,
SubnetId: &s.SubnetId,
Groups: securityGroupIds,
DeleteOnTermination: aws.Bool(true),
},
}
} else {
runOpts.SubnetId = &s.SubnetId
runOpts.SecurityGroupIds = securityGroupIds
}
if s.ExpectedRootDevice == "ebs" {
runOpts.InstanceInitiatedShutdownBehavior = &s.InstanceInitiatedShutdownBehavior
}
runResp, err := ec2conn.RunInstances(runOpts)
if err != nil {
err := fmt.Errorf("Error launching source instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
instanceId = *runResp.Instances[0].InstanceId
// Set the instance ID so that the cleanup works properly
s.instanceId = instanceId
ui.Message(fmt.Sprintf("Instance ID: %s", instanceId))
ui.Say(fmt.Sprintf("Waiting for instance (%v) to become ready...", instanceId))
2018-01-08 14:26:40 -08:00
describeInstance := &ec2.DescribeInstancesInput{
InstanceIds: []*string{aws.String(instanceId)},
}
2018-04-23 12:57:04 -07:00
if err := ec2conn.WaitUntilInstanceRunningWithContext(ctx, describeInstance); err != nil {
err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err)
2013-08-31 12:58:55 -07:00
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
2018-01-08 14:26:40 -08:00
r, err := ec2conn.DescribeInstances(describeInstance)
if err != nil || len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 {
err := fmt.Errorf("Error finding source instance.")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
instance := r.Reservations[0].Instances[0]
if s.Debug {
if instance.PublicDnsName != nil && *instance.PublicDnsName != "" {
ui.Message(fmt.Sprintf("Public DNS: %s", *instance.PublicDnsName))
}
if instance.PublicIpAddress != nil && *instance.PublicIpAddress != "" {
ui.Message(fmt.Sprintf("Public IP: %s", *instance.PublicIpAddress))
2013-11-26 15:03:45 +10:00
}
if instance.PrivateIpAddress != nil && *instance.PrivateIpAddress != "" {
ui.Message(fmt.Sprintf("Private IP: %s", *instance.PrivateIpAddress))
}
}
state.Put("instance", instance)
2018-02-02 20:16:23 -08:00
// If we're in a region that doesn't support tagging on instance creation,
// do that now.
if s.IsRestricted {
ec2Tags.Report(ui)
// Retry creating tags for about 2.5 minutes
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
Tags: ec2Tags,
Resources: []*string{instance.InstanceId},
})
if err == nil {
return true, nil
}
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "InvalidInstanceID.NotFound" {
return false, nil
}
}
return true, err
})
if err != nil {
err := fmt.Errorf("Error tagging source instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Now tag volumes
volumeIds := make([]*string, 0)
for _, v := range instance.BlockDeviceMappings {
if ebs := v.Ebs; ebs != nil {
volumeIds = append(volumeIds, ebs.VolumeId)
}
}
if len(volumeIds) > 0 && s.VolumeTags.IsSet() {
ui.Say("Adding tags to source EBS Volumes")
2018-03-30 10:47:11 +02:00
volumeTags, err := s.VolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
2018-02-02 20:16:23 -08:00
if err != nil {
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
volumeTags.Report(ui)
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
Resources: volumeIds,
Tags: volumeTags,
})
if err != nil {
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}
2013-08-31 12:58:55 -07:00
func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
2013-08-31 12:58:55 -07:00
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
2014-05-11 00:51:35 +08:00
// Terminate the source instance if it exists
if s.instanceId != "" {
2014-05-11 00:51:35 +08:00
ui.Say("Terminating the source AWS instance...")
if _, err := ec2conn.TerminateInstances(&ec2.TerminateInstancesInput{InstanceIds: []*string{&s.instanceId}}); err != nil {
2014-05-11 00:51:35 +08:00
ui.Error(fmt.Sprintf("Error terminating instance, may still be around: %s", err))
return
}
if err := WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil {
2017-03-28 20:35:22 -07:00
ui.Error(err.Error())
}
2014-05-11 00:51:35 +08:00
}
}