ruby_aem_aws_odysseas 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
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