Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b044771
Hoist common consts.
MrJoy Jun 4, 2022
0f0705f
Add some paranoia for cosmic rays and/or future oversights.
MrJoy Jun 4, 2022
2fc0655
Hoist! All! The things!
MrJoy Jun 4, 2022
83df934
Remove dead variables.
MrJoy Jun 4, 2022
0ebbeb7
Add optional support for ExternalId when assuming role.
MrJoy Jun 9, 2022
8d22765
Add ExternalId to one more role.
MrJoy Jun 11, 2022
ac23421
Add a missing variable declaration. D'oh.
MrJoy Jun 11, 2022
6d3e29f
Try only requiring `sts:ExternalId` on `sts:AssumeRole` (not `sts:Tag…
MrJoy Jun 11, 2022
b951832
Merge pull request #2 from FasterBetter/feature/support-external-id
AlexSc Jun 12, 2022
7c5095d
Merge pull request #1 from FasterBetter/maint/tidy-up
AlexSc Jun 12, 2022
ccf219f
Fix language for undeployment slack notifications.
AlexSc Jun 15, 2022
00cba4d
v0.2.16.
AlexSc Jun 15, 2022
bf5efd6
Limit length of undeploy event rule name.
AlexSc Jul 5, 2022
44344d4
v0.2.17.
AlexSc Jul 5, 2022
0710d1d
Shorten undeploy rule name and clamp the length of the primary key co…
AlexSc Jul 5, 2022
4286221
v0.2.18.
AlexSc Jul 5, 2022
8b88b8d
Keep the asg name below 32 characters so target groups can also have …
AlexSc Jul 5, 2022
c066031
v0.2.19.
AlexSc Jul 5, 2022
1e7c288
Let ASG names be more unique.
AlexSc Jul 5, 2022
d7021c3
v0.2.20.
AlexSc Jul 5, 2022
f53edc8
Fix syntax error.
AlexSc Jul 5, 2022
1a9f435
v0.2.21.
AlexSc Jul 5, 2022
2a18786
Support target group attributes.
AlexSc Aug 12, 2022
0ca337b
v0.2.22.
AlexSc Aug 12, 2022
932aec0
Support multiple load balancer conditions for cancel and undeploy.
AlexSc Nov 7, 2022
ebecb18
Support deploying a service with multiple rules.
AlexSc Nov 8, 2022
e3a0da9
v0.3.0.
AlexSc Nov 8, 2022
1bd4c76
Coalesce now works even if no rules containg the target target group
AlexSc Nov 16, 2022
dee73a8
v0.3.1.
AlexSc Nov 16, 2022
2658697
Allow longer undeploy rule names.
AlexSc Dec 7, 2022
c33a92e
v0.3.2.
AlexSc Dec 7, 2022
3fdffc8
Retry creating the new autoscaling group
AlexSc Dec 8, 2022
9ab13a0
v0.3.3.
AlexSc Dec 8, 2022
b3001ca
Support enabling autoscaling group metrics.
AlexSc Dec 14, 2022
9e7ac59
v0.3.4.
AlexSc Dec 14, 2022
e2c91ed
Fix exception when retrying ASG creation.
AlexSc Jan 8, 2023
9eb2b07
v0.3.5.
AlexSc Jan 8, 2023
21e4873
Actually retry auto scaling group creation
AlexSc Jan 18, 2023
b404204
v0.3.6.
AlexSc Jan 18, 2023
3520133
Add elasticloadbalance:AddTags permission to deployer role
AlexSc May 19, 2023
7e8a804
v0.3.7.
AlexSc May 19, 2023
8f9620d
Merge remote-tracking branch 'upstream/main' into maint/sync-with-ups…
gnusosa Jul 12, 2023
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
95 changes: 95 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,98 @@
## 0.3.7

ENHANCEMENTS:

* Add elasticloadbalancing:AddTags IAM permission to deploy_access_role to comply with upcoming AWS security changes.

## 0.3.6

BUG FIXES:

* Actually retry ASG creation.

## 0.3.5

BUG FIXES:

* Fix exception when retrying ASG creation.

## 0.3.4

ENHANCEMENTS:

* Now supports AutoScaling Group metrics.

## 0.3.3

ENHANCEMENTS:

* Retry creating AutoScaling Group.

## 0.3.2

ENHANCEMENTS:

* Permit longer names for automatic undeployment rules to minimize conflicts.

## 0.3.1

BUG FIXES:

* Cancelling a deploy of a web service during its Bake period now correctly rolls back to the previous production deploy.

## 0.3.0

ENHANCEMENTS:

* Deploy, undeploy, and cancel now support services which have multiple load balancer rules directing traffic to them.

## 0.2.22

ENHANCEMENTS:

* Target group attributes (deregistration delay, stickiness, load balancing algorithm) are now supported.

## 0.2.21

BUG FIXES:

* Syntax error.

## 0.2.20

BUG FIXES:

* Limit length of target group name, but better.

## 0.2.19

BUG FIXES:

* Limit length of ASG/target group names to avoid naming conflicts.

## 0.2.18

BUG FIXES:

* Limit length of automatic undeploy rule name, but better.

## 0.2.17

BUG FIXES:

* Limit length of automatic undeploy rule name.

## 0.2.16

BUG FIXES:

* Fixed language for starting undeployment slack notification.
* Support external id when assuming roles ([#2](https://github.com/GoCarrot/terraform-aws-deployomat/pull/2))

SPECIAL THANKS:

* [@MrJoy](https://github.com/MrJoy)

## 0.2.15

ENHANCEMENTS:
Expand Down
46 changes: 43 additions & 3 deletions modules/deploy_access_role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,45 @@ data "aws_default_tags" "tags" {}
data "aws_iam_policy_document" "allow-deployomat-assume" {
statement {
actions = [
"sts:AssumeRole",
"sts:AssumeRole"
]

principals {
type = "AWS"
identifiers = formatlist("arn:${data.aws_partition.current.partition}:iam::%s:root", var.ci_cd_account_ids)
}

condition {
test = "StringEquals"
variable = "aws:ResourceTag/Environment"
values = ["&{aws:PrincipalTag/Environment}"]
}

condition {
test = "StringEquals"
variable = "aws:PrincipalTag/Service"
values = [var.deployomat_service_name]
}

condition {
test = "ForAllValues:StringEquals"
variable = "aws:TagKeys"
values = ["ServiceName", "ServiceLogName"]
}

dynamic "condition" {
for_each = var.external_id != null ? [1] : []

content {
test = "StringEquals"
variable = "sts:ExternalId"
values = [var.external_id]
}
}
}

statement {
actions = [
"sts:TagSession"
]

Expand Down Expand Up @@ -104,7 +142,8 @@ data "aws_iam_policy_document" "allow-deploy" {
"autoscaling:AttachLoadBalancerTargetGroups",
"autoscaling:PutScalingPolicy",
"autoscaling:PutWarmPool",
"autoscaling:UpdateAutoScalingGroup"
"autoscaling:UpdateAutoScalingGroup",
"autoscaling:EnableMetricsCollection"
]

resources = [
Expand Down Expand Up @@ -168,7 +207,8 @@ data "aws_iam_policy_document" "allow-deploy" {
statement {
actions = [
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:CreateTargetGroup"
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:AddTags"
]

resources = [
Expand Down
2 changes: 1 addition & 1 deletion modules/deployomat/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ resource "aws_iam_policy" "deployomat-lambda-logging" {
}

locals {
automatic_undeploy_rule_arn = "arn:${data.aws_partition.current.partition}:events:*:${data.aws_caller_identity.current.account_id}:rule/*-automatic-undeploy"
automatic_undeploy_rule_arn = "arn:${data.aws_partition.current.partition}:events:*:${data.aws_caller_identity.current.account_id}:rule/*-undeploy"
}

data "aws_iam_policy_document" "deployomat-lambda" {
Expand Down
16 changes: 10 additions & 6 deletions modules/deployomat/src/cancel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ def call
def reset_listeners(listeners, production_asg, deploy_asg)
elbv2 = ElbV2.new(@config)

target_group = production_asg&.target_group_arns&.first || deploy_asg&.target_group_arns&.first
target_groups = [production_asg&.target_group_arns&.first, deploy_asg&.target_group_arns&.first].compact

production_rules = listeners.map do |(key, listener)|
production_rules = listeners.flat_map do |(key, listener)|
if !listener
listener = key
key = nil
end

puts "Identifying deploy rule for #{key} listener #{listener}"
elbv2.find_rule_with_target_in_listener(
listener, target_group
puts "Identifying deploy rules for #{key} listener #{listener}"
elbv2.find_rules_with_targets_in_listener(
listener, target_groups
)
end.compact

Expand All @@ -95,7 +95,11 @@ def reset_listeners(listeners, production_asg, deploy_asg)
if production_asg
puts "Coalescing on production asg #{production_asg.auto_scaling_group_name}"
production_rules.each do |rule|
elbv2.coalesce(rule, production_asg.target_group_arns.first)
if !elbv2.coalesce(rule, production_asg.target_group_arns.first)
puts "Could not coalesece rule #{rule.rule_arn}. Destroying instead."
elbv2.delete_rule(rule.rule_arn)
puts "Destroyed #{rule.rule_arn}"
end
end
puts "Coalesced."
return :wait
Expand Down
45 changes: 25 additions & 20 deletions modules/deployomat/src/deploy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
require_relative 'lib'

module Deployomat
ACTIVE_DEPLOY_MSG = "Asserting active deploy"
NOT_ACTIVE_MSG = "No longer active deploy."
ACTIVE_ASSERT_MSG = "Asserted active"

class StartDeploy
extend Forwardable

Expand All @@ -28,7 +32,7 @@ class StartDeploy

def_delegators :@config, :account_name, :service_name, :prefix, :deploy_id, :params

attr_reader :ami_id, :new_asg_name, :bake_time, :health_timeout,
attr_reader :ami_id, :new_asg_name, :bake_time, :health_timeout, :new_tg_name,
:traffic_shift_per_step, :wait_per_step, :allow_undeploy, :automatic_undeploy_minutes

GREATER_THAN_ZERO = %i[bake_time traffic_shift_per_step wait_per_step health_timeout].freeze
Expand All @@ -38,6 +42,7 @@ def initialize(config, ami_id:, deploy_config:)
@config = config
@ami_id = ami_id
@new_asg_name = "#{service_name}-#{Time.now.utc.strftime("%Y%m%dT%H%M%SZ")}"
@new_tg_name = "#{service_name[0...15]}-#{Time.now.utc.strftime("%Y%m%dT%H%M%SZ")}"

# TODO: These should be configurable.
@bake_time = deploy_config.fetch('BakeTime', DEFAULT_BAKE_TIME)
Expand Down Expand Up @@ -128,7 +133,7 @@ def call
new_target_group_arn = nil
if exemplar_tg_arn
puts "Cloning target group..."
new_target_group = elbv2.clone_target_group(exemplar_tg_arn, new_asg_name)
new_target_group = elbv2.clone_target_group(exemplar_tg_arn, new_tg_name)
new_target_group_arn = new_target_group.target_group_arn
puts "Cloned target group #{new_target_group_arn}"
end
Expand All @@ -150,14 +155,14 @@ def call


if production_asg
puts "Asserting active deploy"
puts ACTIVE_DEPLOY_MSG
begin
@config.assert_active
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
error "No longer active deploy."
error NOT_ACTIVE_MSG
return error_response
end
puts "Asserted active"
puts ACTIVE_ASSERT_MSG
puts "Preventing scale-in of #{production_asg.auto_scaling_group_name}"
asg.prevent_scale_in(production_asg)
end
Expand All @@ -167,14 +172,14 @@ def call
if exemplar_tg_arn
production_rules = []
listeners = params.get_list_or_json("#{prefix}/config/#{service_name}/listener_arns")
listeners.each do |(key, listener)|
production_rules = listeners.flat_map do |(key, listener)|
if !listener
listener = key
key = nil
end

puts "Preparing deploy rule for #{key} listener #{listener}..."
production_rules << elbv2.prepare_deploy_rule(
puts "Preparing deploy rules for #{key} listener #{listener}..."
elbv2.prepare_deploy_rules(
listener, production_tg_arn, exemplar_tg_arn, new_target_group_arn
)
end
Expand Down Expand Up @@ -264,14 +269,14 @@ def initialize(config, max_wait:, min_healthy:, target_group_arn:, remaining_tim
def call
elbv2 = ElbV2.new(@config)

puts "Asserting active deploy"
puts ACTIVE_DEPLOY_MSG
begin
@config.assert_active
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
puts "No longer active deploy."
puts NOT_ACTIVE_MSG
return { Status: :deploy_aborted }
end
puts "Asserted active"
puts ACTIVE_ASSERT_MSG

healthy_count = elbv2.count_healthy(target_group_arn)
puts "Waiting for #{min_healthy} healthy instances, have #{healthy_count}"
Expand Down Expand Up @@ -310,14 +315,14 @@ def call

total = progress + step_size
production_rules.each do |rule|
puts "Asserting active deploy"
puts ACTIVE_DEPLOY_MSG
begin
@config.assert_active
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
puts "No longer active deploy."
puts NOT_ACTIVE_MSG
return { Status: :deploy_aborted }
end
puts "Asserted active"
puts ACTIVE_ASSERT_MSG
elbv2.shift_traffic(rule, step_size, old_target_group_arn, target_group_arn)
puts "Shifted traffic on #{rule.rule_arn} to #{total}%"
end
Expand Down Expand Up @@ -348,14 +353,14 @@ def call
production_rules = elbv2.describe_rules(rule_ids)

production_rules.each do |rule|
puts "Asserting active deploy"
puts ACTIVE_DEPLOY_MSG
begin
@config.assert_active
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
puts "No longer active deploy."
puts NOT_ACTIVE_MSG
return { Status: :deploy_aborted }
end
puts "Asserted active"
puts ACTIVE_ASSERT_MSG
elbv2.coalesce(rule, target_group_arn)
puts "Coalesced traffic on #{rule.rule_arn}"
end
Expand Down Expand Up @@ -403,14 +408,14 @@ def initialize(config, allow_undeploy:, automatic_undeploy_minutes:)
def call
asg = Asg.new(@config)

puts "Asserting active deploy"
puts ACTIVE_DEPLOY_MSG
begin
@config.assert_active
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
puts "No longer active deploy."
puts NOT_ACTIVE_MSG
return { Status: :deploy_aborted }
end
puts "Asserted active"
puts ACTIVE_ASSERT_MSG

deploy_asg = asg.get(@config.deploy_asg)
puts "Allowing scale-in of new ASG #{deploy_asg.auto_scaling_group_name}"
Expand Down
2 changes: 2 additions & 0 deletions modules/deployomat/src/lambda_handlers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def self.deploy(event:, context:)
Deployomat::FinishDeploy.new(
config, allow_undeploy: event['AllowUndeploy'], automatic_undeploy_minutes: event['AutomaticUndeployMinutes']
)
else
return { Status: :fail, Error: ["Unexpected step: '#{event['Step']}'"] }
end
op.call
end
Expand Down
Loading