github.com/angdraug/packer@v1.3.2/builder/amazon/ebssurrogate/step_register_ami.go (about)

     1  package ebssurrogate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/aws/aws-sdk-go/aws"
     8  	"github.com/aws/aws-sdk-go/service/ec2"
     9  	awscommon "github.com/hashicorp/packer/builder/amazon/common"
    10  	"github.com/hashicorp/packer/helper/multistep"
    11  	"github.com/hashicorp/packer/packer"
    12  )
    13  
    14  // StepRegisterAMI creates the AMI.
    15  type StepRegisterAMI struct {
    16  	RootDevice               RootBlockDevice
    17  	AMIDevices               []*ec2.BlockDeviceMapping
    18  	LaunchDevices            []*ec2.BlockDeviceMapping
    19  	EnableAMIENASupport      *bool
    20  	EnableAMISriovNetSupport bool
    21  	image                    *ec2.Image
    22  }
    23  
    24  func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
    25  	config := state.Get("config").(*Config)
    26  	ec2conn := state.Get("ec2").(*ec2.EC2)
    27  	snapshotIds := state.Get("snapshot_ids").(map[string]string)
    28  	ui := state.Get("ui").(packer.Ui)
    29  
    30  	ui.Say("Registering the AMI...")
    31  
    32  	blockDevices := s.combineDevices(snapshotIds)
    33  
    34  	registerOpts := &ec2.RegisterImageInput{
    35  		Name:                &config.AMIName,
    36  		Architecture:        aws.String(ec2.ArchitectureValuesX8664),
    37  		RootDeviceName:      aws.String(s.RootDevice.DeviceName),
    38  		VirtualizationType:  aws.String(config.AMIVirtType),
    39  		BlockDeviceMappings: blockDevices,
    40  	}
    41  
    42  	if s.EnableAMISriovNetSupport {
    43  		// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
    44  		// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
    45  		registerOpts.SriovNetSupport = aws.String("simple")
    46  	}
    47  	if s.EnableAMIENASupport != nil && *s.EnableAMIENASupport {
    48  		// Set EnaSupport to true
    49  		// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
    50  		registerOpts.EnaSupport = aws.Bool(true)
    51  	}
    52  	registerResp, err := ec2conn.RegisterImage(registerOpts)
    53  	if err != nil {
    54  		state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
    55  		ui.Error(state.Get("error").(error).Error())
    56  		return multistep.ActionHalt
    57  	}
    58  
    59  	// Set the AMI ID in the state
    60  	ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageId))
    61  	amis := make(map[string]string)
    62  	amis[*ec2conn.Config.Region] = *registerResp.ImageId
    63  	state.Put("amis", amis)
    64  
    65  	// Wait for the image to become ready
    66  	ui.Say("Waiting for AMI to become ready...")
    67  	if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil {
    68  		err := fmt.Errorf("Error waiting for AMI: %s", err)
    69  		state.Put("error", err)
    70  		ui.Error(err.Error())
    71  		return multistep.ActionHalt
    72  	}
    73  
    74  	imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{registerResp.ImageId}})
    75  	if err != nil {
    76  		err := fmt.Errorf("Error searching for AMI: %s", err)
    77  		state.Put("error", err)
    78  		ui.Error(err.Error())
    79  		return multistep.ActionHalt
    80  	}
    81  	s.image = imagesResp.Images[0]
    82  
    83  	snapshots := make(map[string][]string)
    84  	for _, blockDeviceMapping := range imagesResp.Images[0].BlockDeviceMappings {
    85  		if blockDeviceMapping.Ebs != nil && blockDeviceMapping.Ebs.SnapshotId != nil {
    86  
    87  			snapshots[*ec2conn.Config.Region] = append(snapshots[*ec2conn.Config.Region], *blockDeviceMapping.Ebs.SnapshotId)
    88  		}
    89  	}
    90  	state.Put("snapshots", snapshots)
    91  
    92  	return multistep.ActionContinue
    93  }
    94  
    95  func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {
    96  	if s.image == nil {
    97  		return
    98  	}
    99  
   100  	_, cancelled := state.GetOk(multistep.StateCancelled)
   101  	_, halted := state.GetOk(multistep.StateHalted)
   102  	if !cancelled && !halted {
   103  		return
   104  	}
   105  
   106  	ec2conn := state.Get("ec2").(*ec2.EC2)
   107  	ui := state.Get("ui").(packer.Ui)
   108  
   109  	ui.Say("Deregistering the AMI because cancellation or error...")
   110  	deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
   111  	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
   112  		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
   113  		return
   114  	}
   115  }
   116  
   117  func (s *StepRegisterAMI) combineDevices(snapshotIds map[string]string) []*ec2.BlockDeviceMapping {
   118  	devices := map[string]*ec2.BlockDeviceMapping{}
   119  
   120  	for _, device := range s.AMIDevices {
   121  		devices[*device.DeviceName] = device
   122  	}
   123  
   124  	// Devices in launch_block_device_mappings override any with
   125  	// the same name in ami_block_device_mappings, except for the
   126  	// one designated as the root device in ami_root_device
   127  	for _, device := range s.LaunchDevices {
   128  		snapshotId, ok := snapshotIds[*device.DeviceName]
   129  		if ok {
   130  			device.Ebs.SnapshotId = aws.String(snapshotId)
   131  			// Block devices with snapshot inherit
   132  			// encryption settings from the snapshot
   133  			device.Ebs.Encrypted = nil
   134  			device.Ebs.KmsKeyId = nil
   135  		}
   136  		if *device.DeviceName == s.RootDevice.SourceDeviceName {
   137  			device.DeviceName = aws.String(s.RootDevice.DeviceName)
   138  		}
   139  		devices[*device.DeviceName] = device
   140  	}
   141  
   142  	blockDevices := []*ec2.BlockDeviceMapping{}
   143  	for _, device := range devices {
   144  		blockDevices = append(blockDevices, device)
   145  	}
   146  	return blockDevices
   147  }