ruby_aem_aws 0.9.0 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 39ea851d8557738c522f0f06fd811dee54378d83
4
- data.tar.gz: 931e7372b825518b9e0ad3b23498b0013831c0bb
3
+ metadata.gz: 2611b58397e0504f3ae61a016d05397f2958b71a
4
+ data.tar.gz: 81d26e4fab9981f591ac10a56725d9a1b467895e
5
5
  SHA512:
6
- metadata.gz: da5c388b8803836046e97f8af7ce3308fbd419b230a7fa86483d54508c0f8f0bfdbeb149cc6a3c41dffb2577ddd8387c3a3443b09835f64a75221b86eb53c312
7
- data.tar.gz: f66ff53f11b7f035fa337ce3524f5c9630c489b453e799887b09e6e56f6e7ec2237c90e9a7acc88a0332d6241b66a23ead413f9eaa94e2d30aa09c12bb229535
6
+ metadata.gz: ec8d87c5a984a70506d23a849ed3c21209e4db952c7efe3f35e56d0eb4bdfb3c2130aab5ac26c7338317637a65c8b41b99d066db6a2d91736d3a2c0668c68622
7
+ data.tar.gz: a10df33c155883eccbe5b3adf3c16dd68cb0ae1e629ed070045889b7b5d8d094a9d7354ee05b32eef8b3c2e470cc389bddc7d171781c1132fad7cc615b579a39
@@ -14,15 +14,30 @@
14
14
 
15
15
  require_relative 'ruby_aem_aws/consolidated_stack'
16
16
  require_relative 'ruby_aem_aws/full_set_stack'
17
+ require_relative 'ruby_aem_aws/stack_manager'
17
18
 
18
19
  module RubyAemAws
19
20
  # AemAws class represents the AWS stack for AEM.
20
21
  class AemAws
21
22
  # @param conf configuration hash of the following configuration values:
22
23
  # - region: the AWS region (eg ap-southeast-2)
24
+ # - aws_access_key_id: the AWS access key
25
+ # - aws_secret_access_key: the AWS secret access key
26
+ # - aws_profile: AWS profile name
23
27
  # @return new RubyAemAws::AemAws instance
24
28
  def initialize(conf = {})
25
29
  conf[:region] ||= Constants::REGION_DEFAULT
30
+ conf[:aws_access_key_id] ||= Constants::ACCESS_KEY_ID
31
+ conf[:aws_secret_access_key] ||= Constants::SECRET_ACCESS_KEY
32
+ conf[:aws_profile] ||= Constants::PROFILE
33
+
34
+ Aws.config.update(region: conf[:region])
35
+
36
+ credentials = Aws::Credentials.new(conf[:aws_access_key_id], conf[:aws_secret_access_key]) unless conf[:aws_access_key_id].nil?
37
+ credentials = Aws::SharedCredentials.new(profile_name: conf[:aws_profile]) unless conf[:aws_profile].nil?
38
+ credentials = Aws::InstanceProfileCredentials.new if conf[:aws_profile].nil? && conf[:aws_access_key_id].nil?
39
+ raise RubyAemAws::ArgumentError unless defined? credentials
40
+ Aws.config.update(credentials: credentials)
26
41
 
27
42
  aws = AwsCreator.create_aws
28
43
  @ec2_client = aws[:Ec2Client]
@@ -32,8 +47,14 @@ module RubyAemAws
32
47
  # The V2 API only supports Application ELBs, and we currently use Classic.
33
48
  # @elb_client = Aws::ElasticLoadBalancingV2::Client.new(region: conf[:region])
34
49
  @cloud_watch_client = aws[:CloudWatchClient]
50
+ @dynamodb_client = aws[:DynamoDBClient]
51
+ @s3_client = aws[:S3Client]
52
+ @s3_resource = aws[:S3Resource]
35
53
  end
36
54
 
55
+ # Test connection to Amazon AWS
56
+ #
57
+ # @return One or more regions that are currently available.
37
58
  def test_connection
38
59
  result = []
39
60
  @ec2_client.describe_regions.regions.each do |region|
@@ -57,17 +78,28 @@ module RubyAemAws
57
78
  def full_set(stack_prefix)
58
79
  RubyAemAws::FullSetStack.new(stack_prefix, @ec2_resource, @elb_client, @auto_scaling_client, @cloud_watch_client)
59
80
  end
81
+
82
+ # Create Stack Manager resources
83
+ #
84
+ # @param stack_prefix AWS tag: StackPrefix
85
+ # @return new RubyAemAws::StackManager instance
86
+ def stack_manager(stack_prefix)
87
+ RubyAemAws::StackManager.new(stack_prefix, @dynamodb_client, @s3_client, @s3_resource)
88
+ end
60
89
  end
61
90
 
62
91
  # Encapsulate AWS class creation for mocking.
63
92
  class AwsCreator
