manheim/cf_deployer

View on GitHub
lib/cf_deployer/driver/auto_scaling_group.rb

Summary

Maintainability
A
0 mins
Test Coverage
module CfDeployer
  module Driver
    class AutoScalingGroup
      extend Forwardable

      def_delegators :aws_group, :auto_scaling_instances, :ec2_instances, :load_balancers, :desired_capacity

      attr_reader :group_name, :group

      def initialize name, timeout = CfDeployer::Defaults::Timeout
        @group_name = name
        @timeout = timeout
      end

      def describe
        { desired: aws_group.desired_capacity, min: aws_group.min_size, max: aws_group.max_size }
      end

      def warm_up desired
        return if desired < aws_group.min_size
        desired = aws_group.max_size if desired > aws_group.max_size
        Log.info "warming up auto scaling group #{group_name} to #{desired}"

        CfDeployer::Driver::DryRun.guard "Skipping ASG warmup" do
          aws_group.set_desired_capacity desired
          wait_for_desired_capacity
        end
      end

      def warm_up_cooled_group options
        CfDeployer::Driver::DryRun.guard 'Skipping update of ASG min & max instance count' do
          aws_group.update :min_size => options[:min], :max_size => options[:max]
        end
        warm_up options[:desired]
      end

      def cool_down
        Log.info "Cooling down #{group_name}"
        CfDeployer::Driver::DryRun.guard "Skipping ASG cooldown" do
          aws_group.update :min_size => 0, :max_size => 0
          aws_group.set_desired_capacity 0
        end
      end

      def instance_statuses
        instance_info = {}
        ec2_instances.each do |instance|
          instance_info[instance.id] = CfDeployer::Driver::Instance.new(instance).status
        end
        instance_info
      end

      def wait_for_desired_capacity
        Timeout::timeout(@timeout){
          until desired_capacity_reached?
            sleep 15
          end
        }
      end

      def desired_capacity_reached?
        healthy_instance_count >= desired_capacity
      end

      def healthy_instance_ids
        instances = auto_scaling_instances.select do |instance|
          'HEALTHY'.casecmp(instance.health_status) == 0
        end
        instances.map(&:id)
      end

      def in_service_instance_ids
        elbs = load_balancers
        return [] if elbs.empty?

        ids = elbs.collect(&:instances)
                  .collect(&:health)
                  .to_a
                  .collect { |elb_healths|
                     elb_healths.select { |health| health[:state] == 'InService' }
                                .map { |health| health[:instance].id }
                  }

        ids.inject(:&)
      end

      def healthy_instance_count
        AWS.memoize do
          instances = healthy_instance_ids
          instances &= in_service_instance_ids unless load_balancers.empty?
          Log.info "Healthy instance count: #{instances.count}"
          instances.count
        end
      rescue => e
        Log.error "Unable to determine healthy instance count due to error: #{e.message}"
        -1
      end

      private

      def aws_group
        @my_group ||= AWS::AutoScaling.new.groups[group_name]
      end
    end
  end
end