rollo 0.5.0 → 0.8.0.pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
4
  require_relative '../model'
3
5
 
4
6
  module Rollo
5
7
  module Commands
8
+ # rubocop:disable Metrics/ClassLength
6
9
  class Services < Thor
7
10
  namespace :services
8
11
 
@@ -11,116 +14,160 @@ module Rollo
11
14
  end
12
15
 
13
16
  desc(
14
- 'expand REGION ASG_NAME ECS_CLUSTER_NAME',
15
- 'Expands the service cluster by one batch.')
17
+ 'expand REGION ASG_NAME ECS_CLUSTER_NAME',
18
+ 'Expands the service cluster by one batch.'
19
+ )
16
20
  method_option(
17
- :batch_size,
18
- aliases: '-b',
19
- type: :numeric,
20
- default: 3,
21
- desc: 'The number of service instances to add at a time.')
21
+ :batch_size,
22
+ aliases: '-b',
23
+ type: :numeric,
24
+ default: 3,
25
+ desc: 'The number of service instances to add at a time.'
26
+ )
22
27
  method_option(
23
- :startup_time,
24
- aliases: '-t',
25
- type: :numeric,
26
- default: 2,
27
- desc: 'The number of minutes to wait for services to start up.')
28
-
28
+ :startup_time,
29
+ aliases: '-t',
30
+ type: :numeric,
31
+ default: 2,
32
+ desc: 'The number of minutes to wait for services to start up.'
33
+ )
34
+ method_option(
35
+ :maximum_instances,
36
+ aliases: '-mx',
37
+ type: :numeric,
38
+ desc: 'The maximum number of service instances to expand to.'
39
+ )
40
+ # rubocop:disable Metrics/AbcSize
41
+ # rubocop:disable Metrics/MethodLength
29
42
  def expand(
30
- region, _, ecs_cluster_name,
31
- service_cluster = nil)
43
+ region, _, ecs_cluster_name,
44
+ service_cluster = nil
45
+ )
32
46
  batch_size = options[:batch_size]
47
+ maximum_instances = options[:maximum_instances]
33
48
  service_start_wait_minutes = options[:startup_time]
34
49
  service_start_wait_seconds = 60 * service_start_wait_minutes
35
50
 
36
- service_cluster = service_cluster ||
37
- Rollo::Model::ServiceCluster.new(ecs_cluster_name, region)
51
+ service_cluster ||= Rollo::Model::ServiceCluster.new(ecs_cluster_name,
52
+ region)
38
53
 
39
54
  say("Increasing service instance counts by #{batch_size}...")
55
+ # rubocop:disable Metrics/BlockLength
40
56
  with_padding do
41
57
  service_cluster.with_replica_services do |on|
42
58
  on.start do |services|
43
59
  say(
44
- 'Service cluster contains services:' +
45
- "\n\t\t[#{services.map(&:name).join(",\n\t\t ")}]")
60
+ 'Service cluster contains services:' \
61
+ "\n\t\t[#{services.map(&:name).join(",\n\t\t ")}]"
62
+ )
46
63
  end
47
64
  on.each_service do |service|
48
65
  say(
49
- "Increasing instance count by #{batch_size} " +
50
- "for #{service.name}")
66
+ "Increasing instance count by #{batch_size} " \
67
+ "for #{service.name}"
68
+ )
69
+ # rubocop:disable Lint/ShadowingOuterLocalVariable
51
70
  with_padding do
52
- service.increase_instance_count_by(batch_size) do |on|
71
+ service.increase_instance_count_by(
72
+ batch_size, maximum_instances: maximum_instances
73
+ ) do |on|
53
74
  on.prepare do |current, target|
54
75
  say(
55
- "Changing instance count from #{current} " +
56
- "to #{target}...")
76
+ "Changing instance count from #{current} " \
77
+ "to #{target}..."
78
+ )
57
79
  end
58
80
  on.waiting_for_health do |attempt|
59
81
  say(
60
- "Waiting for service to reach a steady state " +
61
- "(attempt #{attempt})...")
82
+ 'Waiting for service to reach a steady state ' \
83
+ "(attempt #{attempt})..."
84
+ )
62
85
  end
63
86
  end
64
87
  end
88
+ # rubocop:enable Lint/ShadowingOuterLocalVariable
65
89
  end
66
90
  end