64
- def self.create_aws(region = Constants::REGION_DEFAULT)
93
+ def self.create_aws
65
94
  {
66
- Ec2Client: Aws::EC2::Client.new(region: region),
67
- Ec2Resource: Aws::EC2::Resource.new(region: region),
68
- ElbClient: Aws::ElasticLoadBalancing::Client.new(region: region),
69
- AutoScalingClient: Aws::AutoScaling::Client.new(region: region),
70
- CloudWatchClient: Aws::CloudWatch::Client.new(region: region)
95
+ Ec2Client: Aws::EC2::Client.new,
96
+ Ec2Resource: Aws::EC2::Resource.new,
97
+ ElbClient: Aws::ElasticLoadBalancing::Client.new,
98
+ AutoScalingClient: Aws::AutoScaling::Client.new,
99
+ CloudWatchClient: Aws::CloudWatch::Client.new,
100
+ DynamoDBClient: Aws::DynamoDB::Client.new,
101
+ S3Client: Aws::S3::Client.new,
102
+ S3Resource: Aws::S3::Resource.new
71
103
  }
72
104
  end
73
105
  end
@@ -35,5 +35,15 @@ module RubyAemAws
35
35
  ]
36
36
  }
37
37
  end
38
+
39
+ private def filter_for_snapshot(snapshot_type)
40
+ {
41
+ filters: [
42
+ { name: 'tag:StackPrefix', values: [@descriptor.stack_prefix] },
43
+ { name: 'tag:SnapshotType', values: [snapshot_type] },
44
+ { name: 'tag:Component', values: [@descriptor.ec2.component] }
45
+ ]
46
+ }
47
+ end
38
48
  end
39
49
  end
@@ -0,0 +1,35 @@
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 'aws-sdk'
16
+ require_relative 'abstract_component'
17
+
18
+ module RubyAemAws
19
+ # Add method to scan for snapshots
20
+ module AbstractSnapshot
21
+ include AbstractComponent
22
+
23
+ # @param snapshot_id Type of snapsthot to look for
24
+ # @return Class Aws::EC2::Snapshot
25
+ def get_snapshot_by_id(snapshot_id)
26
+ ec2_resource.snapshot(snapshot_id).data
27
+ end
28
+
29
+ # @param snapshot_type Type of snapsthot to look for
30
+ # @return EC2 Resource snapshots collection
31
+ def get_snapshots_by_type(snapshot_type)
32
+ ec2_resource.snapshots(filter_for_snapshot(snapshot_type))
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,56 @@
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
+ # Add common methods to StackManager resource
17
+ module AbstractStackManager
18
+ private def filter_for_db_scan(dynamodb_tablename, attribute_value)
19
+ {
20
+ table_name: dynamodb_tablename,
21
+ attributes_to_get: ['command_id'],
22
+ scan_filter: {
23
+ 'message_id' => {
24
+ attribute_value_list: [attribute_value],
25
+ comparison_operator: 'EQ'
26
+ }
27
+ },
28
+ consistent_read: true
29
+ }
30
+ end
31
+
32
+ private def filter_for_db_query(dynamodb_tablename, key_attribute_value)
33
+ {
34
+ table_name: dynamodb_tablename,
35
+ consistent_read: true,
36
+ attributes_to_get: ['state'],
37
+ key_conditions: {
38
+ 'command_id' => {
39
+ attribute_value_list: [key_attribute_value],
40
+ comparison_operator: 'EQ'
41
+ }
42
+ },
43
+ query_filter: {
44
+ 'state' => {
45
+ attribute_value_list: ['Pending'],
46
+ comparison_operator: 'NE'
47
+ }
48
+ }
49
+ }
50
+ end
51
+
52
+ private def message_for_sns_publish(task, stack_prefix, details)
53
+ "{ \"default\": \"{ 'task': '#{task}', 'stack_prefix': '#{stack_prefix}', 'details': #{details} }\"}"
54
+ end
55
+ end
56
+ end
@@ -21,24 +21,36 @@ module RubyAemAws
21
21
  class Author
22
22
  attr_reader :author_primary, :author_standby
23
23
 
24
- # ELB_ID = 'AuthorLoadBalancer'.freeze
25
- # ELB_NAME = 'AEM Author Load Balancer'.freeze
24
+ ELB_ID = 'AuthorLoadBalancer'.freeze
25
+ ELB_NAME = 'AEM Author Load Balancer'.freeze
26
26
 
27
27
  # @param stack_prefix AWS tag: StackPrefix
28
28
  # @param ec2_resource AWS EC2 resource
29
+ # @param elb_client AWS ELB client
30
+ # @param cloud_watch_client AWS CloudWatch client
29
31
  # @return new RubyAemAws::FullSet::Author
30
- def initialize(stack_prefix, ec2_resource, cloud_watch_client)
32
+ def initialize(stack_prefix, ec2_resource, elb_client, cloud_watch_client)
31
33
  @author_primary = Component::AuthorPrimary.new(stack_prefix, ec2_resource, cloud_watch_client)
