Class: EC2Ctl::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/ec2ctl/client.rb

Constant Summary collapse

PingCheckError =
Class.new RuntimeError
CommandNotSucceeded =
Class.new RuntimeError
NoInstanceForExecCommand =
Class.new RuntimeError
InvalidFilter =
Class.new RuntimeError
INSTANCE_IDS_LIMIT =
50
COMMAND_STATUS_FINISHED =
%w(Success TimedOut Cancelled Failed).freeze
COMMAND_STATUS_NOT_SUCCEEDED =
%w(TimedOut Cancelled Failed).freeze
SSM_DOCUMENT_NAMES =
{
  Linux:   "AWS-RunShellScript",
  Windows: "AWS-RunPowerShellScript",
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(logger: nil, load_balancer_name: nil, platform_type: "Linux", skip_ping_check: false, commands: [], working_directory: nil, execution_timeout: 3600, skip_command_waits: false, wait_interval: 5, timeout_seconds: nil, comment: "Started at #{Time.now} (#{self.class}@#{VERSION})", output_s3_bucket_name: nil, output_s3_key_prefix: nil, service_role_arn: nil, notification_arn: nil, notification_events: nil, notification_type: nil, rolling_group_size: 1, skip_draining_waits: false, skip_inservice_waits: false, inservice_wait_timeout: 180, instance_ids: [], filters: [], attributes: [], search: [], count: false, sort: nil) ⇒ Client

Returns a new instance of Client.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ec2ctl/client.rb', line 20

def initialize(
  logger:                 nil,
  load_balancer_name:     nil,
  platform_type:          "Linux",
  skip_ping_check:        false,
  commands:               [],
  working_directory:      nil,
  execution_timeout:      3600,
  skip_command_waits:     false,
  wait_interval:          5,
  timeout_seconds:        nil,
  comment:                "Started at #{Time.now} (#{self.class}@#{VERSION})",
  output_s3_bucket_name:  nil,
  output_s3_key_prefix:   nil,
  service_role_arn:       nil,
  notification_arn:       nil,
  notification_events:    nil,
  notification_type:      nil,
  rolling_group_size:     1,
  skip_draining_waits:    false,
  skip_inservice_waits:   false,
  inservice_wait_timeout: 180,
  instance_ids:           [],
  filters:                [],
  attributes:             [],
  search:                 [],
  count:                  false,
  sort:                   nil
)
  @logger = logger

  @ec2_resource = Aws::EC2::Resource.new
  @elb_client   = Aws::ElasticLoadBalancing::Client.new
  @ssm_client   = Aws::SSM::Client.new

  @load_balancer_name     = load_balancer_name
  @platform_type          = platform_type
  @skip_ping_check        = skip_ping_check
  @commands               = commands
  @working_directory      = working_directory
  @execution_timeout      = execution_timeout
  @skip_command_waits     = skip_command_waits
  @wait_interval          = wait_interval
  @timeout_seconds        = timeout_seconds
  @comment                = comment
  @output_s3_bucket_name  = output_s3_bucket_name
  @output_s3_key_prefix   = output_s3_key_prefix
  @service_role_arn       = service_role_arn
  @notification_arn       = notification_arn
  @notification_events    = notification_events
  @notification_type      = notification_type
  @skip_draining_waits    = skip_draining_waits
  @skip_inservice_waits   = skip_inservice_waits
  @inservice_wait_timeout = inservice_wait_timeout
  @instance_ids           = instance_ids
  @filters                = filters
  @attributes             = attributes
  @search                 = search
  @count                  = count
  @sort                   = sort

  if @load_balancer_name
    @elb_instance_ids = elb_instance_states.map(&:instance_id).select do |instance_id|
      if instance_ids.empty?
        true
      else
        instance_ids.include? instance_id
      end
    end

    @rolling_group_size = [rolling_group_size, @elb_instance_ids.size].min
  end
end

Instance Method Details

#attach_instances(instance_ids = []) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/ec2ctl/client.rb', line 229

def attach_instances(instance_ids = [])
  response = @elb_client.register_instances_with_load_balancer(
    load_balancer_name: @load_balancer_name,
    instances:          instance_ids.map {|i| {instance_id: i}},
  )

  status = {
    attached:   instance_ids,
    registered: response.instances.map(&:instance_id),
  }

  if @logger
    if @logger.debug?
      @logger.debug status: status
    else
      @logger.info attached: instance_ids
    end
  end

  status
end

#detach_instances(instance_ids = []) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/ec2ctl/client.rb', line 207

def detach_instances(instance_ids = [])
  response = @elb_client.deregister_instances_from_load_balancer(
    load_balancer_name: @load_balancer_name,
    instances:          instance_ids.map {|i| {instance_id: i}},
  )

  status = {
    detached:   instance_ids,
    registered: response.instances.map(&:instance_id),
  }

  if @logger
    if @logger.debug?
      @logger.debug status: status
    else
      @logger.info detached: instance_ids
    end
  end

  status
end

#ec2_executeObject



127
128
129
130
# File 'lib/ec2ctl/client.rb', line 127

def ec2_execute
  ping_check ec2_instances.map(&:instance_id) unless @skip_ping_check
  execute_commands ec2_instances.map(&:instance_id)
end

#ec2_listObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ec2ctl/client.rb', line 94

def ec2_list
  if @logger
    if @logger.debug?
      @logger.debug ec2_instances:       ec2_instances
      @logger.debug ssm_instance_states: ssm_instance_states(ec2_instances.map(&:instance_id))
    else
      ec2_instances_summary = if @count
        @attributes.each_with_object Hash.new do |attribute, attributes_memo|
          attribute_hash = ec2_instances.each_with_object Hash.new(0) do |instance, instances_memo|
            instances_memo[query_instance_attribute(instance, attribute)] += 1
          end

          attributes_memo[attribute] = attribute_hash
        end
      else
        ec2_instances.each_with_object Array.new do |instance, instances_memo|
          instance_summary = @attributes.each_with_object Hash.new do |attribute, attributes_memo|
            attributes_memo[attribute] = query_instance_attribute(instance, attribute)
          end

          instances_memo.push instance_summary
        end
      end

      @logger.info ec2_instances_summary: ec2_instances_summary
    end
  end

  {
    ec2_instances: ec2_instances,
  }
end

#elb_attachObject



172
173
174
# File 'lib/ec2ctl/client.rb', line 172

def elb_attach
  attach_instances @instance_ids
end

#elb_detachObject



176
177
178
# File 'lib/ec2ctl/client.rb', line 176

def elb_detach
  detach_instances @instance_ids
end

#elb_executeObject



180
181
182
183
# File 'lib/ec2ctl/client.rb', line 180

def elb_execute
  ping_check @elb_instance_ids unless @skip_ping_check
  execute_commands @elb_instance_ids
end

#elb_gracefulObject



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/ec2ctl/client.rb', line 185

def elb_graceful
  ping_check @elb_instance_ids unless @skip_ping_check

  @elb_instance_ids.each_slice(@rolling_group_size).to_a.tap do |instance_id_groups|
    instance_id_groups.each.with_index do |instance_id_group, group_index|
      @logger.info(progress: {
        completed: @rolling_group_size * group_index,
        remaining: @elb_instance_ids.size - @rolling_group_size * group_index,
        total:     @elb_instance_ids.size,
      }) if @logger

      detach_instances instance_id_group
      wait_draining instance_id_group unless @skip_draining_waits
      execute_commands instance_id_group
      attach_instances instance_id_group
      wait_inservice instance_id_group unless @skip_inservice_waits
    end

    @logger.info "Everything done!" if @logger
  end
end

#elb_listObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/ec2ctl/client.rb', line 132

def elb_list
  if @logger
    if @logger.debug?
      @logger.debug load_balancer_descriptions: load_balancer_descriptions
    else
      @logger.info load_balancer_descriptions_summary: load_balancer_descriptions.map {|lb|
        {
          load_balancer_name: lb.load_balancer_name,
          dns_name:           lb.dns_name,
          instances:          lb.instances.size,
        }
      }
    end
  end

  {load_balancer_descriptions: load_balancer_descriptions}
end

#elb_statusObject



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/ec2ctl/client.rb', line 150

def elb_status
  load_balancer_description = load_balancer_descriptions.first

  if @logger
    @logger.info elb_instance_counts: elb_instance_counts
    @logger.info elb_instance_states: elb_instance_states

    instance_ids = elb_instance_states.map(&:instance_id)

    @logger.debug ssm_instance_ping_counts:  ssm_instance_ping_counts(instance_ids)
    @logger.debug ssm_instance_states:       ssm_instance_states(instance_ids)
    @logger.debug load_balancer_description: load_balancer_description
    @logger.debug load_balancer_attributes:  load_balancer_attributes
  end

  {
    load_balancer_description: load_balancer_description,
    load_balancer_attributes:  load_balancer_attributes,
    elb_instance_states:       elb_instance_states,
  }
end

#wait_draining(instance_ids = []) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/ec2ctl/client.rb', line 251

def wait_draining(instance_ids = [])
  connection_draining = load_balancer_attributes.connection_draining

  unless connection_draining.enabled
    @logger.info wait_draining_timeout: "Disabled.".freeze if @logger
    return
  end

  if @logger
    @logger.debug detached_instances: elb_instance_states!(instance_ids)
    @logger.info wait_draining_timeout: connection_draining.timeout
  end

  sleep connection_draining.timeout
end

#wait_inservice(instance_ids = []) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/ec2ctl/client.rb', line 267

def wait_inservice(instance_ids = [])
  s = elb_instance_states! instance_ids

  @logger.info wait_instance_inservice: s if @logger

  Timeout.timeout @inservice_wait_timeout do
    loop do
      sleep @wait_interval

      s = elb_instance_states! instance_ids
      @logger.debug wait_instance_inservice: s if @logger

      break if s.all? {|i| i.state == "InService".freeze}
    end
  end

  @logger.info wait_instance_inservice: s if @logger
end