67
91
  end
92
+ # rubocop:enable Metrics/BlockLength
68
93
  say(
69
- "Waiting #{service_start_wait_minutes} minute(s) for " +
70
- 'services to finish starting...')
94
+ "Waiting #{service_start_wait_minutes} minute(s) for " \
95
+ 'services to finish starting...'
96
+ )
71
97
  with_padding do
72
98
  sleep(service_start_wait_seconds)
73
99
  say(
74
- "Waited #{service_start_wait_minutes} minute(s). " +
75
- 'Continuing...')
100
+ "Waited #{service_start_wait_minutes} minute(s). " \
101
+ 'Continuing...'
102
+ )
76
103
  end
77
104
  say('Service instance counts increased, continuing...')
78
105
  end
106
+ # rubocop:enable Metrics/AbcSize
107
+ # rubocop:enable Metrics/MethodLength
79
108
 
80
109
  desc(
81
- 'contract REGION ASG_NAME ECS_CLUSTER_NAME',
82
- 'Contracts the service cluster by one batch.')
110
+ 'contract REGION ASG_NAME ECS_CLUSTER_NAME',
111
+ 'Contracts the service cluster by one batch.'
112
+ )
83
113
  method_option(
84
- :batch_size,
85
- aliases: '-b',
86
- type: :numeric,
87
- default: 3,
88
- desc: 'The number of service instances to remove at a time.')
89
-
114
+ :batch_size,
115
+ aliases: '-b',
116
+ type: :numeric,
117
+ default: 3,
118
+ desc: 'The number of service instances to remove at a time.'
119
+ )
120
+ method_option(
121
+ :minimum_instances,
122
+ aliases: '-mn',
123
+ type: :numeric,
124
+ desc: 'The minimum number of service instances to contract to.'
125
+ )
126
+ # rubocop:disable Metrics/AbcSize
127
+ # rubocop:disable Metrics/MethodLength
90
128
  def contract(
91
- region, _, ecs_cluster_name,
92
- service_cluster = nil)
129
+ region, _, ecs_cluster_name,
130
+ service_cluster = nil
131
+ )
93
132
  batch_size = options[:batch_size]
94
133
 
95
- service_cluster = service_cluster ||
96
- Rollo::Model::ServiceCluster.new(ecs_cluster_name, region)
134
+ service_cluster ||= Rollo::Model::ServiceCluster.new(ecs_cluster_name,
135
+ region)
97
136
 
98
137
  say("Decreasing service instance counts by #{batch_size}...")
99
138
  with_padding do
100
139
  service_cluster.with_replica_services do |on|
101
140
  on.each_service do |service|
102
141
  say(
103
- "Decreasing instance count by #{batch_size} " +
104
- "for #{service.name}")
142
+ "Decreasing instance count by #{batch_size} " \
143
+ "for #{service.name}"
144
+ )
145
+ # rubocop:disable Lint/ShadowingOuterLocalVariable
105
146
  with_padding do
106
147
  service.decrease_instance_count_by(batch_size) do |on|
107
148
  on.prepare do |current, target|
108
149
  say(
109
- "Changing instance count from #{current} " +
110
- "to #{target}...")
150
+ "Changing instance count from #{current} " \
151
+ "to #{target}..."
152
+ )
111
153
  end
112
154
  on.waiting_for_health do |attempt|
113
155
  say(
114
- 'Waiting for service to reach a steady state ' +
115
- "(attempt #{attempt})...")
156
+ 'Waiting for service to reach a steady state ' \
157
+ "(attempt #{attempt})..."
158
+ )
116
159
  end
117
160
  end
118
161
  end
162
+ # rubocop:enable Lint/ShadowingOuterLocalVariable
119
163
  end
120
164
  end
121
165
  end
122
- say("Service instance counts decreased, continuing...")
166
+ say('Service instance counts decreased, continuing...')
123
167
  end
168
+ # rubocop:enable Metrics/AbcSize
169
+ # rubocop:enable Metrics/MethodLength
124
170
  end
171
+ # rubocop:enable Metrics/ClassLength
125
172
  end
126
173
  end
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'commands/main'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rollo
2
4
  module Model
3
5
  class Host
@@ -13,11 +15,11 @@ module Rollo
13
15
  @instance.terminate(should_decrement_desired_capacity: false)
14
16
  end
