github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/amazon/common/step_source_ami_info.go (about)

     1  package common
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"sort"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/service/ec2"
    11  	"github.com/hashicorp/packer/helper/multistep"
    12  	"github.com/hashicorp/packer/packer"
    13  )
    14  
    15  // StepSourceAMIInfo extracts critical information from the source AMI
    16  // that is used throughout the AMI creation process.
    17  //
    18  // Produces:
    19  //   source_image *ec2.Image - the source AMI info
    20  type StepSourceAMIInfo struct {
    21  	SourceAmi                string
    22  	EnableAMISriovNetSupport bool
    23  	EnableAMIENASupport      bool
    24  	AmiFilters               AmiFilterOptions
    25  }
    26  
    27  // Build a slice of AMI filter options from the filters provided.
    28  func buildAmiFilters(input map[*string]*string) []*ec2.Filter {
    29  	var filters []*ec2.Filter
    30  	for k, v := range input {
    31  		filters = append(filters, &ec2.Filter{
    32  			Name:   k,
    33  			Values: []*string{v},
    34  		})
    35  	}
    36  	return filters
    37  }
    38  
    39  type imageSort []*ec2.Image
    40  
    41  func (a imageSort) Len() int      { return len(a) }
    42  func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
    43  func (a imageSort) Less(i, j int) bool {
    44  	itime, _ := time.Parse(time.RFC3339, *a[i].CreationDate)
    45  	jtime, _ := time.Parse(time.RFC3339, *a[j].CreationDate)
    46  	return itime.Unix() < jtime.Unix()
    47  }
    48  
    49  // Returns the most recent AMI out of a slice of images.
    50  func mostRecentAmi(images []*ec2.Image) *ec2.Image {
    51  	sortedImages := images
    52  	sort.Sort(imageSort(sortedImages))
    53  	return sortedImages[len(sortedImages)-1]
    54  }
    55  
    56  func (s *StepSourceAMIInfo) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    57  	ec2conn := state.Get("ec2").(*ec2.EC2)
    58  	ui := state.Get("ui").(packer.Ui)
    59  
    60  	params := &ec2.DescribeImagesInput{}
    61  
    62  	if s.SourceAmi != "" {
    63  		params.ImageIds = []*string{&s.SourceAmi}
    64  	}
    65  
    66  	// We have filters to apply
    67  	if len(s.AmiFilters.Filters) > 0 {
    68  		params.Filters = buildAmiFilters(s.AmiFilters.Filters)
    69  	}
    70  	if len(s.AmiFilters.Owners) > 0 {
    71  		params.Owners = s.AmiFilters.Owners
    72  	}
    73  
    74  	log.Printf("Using AMI Filters %v", params)
    75  	imageResp, err := ec2conn.DescribeImages(params)
    76  	if err != nil {
    77  		err := fmt.Errorf("Error querying AMI: %s", err)
    78  		state.Put("error", err)
    79  		ui.Error(err.Error())
    80  		return multistep.ActionHalt
    81  	}
    82  
    83  	if len(imageResp.Images) == 0 {
    84  		err := fmt.Errorf("No AMI was found matching filters: %v", params)
    85  		state.Put("error", err)
    86  		ui.Error(err.Error())
    87  		return multistep.ActionHalt
    88  	}
    89  
    90  	if len(imageResp.Images) > 1 && !s.AmiFilters.MostRecent {
    91  		err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
    92  		state.Put("error", err)
    93  		ui.Error(err.Error())
    94  		return multistep.ActionHalt
    95  	}
    96  
    97  	var image *ec2.Image
    98  	if s.AmiFilters.MostRecent {
    99  		image = mostRecentAmi(imageResp.Images)
   100  	} else {
   101  		image = imageResp.Images[0]
   102  	}
   103  
   104  	ui.Message(fmt.Sprintf("Found Image ID: %s", *image.ImageId))
   105  
   106  	// Enhanced Networking can only be enabled on HVM AMIs.
   107  	// See http://goo.gl/icuXh5
   108  	if (s.EnableAMIENASupport || s.EnableAMISriovNetSupport) && *image.VirtualizationType != "hvm" {
   109  		err := fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
   110  		state.Put("error", err)
   111  		ui.Error(err.Error())
   112  		return multistep.ActionHalt
   113  	}
   114  
   115  	state.Put("source_image", image)
   116  	return multistep.ActionContinue
   117  }
   118  
   119  func (s *StepSourceAMIInfo) Cleanup(multistep.StateBag) {}