ruby_aem_aws_odysseas 1.4.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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/conf/gem.yaml +1 -0
  3. data/lib/ruby_aem_aws/abstract/cloudwatch.rb +83 -0
  4. data/lib/ruby_aem_aws/abstract/component.rb +54 -0
  5. data/lib/ruby_aem_aws/abstract/grouped_component.rb +39 -0
  6. data/lib/ruby_aem_aws/abstract/single_component.rb +36 -0
  7. data/lib/ruby_aem_aws/abstract/snapshot.rb +35 -0
  8. data/lib/ruby_aem_aws/abstract/stackmanager.rb +68 -0
  9. data/lib/ruby_aem_aws/architecture/consolidated_stack.rb +49 -0
  10. data/lib/ruby_aem_aws/architecture/full_set_stack.rb +141 -0
  11. data/lib/ruby_aem_aws/architecture/stack_manager.rb +44 -0
  12. data/lib/ruby_aem_aws/client/cloudwatch.rb +87 -0
  13. data/lib/ruby_aem_aws/client/dynamo_db.rb +36 -0
  14. data/lib/ruby_aem_aws/client/s3.rb +42 -0
  15. data/lib/ruby_aem_aws/client/sns_topic.rb +30 -0
  16. data/lib/ruby_aem_aws/component/author.rb +71 -0
  17. data/lib/ruby_aem_aws/component/author_dispatcher.rb +84 -0
  18. data/lib/ruby_aem_aws/component/author_primary.rb +62 -0
  19. data/lib/ruby_aem_aws/component/author_publish_dispatcher.rb +56 -0
  20. data/lib/ruby_aem_aws/component/author_standby.rb +62 -0
  21. data/lib/ruby_aem_aws/component/chaos_monkey.rb +78 -0
  22. data/lib/ruby_aem_aws/component/component_descriptor.rb +28 -0
  23. data/lib/ruby_aem_aws/component/orchestrator.rb +78 -0
  24. data/lib/ruby_aem_aws/component/publish.rb +78 -0
  25. data/lib/ruby_aem_aws/component/publish_dispatcher.rb +83 -0
  26. data/lib/ruby_aem_aws/component/stack_manager_resources.rb +90 -0
  27. data/lib/ruby_aem_aws/constants.rb +53 -0
  28. data/lib/ruby_aem_aws/error.rb +68 -0
  29. data/lib/ruby_aem_aws/mixins/healthy_count_verifier.rb +154 -0
  30. data/lib/ruby_aem_aws/mixins/healthy_resource_verifier.rb +188 -0
  31. data/lib/ruby_aem_aws/mixins/healthy_state_verifier.rb +45 -0
  32. data/lib/ruby_aem_aws/mixins/instance_describer.rb +34 -0
  33. data/lib/ruby_aem_aws/mixins/metric_verifier.rb +189 -0
  34. data/lib/ruby_aem_aws/mixins/snapshot_verifier.rb +37 -0
  35. data/lib/ruby_aem_aws_odysseas.rb +152 -0
  36. metadata +148 -0
