class AutoCanary24::CanaryStack

Attributes

stack_name[R]

Public Class Methods

new(stack_name, wait_timeout, sleep_during_wait = 5) click to toggle source
# File lib/autocanary24/canarystack.rb, line 10
def initialize(stack_name, wait_timeout, sleep_during_wait = 5)
  raise "ERR: stack_name is missing" if stack_name.nil?
  raise "ERR: wait_timeout is missing" if wait_timeout.nil?
  @stack_name = stack_name
  @wait_timeout = wait_timeout
  @sleep_during_wait = sleep_during_wait
end

Public Instance Methods

attach_asg_to_elb_and_wait(elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 59
def attach_asg_to_elb_and_wait(elb)
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  asg_client.attach_load_balancers({auto_scaling_group_name: asg, load_balancer_names: [elb]})
  wait_for_asg_on_elb(asg, elb)
end
attach_instances_to_elb_and_wait(elb, instances) click to toggle source
# File lib/autocanary24/canarystack.rb, line 41
def attach_instances_to_elb_and_wait(elb, instances)
  elb_client = Aws::ElasticLoadBalancing::Client.new
  elb_client.register_instances_with_load_balancer({ load_balancer_name: elb, instances: instances })
  wait_for_instances_attached_to_elb(instances, elb)
end
detach_asg_from_elb_and_wait(elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 52
def detach_asg_from_elb_and_wait(elb)
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  asg_client.detach_load_balancers({auto_scaling_group_name: asg, load_balancer_names: [elb]})
  wait_for_asg_detached_from_elb(asg, elb)
end
detach_instances_from_elb(elb, instances) click to toggle source
# File lib/autocanary24/canarystack.rb, line 47
def detach_instances_from_elb(elb, instances)
  elb_client = Aws::ElasticLoadBalancing::Client.new
  elb_client.deregister_instances_from_load_balancer({ load_balancer_name: elb, instances: instances })
end
get_desired_capacity() click to toggle source
# File lib/autocanary24/canarystack.rb, line 18
def get_desired_capacity
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  describe_asg(asg).desired_capacity
end
get_instance_ids() click to toggle source
# File lib/autocanary24/canarystack.rb, line 80
def get_instance_ids
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  describe_asg(asg)[:instances] \
    .select { |i| i[:lifecycle_state]=="InService" } \
    .map{ |i| { instance_id: i[:instance_id] } }
end
is_attached_to(elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 35
def is_attached_to(elb)
  asg = get_autoscaling_group
  elbs = get_attached_loadbalancers(asg) unless asg.nil?
  (!elbs.nil? && elbs.any? { |e| e.load_balancer_name == elb && e.state != "Removing" && e.state != "Removed" })
end
is_stack_created?() click to toggle source
# File lib/autocanary24/canarystack.rb, line 88
def is_stack_created?
  begin
    get_instance_ids
  rescue
    return false
  end
  true
end
resume_asg_processes() click to toggle source
# File lib/autocanary24/canarystack.rb, line 73
def resume_asg_processes
  processes = ['Launch', 'Terminate', 'AddToLoadBalancer', 'AlarmNotification', 'AZRebalance']
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  asg_client.resume_processes({auto_scaling_group_name: asg, scaling_processes: processes})
end
set_desired_capacity_and_wait(desired_capacity) click to toggle source
# File lib/autocanary24/canarystack.rb, line 24
def set_desired_capacity_and_wait(desired_capacity)
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  resp = asg_client.set_desired_capacity({
    auto_scaling_group_name: asg,
    desired_capacity: desired_capacity,
    honor_cooldown: false,
  })
  wait_for_instances_in_asg(asg, desired_capacity)
end
suspend_asg_processes() click to toggle source
# File lib/autocanary24/canarystack.rb, line 66
def suspend_asg_processes
  processes = ['Launch', 'Terminate', 'AddToLoadBalancer', 'AlarmNotification', 'AZRebalance']
  asg = get_autoscaling_group
  asg_client = Aws::AutoScaling::Client.new
  asg_client.suspend_processes({auto_scaling_group_name: asg, scaling_processes: processes})
end

Private Instance Methods

describe_asg(asg) click to toggle source
# File lib/autocanary24/canarystack.rb, line 98
def describe_asg(asg)
  asg_client = Aws::AutoScaling::Client.new
  asg_client.describe_auto_scaling_groups({auto_scaling_group_names: [asg], max_records: 1})[:auto_scaling_groups][0]
end
get_attached_loadbalancers(asg) click to toggle source
# File lib/autocanary24/canarystack.rb, line 189
def get_attached_loadbalancers(asg)
  asg_client = Aws::AutoScaling::Client.new
  asg_client.describe_load_balancers({ auto_scaling_group_name: asg }).load_balancers
end
get_autoscaling_group() click to toggle source
# File lib/autocanary24/canarystack.rb, line 194
def get_autoscaling_group
  get_first_resource_id('AWS::AutoScaling::AutoScalingGroup')
end
get_first_resource_id(resource_type) click to toggle source
# File lib/autocanary24/canarystack.rb, line 198
def get_first_resource_id(resource_type)
  client = Aws::CloudFormation::Client.new
  begin
    response = client.list_stack_resources({ stack_name: @stack_name })
  rescue Exception
    return nil
  end
  resources = response.data.stack_resource_summaries.select{|x| x[:resource_type] == resource_type }
  resources.map{ |e| e.physical_resource_id }[0]
end
wait_for_asg_detached_from_elb(asg, elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 103
def wait_for_asg_detached_from_elb(asg, elb)
  auto_scaling_group = describe_asg(asg)

  if auto_scaling_group[:load_balancer_names].select{|l| l == elb}.length == 1
    puts "WARNING: ASG still on the ELB!"
  end

  asg_instances = auto_scaling_group[:instances] \
    .select { |i| i[:lifecycle_state]=="InService" } \
    .map{ |i| { instance_id: i[:instance_id] } }

  elb_client = Aws::ElasticLoadBalancing::Client.new
  elb_instances = elb_client.describe_instance_health({load_balancer_name: elb})[:instance_states] \
    .map{ |i| { instance_id: i[:instance_id] } }

  # Remove instances that are not registered at the ELB
  instances = elb_instances & asg_instances

  if instances.length > 0
    wait_for_instances_detached_from_elb(instances, elb)
  end
end
wait_for_asg_on_elb(asg, elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 145
def wait_for_asg_on_elb(asg, elb)
  auto_scaling_group = describe_asg(asg)

  if auto_scaling_group[:load_balancer_names].select{|l| l == elb}.length == 0
    puts "WARNING: ASG not on the ELB yet!"
  end

  instances = auto_scaling_group[:instances] \
    .select { |i| i[:lifecycle_state]=="InService" } \
    .map{ |i| { instance_id: i[:instance_id] } }

  wait_for_instances_attached_to_elb(instances, elb)
end
wait_for_instances_attached_to_elb(instances, elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 159
def wait_for_instances_attached_to_elb(instances, elb)
  elb_client = Aws::ElasticLoadBalancing::Client.new
  retries = (@wait_timeout / @sleep_during_wait).round
  while retries > 0
    begin
      elb_instances = elb_client.describe_instance_health({load_balancer_name: elb, instances: instances})
      break if elb_instances[:instance_states].select{ |s| s.state != 'InService' }.length == 0
    rescue Aws::ElasticLoadBalancing::Errors::InvalidInstance
    end
    sleep @sleep_during_wait
    retries -= 1
  end

  raise "Timeout. Couldn't wait for instances '#{instances}' to get attached to ELB '#{elb}'." if retries == 0
end
wait_for_instances_detached_from_elb(instances, elb) click to toggle source
# File lib/autocanary24/canarystack.rb, line 126
def wait_for_instances_detached_from_elb(instances, elb)
  elb_client = Aws::ElasticLoadBalancing::Client.new
  retries = (@wait_timeout / @sleep_during_wait).round
  while retries > 0
    begin
      elb_instances = {}
      Retriable.retriable(on: Aws::CloudFormation::Errors::Throttling, tries: 5, base_interval: 1) do
        elb_instances = elb_client.describe_instance_health({load_balancer_name: elb, instances: instances})
      end
      break if elb_instances[:instance_states].select{ |s| s.state == 'InService' }.length == 0
    rescue Aws::ElasticLoadBalancing::Errors::InvalidInstance
    end
    sleep @sleep_during_wait
    retries -= 1
  end

  raise "Timeout. Couldn't wait for instances '#{instances}' to get detached from ELB '#{elb}'." if retries == 0
end
wait_for_instances_in_asg(asg, expected_number_of_instances) click to toggle source
# File lib/autocanary24/canarystack.rb, line 175
def wait_for_instances_in_asg(asg, expected_number_of_instances)
  asg_client = Aws::AutoScaling::Client.new
  retries = (@wait_timeout / @sleep_during_wait).round
  while retries > 0
    instances = asg_client.describe_auto_scaling_groups({auto_scaling_group_names: [asg]})[:auto_scaling_groups][0].instances
    healthy_instances = instances.select{ |i| i[:health_status] == "Healthy" && i[:lifecycle_state]=="InService"}.length
    break if healthy_instances == expected_number_of_instances
    sleep @sleep_during_wait
    retries -= 1
  end

  raise "Timeout. Only #{healthy_instances} of #{expected_number_of_instances} instances got healthy in ASG '#{asg}'." if retries == 0
end