[dynamic-source-ami] proof of concept

This commit is contained in:
Chris Lundquist 2016-08-20 18:58:36 +00:00
parent 6cd7ad82bb
commit ec1abb9448
4 changed files with 55 additions and 25 deletions

View File

@ -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 == "" {

View File

@ -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 {

View File

@ -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

View File

@ -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,