15
17
 
16
- def is_in_service?
18
+ def in_service?
17
19
  @instance.lifecycle_state == 'InService'
18
20
  end
19
21
 
20
- def is_healthy?
22
+ def healthy?
21
23
  @instance.health_status == 'Healthy'
22
24
  end
23
25
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aws-sdk'
2
4
  require 'hollerback'
5
+ require 'wait'
3
6
 
4
7
  require_relative './scaling_activity'
5
8
  require_relative './host'
@@ -13,11 +16,11 @@ module Rollo
13
16
  @region = region
14
17
  @asg_name = asg_name
15
18
  @asg_resource = asg_resource ||
16
- Aws::AutoScaling::Resource.new(region: region)
19
+ Aws::AutoScaling::Resource.new(region: region)
17
20
  @asg = @asg_resource.group(@asg_name)
18
21
  record_latest_scaling_activity
19
22
 
20
- @waiter = waiter || Wait.new(attempts: 300, timeout: 30, delay: 5)
23
+ @waiter = waiter || Wait.new(attempts: 720, timeout: 30, delay: 5)
21
24
  end
22
25
 
23
26
  def reload
@@ -33,30 +36,30 @@ module Rollo
33
36
  end
34
37
 
35
38
  def desired_capacity=(capacity)
36
- @asg.set_desired_capacity({desired_capacity: capacity})
39
+ @asg.set_desired_capacity({ desired_capacity: capacity })
37
40
  end
38
41
 
39
- def has_desired_capacity?
42
+ def desired_capacity?
40
43
  hosts.size == desired_capacity &&
41
- hosts.all? {|h| h.is_in_service? && h.is_healthy?}
44
+ hosts.all? { |h| h.in_service? && h.healthy? }
42
45
  end
43
46
 
44
47
  def scaling_activities
45
- @asg.activities.collect {|a| ScalingActivity.new(a)}
48
+ @asg.activities.collect { |a| ScalingActivity.new(a) }
46
49
  end
47
50
 
48
- def has_started_changing_capacity?
51
+ def started_changing_capacity?
49
52
  scaling_activities
50
- .select {|a| a.started_after_completion_of?(@last_scaling_activity)}
51
- .size > 0
53
+ .select { |a| a.started_after_completion_of?(@last_scaling_activity) }
54
+ .size.positive?
52
55
  end
53
56
 
54
- def has_completed_changing_capacity?
55
- scaling_activities.all?(&:is_complete?)
57
+ def completed_changing_capacity?
58
+ scaling_activities.all?(&:complete?)
56
59
  end
57
60
 
58
61
  def hosts
59
- @asg.instances.collect {|h| Host.new(h)}
62
+ @asg.instances.collect { |h| Host.new(h) }
60
63
  end
61
64
 
62
65
  def increase_capacity_by(capacity_delta, &block)
@@ -64,7 +67,8 @@ module Rollo
64
67
  increased = initial + capacity_delta
65
68
 
66
69
  callbacks_for(block).try_respond_with(
67
- :prepare, initial, increased)
70
+ :prepare, initial, increased
71
+ )
68
72
 
69
73
  ensure_capacity_changed_to(increased, &block)
70
74
  end
@@ -74,7 +78,8 @@ module Rollo
74
78
  decreased = initial - capacity_delta
75
79
 
76
80
  callbacks_for(block).try_respond_with(
77
- :prepare, initial, decreased)
81
+ :prepare, initial, decreased
82
+ )
78
83
 
79
84
  ensure_capacity_changed_to(decreased, &block)
80
85
  end
@@ -90,27 +95,33 @@ module Rollo
90
95
  def wait_for_capacity_change_start(&block)
91
96
  @waiter.until do |attempt|
92
97
  reload
93
- callbacks_for(block)
94
- .try_respond_with(:waiting_for_start, attempt) if block
95
- has_started_changing_capacity?
98
+ if block
99
+ callbacks_for(block)
100
+ .try_respond_with(:waiting_for_start, attempt)
101
+ end
102
+ started_changing_capacity?
96
103
  end
97
104
  end
98
105
 
99
106
  def wait_for_capacity_change_end(&block)
100
107
  @waiter.until do |attempt|
101
108
  reload