@@ -0,0 +1,90 @@
1
+ # Copyright 2018 Shine Solutions
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../abstract/stackmanager'
16
+ require_relative '../client/sns_topic'
17
+ require_relative '../client/dynamo_db'
18
+ require_relative '../client/s3'
19
+ require_relative '../mixins/metric_verifier'
20
+
21
+ module RubyAemAws
22
+ module Component
23
+ # Interface to the AWS StackManager to send out commands
24
+ class StackManagerResources
25
+ attr_reader :dynamodb_client, :s3_client, :s3_resource, :cloud_watch_client, :cloud_watch_log_client
26
+ include AbstractStackManager
27
+ include DynamoDB
28
+ include MetricVerifier
29
+ include SNSTopic
30
+ include S3Access
31
+
32
+ # @param dynamodb_client AWS DynamoDB client connection
33
+ # @param params Array of AWS Clients and Resource connections:
34
+ # - CloudWatchClient: AWS Cloudwatch Client.
35
+ # - CloudWatchLogsClient: AWS Cloudwatch Logs Client.
36
+ # - DynamoDBClient: AWS DynamoDB Client.
37
+ # - S3Client: AWS S3 Client.
38
+ # - S3Resource: AWS S3 Resource connection.
39
+ # @return new RubyAemAws::StackManager::StackManagerResources
40
+ def initialize(params)
41
+ @dynamodb_client = params[:DynamoDBClient]
42
+ @s3_client = params[:S3Client]
43
+ @s3_resource = params[:S3Resource]
44
+ @cloud_watch_client = params[:CloudWatchLogsClient]
45
+ @cloud_watch_log_client = params[:CloudWatchLogsClient]
46
+ end
47
+
48
+ # @param topicarn AWS SNS-Topic ARN
49
+ # @param task AEM StackManager task
50
+ # @param stack_prefix AEM Stack-Prefix
51
+ # @param details AEM StackManager task detail message
52
+ # @return AWS SNS publish message id
53
+ def sns_publish(topicarn, task, stack_prefix, details)
54
+ details = JSON.generate(details).tr('\"', '\'')
55
+ publish(topicarn, message_for_sns_publish(task, stack_prefix, details))
56
+ end
57
+
58
+ # @param dynamodb_tablename AWS DynamoDB table name
59
+ # @param attribute_value Attribute value to scan for
60
+ # @return Scan result
61
+ def dyn_db_msg_scan(dynamodb_tablename, attribute_value)
62
+ scan(filter_for_db_scan(dynamodb_tablename, attribute_value))
63
+ end
64
+
65
+ # @param dynamodb_tablename AWS DynamoDB table name
66
+ # @param attribute_value Attribute value to query for
67
+ # @return Command state
68
+ def dyn_db_cmd_query(dynamodb_tablename, attribute_value)
69
+ key_attribute_value = attribute_value.items[0]['command_id']
70
+ state = query(filter_for_db_query(dynamodb_tablename, key_attribute_value))
71
+ state.items[0]['state']
72
+ end
73
+
74
+ # @param s3_bucket_name S3 bucketname
75
+ # @param s3_object_name S3 Object name
76
+ # @return AWS S3 resource object
77
+ def s3_resource_object(s3_bucket_name, s3_object_name)
78
+ get_s3_bucket_object(s3_bucket_name, s3_object_name)
79
+ end
80
+
81
+ # @param s3_bucket_name S3 bucketname
82
+ # @param s3_object_name S3 Object name
83
+ # @param dest_path local download path, default: nil
84
+ # @return AWS S3 client object
85
+ def s3_download_object(s3_bucket_name, s3_object_name, dest_path = nil)
86
+ get_s3_object(s3_bucket_name, s3_object_name, dest_path)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ # Copyright 2018 Shine Solutions
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module RubyAemAws
16
+ class InstanceState
17
+ PENDING = 'pending'.freeze
18
+ RUNNING = 'running'.freeze
19
+ SHUTTING_DOWN = 'shutting_down'.freeze
20
+ TERMINATED = 'terminated'.freeze
21
+ STOPPING = 'stopping'.freeze
22
+ STOPPED = 'stopped'.freeze
23
+
24
+ ALL_ACTIVE = [PENDING, RUNNING, SHUTTING_DOWN, STOPPING, STOPPED].freeze
25
+ end
26
+
27
+ # API InstanceState codes https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_InstanceState.html
28
+ class InstanceStateCode
29
+ PENDING = 0
30
+ RUNNING = 16
31
+ SHUTTING_DOWN = 32
32
+ TERMINATED = 48
33
+ STOPPING = 64
34
+ STOPPED = 80
35
+ end
36
+
37
+ class ELBInstanceState
38
+ INSERVICE = 'InService'.freeze
39
+ OUTOFSERVICE = 'OutOfService'.freeze
40
+
41
+ ALL_ACTIVE = [INSERVICE, OUTOFSERVICE].freeze
42
+ end
43
+
44
+ class Constants
45
+ REGION_DEFAULT = ENV['AWS_DEFAULT_REGION'] || ENV['aws_default_region'] || 'ap-southeast-2'.freeze
46
+ ACCESS_KEY_ID = ENV['AWS_ACCESS_KEY_ID'] || ENV['aws_access_key_id']
47
+ SECRET_ACCESS_KEY = ENV['AWS_SECRET_ACCESS_KEY'] || ENV['aws_scret_access_key']
48
+ PROFILE = ENV['AWS_PROFILE']
49
+ INSTANCE_STATE_HEALTHY = RubyAemAws::InstanceState::RUNNING.freeze
50
+ INSTANCE_STATE_CODE_RUNNING = RubyAemAws::InstanceStateCode::RUNNING
51
+ ELB_INSTANCE_INSERVICE = RubyAemAws::ELBInstanceState::INSERVICE.freeze
52
+ end
53
+ end
@@ -0,0 +1,68 @@
1
+ # Copyright 2018 Shine Solutions
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module RubyAemAws
16
+ # Raise this when a method is not yet implemented.
17
+ class NotYetImplementedError < StandardError
18
+ def initialize(msg = 'Not yet implemented')
19
+ super
20
+ end
21
+ end
22
+
23
+ # Raise this when ELB is misconfigured.
24
+ class ELBMisconfiguration < StandardError
25
+ def initialize(msg = 'ELB misconfigured')
26
+ super
27
+ end
28
+ end
29
+
30
+ # Raise this when ASG is misconfigured.
31
+ class ASGMisconfiguration < StandardError
32
+ def initialize(msg = 'ASG misconfigured')
33
+ super
34
+ end
35
+ end
36
+
37
+ # Raise this when a unknown Response received.
38
+ class UnknownResponse < StandardError
39
+ def initialize(msg = 'Unknown response code')
40
+ super
41
+ end
42
+ end
43
+
44
+ # Raise this when a component unexpectedly has more than one instance.
45
+ class ExpectedSingleInstanceError < StandardError
46
+ # def initialize(count, descriptor)
47
+ # message << 'Expected exactly one instance'
48
+ # message << " but got #{count}" unless count.nil?
49
+ # message << "for (#{descriptor.stack_prefix}, #{descriptor.ec2.component}, #{descriptor.ec2.name})" unless descriptor.nil?
50
+ # super(message)
51
+ # end
52
+
53
+ def initialize(msg = 'Expected exactly one instance')
54
+ super
55
+ end
56
+ end
57
+
58
+ # Raise this when credentials can't be found
59
+ class ArgumentError < StandardError
60
+ def initialize(msg = "No credentials found!
61
+ Set environment variable AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY or AWS_PROFILE.
62
+ Alternative use following syntax:
63
+ RubyAemAws::AemAws.new(aws_access_key_id, aws_scret_access_key) or
64
+ RubyAemAws::AemAws.new(credentials_profile_name)")
65
+ super
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,154 @@
1
+ # Copyright 2018 Shine Solutions
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../constants'
16
+
17
+ module RubyAemAws
18
+ # Mixin for checking health of a component via ELB 'healthy' count vs ASG desired_capacity.
19
+ # Add this to a component to make it capable of determining its own health.
20
+ module HealthyCountVerifier
21
+ # Aggregate health_states considered healthy.
22
+ # @return health_state is ready or scaling.
23
+ def healthy?
24
+ %i[ready scaling].include? health_state
25
+ end
26
+
27
+ # Provides detail of the state of the instances comprising the component.
28
+ # @return one of:
29
+ # - no_asg: AutoScalingGroup could not be located (by StackPrefix and Component tags).
30
+ # - no_elb: ElasticLoadBalancer could not be located (by StackPrefix and aws:cloudformation:logical-id tags).
31
+ # - misconfigured: AutoScalingGroup.desired_capacity is less than 1.
32
+ # - recovering: ELB running instance count is less than AutoScalingGroup.desired_capacity.
33
+ # - scaling: ELB running instance count is more than AutoScalingGroup.desired_capacity.
34
+ # - ready: ELB running instance count is equal to AutoScalingGroup.desired_capacity.
35
+ def health_state
36
+ asg = find_auto_scaling_group(asg_client)
37
+ return :no_asg if asg.nil?
38
+
39
+ # Debug:
40
+ # unless asg.nil?
41
+ # puts("ASG: #{asg} #{asg.auto_scaling_group_name} (#{asg.desired_capacity})")
42
+ # asg.instances.each do |i|
43
+ # puts(" Instance #{i.instance_id}: #{i.health_status}")
44
+ # end
45
+ # end
46
+
47
+ elb = find_elb(elb_client)
48
+ return :no_elb if elb.nil?
49
+
50
+ elb_running_instances = 0
51
+ get_instances_state_from_elb(elb).each do |i|
52
+ elb_running_instances += 1 if i[:state] == RubyAemAws::Constants::INSTANCE_STATE_HEALTHY
53
+ end
54
+
55
+ desired_capacity = asg.desired_capacity
56
+
57
+ return :misconfigured if desired_capacity < 1
58
+ return :recovering if elb_running_instances < desired_capacity
59
+ return :scaling if elb_running_instances > desired_capacity
60
+
61
+ :ready
62
+ end
63
+
64
+ # @return true, if all EC2 instances within the ELB are running
65
+ def wait_until_healthy
66
+ raise ELBMisconfiguration if health_state.eql?(:misconfigured)
67
+
68
+ sleep 60 while health_state.eql?(:recovering) || health_state.eql?(:scaling)
69
+ return true if health_state.eql?(:ready)
70
+ end
71
+
72
+ private
73
+
74
+ # @return AutoScalingGroup by StackPrefix and Component tags.
75
+ def find_auto_scaling_group(asg_client)
76
+ autoscaling_groups = asg_client.describe_auto_scaling_groups(max_records: 50)
77
+ find_auto_scaling_group = find_auto_scaling_group_name(autoscaling_groups)
78
+
79
+ return find_auto_scaling_group unless find_auto_scaling_group.nil?
80
+
81
+ until autoscaling_groups.next_token.nil?
82
+ autoscaling_groups = asg_client.describe_auto_scaling_groups(max_records: 50, next_token: autoscaling_groups.next_token)
83
+ find_auto_scaling_group = find_auto_scaling_group_name(autoscaling_groups)
84
+ return find_auto_scaling_group unless find_auto_scaling_group.nil?
85
+ end
86
+ return nil if find_auto_scaling_group.nil?
87
+ end
88
+
89
+ def find_auto_scaling_group_name(autoscaling_groups)
90
+ autoscaling_groups.auto_scaling_groups.each do |autoscaling_group|
91
+ asg_matches_stack_prefix = false
92
+ asg_matches_component = false
93
+ tags = autoscaling_group.tags
94
+ tags.each do |tag|
95
+ if tag.key == 'StackPrefix' && tag.value == descriptor.stack_prefix
96
+ asg_matches_stack_prefix = true
97
+ break if asg_matches_component
98
+
99
+ next
100
+ end
101
+ if tag.key == 'Component' && tag.value == descriptor.ec2.component
102
+ asg_matches_component = true
103
+ break if asg_matches_stack_prefix
104
+ end
105
+ end
106
+ return autoscaling_group if asg_matches_stack_prefix && asg_matches_component
107
+ end
108
+ nil
109
+ end
110
+
111
+ # @return ElasticLoadBalancer by StackPrefix and logical-id tags.
112
+ def find_elb(elb_client)
113
+ elbs = elb_client.describe_load_balancers.load_balancer_descriptions
114
+ elbs.each do |elb|
115
+ elb_matches_stack_prefix = false
116
+ elb_matches_logical_id = false
117
+ tag_descriptions = elb_client.describe_tags(load_balancer_names: [elb.load_balancer_name]).tag_descriptions
118
+ next if tag_descriptions.empty?
119
+
120
+ tags = tag_descriptions[0].tags
121
+ tags.each do |tag|
122
+ if tag.key == 'StackPrefix' && tag.value == descriptor.stack_prefix
123
+ elb_matches_stack_prefix = true
124
+ break if elb_matches_logical_id
125
+
126
+ next
127
+ end
128
+ if tag.key == 'aws:cloudformation:logical-id' && tag.value == descriptor.elb.id
129
+ elb_matches_logical_id = true
130
+ break if elb_matches_stack_prefix
131
+ end
132
+ end
133
+ return elb if elb_matches_stack_prefix && elb_matches_logical_id
134
+ end
135
+ nil
136
+ end
137
+
138
+ def get_instances_state_from_elb(elb)
139
+ stack_prefix_instances = []
140
+ elb.instances.each do |i|
141
+ instance = get_instance_by_id(i.instance_id)
142
+ next if instance.nil?
143
+
144
+ instance.tags.each do |tag|
145
+ next if tag.key != 'StackPrefix'
146
+ break if tag.value != descriptor.stack_prefix
147
+
148
+ stack_prefix_instances.push(id: i.instance_id, state: instance.state.name)
149
+ end
150
+ end
151
+ stack_prefix_instances
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,188 @@
1
+ # Copyright 2018 Shine Solutions
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../constants'
16
+
17
+ module RubyAemAws
18
+ # Mixin for checking health of a component via ELB 'healthy' count vs ASG desired_capacity.
19
+ # Add this to a component to make it capable of determining its own health.
20
+ module HealthyResourceVerifier
21
+ # Aggregate health_states considered healthy.
22
+ # @return health_state_elb is ready or scaling.
23
+ def healthy_elb?
24
+ %i[ready scaling].include? health_state_elb
25
+ end
26
+
27
+ # Aggregate health_states considered healthy.
28
+ # @return health_state_asg is ready or scaling.
29
+ def healthy_asg?
30
+ %i[ready scaling].include? health_state_asg
31
+ end
32
+
33
+ # Provides detail of the state of the instances comprising the component.
34
+ # @return one of:
35
+ # - no_asg: AutoScalingGroup could not be located (by StackPrefix and Component tags).
36
+ # - no_elb: ElasticLoadBalancer could not be located (by StackPrefix and aws:cloudformation:logical-id tags).
37
+ # - misconfigured: AutoScalingGroup.desired_capacity is less than 1.
38
+ # - recovering: ELB running instance count is less than AutoScalingGroup.desired_capacity.
39
+ # - scaling: ELB running instance count is more than AutoScalingGroup.desired_capacity.
40
+ # - ready: ELB running instance count is equal to AutoScalingGroup.desired_capacity.
41
+ def health_state_elb
42
+ asg = find_auto_scaling_group(asg_client)
43
+ return :no_asg if asg.nil?
44
+
45
+ # Debug:
46
+ # unless asg.nil?
47
+ # puts("ASG: #{asg} #{asg.auto_scaling_group_name} (#{asg.desired_capacity})")
48
+ # asg.instances.each do |i|
49
+ # puts(" Instance #{i.instance_id}: #{i.health_status}")
50
+ # end
51
+ # end
52
+
53
+ elb = find_elb(elb_client)
54
+ return :no_elb if elb.nil?
55
+
56
+ elb_instance_state = elb_client.describe_instance_health(load_balancer_name: elb.load_balancer_name)
57
+
58
+ elb_running_instances = 0
59
+ elb_instance_state.instance_states.each do |i|
60
+ elb_running_instances += 1 if i.state == RubyAemAws::Constants::ELB_INSTANCE_INSERVICE
61
+ end
62
+
63
+ desired_capacity = asg.desired_capacity
64
+
65
+ return :misconfigured if desired_capacity < 1
66
+ return :recovering if elb_running_instances < desired_capacity
67
+ return :scaling if elb_running_instances > desired_capacity
68
+
69
+ :ready
70
+ end
71
+
72
+ # Provides detail of the state of the instances comprising the component.
73
+ # @return one of:
74
+ # - no_asg: AutoScalingGroup could not be located (by StackPrefix and Component tags).
75
+ # - misconfigured: AutoScalingGroup.desired_capacity is less than 1.
76
+ # - recovering: ELB running instance count is less than AutoScalingGroup.desired_capacity.
77
+ # - scaling: ELB running instance count is more than AutoScalingGroup.desired_capacity.
78
+ # - ready: ELB running instance count is equal to AutoScalingGroup.desired_capacity.
79
+ def health_state_asg
80
+ asg = find_auto_scaling_group(asg_client)
81
+ return :no_asg if asg.nil?
82
+
83
+ # Debug:
84
+ # unless asg.nil?
85
+ # puts("ASG: #{asg} #{asg.auto_scaling_group_name} (#{asg.desired_capacity})")
86
+ # asg.instances.each do |i|
87
+ # puts(" Instance #{i.instance_id}: #{i.health_status}")
88
+ # end
89
+ # end
90
+ instances_inservice = 0
91
+ desired_capacity = asg.desired_capacity
92
+
93
+ get_all_instances.each do |i|
94
+ next if i.nil? || i.state.code != Constants::INSTANCE_STATE_CODE_RUNNING
95
+ return false if i.state.name != Constants::INSTANCE_STATE_HEALTHY
96
+
97
+ instances_inservice += 1
98
+ end
99
+
100
+ return :misconfigured if desired_capacity < 1
101
+ return :recovering if instances_inservice < desired_capacity
102
+ return :scaling if instances_inservice > desired_capacity
103
+
104
+ :ready
105
+ end
106
+
107
+ # @return true, if all instances within the ELB are inService
108
+ def wait_until_healthy_elb
109
+ raise ELBMisconfiguration if health_state_elb.eql?(:misconfigured)
110
+
111
+ sleep 60 while health_state_elb.eql?(:recovering) || health_state_elb.eql?(:scaling)
112
+ return true if health_state_elb.eql?(:ready)
113
+ end
114
+
115
+ def wait_until_healthy_asg
116
+ raise ASGMisconfiguration if health_state_asg.eql?(:misconfigured)
117
+
118
+ sleep 60 while health_state_asg.eql?(:recovering) || health_state_asg.eql?(:scaling)
119
+ return true if health_state_asg.eql?(:ready)
120
+ end
121
+
122
+ private
123
+
124
+ # @return AutoScalingGroup by StackPrefix and Component tags.
125
+ def find_auto_scaling_group(asg_client)
126
+ autoscaling_groups = asg_client.describe_auto_scaling_groups(max_records: 50)
127
+ find_auto_scaling_group = find_auto_scaling_group_name(autoscaling_groups)
128
+
129
+ return find_auto_scaling_group unless find_auto_scaling_group.nil?
130
+
131
+ until autoscaling_groups.next_token.nil?
132
+ autoscaling_groups = asg_client.describe_auto_scaling_groups(max_records: 50, next_token: autoscaling_groups.next_token)
133
+ find_auto_scaling_group = find_auto_scaling_group_name(autoscaling_groups)
134
+ return find_auto_scaling_group unless find_auto_scaling_group.nil?
135
+ end
136
+ return nil if find_auto_scaling_group.nil?
137
+ end
138
+
139
+ def find_auto_scaling_group_name(autoscaling_groups)
140
+ autoscaling_groups.auto_scaling_groups.each do |autoscaling_group|
141
+ asg_matches_stack_prefix = false
142
+ asg_matches_component = false
143
+ tags = autoscaling_group.tags
144
+ tags.each do |tag|
145
+ if tag.key == 'StackPrefix' && tag.value == descriptor.stack_prefix
146
+ asg_matches_stack_prefix = true
147
+ break if asg_matches_component
148
+
149
+ next
150
+ end
151
+ if tag.key == 'Component' && tag.value == descriptor.ec2.component
152
+ asg_matches_component = true
153
+ break if asg_matches_stack_prefix
154
+ end
155
+ end
156
+ return autoscaling_group if asg_matches_stack_prefix && asg_matches_component
157
+ end
158
+ nil
159
+ end
160
+
161
+ # @return ElasticLoadBalancer by StackPrefix and logical-id tags.
162
+ def find_elb(elb_client)
163
+ elbs = elb_client.describe_load_balancers.load_balancer_descriptions
164
+ elbs.each do |elb|
165
+ elb_matches_stack_prefix = false
166
+ elb_matches_logical_id = false
167
+ tag_descriptions = elb_client.describe_tags(load_balancer_names: [elb.load_balancer_name]).tag_descriptions
168
+ next if tag_descriptions.empty?
169
+
170
+ tags = tag_descriptions[0].tags
171
+ tags.each do |tag|
172
+ if tag.key == 'StackPrefix' && tag.value == descriptor.stack_prefix
173
+ elb_matches_stack_prefix = true
174
+ break if elb_matches_logical_id
175
+
176
+ next
177
+ end
178
+ if tag.key == 'aws:cloudformation:logical-id' && tag.value == descriptor.elb.id
179
+ elb_matches_logical_id = true
180
+ break if elb_matches_stack_prefix
181
+ end
182
+ end
183
+ return elb if elb_matches_stack_prefix && elb_matches_logical_id
184
+ end
185
+ nil
186
+ end
187
+ end
188
+ end