From ec1abb94487a733700983f676e69072c4b31c0c1 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 20 Aug 2016 18:58:36 +0000 Subject: [PATCH 01/13] [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, From 0e248f1516f477b6c9b90be4e52b629ae180d10b Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 20 Aug 2016 23:08:45 +0000 Subject: [PATCH 02/13] [dynamic-source-ami] make most_recent work --- builder/amazon/common/run_config.go | 12 ++-- builder/amazon/common/step_source_ami_info.go | 60 +++++++++++++++---- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index c44c5db05..b3fc15a94 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -15,13 +15,13 @@ import ( var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$") type DynamicAmiOptions struct { - Filters map[*string]*string - Owners []*string - Most_recent bool + Filters map[*string]*string + Owners []*string + MostRecent bool `mapstructure:"most_recent"` } -func (d *DynamicAmiOptions) Validate() bool { - return true +func (d *DynamicAmiOptions) Empty() bool { + return len(d.Owners) == 0 && len(d.Filters) == 0 } // RunConfig contains configuration for running an instance from a source @@ -67,7 +67,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) - if c.SourceAmi == "" { + if c.SourceAmi == "" && c.DynamicSourceAmi.Empty() { errs = append(errs, errors.New("A source_ami or dynamic_source_ami must be specified")) } diff --git a/builder/amazon/common/step_source_ami_info.go b/builder/amazon/common/step_source_ami_info.go index a349f3bd9..6946650c1 100644 --- a/builder/amazon/common/step_source_ami_info.go +++ b/builder/amazon/common/step_source_ami_info.go @@ -2,6 +2,9 @@ package common import ( "fmt" + "log" + "sort" + "time" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" @@ -23,11 +26,6 @@ type StepSourceAMIInfo struct { 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}, @@ -36,17 +34,40 @@ func buildAmiFilters(input map[*string]*string) []*ec2.Filter { return filters } +type imageSort []*ec2.Image + +func (a imageSort) Len() int { return len(a) } +func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a imageSort) Less(i, j int) bool { + itime, _ := time.Parse(time.RFC3339, *a[i].CreationDate) + jtime, _ := time.Parse(time.RFC3339, *a[j].CreationDate) + return itime.Unix() < jtime.Unix() +} + +// Returns the most recent AMI out of a slice of images. +func mostRecentAmi(images []*ec2.Image) *ec2.Image { + sortedImages := images + sort.Sort(imageSort(sortedImages)) + return sortedImages[len(sortedImages)-1] +} + func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) params := &ec2.DescribeImagesInput{} - params.Filters = buildAmiFilters(s.AmiFilters.Filters) - params.Owners = s.AmiFilters.Owners - ui.Say(fmt.Sprintf("Using AMI Filters %v", params)) + + if s.SourceAmi != "" { + params.ImageIds = []*string{&s.SourceAmi} + } + + // We have filters to apply + if len(s.AmiFilters.Filters) > 0 { + params.Filters = buildAmiFilters(s.AmiFilters.Filters) + } + + log.Printf("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) @@ -55,14 +76,27 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction { } if len(imageResp.Images) == 0 { - err := fmt.Errorf("Source AMI '%s' was not found!", s.SourceAmi) + err := fmt.Errorf("No AMI was found matching filters: %v", params) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - image := imageResp.Images[0] - ui.Say(fmt.Sprintf("Got Image %v", image)) + if len(imageResp.Images) > 1 && s.AmiFilters.MostRecent == false { + err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.") + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + var image *ec2.Image + if s.AmiFilters.MostRecent { + image = mostRecentAmi(imageResp.Images) + } else { + image = imageResp.Images[0] + } + + log.Printf(fmt.Sprintf("Got Image %v", image)) // Enhanced Networking (SriovNetSupport) can only be enabled on HVM AMIs. // See http://goo.gl/icuXh5 From 95afaa58ca370eaa6d266117c0a0f1cf80b2c722 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 20 Aug 2016 23:34:22 +0000 Subject: [PATCH 03/13] [dynamic-source-ami] rename to source_ami_filter --- builder/amazon/common/run_config.go | 8 ++++---- builder/amazon/common/step_source_ami_info.go | 2 +- builder/amazon/ebs/builder.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index b3fc15a94..4de010a71 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -14,13 +14,13 @@ import ( var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$") -type DynamicAmiOptions struct { +type AmiFilterOptions struct { Filters map[*string]*string Owners []*string MostRecent bool `mapstructure:"most_recent"` } -func (d *DynamicAmiOptions) Empty() bool { +func (d *AmiFilterOptions) Empty() bool { return len(d.Owners) == 0 && len(d.Filters) == 0 } @@ -34,7 +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"` + SourceAmiFilter AmiFilterOptions `mapstructure:"source_ami_filter"` SpotPrice string `mapstructure:"spot_price"` SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"` DisableStopInstance bool `mapstructure:"disable_stop_instance"` @@ -67,7 +67,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) - if c.SourceAmi == "" && c.DynamicSourceAmi.Empty() { + if c.SourceAmi == "" && c.SourceAmiFilter.Empty() { errs = append(errs, errors.New("A source_ami or dynamic_source_ami must be specified")) } diff --git a/builder/amazon/common/step_source_ami_info.go b/builder/amazon/common/step_source_ami_info.go index 6946650c1..caaf167b8 100644 --- a/builder/amazon/common/step_source_ami_info.go +++ b/builder/amazon/common/step_source_ami_info.go @@ -19,7 +19,7 @@ import ( type StepSourceAMIInfo struct { SourceAmi string EnhancedNetworking bool - AmiFilters DynamicAmiOptions + AmiFilters AmiFilterOptions } // Build a slice of AMI filter options from the filters provided. diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 4566679ec..319a8bbd2 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -100,7 +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, + AmiFilters: b.config.SourceAmiFilter, }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug, From 3d6cf567151bb64ec193cf0972c9bdbd9c4ac9ae Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 20 Aug 2016 23:54:02 +0000 Subject: [PATCH 04/13] [dynamic-source-ami] add some docs --- .../source/docs/builders/amazon-ebs.html.md | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index a11f8cbba..88c8e9173 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -58,7 +58,8 @@ builder. how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials) - `source_ami` (string) - The initial AMI used as a base for the newly - created machine. + created machine. `source_ami_filter` may be used instead to populate this + automatically. - `ssh_username` (string) - The username to use in order to communicate over SSH to the running machine. @@ -169,6 +170,31 @@ builder. - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the region configuration option. Defaults to false. +- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. + Example: + ``` {.javascript} + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "*ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": ["099720109477"], + "most_recent": true + } + ``` + This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. + +- `source_ami_filter.filters` (map of strings) - filters used to select a `source_ami`. + Any filter described in the docs for [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) + is valid. + +- `source_ami_filter.owners` (array of strings) - This scopes the AMIs to certain Amazon account IDs. + This is helpful to limit the AMIs to a trusted third party, or to your own account. + +- `source_ami_filter.most_recent` (bool) - Selects the newest created image when true. + This is most useful for selecting a daily distro build. + - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts when the current spot price is less than the maximum price you specify. Spot From 57e7a1a3e6e313080378a89773f59b18341154a3 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 21 Aug 2016 00:07:58 +0000 Subject: [PATCH 05/13] [dynamic-source-ami] fix lint error --- builder/amazon/common/step_run_source_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 34146081d..ee0cf0eb5 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -95,7 +95,7 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi state.Put("error", fmt.Errorf( "The provided source AMI has an invalid root device type.\n"+ "Expected '%s', got '%s'.", - s.ExpectedRootDevice, image.RootDeviceType)) + s.ExpectedRootDevice, *image.RootDeviceType)) return multistep.ActionHalt } From b4d20706cea38af67c2cf27abeaef6e968bdb778 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 21 Aug 2016 00:29:51 +0000 Subject: [PATCH 06/13] [dynamic-source-ami] add some very basic tests --- builder/amazon/common/run_config_test.go | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/builder/amazon/common/run_config_test.go b/builder/amazon/common/run_config_test.go index 0b029169b..4b055fb41 100644 --- a/builder/amazon/common/run_config_test.go +++ b/builder/amazon/common/run_config_test.go @@ -28,6 +28,13 @@ func testConfig() *RunConfig { } } +func testConfigFilter() *RunConfig { + config := testConfig() + config.SourceAmi = "" + config.SourceAmiFilter = AmiFilterOptions{} + return config +} + func TestRunConfigPrepare(t *testing.T) { c := testConfig() err := c.Prepare(nil) @@ -52,6 +59,25 @@ func TestRunConfigPrepare_SourceAmi(t *testing.T) { } } +func TestRunConfigPrepare_SourceAmiFilterBlank(t *testing.T) { + c := testConfigFilter() + if err := c.Prepare(nil); len(err) != 1 { + t.Fatalf("err: %s", err) + } +} + +func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) { + c := testConfigFilter() + owner := "123" + filter_key := "name" + filter_value := "foo" + goodFilter := AmiFilterOptions{Owners: []*string{&owner}, Filters: map[*string]*string{&filter_key: &filter_value}} + c.SourceAmiFilter = goodFilter + if err := c.Prepare(nil); len(err) != 0 { + t.Fatalf("err: %s", err) + } +} + func TestRunConfigPrepare_SpotAuto(t *testing.T) { c := testConfig() c.SpotPrice = "auto" From 3cc8d35450d1178736948e4fe9c6444b982c1c26 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 21 Aug 2016 00:58:01 +0000 Subject: [PATCH 07/13] [dynamic-source-ami] fix old key name in error message --- builder/amazon/common/run_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 4de010a71..65f958567 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -68,7 +68,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) if c.SourceAmi == "" && c.SourceAmiFilter.Empty() { - errs = append(errs, errors.New("A source_ami or dynamic_source_ami must be specified")) + errs = append(errs, errors.New("A source_ami or source_ami_filter must be specified")) } if c.InstanceType == "" { From 0e2637696baedbc9e0ee4df3700c7532b86dfc0b Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 1 Oct 2016 22:27:48 +0000 Subject: [PATCH 08/13] [dynamic-source-ami] minor doc improvements --- .../docs/builders/amazon-chroot.html.md | 27 +++++++++++++++++++ .../source/docs/builders/amazon-ebs.html.md | 16 ++++++----- .../docs/builders/amazon-instance.html.md | 27 +++++++++++++++++++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index 8ac5cbd7d..030c43a3a 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -170,6 +170,33 @@ each category, the available configuration keys are alphabetized. - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the `ami_regions` configuration option. Defaults to false. +- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. + Example: + ``` {.javascript} + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "*ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": ["099720109477"], + "most_recent": true + } + ``` + This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. + NOTE: This will fail unless *exactly* one AMI is returned. + + - `filters` (map of strings) - filters used to select a `source_ami`. + NOTE: This will fail unless *exactly* one AMI is returned. + Any filter described in the docs for [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) + is valid. + + - `owners` (array of strings) - This scopes the AMIs to certain Amazon account IDs. + This is helpful to limit the AMIs to a trusted third party, or to your own account. + + - `most_recent` (bool) - Selects the newest created image when true. + This is most useful for selecting a daily distro build. + - `tags` (object of key/value strings) - Tags applied to the AMI. ## Basic Example diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 21b958f11..2359c7f8c 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -188,16 +188,18 @@ builder. } ``` This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. + NOTE: This will fail unless *exactly* one AMI is returned. -- `source_ami_filter.filters` (map of strings) - filters used to select a `source_ami`. - Any filter described in the docs for [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) - is valid. + - `filters` (map of strings) - filters used to select a `source_ami`. + NOTE: This will fail unless *exactly* one AMI is returned. + Any filter described in the docs for [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) + is valid. -- `source_ami_filter.owners` (array of strings) - This scopes the AMIs to certain Amazon account IDs. - This is helpful to limit the AMIs to a trusted third party, or to your own account. + - `owners` (array of strings) - This scopes the AMIs to certain Amazon account IDs. + This is helpful to limit the AMIs to a trusted third party, or to your own account. -- `source_ami_filter.most_recent` (bool) - Selects the newest created image when true. - This is most useful for selecting a daily distro build. + - `most_recent` (bool) - Selects the newest created image when true. + This is most useful for selecting a daily distro build. - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 846f0f556..148020771 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -194,6 +194,33 @@ builder. - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the region configuration option. Defaults to false. +- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. + Example: + ``` {.javascript} + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "*ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": ["099720109477"], + "most_recent": true + } + ``` + This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. + NOTE: This will fail unless *exactly* one AMI is returned. + + - `filters` (map of strings) - filters used to select a `source_ami`. + NOTE: This will fail unless *exactly* one AMI is returned. + Any filter described in the docs for [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) + is valid. + + - `owners` (array of strings) - This scopes the AMIs to certain Amazon account IDs. + This is helpful to limit the AMIs to a trusted third party, or to your own account. + + - `most_recent` (bool) - Selects the newest created image when true. + This is most useful for selecting a daily distro build. + - `spot_price` (string) - The maximum hourly price to launch a spot instance to create the AMI. It is a type of instances that EC2 starts when the maximum price that you specify exceeds the current spot price. Spot price From ab3500b45c5b83cfff91477c55fbe05e38238880 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 1 Oct 2016 22:56:09 +0000 Subject: [PATCH 09/13] [dynamic-source-ami] support chroot builder --- builder/amazon/chroot/builder.go | 36 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index dcfd7bc41..d2a9fbfa1 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -30,19 +30,20 @@ type Config struct { awscommon.AMIConfig `mapstructure:",squash"` awscommon.AccessConfig `mapstructure:",squash"` - ChrootMounts [][]string `mapstructure:"chroot_mounts"` - CommandWrapper string `mapstructure:"command_wrapper"` - CopyFiles []string `mapstructure:"copy_files"` - DevicePath string `mapstructure:"device_path"` - FromScratch bool `mapstructure:"from_scratch"` - MountOptions []string `mapstructure:"mount_options"` - MountPartition int `mapstructure:"mount_partition"` - MountPath string `mapstructure:"mount_path"` - PostMountCommands []string `mapstructure:"post_mount_commands"` - PreMountCommands []string `mapstructure:"pre_mount_commands"` - RootDeviceName string `mapstructure:"root_device_name"` - RootVolumeSize int64 `mapstructure:"root_volume_size"` - SourceAmi string `mapstructure:"source_ami"` + ChrootMounts [][]string `mapstructure:"chroot_mounts"` + CommandWrapper string `mapstructure:"command_wrapper"` + CopyFiles []string `mapstructure:"copy_files"` + DevicePath string `mapstructure:"device_path"` + FromScratch bool `mapstructure:"from_scratch"` + MountOptions []string `mapstructure:"mount_options"` + MountPartition int `mapstructure:"mount_partition"` + MountPath string `mapstructure:"mount_path"` + PostMountCommands []string `mapstructure:"post_mount_commands"` + PreMountCommands []string `mapstructure:"pre_mount_commands"` + RootDeviceName string `mapstructure:"root_device_name"` + RootVolumeSize int64 `mapstructure:"root_volume_size"` + SourceAmi string `mapstructure:"source_ami"` + SourceAmiFilter awscommon.AmiFilterOptions `mapstructure:"source_ami_filter"` ctx interpolate.Context } @@ -125,8 +126,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if b.config.FromScratch { - if b.config.SourceAmi != "" { - warns = append(warns, "source_ami is unused when from_scratch is true") + if b.config.SourceAmi != "" || !b.config.SourceAmiFilter.Empty() { + warns = append(warns, "source_ami and source_ami_filter are unused when from_scratch is true") } if b.config.RootVolumeSize == 0 { errs = packer.MultiErrorAppend( @@ -149,9 +150,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs, errors.New("ami_block_device_mappings is required with from_scratch.")) } } else { - if b.config.SourceAmi == "" { + if b.config.SourceAmi == "" && b.config.SourceAmiFilter.Empty() { errs = packer.MultiErrorAppend( - errs, errors.New("source_ami is required.")) + errs, errors.New("source_ami or source_ami_filter is required.")) } if len(b.config.AMIMappings) != 0 { warns = append(warns, "ami_block_device_mappings are unused when from_scratch is false") @@ -210,6 +211,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.SourceAmiFilter, }, &StepCheckRootDevice{}, ) From 9379cbc5d9ed9e39dbae5d4d40c079dfc7492eeb Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sat, 1 Oct 2016 23:01:01 +0000 Subject: [PATCH 10/13] [dynamic-source-ami] support instance builder too --- builder/amazon/instance/builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index a900aef9b..38a68d0a4 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -190,6 +190,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.SourceAmiFilter, }, &awscommon.StepKeyPair{ Debug: b.config.PackerDebug, From 77a7728fd52aab29e5834cf923f62a8ca755c756 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Fri, 14 Oct 2016 11:51:19 -0700 Subject: [PATCH 11/13] Update amazon-chroot.html.md --- website/source/docs/builders/amazon-chroot.html.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index 030c43a3a..3a61d8476 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -184,7 +184,8 @@ each category, the available configuration keys are alphabetized. } ``` This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. - NOTE: This will fail unless *exactly* one AMI is returned. + NOTE: This will fail unless *exactly* one AMI is returned. In the above + example, `most_recent` will cause this to succeed by selecting the newest image. - `filters` (map of strings) - filters used to select a `source_ami`. NOTE: This will fail unless *exactly* one AMI is returned. From 7edc3f98578eb89633e03d5f7eea926442bf2e36 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Fri, 14 Oct 2016 11:53:12 -0700 Subject: [PATCH 12/13] Update amazon-ebs.html.md --- website/source/docs/builders/amazon-ebs.html.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 2359c7f8c..becb5fafb 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -188,7 +188,8 @@ builder. } ``` This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. - NOTE: This will fail unless *exactly* one AMI is returned. + NOTE: This will fail unless *exactly* one AMI is returned. In the above + example, `most_recent` will cause this to succeed by selecting the newest image. - `filters` (map of strings) - filters used to select a `source_ami`. NOTE: This will fail unless *exactly* one AMI is returned. From e15c22797bf2adf8da50b07a0e08612d845899b1 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Fri, 14 Oct 2016 11:53:40 -0700 Subject: [PATCH 13/13] Update amazon-instance.html.md --- website/source/docs/builders/amazon-instance.html.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 148020771..bdc17c68d 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -208,7 +208,8 @@ builder. } ``` This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. - NOTE: This will fail unless *exactly* one AMI is returned. + NOTE: This will fail unless *exactly* one AMI is returned. In the above + example, `most_recent` will cause this to succeed by selecting the newest image. - `filters` (map of strings) - filters used to select a `source_ami`. NOTE: This will fail unless *exactly* one AMI is returned.