102
- callbacks_for(block)
103
- .try_respond_with(:waiting_for_end, attempt) if block
104
- has_completed_changing_capacity?
109
+ if block
110
+ callbacks_for(block)
111
+ .try_respond_with(:waiting_for_end, attempt)
112
+ end
113
+ completed_changing_capacity?
105
114
  end
106
115
  end
107
116
 
108
117
  def wait_for_capacity_health(&block)
109
118
  @waiter.until do |attempt|
110
119
  reload
111
- callbacks_for(block)
112
- .try_respond_with(:waiting_for_health, attempt) if block
113
- has_desired_capacity?
120
+ if block
121
+ callbacks_for(block)
122
+ .try_respond_with(:waiting_for_health, attempt)
123
+ end
124
+ desired_capacity?
114
125
  end
115
126
  end
116
127
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rollo
2
4
  module Model
3
5
  class ScalingActivity
@@ -18,14 +20,14 @@ module Rollo
18
20
  end
19
21
 
20
22
  def started_after_completion_of?(other)
21
- self.id != other.id &&
22
- !self.start_time.nil? &&
23
- !other.end_time.nil? &&
24
- self.start_time > other.end_time
23
+ id != other.id &&
24
+ !start_time.nil? &&
25
+ !other.end_time.nil? &&
26
+ start_time > other.end_time
25
27
  end
26
28
 
27
- def is_complete?
28
- %w(Successful Failed Cancelled).include?(@activity.status_code)
29
+ def complete?
30
+ %w[Successful Failed Cancelled].include?(@activity.status_code)
29
31
  end
30
32
  end
31
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aws-sdk'
2
4
  require 'wait'
3
5
  require 'hollerback'
@@ -6,14 +8,15 @@ module Rollo
6
8
  module Model
7
9
  class Service
8
10
  def initialize(
9
- ecs_cluster_name, ecs_service_arn, region,
10
- ecs_resource = nil, waiter = nil)
11
+ ecs_cluster_name, ecs_service_arn, region,
12
+ ecs_resource = nil, waiter = nil
13
+ )
11
14
  @ecs_cluster_name = ecs_cluster_name
12
15
  @ecs_service_arn = ecs_service_arn
13
16
  @ecs_resource = ecs_resource || Aws::ECS::Resource.new(region: region)
14
17
  reload
15
18
 
16
- @waiter = waiter || Wait.new(attempts: 300, timeout: 30, delay: 5)
19
+ @waiter = waiter || Wait.new(attempts: 720, timeout: 30, delay: 5)
17
20
  end
18
21
 
19
22
  def name
@@ -25,10 +28,10 @@ module Rollo
25
28
  end
26
29
 
27
30
  def reload
28
- @ecs_service = get_ecs_service
31
+ @ecs_service = ecs_service
29
32
  end
30
33
 
31
- def is_replica?
34
+ def replica?
32
35
  @ecs_service.scheduling_strategy == 'REPLICA'
33
36
  end
34
37
 
@@ -42,34 +45,41 @@ module Rollo
42
45
 
43
46
  def desired_count=(count)
44
47
  @ecs_resource.client
45
- .update_service(
46
- cluster: @ecs_cluster_name,
47
- service: @ecs_service_arn,
48
- desired_count: count)
48
+ .update_service(
49
+ cluster: @ecs_cluster_name,
50
+ service: @ecs_service_arn,
51
+ desired_count: count
52
+ )
49
53
  end
50
54
 
51
- def has_desired_count?
55
+ def desired_count_met?
52
56
  running_count == desired_count
53
57
  end
54
58
 
55
- def increase_instance_count_by(count_delta, &block)
59
+ def increase_instance_count_by(count_delta, options = {}, &block)
60
+ maximum = options[:maximum_instance_count] || Float::INFINITY
56
61
  initial = desired_count
57
62
  increased = initial + count_delta
63
+ target = [increased, maximum].min
58
64
 
59
65
  callbacks_for(block).try_respond_with(
60
- :prepare, initial, increased)
66
+ :prepare, initial, target
67
+ )
61
68
 
62
- ensure_instance_count(increased, &block)
69
+ ensure_instance_count(target, &block)
63
70
  end
64
71
 
65
- def decrease_instance_count_by(count_delta, &block)
72
+ def decrease_instance_count_by(count_delta, options = {}, &block)
73
+ minimum = options[:minimum_instance_count] || 0
66
74
  initial = desired_count
