Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ebs: add fast-launch support with AMI copies #451

Merged
merged 2 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions .web-docs/components/builder/ebs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1671,24 +1671,43 @@ https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/win-ami-config-fast-launc

- `enable_fast_launch` (bool) - Configure fast-launch for Windows AMIs

- `template_id` (string) - The ID of the launch template to use for the fast launch
- `template_id` (string) - The ID of the launch template to use for fast launch for the main AMI.

This cannot be specified in conjunction with the template name.

If no template is specified, the default launch template will be used,
as specified in the AWS docs.

If you copy the AMI to other regions, this option should not
be used, use instead the `fast_launch_template_config` option.

- `template_name` (string) - The name of the launch template to use for fast launch
- `template_name` (string) - The name of the launch template to use for fast launch for the main AMI.

This cannot be specified in conjunction with the template ID.

If no template is specified, the default launch template will be used,
as specified in the AWS docs.

If you copy the AMI to other regions, this option should not
be used, use instead the `fast_launch_template_config` option.

- `template_version` (int) - The version of the launch template to use
- `template_version` (int) - The version of the launch template to use for fast launch for the main AMI.

If unspecified, and a template is referenced, this will default to
the latest version available for the template.

If you copy the AMI to other regions, this option should not
be used, use instead the `fast_launch_template_config` option.

- `region_launch_templates` ([]FastLaunchTemplateConfig) - RegionLaunchTemplates is the list of launch templates per region.

This should be specified if you want to use a custom launch
template for your fast-launched images, and you are copying
the image to other regions.

Note: all the regions don't need an entry in this map, but if you
don't specify a region's template, a default one will be picked
by AWS.

- `max_parallel_launches` (int) - Maximum number of instances to launch for creating pre-provisioned snapshots

Expand Down
45 changes: 31 additions & 14 deletions builder/ebs/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,24 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
errs = packersdk.MultiErrorAppend(errs, b.config.LaunchMappings.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)

b.config.FastLaunch.defaultRegion = b.config.RawRegion
errs = packersdk.MultiErrorAppend(errs, b.config.FastLaunch.Prepare()...)
for _, templateConfig := range b.config.FastLaunch.RegionLaunchTemplates {
exists := false
for _, cpRegion := range b.config.AMIRegions {
if cpRegion == templateConfig.Region {
exists = true
break
}
}
if b.config.RawRegion == templateConfig.Region {
exists = true
}

if !exists {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Launch template specified for enabling fast-launch on region %q, but the AMI won't be copied there.", templateConfig.Region))
}
}

if b.config.IsSpotInstance() && (b.config.AMIENASupport.True() || b.config.AMISriovNetSupport) {
errs = packersdk.MultiErrorAppend(errs,
Expand Down Expand Up @@ -404,20 +421,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
Tags: b.config.RunTags,
Ctx: b.config.ctx,
},
&stepPrepareFastLaunchTemplate{
AMISkipCreateImage: b.config.AMISkipCreateImage,
EnableFastLaunch: b.config.FastLaunch.UseFastLaunch,
TemplateID: b.config.FastLaunch.LaunchTemplateID,
TemplateName: b.config.FastLaunch.LaunchTemplateName,
TemplateVersion: b.config.FastLaunch.LaunchTemplateVersion,
},
&stepEnableFastLaunch{
PollingConfig: b.config.PollingConfig,
ResourceCount: b.config.FastLaunch.TargetResourceCount,
AMISkipCreateImage: b.config.AMISkipCreateImage,
EnableFastLaunch: b.config.FastLaunch.UseFastLaunch,
MaxInstances: b.config.FastLaunch.MaxParallelLaunches,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
Expand All @@ -429,6 +432,20 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
AMISkipCreateImage: b.config.AMISkipCreateImage,
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
},
&stepPrepareFastLaunchTemplate{
AccessConfig: &b.config.AccessConfig,
AMISkipCreateImage: b.config.AMISkipCreateImage,
EnableFastLaunch: b.config.FastLaunch.UseFastLaunch,
RegionTemplates: b.config.FastLaunch.RegionLaunchTemplates,
},
&stepEnableFastLaunch{
AccessConfig: &b.config.AccessConfig,
PollingConfig: b.config.PollingConfig,
ResourceCount: b.config.FastLaunch.TargetResourceCount,
AMISkipCreateImage: b.config.AMISkipCreateImage,
EnableFastLaunch: b.config.FastLaunch.UseFastLaunch,
MaxInstances: b.config.FastLaunch.MaxParallelLaunches,
},
&stepEnableDeprecation{
AccessConfig: &b.config.AccessConfig,
DeprecationTime: b.config.DeprecationTime,
Expand Down
232 changes: 231 additions & 1 deletion builder/ebs/builder_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
awscommon "github.com/hashicorp/packer-plugin-amazon/builder/common"
amazon_acc "github.com/hashicorp/packer-plugin-amazon/builder/ebs/acceptance"
"github.com/hashicorp/packer-plugin-sdk/acctest"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)

func TestAccBuilder_EbsBasic(t *testing.T) {
Expand Down Expand Up @@ -1122,7 +1123,7 @@ func TestAccBuilder_EbsWindowsFastLaunch(t *testing.T) {
}

if len(fastLaunchImages.FastLaunchImages) != 1 {
return fmt.Errorf("go too many fast-launch images, expected 1, got %d", len(fastLaunchImages.FastLaunchImages))
return fmt.Errorf("got too many fast-launch images, expected 1, got %d", len(fastLaunchImages.FastLaunchImages))
}

img := fastLaunchImages.FastLaunchImages[0]
Expand All @@ -1142,6 +1143,147 @@ func TestAccBuilder_EbsWindowsFastLaunch(t *testing.T) {
}
}

func TestAccBuilder_EbsWindowsFastLaunchWithAMICopies(t *testing.T) {
amiNameWithoutLT := fmt.Sprintf("packer-ebs-windows-fastlaunch-with-copies-%d", time.Now().Unix())
amiNameWithLT := fmt.Sprintf("packer-ebs-windows-fastlaunch-with-copies-and-launch-templates-%d", time.Now().Unix())

flWithCopiesAMIs := []amazon_acc.AMIHelper{
{
Region: "us-east-1",
Name: amiNameWithoutLT,
},
{
Region: "us-east-2",
Name: amiNameWithoutLT,
},
}

flWithCopiesAMIsAndLTs := []amazon_acc.AMIHelper{
{
Region: "us-east-1",
Name: amiNameWithLT,
},
{
Region: "us-east-2",
Name: amiNameWithLT,
},
}

tests := []struct {
name string
amiName string
amiSpec []amazon_acc.AMIHelper
stringsToFindInLog []string
template string
}{
{
"ebs-windows-fast-launch-with-copies",
amiNameWithoutLT,
flWithCopiesAMIs,
[]string{
"no launch-template configured, will use defaults.",
"no template specified for region \"us-east-1\"",
"no template specified for region \"us-east-2\"",
},
testWindowsFastBootWithAMICopies,
},
{
"ebs-windows-fast-launch-with-copies-and-launch-templates",
amiNameWithLT,
flWithCopiesAMIsAndLTs,
[]string{
"found template in region \"us-east-1\": ID \"lt-0c82d8943c032fc0b\"",
"found template in region \"us-east-2\": ID \"lt-0083091b6614b118c\"",
},
testWindowsFastBootWithAMICopiesAndLTs,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testcase := &acctest.PluginTestCase{
Name: tt.name,
Template: fmt.Sprintf(tt.template, tt.amiName),
Teardown: func() error {
var errs error

for _, ami := range tt.amiSpec {
err := ami.CleanUpAmi()
if err != nil {
t.Logf("cleaning up AMI %q in region %q failed: %s. It will need to be manually removed", ami.Name, ami.Region, err)
errs = packersdk.MultiErrorAppend(errs, err)
}
}

return errs
},
Check: func(buildCommand *exec.Cmd, logfile string) error {
if buildCommand.ProcessState.ExitCode() != 0 {
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
}

for _, ami := range tt.amiSpec {
amis, err := ami.GetAmi()
if err != nil {
return fmt.Errorf("failed to get AMI: %s", err)
}
if len(amis) != 1 {
return fmt.Errorf("got too many AMIs, expected 1, got %d", len(amis))
}

accessConfig := &awscommon.AccessConfig{}
session, err := accessConfig.Session()
if err != nil {
return fmt.Errorf("Unable to create aws session %s", err.Error())
}

regionconn := ec2.New(session.Copy(&aws.Config{
Region: aws.String(ami.Region),
}))

ami := amis[0]

fastLaunchImages, err := regionconn.DescribeFastLaunchImages(&ec2.DescribeFastLaunchImagesInput{
ImageIds: []*string{ami.ImageId},
})

if err != nil {
return fmt.Errorf("failed to get fast-launch images: %s", err)
}

if len(fastLaunchImages.FastLaunchImages) != 1 {
return fmt.Errorf("got too many fast-launch images, expected 1, got %d", len(fastLaunchImages.FastLaunchImages))
}

img := fastLaunchImages.FastLaunchImages[0]
if img.State == nil {
return fmt.Errorf("unexpected null fast-launch state")
}

if *img.State != "enabled" {
return fmt.Errorf("expected fast-launch state to be enabled, but is %q: transition state was %q", *img.State, *img.StateTransitionReason)
}
}

logs, err := os.ReadFile(logfile)
if err != nil {
t.Fatalf("failed to read logs from logifle: %s", err)
}
logStr := string(logs)
for _, str := range tt.stringsToFindInLog {
if !strings.Contains(logStr, str) {
t.Errorf("exptected to find %q in logs, but did not", str)
}
}

return nil
},
}
acctest.TestPlugin(t, testcase)
})
}
}

func checkAMITags(ami amazon_acc.AMIHelper, tagList map[string]string) error {
images, err := ami.GetAmi()
if err != nil || len(images) == 0 {
Expand Down Expand Up @@ -1835,6 +1977,94 @@ build {
}
`

const testWindowsFastBootWithAMICopies = `
data "amazon-ami" "windows-ami" {
filters = {
name = "Windows_Server-2016-English-Core-Base-*"
}
owners = ["801119661308"]
most_recent = true
region = "us-east-1"
}

source "amazon-ebs" "windows-fastboot" {
ami_name = "%s"
source_ami = data.amazon-ami.windows-ami.id
instance_type = "m3.medium"
region = "us-east-1"
ami_regions = ["us-east-2", "us-east-1"]
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "e4sypa55!"
user_data_file = "test-fixtures/ps_enable.ps"

fast_launch {
enable_fast_launch = true
target_resource_count = 1
}
}

build {
sources = ["amazon-ebs.windows-fastboot"]

provisioner "powershell" {
inline = [
"C:/ProgramData/Amazon/EC2-Windows/Launch/Scripts/InitializeInstance.ps1 -Schedule",
"C:/ProgramData/Amazon/EC2-Windows/Launch/Scripts/SysprepInstance.ps1 -NoShutdown"
]
}
}
`

const testWindowsFastBootWithAMICopiesAndLTs = `
data "amazon-ami" "windows-ami" {
filters = {
name = "Windows_Server-2016-English-Core-Base-*"
}
owners = ["801119661308"]
most_recent = true
region = "us-east-1"
}

source "amazon-ebs" "windows-fastboot" {
ami_name = "%s"
source_ami = data.amazon-ami.windows-ami.id
instance_type = "m3.medium"
region = "us-east-1"
ami_regions = ["us-east-2", "us-east-1"]
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "e4sypa55!"
user_data_file = "test-fixtures/ps_enable.ps"

fast_launch {
enable_fast_launch = true
target_resource_count = 1

region_launch_templates {
region = "us-east-1"
template_id = "lt-0c82d8943c032fc0b"
}

region_launch_templates {
region = "us-east-2"
template_id = "lt-0083091b6614b118c"
}
}
}

build {
sources = ["amazon-ebs.windows-fastboot"]

provisioner "powershell" {
inline = [
"C:/ProgramData/Amazon/EC2-Windows/Launch/Scripts/InitializeInstance.ps1 -Schedule",
"C:/ProgramData/Amazon/EC2-Windows/Launch/Scripts/SysprepInstance.ps1 -NoShutdown"
]
}
}
`

const testSubnetFilterWithPublicIP = `
source "amazon-ebs" "test-subnet-filter" {
subnet_filter {
Expand Down
Loading
Loading