32
34
  @author_standby = Component::AuthorStandby.new(stack_prefix, ec2_resource, cloud_watch_client)
35
+ @ec2_resource = ec2_resource
36
+ @elb_client = elb_client
33
37
  end
34
38
 
35
- # def terminate_primary_instance
36
-
37
- # def terminate_standby_instance
39
+ # @return true, if all author instances are healthy
40
+ def healthy?
41
+ instance = 0
42
+ instance += 1 if author_primary.healthy?
43
+ instance += 1 if author_standby.healthy?
44
+ return true unless instance < 2
45
+ end
38
46
 
39
- # def wait_until_healthy
40
- # - wait until both primary and standby are healthy
41
- # end
47
+ # @return true, if all author instances are healthy
48
+ def wait_until_healthy
49
+ instance = 0
50
+ instance += 1 if author_primary.wait_until_healthy.eql? true
51
+ instance += 1 if author_standby.wait_until_healthy.eql? true
52
+ return true unless instance < 2
53
+ end
42
54
  end
43
55
  end
44
56
  end
@@ -13,9 +13,11 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require_relative 'abstract_grouped_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_count_verifier'
17
18
  require_relative 'mixins/metric_verifier'
18
19
  require_relative 'component_descriptor'
20
+ require_relative 'mixins/snapshot_verifier'
19
21
 
20
22
  module RubyAemAws
21
23
  module Component
@@ -23,8 +25,10 @@ module RubyAemAws
23
25
  class AuthorDispatcher
24
26
  attr_reader :descriptor, :ec2_resource, :asg_client, :elb_client, :cloud_watch_client
25
27
  include AbstractGroupedComponent
28
+ include AbstractSnapshot
26
29
  include HealthyCountVerifier
27
30
  include MetricVerifier
31
+ include SnapshotVerifier
28
32
 
29
33
  EC2_COMPONENT = 'author-dispatcher'.freeze
30
34
  EC2_NAME = 'AEM Author Dispatcher'.freeze
@@ -47,9 +51,19 @@ module RubyAemAws
47
51
  @cloud_watch_client = cloud_watch_client
48
52
  end
49
53
 
50
- # def terminate_all_instances
54
+ def terminate_all_instances
55
+ get_all_instances.each do |i|
56
+ next if i.nil?
57
+ i.terminate
58
+ i.wait_until_terminated
59
+ end
60
+ end
51
61
 
52
- # def terminate_random_instance
62
+ def terminate_random_instance
63
+ instance = get_random_instance
64
+ instance.terminate
65
+ instance.wait_until_terminated
66
+ end
53
67
  end
54
68
  end
55
69
  end
@@ -13,8 +13,10 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require_relative 'abstract_single_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_state_verifier'
17
18
  require_relative 'mixins/metric_verifier'
19
+ require_relative 'mixins/snapshot_verifier'
18
20
 
19
21
  module RubyAemAws
20
22
  module Component
@@ -22,14 +24,17 @@ module RubyAemAws
22
24
  class AuthorPrimary
23
25
  attr_reader :descriptor, :ec2_resource, :cloud_watch_client
24
26
  include AbstractSingleComponent
27
+ include AbstractSnapshot
25
28
  include HealthyStateVerifier
26
29
  include MetricVerifier
30
+ include SnapshotVerifier
27
31
 
28
32
  EC2_COMPONENT = 'author-primary'.freeze
29
33
  EC2_NAME = 'AEM Author - Primary'.freeze
30
34
 
31
35
  # @param stack_prefix AWS tag: StackPrefix
32
36
  # @param ec2_resource AWS EC2 resource
37
+ # @param cloud_watch_client AWS CloudWatch client
33
38
  # @return new RubyAemAws::FullSet::AuthorPrimary
34
39
  def initialize(stack_prefix, ec2_resource, cloud_watch_client)
