ruby_aem_aws 0.9.0 → 0.9.1

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