67
75
  decreased = initial - count_delta
76
+ target = [decreased, minimum].max
68
77
 
69
78
  callbacks_for(block).try_respond_with(
70
- :prepare, initial, decreased)
79
+ :prepare, initial, target
80
+ )
71
81
 
72
- ensure_instance_count(decreased, &block)
82
+ ensure_instance_count(target, &block)
73
83
  end
74
84
 
75
85
  def ensure_instance_count(count, &block)
@@ -80,20 +90,23 @@ module Rollo
80
90
  def wait_for_service_health(&block)
81
91
  @waiter.until do |attempt|
82
92
  reload
83
- callbacks_for(block)
84
- .try_respond_with(:waiting_for_health, attempt) if block
85
- has_desired_count?
93
+ if block
94
+ callbacks_for(block)
95
+ .try_respond_with(:waiting_for_health, attempt)
96
+ end
97
+ desired_count_met?
86
98
  end
87
99
  end
88
100
 
89
101
  private
90
102
 
91
- def get_ecs_service
103
+ def ecs_service
92
104
  @ecs_resource.client
93
- .describe_services(
94
- cluster: @ecs_cluster_name,
95
- services: [@ecs_service_arn])
96
- .services[0]
105
+ .describe_services(
106
+ cluster: @ecs_cluster_name,
107
+ services: [@ecs_service_arn]
108
+ )
109
+ .services[0]
97
110
  end
98
111
 
99
112
  def callbacks_for(block)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aws-sdk'
2
4
  require 'hollerback'
3
5
 
@@ -10,7 +12,7 @@ module Rollo
10
12
  @region = region
11
13
  @ecs_cluster_name = ecs_cluster_name
12
14
  @ecs_resource = ecs_resource || Aws::ECS::Resource.new(region: region)
13
- @ecs_cluster = get_ecs_cluster
15
+ @ecs_cluster = ecs_cluster
14
16
  end
15
17
 
16
18
  def name
@@ -18,9 +20,9 @@ module Rollo
18
20
  end
19
21
 
20
22
  def replica_services
21
- get_ecs_service_arns
22
- .collect {|arn| Service.new(@ecs_cluster_name, arn, @region)}
23
- .select(&:is_replica?)
23
+ ecs_service_arns
24
+ .collect { |arn| Service.new(@ecs_cluster_name, arn, @region) }
25
+ .select(&:replica?)
24
26
  end
25
27
 
26
28
  def with_replica_services(&block)
@@ -28,7 +30,8 @@ module Rollo
28
30
 
29
31
  callbacks = Hollerback::Callbacks.new(block)
30
32
  callbacks.try_respond_with(
31
- :start, all_replica_services)
33
+ :start, all_replica_services
34
+ )
32
35
 
33
36
  all_replica_services.each do |service|
34
37
  callbacks.try_respond_with(:each_service, service)
@@ -37,17 +40,19 @@ module Rollo
37
40
 
38
41
  private
39
42
 
40
- def get_ecs_cluster
41
- @ecs_resource.client
42
- .describe_clusters(clusters: [@ecs_cluster_name])
43
- .clusters[0]
43
+ def ecs_cluster
44
+ @ecs_resource
45
+ .client
46
+ .describe_clusters(clusters: [@ecs_cluster_name])
47
+ .clusters[0]
44
48
  end
45
49
 
46
- def get_ecs_service_arns
47
- @ecs_resource.client
48
- .list_services(cluster: @ecs_cluster.cluster_name)
49
- .inject([]) {|arns, response| arns + response.service_arns}
50
+ def ecs_service_arns
51
+ @ecs_resource
52
+ .client
53
+ .list_services(cluster: @ecs_cluster.cluster_name)
54
+ .inject([]) { |arns, response| arns + response.service_arns }
50
55
  end
51
56
  end
52
57
  end
53
- end
58
+ end
data/lib/rollo/model.rb CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'model/host_cluster'
2
4
  require_relative 'model/service_cluster'
data/lib/rollo/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rollo
2
- VERSION = "0.5.0"
4
+ VERSION = '0.8.0.pre.2'
3
5
  end
data/lib/rollo.rb CHANGED
@@ -1,4 +1,6 @@
1
- require_relative "rollo/version"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rollo/version'
2
4
  require_relative 'rollo/commands'
3
5
 
4
6
  module Rollo