35
40
  @descriptor = ComponentDescriptor.new(stack_prefix,
@@ -37,6 +42,13 @@ module RubyAemAws
37
42
  @ec2_resource = ec2_resource
38
43
  @cloud_watch_client = cloud_watch_client
39
44
  end
45
+
46
+ # @return Aws::EC2::Instance
47
+ def terminate
48
+ instance = get_instance
49
+ instance.terminate
50
+ instance.wait_until_terminated
51
+ end
40
52
  end
41
53
  end
42
54
  end
@@ -14,9 +14,11 @@
14
14
 
15
15
  require 'aws-sdk'
16
16
  require_relative 'abstract_single_component'
17
+ require_relative 'abstract_snapshot'
17
18
  require_relative 'mixins/healthy_state_verifier'
18
19
  require_relative 'mixins/metric_verifier'
19
20
  require_relative 'component_descriptor'
21
+ require_relative 'mixins/snapshot_verifier'
20
22
 
21
23
  module RubyAemAws
22
24
  module Component
@@ -24,8 +26,10 @@ module RubyAemAws
24
26
  class AuthorPublishDispatcher
25
27
  attr_reader :descriptor, :ec2_resource, :cloud_watch_client
26
28
  include AbstractSingleComponent
29
+ include AbstractSnapshot
27
30
  include HealthyStateVerifier
28
31
  include MetricVerifier
32
+ include SnapshotVerifier
29
33
 
30
34
  EC2_COMPONENT = 'author-publish-dispatcher'.freeze
31
35
  EC2_NAME = 'AuthorPublishDispatcher'.freeze
@@ -12,9 +12,11 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require_relative 'abstract_grouped_component'
15
+ require_relative 'abstract_single_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_state_verifier'
17
18
  require_relative 'mixins/metric_verifier'
19
+ require_relative 'mixins/snapshot_verifier'
18
20
 
19
21
  module RubyAemAws
20
22
  module Component
@@ -22,9 +24,11 @@ module RubyAemAws
22
24
  class AuthorStandby
23
25
  attr_reader :descriptor, :ec2_resource, :cloud_watch_client
24
26
  include AbstractGroupedComponent
27
+ include AbstractSnapshot
25
28
  # Can't verify state by count as there's no ASG.
26
29
  include HealthyStateVerifier
27
30
  include MetricVerifier
31
+ include SnapshotVerifier
28
32
 
29
33
  EC2_COMPONENT = 'author-standby'.freeze
30
34
  EC2_NAME = 'AEM Author - Standby'.freeze
@@ -39,6 +43,13 @@ module RubyAemAws
39
43
  @ec2_resource = ec2_resource
40
44
  @cloud_watch_client = cloud_watch_client
41
45
  end
46
+
47
+ # @return Aws::EC2::Instance
48
+ def terminate
49
+ instance = get_instance
50
+ instance.terminate
51
+ instance.wait_until_terminated
52
+ end
42
53
  end
43
54
  end
44
55
  end
@@ -13,8 +13,10 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require_relative 'abstract_single_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_state_verifier'
17
18
  require_relative 'mixins/metric_verifier'
19
+ require_relative 'mixins/snapshot_verifier'
18
20
 
19
21
  module RubyAemAws
20
22
  module Component
@@ -22,9 +24,11 @@ module RubyAemAws
22
24
  class ChaosMonkey
23
25
  attr_reader :descriptor, :ec2_resource, :asg_client, :cloud_watch_client
24
26
  include AbstractSingleComponent
27
+ include AbstractSnapshot
25
28
  # Can't verify state by count as there's no ELB.
26
29
  include HealthyStateVerifier
27
30
  include MetricVerifier
31
+ include SnapshotVerifier
28
32
 
29
33
  EC2_COMPONENT = 'chaos-monkey'.freeze
30
34
  EC2_NAME = 'AEM Chaos Monkey'.freeze
@@ -0,0 +1,36 @@
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
+ # Mixin for interaction with AWS DynamoDB
17
+ module DynamoDB
18
+ # @param filter Filter to scan for
19
+ # @return scanned attribute
20
+ def scan(filter)
21
+ # We need to give AWS time to update the DynamoDB
22
+ # consistent_read seems not to work everytime
23
+ sleep 5
24
+ dynamodb_client.scan(filter)
25
+ end
26
+
27
+ # @param filter Filter to query for
28
+ # @return queried attribute
29
+ def query(filter)
30
+ # We need to give AWS time to update the DynamoDB
31
+ # consistent_read seems not to work everytime
32
+ sleep 5
33
+ dynamodb_client.query(filter)
34
+ end
35
+ end
36
+ end
@@ -48,23 +48,68 @@ module RubyAemAws
48
48
  return :no_elb if elb.nil?
49
49
 
50
50
  elb_running_instances = 0
51
- # puts("ELB: #{elb.load_balancer_name} (#{elb.instances.length})")
52
51
  get_instances_state_from_elb(elb).each do |i|
53
- # puts(" Instance #{i[:id]}: #{i[:state]}")
54
52
  elb_running_instances += 1 if i[:state] == RubyAemAws::Constants::INSTANCE_STATE_HEALTHY
55
53
  end
56
54
 
57
55
  desired_capacity = asg.desired_capacity
58
- # puts("calc health_state: #{elb_running_instances} / #{desired_capacity}")
56
+
59
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
+ :ready
61
+ end
62
+
63
+ # Provides detail of the state of the instances comprising the component.
64
+ # @return one of:
65
+ # - no_asg: AutoScalingGroup could not be located (by StackPrefix and Component tags).
66
+ # - no_elb: ElasticLoadBalancer could not be located (by StackPrefix and aws:cloudformation:logical-id tags).
67
+ # - misconfigured: AutoScalingGroup.desired_capacity is less than 1.
68
+ # - recovering: ELB running instance count is less than AutoScalingGroup.desired_capacity.
69
+ # - scaling: ELB running instance count is more than AutoScalingGroup.desired_capacity.
70
+ # - ready: ELB running instance count is equal to AutoScalingGroup.desired_capacity.
71
+ def health_state_elb
72
+ asg = find_auto_scaling_group(asg_client)
73
+ return :no_asg if asg.nil?
74
+
75
+ # Debug:
76
+ # unless asg.nil?
77
+ # puts("ASG: #{asg} #{asg.auto_scaling_group_name} (#{asg.desired_capacity})")
78
+ # asg.instances.each do |i|
79
+ # puts(" Instance #{i.instance_id}: #{i.health_status}")
80
+ # end
81
+ # end
82
+
83
+ elb = find_elb(elb_client)
84
+ return :no_elb if elb.nil?
60
85
 
86
+ elb_instance_state = elb_client.describe_instance_health(load_balancer_name: elb.load_balancer_name)
87
+
88
+ elb_running_instances = 0
89
+ elb_instance_state.instance_states.each do |i|
90
+ elb_running_instances += 1 if i.state == RubyAemAws::Constants::ELB_INSTANCE_INSERVICE
91
+ end
92
+
93
+ desired_capacity = asg.desired_capacity
94
+
95
+ return :misconfigured if desired_capacity < 1
61
96
  return :recovering if elb_running_instances < desired_capacity
62
97
  return :scaling if elb_running_instances > desired_capacity
63
98
  :ready
64
99
  end
65
100
 
101
+ # @return true, if all EC2 instances within the ELB are running
66
102
  def wait_until_healthy
67
- raise NotYetImplementedError
103
+ raise ELBMisconfiguration if health_state.eql?(:misconfigured)
104
+ sleep 2 while health_state.eql?(:recovering) || health_state.eql?(:scaling)
105
+ return true if health_state.eql?(:ready)
106
+ end
107
+
108
+ # @return true, if all instances within the ELB are inService
109
+ def wait_until_healthy_elb
110
+ raise ELBMisconfiguration if health_state_elb.eql?(:misconfigured)
111
+ sleep 2 while health_state_elb.eql?(:recovering) || health_state_elb.eql?(:scaling)
112
+ return true if health_state_elb.eql?(:ready)
68
113
  end
69
114
 
70
115
  private
@@ -22,7 +22,7 @@ module RubyAemAws
22
22
  def healthy?
23
23
  has_instance = false
24
24
  get_all_instances.each do |i|
25
- next if i.nil?
25
+ next if i.nil? || i.state.code != 16
26
26
  has_instance = true
27
27
  return false if i.state.name != Constants::INSTANCE_STATE_HEALTHY
28
28
  end
@@ -30,7 +30,14 @@ module RubyAemAws
30
30
  end
31
31
 
32
32
  def wait_until_healthy
33
- raise NotYetImplementedError
33
+ instance_healthy = false
34
+ get_all_instances.each do |i|
35
+ next if i.nil? || i.state.code != 16
36
+ i.wait_until_running
37
+ instance_healthy = true
38
+ return false if i.state.name != Constants::INSTANCE_STATE_HEALTHY
39
+ end
40
+ instance_healthy
34
41
  end
35
42
  end
36
43
  end
@@ -0,0 +1,42 @@
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
+ # Mixin for interaction with AWS S3
17
+ module S3Access
18
+ # @param bucket AWS S3 bucket name
19
+ # @return AWS S3 resource bucket connection
20
+ def get_s3_bucket(bucket)
21
+ s3_resource.bucket(bucket)
22
+ end
23
+
24
+ # @param bucket AWS S3 bucket name
25
+ # @param s3_object_name AWS S3 object name
26
+ # @return S3 object
27
+ def get_s3_bucket_object(bucket, s3_object_name)
28
+ bucket = get_s3_bucket(bucket)
29
+ bucket.object(s3_object_name)
30
+ end
31
+
32
+ # @param bucket AWS S3 bucket name
33
+ # @param s3_object_name AWS S3 object name
34
+ # @param dest_path Download destionation path
35
+ # @return S3 object
36
+ def get_s3_object(bucket, s3_object_name, dest_path)
37
+ options = { bucket: bucket, key: s3_object_name }
38
+ options = options.merge(response_target: dest_path) unless dest_path.nil?
39
+ s3_client.get_object(**options)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
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
+ # Mixin for checking snapshots of a component via EC2 client
17
+ # Add this to a component to make it capable of determining its own snapshots.
18
+ module SnapshotVerifier
19
+ # @param snapshot_id AWS Snapshot ID
20
+ # @return true if snapshot exists, nil if no snapshot exists
21
+ def snapshot?(snapshot_id)
22
+ return true unless get_snapshot_by_id(snapshot_id).nil?
23
+ end
24
+
25
+ # @param snapshot_type AEM snapshot type
26
+ # @return true if snapshots exist, false is no snapshots exist
27
+ def snapshots?(snapshot_type)
28
+ has_snapshot = false
29
+ get_snapshots_by_type(snapshot_type).each do |s|
30
+ next if s.nil?
31
+ has_snapshot = true
32
+ end
33
+ has_snapshot
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
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
+ # Mixin for interaction with AWS SNS
17
+ module SNSTopic
18
+ # @param topicarn the ARN of the SNS Topix to publish the message
19
+ # @param message the SNS Message to publish
20
+ # @return Message ID
21
+ def publish(topicarn, message)
22
+ client = Aws::SNS::Topic.new(topicarn)
23
+ sns_message = { subject: 'Publish',
24
+ message: message,
25
+ message_structure: 'json' }
26
+ publish = client.publish(sns_message)
27
+ publish.message_id
28
+ end
29
+ end
30
+ end
@@ -13,8 +13,10 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require_relative 'abstract_single_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_state_verifier'
17
18
  require_relative 'mixins/metric_verifier'
19
+ require_relative 'mixins/snapshot_verifier'
18
20
 
19
21
  module RubyAemAws
20
22
  module Component
@@ -22,9 +24,11 @@ module RubyAemAws
22
24
  class Orchestrator
23
25
  attr_reader :descriptor, :ec2_resource, :asg_client, :cloud_watch_client
24
26
  include AbstractSingleComponent
27
+ include AbstractSnapshot
25
28
  # Can't verify state by count as there's no ELB.
26
29
  include HealthyStateVerifier
27
30
  include MetricVerifier
31
+ include SnapshotVerifier
28
32
 
29
33
  EC2_COMPONENT = 'orchestrator'.freeze
30
34
  EC2_NAME = 'AEM Orchestrator'.freeze
@@ -13,8 +13,10 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require_relative 'abstract_grouped_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_state_verifier'
17
18
  require_relative 'mixins/metric_verifier'
19
+ require_relative 'mixins/snapshot_verifier'
18
20
 
19
21
  module RubyAemAws
20
22
  module Component
@@ -22,9 +24,11 @@ module RubyAemAws
22
24
  class Publish
23
25
  attr_reader :descriptor, :ec2_resource, :cloud_watch_client, :asg_client
24
26
  include AbstractGroupedComponent
27
+ include AbstractSnapshot
25
28
  # Can't verify state by count as there's no ELB.
26
29
  include HealthyStateVerifier
27
30
  include MetricVerifier
31
+ include SnapshotVerifier
28
32
 
29
33
  EC2_COMPONENT = 'publish'.freeze
30
34
  EC2_NAME = 'AEM Publish'.freeze
@@ -42,9 +46,19 @@ module RubyAemAws
42
46
  @cloud_watch_client = cloud_watch_client
43
47
  end
44
48
 
45
- # def terminate_all_instances
49
+ def terminate_all_instances
50
+ get_all_instances.each do |i|
51
+ next if i.nil?
52
+ i.terminate
53
+ i.wait_until_terminated
54
+ end
55
+ end
46
56
 
47
- # def terminate_random_instance
57
+ def terminate_random_instance
58
+ instance = get_random_instance
59
+ instance.terminate
60
+ instance.wait_until_terminated
61
+ end
48
62
  end
49
63
  end
50
64
  end
@@ -13,8 +13,10 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require_relative 'abstract_grouped_component'
16
+ require_relative 'abstract_snapshot'
16
17
  require_relative 'mixins/healthy_count_verifier'
17
18
  require_relative 'mixins/metric_verifier'
19
+ require_relative 'mixins/snapshot_verifier'
18
20
 
19
21
  module RubyAemAws
20
22
  module Component
@@ -22,8 +24,10 @@ module RubyAemAws
22
24
  class PublishDispatcher
23
25
  attr_reader :descriptor, :ec2_resource, :asg_client, :elb_client, :cloud_watch_client
24
26
  include AbstractGroupedComponent
27
+ include AbstractSnapshot
25
28
  include HealthyCountVerifier
26
29
  include MetricVerifier
30
+ include SnapshotVerifier
27
31
 
28
32
  EC2_COMPONENT = 'publish-dispatcher'.freeze
29
33
  EC2_NAME = 'AEM Publish Dispatcher'.freeze
@@ -33,6 +37,7 @@ module RubyAemAws
33
37
  # @param stack_prefix AWS tag: StackPrefix
34
38
  # @param ec2_resource AWS EC2 resource
35
39
  # @param asg_client AWS AutoScalingGroup client
40
+ # @param elb_client AWS ELB client
36
41
  # @param cloud_watch_client AWS CloudWatch client
37
42
  # @return new RubyAemAws::FullSet::PublishDispatcher
38
43
  def initialize(stack_prefix, ec2_resource, asg_client, elb_client, cloud_watch_client)
@@ -45,9 +50,19 @@ module RubyAemAws
45
50
  @cloud_watch_client = cloud_watch_client
46
51
  end
47
52
 
48
- # def terminate_all_instances
53
+ def terminate_all_instances
54
+ get_all_instances.each do |i|
55
+ next if i.nil?
56
+ i.terminate
57
+ i.wait_until_terminated
58
+ end
59
+ end
49
60
 
50
- # def terminate_random_instance
61
+ def terminate_random_instance
62
+ instance = get_random_instance
63
+ instance.terminate
64
+ instance.wait_until_terminated
65
+ end
51
66
  end
52
67
  end
53
68
  end
@@ -0,0 +1,82 @@
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 'mixins/sns_topic'
17
+ require_relative 'mixins/dynamo_db'
18
+ require_relative 'mixins/s3'
19
+
20
+ module RubyAemAws
21
+ module Component
22
+ # Interface to the AWS StackManager to send out commands
23
+ class StackManagerResources
24
+ attr_reader :dynamodb_client, :s3_client, :s3_resource
25
+ include AbstractStackManager
26
+ include DynamoDB
27
+ include SNSTopic
28
+ include S3Access
29
+
30
+ # @param dynamodb_client AWS DynamoDB client connection
31
+ # @param s3_client AWS S3 client connection
32
+ # @param s3_resource AWS S3 client connection
33
+ # @return new RubyAemAws::StackManager::StackManagerResources
34
+ def initialize(dynamodb_client, s3_client, s3_resource)
35
+ @dynamodb_client = dynamodb_client
36
+ @s3_client = s3_client
37
+ @s3_resource = s3_resource
38
+ end
39
+
40
+ # @param topicarn AWS SNS-Topic ARN
41
+ # @param task AEM StackManager task
42
+ # @param stack_prefix AEM Stack-Prefix
43
+ # @param details AEM StackManager task detail message
44
+ # @return AWS SNS publish message id
45
+ def sns_publish(topicarn, task, stack_prefix, details)
46
+ details = JSON.generate(details).tr('\"', '\'')
47
+ publish(topicarn, message_for_sns_publish(task, stack_prefix, details))
48
+ end
49
+
50
+ # @param dynamodb_tablename AWS DynamoDB table name
51
+ # @param attribute_value Attribute value to scan for
52
+ # @return Scan result
53
+ def dyn_db_msg_scan(dynamodb_tablename, attribute_value)
54
+ scan(filter_for_db_scan(dynamodb_tablename, attribute_value))
55
+ end
56
+
57
+ # @param dynamodb_tablename AWS DynamoDB table name
58
+ # @param attribute_value Attribute value to query for
59
+ # @return Command state
60
+ def dyn_db_cmd_query(dynamodb_tablename, attribute_value)
61
+ key_attribute_value = attribute_value.items[0]['command_id']
62
+ state = query(filter_for_db_query(dynamodb_tablename, key_attribute_value))
63
+ state.items[0]['state']
64
+ end
65
+
66
+ # @param s3_bucket_name S3 bucketname
67
+ # @param s3_object_name S3 Object name
68
+ # @return AWS S3 resource object
69
+ def s3_resource_object(s3_bucket_name, s3_object_name)
70
+ get_s3_bucket_object(s3_bucket_name, s3_object_name)
71
+ end
72
+
73
+ # @param s3_bucket_name S3 bucketname
74
+ # @param s3_object_name S3 Object name
75
+ # @param dest_path local download path, default: nil
76
+ # @return AWS S3 client object
77
+ def s3_download_object(s3_bucket_name, s3_object_name, dest_path = nil)
78
+ get_s3_object(s3_bucket_name, s3_object_name, dest_path)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -24,9 +24,19 @@ module RubyAemAws
24
24
  ALL_ACTIVE = [PENDING, RUNNING, SHUTTING_DOWN, STOPPING, STOPPED].freeze
25
25
  end
26
26
 
27
+ class ELBInstanceState
28
+ INSERVICE = 'InService'.freeze
29
+ OUTOFSERVICE = 'OutOfService'.freeze
30
+
31
+ ALL_ACTIVE = [INSERVICE, OUTOFSERVICE].freeze
32
+ end
33
+
27
34
  class Constants
28
35
  REGION_DEFAULT = 'ap-southeast-2'.freeze
29
-
36
+ ACCESS_KEY_ID = ENV['AWS_ACCESS_KEY_ID'] || ENV['aws_access_key_id']
37
+ SECRET_ACCESS_KEY = ENV['AWS_SECRET_ACCESS_KEY'] || ENV['aws_scret_access_key']
38
+ PROFILE = ENV['AWS_PROFILE']
30
39
  INSTANCE_STATE_HEALTHY = RubyAemAws::InstanceState::RUNNING.freeze
40
+ ELB_INSTANCE_INSERVICE = RubyAemAws::ELBInstanceState::INSERVICE.freeze
31
41
  end
32
42
  end
@@ -20,6 +20,20 @@ module RubyAemAws
20
20
  end
21
21
  end
22
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 a unknown Response received.
31
+ class UnknownResponse < StandardError
32
+ def initialize(msg = 'Unknown response code')
33
+ super
34
+ end
35
+ end
36
+
23
37
  # Raise this when a component unexpectedly has more than one instance.
24
38
  class ExpectedSingleInstanceError < StandardError
25
39
  # def initialize(count, descriptor)
@@ -33,4 +47,15 @@ module RubyAemAws
33
47
  super
34
48
  end
35
49
  end
50
+
51
+ # Raise this when credentials can't be found
52
+ class ArgumentError < StandardError
53
+ def initialize(msg = "No credentials found!
54
+ Set environment variable AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY or AWS_PROFILE.
55
+ Alternative use following syntax:
56
+ RubyAemAws::AemAws.new(aws_access_key_id, aws_scret_access_key) or
57
+ RubyAemAws::AemAws.new(credentials_profile_name)")
58
+ super
59
+ end
60
+ end
36
61
  end
@@ -43,7 +43,7 @@ module RubyAemAws
43
43
 
44
44
  # @return new RubyAemAws::Component::Author instance
45
45
  def author
46
- RubyAemAws::Component::Author.new(@stack_prefix, @ec2_resource, @cloud_watch_client)
46
+ RubyAemAws::Component::Author.new(@stack_prefix, @ec2_resource, @elb_client, @cloud_watch_client)
47
47
  end
48
48
 
49
49
  # @return new RubyAemAws::Component::ChaosMonkey instance
@@ -0,0 +1,31 @@
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 'component/stack_manager_resources'
16
+
17
+ module RubyAemAws
18
+ # Interface to interact with AEM StackManager
19
+ class StackManager
20
+ attr_reader :sm_resources
21
+ # @param stack_prefix AWS tag: StackPrefix
22
+ # @param dynamodb_client AWS DynamoDB client
23
+ # @param s3_client AWS S3 client
24
+ # @param s3_resource AWS S3 resource
25
+ # @return new RubyAemAws::StackManager instance
26
+ def initialize(stack_prefix, dynamodb_client, s3_client, s3_resource)
27
+ @sm_resources = RubyAemAws::Component::StackManagerResources.new(dynamodb_client, s3_client, s3_resource)
28
+ @stack_prefix = stack_prefix
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_aem_aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shine Solutions
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-05 00:00:00.000000000 Z
11
+ date: 2018-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -78,6 +78,8 @@ files:
78
78
  - lib/ruby_aem_aws/component/abstract_component.rb
79
79
  - lib/ruby_aem_aws/component/abstract_grouped_component.rb
80
80
  - lib/ruby_aem_aws/component/abstract_single_component.rb
81
+ - lib/ruby_aem_aws/component/abstract_snapshot.rb
82
+ - lib/ruby_aem_aws/component/abstract_stackmanager.rb
81
83
  - lib/ruby_aem_aws/component/author.rb
82
84
  - lib/ruby_aem_aws/component/author_dispatcher.rb
83
85
  - lib/ruby_aem_aws/component/author_primary.rb
@@ -85,17 +87,23 @@ files:
85
87
  - lib/ruby_aem_aws/component/author_standby.rb
86
88
  - lib/ruby_aem_aws/component/chaos_monkey.rb
87
89
  - lib/ruby_aem_aws/component/component_descriptor.rb
90
+ - lib/ruby_aem_aws/component/mixins/dynamo_db.rb
88
91
  - lib/ruby_aem_aws/component/mixins/healthy_count_verifier.rb
89
92
  - lib/ruby_aem_aws/component/mixins/healthy_state_verifier.rb
90
93
  - lib/ruby_aem_aws/component/mixins/instance_describer.rb
91
94
  - lib/ruby_aem_aws/component/mixins/metric_verifier.rb
95
+ - lib/ruby_aem_aws/component/mixins/s3.rb
96
+ - lib/ruby_aem_aws/component/mixins/snapshot_verifier.rb
97
+ - lib/ruby_aem_aws/component/mixins/sns_topic.rb
92
98
  - lib/ruby_aem_aws/component/orchestrator.rb
93
99
  - lib/ruby_aem_aws/component/publish.rb
94
100
  - lib/ruby_aem_aws/component/publish_dispatcher.rb
101
+ - lib/ruby_aem_aws/component/stack_manager_resources.rb
95
102
  - lib/ruby_aem_aws/consolidated_stack.rb
96
103
  - lib/ruby_aem_aws/constants.rb
97
104
  - lib/ruby_aem_aws/error.rb
98
105
  - lib/ruby_aem_aws/full_set_stack.rb
106
+ - lib/ruby_aem_aws/stack_manager.rb
99
107
  homepage: https://github.com/shinesolutions/ruby_aem_aws
100
108
  licenses:
101
109
  - Apache-2.0
@@ -119,5 +127,5 @@ rubyforge_project:
119
127
  rubygems_version: 2.6.14
120
128
  signing_key:
121
129
  specification_version: 4
122
- summary: AEM API Ruby client
130
+ summary: AEM on AWS API Ruby client
123
131
  test_files: []