From ec1abb94487a733700983f676e69072c4b31c0c1 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 20 Aug 2016 18:58:36 +0000 Subject: [PATCH] [dynamic-source-ami] proof of concept --- builder/amazon/common/run_config.go | 13 ++++++- .../amazon/common/step_run_source_instance.go | 38 ++++++++----------- builder/amazon/common/step_source_ami_info.go | 28 +++++++++++++- builder/amazon/ebs/builder.go | 1 + 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 729ebce13..c44c5db05 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -14,6 +14,16 @@ import ( var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$") +type DynamicAmiOptions struct { + Filters map[*string]*string + Owners []*string + Most_recent bool +} + +func (d *DynamicAmiOptions) Validate() bool { + return true +} + // RunConfig contains configuration for running an instance from a source // AMI and details on how to access that launched image. type RunConfig struct { @@ -24,6 +34,7 @@ type RunConfig struct { InstanceType string `mapstructure:"instance_type"` RunTags map[string]string `mapstructure:"run_tags"` SourceAmi string `mapstructure:"source_ami"` + DynamicSourceAmi DynamicAmiOptions `mapstructure:"dynamic_source_ami"` SpotPrice string `mapstructure:"spot_price"` SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"` DisableStopInstance bool `mapstructure:"disable_stop_instance"` @@ -57,7 +68,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) if c.SourceAmi == "" { - errs = append(errs, errors.New("A source_ami must be specified")) + errs = append(errs, errors.New("A source_ami or dynamic_source_ami must be specified")) } if c.InstanceType == "" { diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 6eeb59604..34146081d 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -84,24 +84,18 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi } ui.Say("Launching a source AWS instance...") - 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)) + image, ok := state.Get("source_image").(*ec2.Image) + if !ok { + state.Put("error", fmt.Errorf("source_image type assertion failed")) return multistep.ActionHalt } + s.SourceAMI = *image.ImageId - if len(imageResp.Images) != 1 { - state.Put("error", fmt.Errorf("The source AMI '%s' could not be found.", s.SourceAMI)) - return multistep.ActionHalt - } - - if s.ExpectedRootDevice != "" && *imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice { + if s.ExpectedRootDevice != "" && *image.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, image.RootDeviceType)) return multistep.ActionHalt } @@ -156,16 +150,16 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi if spotPrice == "" || spotPrice == "0" { runOpts := &ec2.RunInstancesInput{ - KeyName: &keyName, - 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, + KeyName: &keyName, + 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.SubnetId != "" && s.AssociatePublicIpAddress { diff --git a/builder/amazon/common/step_source_ami_info.go b/builder/amazon/common/step_source_ami_info.go index 479faed66..a349f3bd9 100644 --- a/builder/amazon/common/step_source_ami_info.go +++ b/builder/amazon/common/step_source_ami_info.go @@ -16,14 +16,37 @@ import ( type StepSourceAMIInfo struct { SourceAmi string EnhancedNetworking bool + AmiFilters DynamicAmiOptions +} + +// Build a slice of AMI filter options from the filters provided. +func buildAmiFilters(input map[*string]*string) []*ec2.Filter { + var filters []*ec2.Filter + for k, v := range input { + /*m := v.(map[string]interface{}) + var filterValues []*string + for _, e := range m["values"].([]interface{}) { + filterValues = append(filterValues, aws.String(e.(string))) + }*/ + filters = append(filters, &ec2.Filter{ + Name: k, + Values: []*string{v}, + }) + } + return filters } func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) - ui.Say(fmt.Sprintf("Inspecting the source AMI (%s)...", s.SourceAmi)) - imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{&s.SourceAmi}}) + params := &ec2.DescribeImagesInput{} + params.Filters = buildAmiFilters(s.AmiFilters.Filters) + params.Owners = s.AmiFilters.Owners + ui.Say(fmt.Sprintf("Using AMI Filters %v", params)) + imageResp, err := ec2conn.DescribeImages(params) + //ui.Say(fmt.Sprintf("Inspecting the source AMI (%s)...", s.SourceAmi)) + //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) @@ -39,6 +62,7 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction { } image := imageResp.Images[0] + ui.Say(fmt.Sprintf("Got Image %v", image)) // Enhanced Networking (SriovNetSupport) can only be enabled on HVM AMIs. // See http://goo.gl/icuXh5 diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 638efa5a3..4566679ec 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -100,6 +100,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &awscommon.StepSourceAMIInfo{ SourceAmi: b.config.SourceAmi, EnhancedNetworking: b.config.AMIEnhancedNetworking, + AmiFilters: b.config.DynamicSourceAmi, }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug,