Skip to content

Commit 70ce1ae

Browse files
committed
feat: support for mixed-arch mixed instances policies
1 parent abad4f8 commit 70ce1ae

File tree

3 files changed

+62
-21
lines changed

3 files changed

+62
-21
lines changed

functions/lambda.py

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import logging
33
import logging.config
4+
from dataclasses import dataclass
45
from os import environ
56

67
import boto3
@@ -20,16 +21,29 @@ class InstanceRefreshInProgress(LambdaError):
2021
pass
2122

2223

24+
class MissingEnvironmentVariable(LambdaError):
25+
pass
26+
27+
28+
@dataclass
29+
class LaunchTemplate:
30+
31+
is_arm: bool
32+
name: str
33+
34+
35+
AUTO_SCALING_GROUP_IS_ARM_DEFAULT = environ['AUTO_SCALING_GROUP_IS_ARM_DEFAULT'] == 'True'
2336
AUTO_SCALING_GROUP_NAME = environ['AUTO_SCALING_GROUP_NAME']
2437
DESCRIBE_INSTANCE_REFRESHES_MAX_RECORDS = int(environ['DESCRIBE_INSTANCE_REFRESHES_MAX_RECORDS'])
2538
FINISHED_INSTANCE_REFRESH_STATES = ('Cancelled', 'Failed', 'Successful')
2639
LOGGING_LEVEL = environ.get('LOGGING_LEVEL', logging.INFO)
2740
REFRESH_INSTANCE_WARMUP = int(environ['REFRESH_INSTANCE_WARMUP'])
2841
REFRESH_MIN_HEALTHY_PERCENTAGE = int(environ['REFRESH_MIN_HEALTHY_PERCENTAGE'])
29-
REFRESH_SKIP_MATCHING = bool(environ['REFRESH_SKIP_MATCHING'])
42+
REFRESH_SKIP_MATCHING = environ['REFRESH_SKIP_MATCHING'] == 'True'
3043
SENTRY_DSN = environ.get('SENTRY_DSN')
3144
SSM_PARAMETER_NAME = environ['SSM_PARAMETER_NAME']
32-
UPDATE_MIXED_INSTANCES_POLICY_OVERRIDEN_LAUNCH_TEMPLATES = bool(environ['UPDATE_MIXED_OVERRIDE_LAUNCH_TEMPLATES'])
45+
SSM_PARAMETER_NAME_ARM = environ.get('SSM_PARAMETER_NAME_ARM')
46+
UPDATE_MIXED_INSTANCES_POLICY_OVERRIDEN_LAUNCH_TEMPLATES = environ['UPDATE_MIXED_OVERRIDE_LAUNCH_TEMPLATES'] == 'True'
3347

3448
if SENTRY_DSN:
3549
sentry_sdk.init(SENTRY_DSN, integrations=[AwsLambdaIntegration()])
@@ -59,10 +73,10 @@ class InstanceRefreshInProgress(LambdaError):
5973
ssm = boto3.client('ssm')
6074

6175

62-
def get_current_image_id():
76+
def get_current_image_id(ssm_parameter_name):
6377
"""Returns current AMI from SSM"""
6478
param_value = None
65-
param = ssm.get_parameter(Name=SSM_PARAMETER_NAME)
79+
param = ssm.get_parameter(Name=ssm_parameter_name)
6680
value = param['Parameter']['Value']
6781
if value.startswith('ami-'):
6882
image_id = value
@@ -73,36 +87,43 @@ def get_current_image_id():
7387
return image_id
7488

7589

76-
def get_launch_template_names_and_auto_scaling_group():
90+
def get_launch_templates(auto_scaling_group_names):
7791
"""Returns Launch Template names for Auto Scaling Group"""
78-
group_description_resp = autoscaling.describe_auto_scaling_groups(AutoScalingGroupNames=(AUTO_SCALING_GROUP_NAME,))
92+
group_description_resp = autoscaling.describe_auto_scaling_groups(AutoScalingGroupNames=auto_scaling_group_names)
7993
group_details = group_description_resp['AutoScalingGroups'][0]
8094
mixed_instances_policy = group_details['MixedInstancesPolicy']
81-
launch_template_names = []
95+
launch_templates = []
8296

8397
if 'MixedInstancesPolicy' in group_details:
8498
launch_template_details = mixed_instances_policy['LaunchTemplate']
85-
launch_template_names.append(launch_template_details['LaunchTemplateSpecification']['LaunchTemplateName'])
99+
launch_templates.append(
100+
LaunchTemplate(AUTO_SCALING_GROUP_IS_ARM_DEFAULT,
101+
launch_template_details['LaunchTemplateSpecification']['LaunchTemplateName']),
102+
)
86103

87104
if UPDATE_MIXED_INSTANCES_POLICY_OVERRIDEN_LAUNCH_TEMPLATES:
88105
overrides = launch_template_details['Overrides']
89106
for override in overrides:
107+
instance_type_parts = override['InstanceType'].split('.')
108+
is_instance_type_arm = instance_type_parts[0].endswith('g')
90109
if override_lt_specification := override.get('LaunchTemplateSpecification'):
91110
if override_lt_name := override_lt_specification.get('LaunchTemplateName'):
92-
launch_template_names.append(override_lt_name)
111+
launch_templates.append(LaunchTemplate(is_instance_type_arm, override_lt_name))
93112
else:
94-
launch_template_names.append(group_details['LaunchTemplate']['LaunchTemplateName'])
113+
launch_templates.append(
114+
LaunchTemplate(AUTO_SCALING_GROUP_IS_ARM_DEFAULT, group_details['LaunchTemplate']['LaunchTemplateName']),
115+
)
95116

96-
logger.info('Using Launch Templates "%s"', launch_template_names)
97-
return (launch_template_names, group_description_resp['AutoScalingGroups'][0])
117+
logger.info('Using Launch Templates "%s"', launch_templates)
118+
return launch_templates
98119

99120

100-
def is_launch_template_updated(image_id, launch_template_name):
121+
def is_launch_template_updated(image_id, launch_template):
101122
"""Checks consistency between $Default and $Latest versions of Launch Template and returns bool wheter versions
102123
match
103124
"""
104125
default_and_latest_versions = ec2.describe_launch_template_versions(
105-
LaunchTemplateName=launch_template_name,
126+
LaunchTemplateName=launch_template.name,
106127
Versions=('$Default', '$Latest'),
107128
)
108129
if (size := len(default_and_latest_versions)) != 2:
@@ -176,19 +197,25 @@ def start_instance_refresh():
176197

177198

178199
def main():
179-
image_id = get_current_image_id()
180-
launch_template_names, autoscaling_group = get_launch_template_names_and_auto_scaling_group()
200+
image_id = get_current_image_id(SSM_PARAMETER_NAME)
201+
image_id_arm = get_current_image_id(SSM_PARAMETER_NAME_ARM) if SSM_PARAMETER_NAME_ARM else None
202+
203+
launch_templates = get_launch_templates((AUTO_SCALING_GROUP_NAME,))
181204
all_templates_up_to_date = True
182205

183-
for launch_template_name in launch_template_names:
184-
if not is_launch_template_updated(image_id, launch_template_name):
206+
for launch_template in launch_templates:
207+
if launch_template.is_arm and not image_id_arm:
208+
raise MissingEnvironmentVariable('Set the "SSM_PARAMETER_NAME_ARM" environment variable')
209+
lt_image_id = image_id_arm if launch_template.is_arm else image_id
210+
211+
if not is_launch_template_updated(lt_image_id, launch_template):
185212
all_templates_up_to_date = False
186213
logger.info('Launch Template "%s" for Auto Scaling Group "%s" is not up to date',
187-
launch_template_name, AUTO_SCALING_GROUP_NAME)
188-
update_launch_template(image_id, launch_template_name)
214+
launch_template, AUTO_SCALING_GROUP_NAME)
215+
update_launch_template(lt_image_id, launch_template)
189216
else:
190217
logger.info('Launch Template "%s" for Auto Scaling Group "%s" is already up to date',
191-
launch_template_name, AUTO_SCALING_GROUP_NAME)
218+
launch_template, AUTO_SCALING_GROUP_NAME)
192219

193220
if not all_templates_up_to_date:
194221
start_instance_refresh()

lambda.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ resource "aws_lambda_function" "refresh" {
2222

2323
environment {
2424
variables = {
25+
AUTO_SCALING_GROUP_IS_ARM_DEFAULT = var.auto_scaling_group_is_arm_default ? "True" : "False"
2526
AUTO_SCALING_GROUP_NAME = var.autoscaling_group_name
2627
DESCRIBE_INSTANCE_REFRESHES_MAX_RECORDS = var.describe_instance_refreshes_max_records
2728
REFRESH_INSTANCE_WARMUP = var.instance_refresh_instance_warmup
@@ -30,6 +31,7 @@ resource "aws_lambda_function" "refresh" {
3031
SENTRY_DSN = var.sentry_dsn
3132
SENTRY_ENVIRONMENT = var.sentry_environment
3233
SSM_PARAMETER_NAME = var.ami_ssm_parameter
34+
SSM_PARAMETER_NAME_ARM = var.ami_ssm_parameter_arm
3335
UPDATE_MIXED_INSTANCES_POLICY_OVERRIDEN_LAUNCH_TEMPLATES = var.update_mixed_instances_policy_overriden_launch_templates ? "True" : "False"
3436
}
3537
}

variables.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ variable "ami_ssm_parameter" {
44
type = string
55
}
66

7+
variable "ami_ssm_parameter_arm" {
8+
default = "/aws/service/ecs/optimized-ami/amazon-linux-2/arm64/recommended"
9+
description = "Name of SSM parameter containing the current AMI (ARM)"
10+
type = string
11+
}
12+
13+
variable "auto_scaling_group_is_arm_default" {
14+
default = false
15+
description = "Set to true if your ASG uses ARM instances in the default launch template"
16+
type = string
17+
}
18+
719
variable "autoscaling_group_name" {
820
description = "Name of the auto scaling group to refresh"
921
type = string

0 commit comments

Comments
 (0)