Created
July 31, 2015 02:01
-
-
Save woodhull/c56cbd0a68cb9b3fd1f4 to your computer and use it in GitHub Desktop.
random bits of ruby for doing green/blue deploys
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "timeout" | |
class AutoScalingGroup | |
attr_accessor :name | |
def initialize(attrs = {}) | |
attrs.each do |key,value| | |
if self.respond_to?("#{key}=") | |
self.send("#{key}=", value) | |
end | |
end | |
end | |
def wait_until_instances_ready | |
groups.each do |group| | |
wait_until do | |
group = autoscaling.describe_auto_scaling_groups(auto_scaling_group_names: [group.auto_scaling_group_name]).auto_scaling_groups.first | |
group.instances.any? | |
end | |
group = autoscaling.describe_auto_scaling_groups(auto_scaling_group_names: [group.auto_scaling_group_name]).auto_scaling_groups.first | |
instance_ids = group.instances.collect{|i| i.instance_id} | |
puts "waiting for #{group.auto_scaling_group_name} servers to come online" | |
ec2.wait_until(:instance_running, instance_ids: instance_ids) if instance_ids.any? | |
wait_for_activities_to_complete(group) | |
end | |
end | |
def delete! | |
groups.each do |group| | |
wait_for_instances_to_delete(group) | |
autoscaling.delete_auto_scaling_group(auto_scaling_group_name: group.auto_scaling_group_name) | |
puts "waiting for #{group.auto_scaling_group_name} to delete" | |
wait_until do | |
autoscaling.describe_auto_scaling_groups(auto_scaling_group_names: [group.auto_scaling_group_name]).auto_scaling_groups.empty? | |
end | |
autoscaling.delete_launch_configuration(launch_configuration_name: group.launch_configuration_name) | |
end | |
end | |
private | |
def wait_for_activities_to_complete(group) | |
autoscaling.describe_scaling_activities(auto_scaling_group_name: group.auto_scaling_group_name).activities.each do |activity| | |
if activity.status_code != 'Successful' | |
wait_until do | |
activity = autoscaling.describe_scaling_activities(auto_scaling_group_name: group.auto_scaling_group_name, activity_ids: [activity.activity_id]).activities.first | |
activity.status_code == 'Successful' || activity.status_code == 'Failed' | |
end | |
end | |
end | |
end | |
def autoscaling | |
@autoscaling ||= Aws::AutoScaling::Client.new(region: 'us-east-1') | |
end | |
def ec2 | |
@ec2 ||= Aws::EC2::Client.new(region: 'us-east-1') | |
end | |
def groups | |
autoscaling.describe_auto_scaling_groups(auto_scaling_group_names: [name]).auto_scaling_groups | |
end | |
def wait_for_instances_to_delete(group) | |
autoscaling.update_auto_scaling_group(auto_scaling_group_name: group.auto_scaling_group_name, min_size: 0, max_size: 0, desired_capacity: 0) | |
instance_ids = group.instances.collect{|i| i.instance_id} | |
puts "waiting for #{name} to empty" | |
ec2.wait_until(:instance_terminated, instance_ids: instance_ids) if instance_ids.any? | |
wait_until do | |
autoscaling.describe_auto_scaling_groups(auto_scaling_group_names: [group.auto_scaling_group_name]).auto_scaling_groups.first.instances.empty? | |
end | |
wait_for_activities_to_complete(group) | |
end | |
def wait_until | |
Timeout.timeout(120) do | |
sleep(1) until value = yield | |
value | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
currently_blue = current_tfstate['modules'].first['resources']['aws_autoscaling_group.haproxy_blue'].present? | |
currently_green = current_tfstate['modules'].first['resources']['aws_autoscaling_group.haproxy_green'].present? | |
if currently_blue || currently_green | |
if currently_blue && currently_green | |
puts "app is in inconsistent state, removing all ASGs" | |
['blue', 'green'].each do |color| | |
delete_autoscaling_group(color) | |
end | |
refresh | |
return | |
end | |
current_tfstate_ami = if currently_blue | |
current_tfstate['modules'].first['outputs']['current_haproxy_blue_ami'] | |
else | |
current_tfstate['modules'].first['outputs']['current_haproxy_green_ami'] | |
end | |
new_ami = current_ami('agra-haproxy-server').image_id | |
if current_tfstate_ami != new_ami | |
puts "performing multi-stage blue/green apply" | |
if currently_blue | |
_apply(['blue', 'green'], {ha_proxy_server_blue_ami: current_tfstate_ami, ha_proxy_server_green_ami: new_ami}) | |
else | |
_apply(['blue', 'green'], {ha_proxy_server_blue_ami: new_ami, ha_proxy_server_green_ami: current_tfstate_ami}) | |
end | |
['blue', 'green'].each do |color| | |
AutoScalingGroup.new(name: "haproxy_asg_#{color}").wait_until_instances_ready | |
end | |
# TODO some sort of health check | |
puts "destroying old asg and launch configuration" | |
if currently_blue | |
# if currently blue, then transition to new green ASG | |
delete_autoscaling_group('blue') | |
refresh | |
generate_blue_green_haproxy_tf(['green']) | |
else | |
# if currently green, then transition to new blue ASG | |
delete_autoscaling_group('green') | |
refresh | |
generate_blue_green_haproxy_tf(['blue']) | |
end | |
else | |
puts "latest ami is already live, running normal apply" | |
if currently_blue | |
_apply(['blue'], {ha_proxy_server_blue_ami: new_ami}) | |
else | |
_apply(['green'], {ha_proxy_server_green_ami: new_ami}) | |
end | |
end | |
else | |
puts "asg blue/green resources not present, performing a standard apply to default app to blue" | |
_apply(['blue']) | |
AutoScalingGroup.new(name: "haproxy_asg_blue").wait_until_instances_ready | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
We generate the green/blue terraform scripts with erb:
def generate_elb_terraform_file
template = ERB.new File.new("config/templates/terraform/elb.tf.erb").read, nil, "%"
result = template.result(OpenStruct.new(elb_mapping: elb_mapping).instance_eval { binding })
File.write("#{config_directory}/elb.tf", result)
end
def generate_blue_green_haproxy_tf(colors)
template = ERB.new File.new("config/templates/terraform/haproxy.tf.erb").read, nil, "%"
result = template.result(OpenStruct.new(colors: colors, elb_mapping: elb_mapping).instance_eval { binding })
File.write("#{config_directory}/haproxy.tf", result)
end