the-maestro 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/LICENSE +23 -0
  4. data/README.rdoc +378 -0
  5. data/Rakefile +116 -0
  6. data/VERSION +1 -0
  7. data/lib/maestro.rb +354 -0
  8. data/lib/maestro/cloud.rb +384 -0
  9. data/lib/maestro/cloud/aws.rb +1231 -0
  10. data/lib/maestro/dsl_property.rb +15 -0
  11. data/lib/maestro/log4r/console_formatter.rb +18 -0
  12. data/lib/maestro/log4r/file_formatter.rb +24 -0
  13. data/lib/maestro/node.rb +123 -0
  14. data/lib/maestro/operating_system.rb +53 -0
  15. data/lib/maestro/operating_system/cent_os.rb +23 -0
  16. data/lib/maestro/operating_system/debian.rb +40 -0
  17. data/lib/maestro/operating_system/fedora.rb +23 -0
  18. data/lib/maestro/operating_system/ubuntu.rb +100 -0
  19. data/lib/maestro/role.rb +36 -0
  20. data/lib/maestro/tasks.rb +52 -0
  21. data/lib/maestro/validator.rb +32 -0
  22. data/rails/init.rb +1 -0
  23. data/test/integration/base_aws.rb +156 -0
  24. data/test/integration/fixtures/config/maestro/cookbooks/emacs/metadata.json +41 -0
  25. data/test/integration/fixtures/config/maestro/cookbooks/emacs/metadata.rb +3 -0
  26. data/test/integration/fixtures/config/maestro/cookbooks/emacs/recipes/default.rb +21 -0
  27. data/test/integration/fixtures/config/maestro/roles/default.json +9 -0
  28. data/test/integration/fixtures/config/maestro/roles/web.json +9 -0
  29. data/test/integration/helper.rb +8 -0
  30. data/test/integration/test_aws_cloud.rb +805 -0
  31. data/test/integration/test_cent_os.rb +104 -0
  32. data/test/integration/test_debian.rb +119 -0
  33. data/test/integration/test_fedora.rb +104 -0
  34. data/test/integration/test_ubuntu.rb +149 -0
  35. data/test/unit/fixtures/invalid-clouds-not-a-directory/config/maestro/clouds +1 -0
  36. data/test/unit/fixtures/invalid-cookbooks-not-a-directory/config/maestro/cookbooks +0 -0
  37. data/test/unit/fixtures/invalid-maestro-not-a-directory/config/maestro +0 -0
  38. data/test/unit/fixtures/invalid-missing-cookbooks/config/maestro/clouds/valid.yml +21 -0
  39. data/test/unit/fixtures/invalid-missing-roles/config/maestro/clouds/valid.yml +21 -0
  40. data/test/unit/fixtures/invalid-roles-not-a-directory/config/maestro/roles +1 -0
  41. data/test/unit/fixtures/ssh/id_rsa-maestro-test-keypair +27 -0
  42. data/test/unit/helper.rb +6 -0
  43. data/test/unit/test_aws_cloud.rb +133 -0
  44. data/test/unit/test_aws_ec2_node.rb +76 -0
  45. data/test/unit/test_aws_elb_node.rb +221 -0
  46. data/test/unit/test_aws_rds_node.rb +380 -0
  47. data/test/unit/test_cent_os.rb +28 -0
  48. data/test/unit/test_cloud.rb +142 -0
  49. data/test/unit/test_debian.rb +62 -0
  50. data/test/unit/test_fedora.rb +28 -0
  51. data/test/unit/test_invalid_mode.rb +11 -0
  52. data/test/unit/test_maestro.rb +140 -0
  53. data/test/unit/test_node.rb +50 -0
  54. data/test/unit/test_operating_system.rb +19 -0
  55. data/test/unit/test_rails_mode.rb +77 -0
  56. data/test/unit/test_role.rb +59 -0
  57. data/test/unit/test_standalone_mode.rb +75 -0
  58. data/test/unit/test_ubuntu.rb +95 -0
  59. data/the-maestro.gemspec +150 -0
  60. metadata +228 -0
@@ -0,0 +1,52 @@
1
+ require "maestro"
2
+
3
+ namespace :maestro do
4
+
5
+ desc "Creates the Maestro config directory structure. If the directories already exist, no action is taken."
6
+ task :create_config_dirs do
7
+ Maestro.create_config_dirs
8
+ end
9
+
10
+ desc "Validates your Maestro configuration files"
11
+ task :validate_configs do
12
+ result = Maestro.validate_configs
13
+ result[1].each {|msg| puts msg}
14
+ end
15
+
16
+ if !Maestro.clouds.nil? && !Maestro.clouds.empty?
17
+ Maestro.clouds.each_pair do |cloud_name, cloud|
18
+ if cloud.valid?
19
+ namespace "#{cloud_name}" do
20
+ desc "Reports the status of the #{cloud_name} cloud"
21
+ task "status" do |t|
22
+ cloud.status
23
+ end
24
+
25
+ desc "Ensures that the #{cloud_name} cloud is running as currently configured"
26
+ task "start" do |t|
27
+ cloud.start
28
+ end
29
+
30
+ desc "Configures the nodes in the #{cloud_name} cloud. This installs Chef and runs your Chef recipes on the node."
31
+ task "configure" do |t|
32
+ cloud.configure
33
+ end
34
+
35
+ desc "Shuts down the #{cloud_name} cloud"
36
+ task "shutdown" do |t|
37
+ cloud.shutdown
38
+ end
39
+
40
+ if cloud.is_a?(Maestro::Cloud::Aws) && !cloud.rds_nodes.empty?
41
+ cloud.rds_nodes.each_pair do |name, node|
42
+ desc "Reboots the #{name} RDS node. Make sure you put up the appropriate maintanance pages first."
43
+ task "reboot-#{name}" do |t|
44
+ cloud.reboot_rds_node(name)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module Maestro
2
+ # The Validator mixin provides methods for performing validation and reporting validation errors
3
+ module Validator
4
+
5
+ # whether this object is valid or not. defaults to true
6
+ attr :valid
7
+ # the collection of validation error strings. if valid is false, this should contain details as to why the object is invalid
8
+ attr_reader :validation_errors
9
+
10
+ def initialize
11
+ @validation_errors = Array.new
12
+ @valid = true
13
+ end
14
+
15
+ # calls the validate_internal method, which classes including this Module should implement
16
+ def validate
17
+ validate_internal
18
+ end
19
+
20
+ # returns whether this object is valid or not
21
+ def valid?
22
+ @valid
23
+ end
24
+
25
+ # sets this object's valid attribute to false, and records the given
26
+ # validation error string in the validation_errors attribute
27
+ def invalidate(error_str)
28
+ @valid = false
29
+ @validation_errors << error_str
30
+ end
31
+ end
32
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../lib/maestro'
@@ -0,0 +1,156 @@
1
+ require "helper"
2
+ require "etc"
3
+ require "AWS"
4
+ require "aws/s3"
5
+
6
+ # Base AWS contectivity
7
+ module BaseAws
8
+
9
+ #
10
+ # BE CAREFUL!
11
+ #
12
+ # A poorly written test will cost you and others money. The onus is on you to double check
13
+ # that you don't orphan EC2 instances, S3 buckets/objects, Elastic IPs, RDS instances,
14
+ # ELBs, EBS Volumes/Snapshots, etc. The AWS Console is your friend.
15
+ #
16
+ # To be safe, create a new keypair just for use with running these tests. Don't use
17
+ # an existing keypair associated with any sensitive production data!
18
+ #
19
+ # To run the integration tests, place a file named maestro_tests_aws_credentials.rb
20
+ # in your home directory, with the following Hash:
21
+ #
22
+ # {
23
+ # # The name of the keypair to use to authenticate with AWS, start instances, etc
24
+ # :keypair_name => "XXXXXXX-keypair",
25
+ #
26
+ # # Path to the keypair file matching the :keypair_name
27
+ # :keypair_file => "/path/to/your/id_rsa-XXXXXXX-keypair",
28
+ #
29
+ # # Your AWS Account ID
30
+ # :aws_account_id => "XXXX-XXXX-XXXX",
31
+ #
32
+ # # Your AWS Access Key
33
+ # :aws_access_key => "XXXXXXXXXXXXXXXXXXXX",
34
+ #
35
+ # # Your AWS Secret Access Key
36
+ # :aws_secret_access_key => "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
37
+ #
38
+ # # Name of the S3 bucket to store Chef assets in
39
+ # :chef_bucket => "maestro-tests-aws.XXXXXXXX.com"
40
+ # }
41
+ #
42
+ # Make sure you set the appropriate permissions on this file, and delete it when you're done running the integration tests.
43
+ #
44
+
45
+
46
+ #######################
47
+ # Setup
48
+ #######################
49
+ def setup
50
+ @config_file_name = 'maestro_tests_aws_credentials.rb'
51
+ @maestro_aws_credentials = Etc.getpwuid.dir + "/#{@config_file_name}"
52
+ raise "Missing Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}." if !File.exists?(@maestro_aws_credentials)
53
+ raise "Cannot read Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}." if !File.readable?(@maestro_aws_credentials)
54
+ @credentials = eval(File.read(@maestro_aws_credentials))
55
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Does not contain a Hash" if !@credentials.instance_of?(Hash)
56
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Missing :keypair_name key" if !@credentials.has_key?(:keypair_name)
57
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Missing :keypair_file key" if !@credentials.has_key?(:keypair_file)
58
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Missing :aws_account_id key" if !@credentials.has_key?(:aws_account_id)
59
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Missing :aws_access_key key" if !@credentials.has_key?(:aws_access_key)
60
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Missing :aws_secret_access_key key" if !@credentials.has_key?(:aws_secret_access_key)
61
+ raise "Invalid Maestro::Cloud::Aws integration test config file: ~/#{@config_file_name}. Missing :chef_bucket key" if !@credentials.has_key?(:chef_bucket)
62
+
63
+ @ec2 = AWS::EC2::Base.new(:access_key_id => @credentials[:aws_access_key], :secret_access_key => @credentials[:aws_secret_access_key], :use_ssl => true)
64
+ @elb = AWS::ELB::Base.new(:access_key_id => @credentials[:aws_access_key], :secret_access_key => @credentials[:aws_secret_access_key], :use_ssl => true)
65
+ @rds = AWS::RDS::Base.new(:access_key_id => @credentials[:aws_access_key], :secret_access_key => @credentials[:aws_secret_access_key], :use_ssl => true)
66
+ AWS::S3::Base.establish_connection!(:access_key_id => @credentials[:aws_access_key], :secret_access_key => @credentials[:aws_secret_access_key], :use_ssl => true)
67
+
68
+ # keep track of the Elastic IPs we started with
69
+ before_addresses = @ec2.describe_addresses()
70
+ @before_elastic_ips = Array.new
71
+ if !before_addresses.addressesSet.nil?
72
+ before_addresses.addressesSet.item.each {|item| @before_elastic_ips << item.publicIp}
73
+ end
74
+
75
+ # keep track of the EBS volumes we started with
76
+ before_volumes = @ec2.describe_volumes()
77
+ @before_volumes = Array.new
78
+ if !before_volumes.volumeSet.nil?
79
+ before_volumes.volumeSet.item.each {|item| @before_volumes << item.volumeId if !item.status.eql? "deleting"}
80
+ end
81
+
82
+ # keep track of the S3 Buckets we started with
83
+ before_buckets = AWS::S3::Service.buckets
84
+ @before_buckets = Array.new
85
+ if !before_buckets.empty?
86
+ before_buckets.each {|bucket| @before_buckets << bucket.name}
87
+ end
88
+ end
89
+
90
+
91
+ #######################
92
+ # Teardown
93
+ #######################
94
+
95
+ def teardown
96
+ # release elastic ips
97
+ after_addresses = @ec2.describe_addresses()
98
+ @after_elastic_ips = Array.new
99
+ if !after_addresses.addressesSet.nil?
100
+ after_addresses.addressesSet.item.each {|item| @after_elastic_ips << item.publicIp}
101
+ end
102
+ @before_elastic_ips.each do |before_elastic_ip|
103
+ found = @after_elastic_ips.find {|after_elastic_ip| after_elastic_ip.eql?(before_elastic_ip)}
104
+ puts "ERROR! AWS integration test error: It appears an Elastic IP address associated with the account before the integration tests ran has been released. This should not happen." if !found
105
+ end
106
+ @after_elastic_ips.each do |after_elastic_ip|
107
+ found = @before_elastic_ips.find {|before_elastic_ip| before_elastic_ip.eql?(after_elastic_ip)}
108
+ if !found
109
+ puts "Releasing AWS integration test Elastic IP: #{after_elastic_ip}"
110
+ @ec2.release_address(:public_ip => after_elastic_ip)
111
+ end
112
+ end
113
+
114
+ # delete EBS volumes
115
+ after_volumes = @ec2.describe_volumes()
116
+ @after_volumes = Array.new
117
+ if !after_volumes.volumeSet.nil?
118
+ after_volumes.volumeSet.item.each {|item| @after_volumes << item.volumeId if !item.status.eql? "deleting"}
119
+ end
120
+ @before_volumes.each do |before_volume|
121
+ found = @after_volumes.find {|after_volume| after_volume.eql?(before_volume)}
122
+ puts "ERROR! AWS Cloud integration test error: It appears an EBS volume associated with the account before the integration tests ran has been deleted. This should not happen." if !found
123
+ end
124
+ @after_volumes.each do |after_volume|
125
+ found = @before_volumes.find {|before_volume| before_volume.eql?(after_volume)}
126
+ if !found
127
+ puts "Deleting AWS integration test EBS Volume: #{after_volume}"
128
+ @ec2.delete_volume(:volume_id => after_volume)
129
+ end
130
+ end
131
+
132
+ # delete S3 buckets
133
+ after_buckets = AWS::S3::Service.buckets
134
+ @after_buckets = Array.new
135
+ if !after_buckets.empty?
136
+ after_buckets.each {|bucket| @after_buckets << bucket.name}
137
+ end
138
+ @before_buckets.each do |before_bucket_name|
139
+ found = @after_buckets.find {|after_bucket_name| after_bucket_name.eql?(before_bucket_name)}
140
+ puts "ERROR! AWS Cloud integration test error: It appears an S3 Bucket belonging to the account before the integration tests ran has been deleted. This should not happen." if !found
141
+ end
142
+ @after_buckets.each do |after_bucket_name|
143
+ found = @before_buckets.find {|before_bucket_name| before_bucket_name.eql?(after_bucket_name)}
144
+ if !found
145
+ puts "Deleting all objects in S3 Bucket: #{after_bucket_name}"
146
+ bucket = AWS::S3::Bucket.find(after_bucket_name)
147
+ bucket.delete_all
148
+ puts "Deleting S3 Bucket: #{after_bucket_name}"
149
+ bucket.delete
150
+ end
151
+ end
152
+
153
+ ENV.delete Maestro::MAESTRO_DIR_ENV_VAR
154
+ end
155
+
156
+ end
@@ -0,0 +1,41 @@
1
+ {
2
+ "dependencies": {
3
+ },
4
+ "replacing": {
5
+ },
6
+ "description": "Installs emacs",
7
+ "groupings": {
8
+ },
9
+ "platforms": {
10
+ "ubuntu": [
11
+
12
+ ],
13
+ "centos": [
14
+
15
+ ],
16
+ "redhat": [
17
+
18
+ ],
19
+ "debian": [
20
+
21
+ ]
22
+ },
23
+ "version": "0.7.0",
24
+ "recommendations": {
25
+ },
26
+ "name": "emacs",
27
+ "maintainer": "Opscode, Inc.",
28
+ "long_description": "",
29
+ "recipes": {
30
+ },
31
+ "suggestions": {
32
+ },
33
+ "maintainer_email": "cookbooks@opscode.com",
34
+ "attributes": {
35
+ },
36
+ "conflicting": {
37
+ },
38
+ "license": "Apache 2.0",
39
+ "providing": {
40
+ }
41
+ }
@@ -0,0 +1,3 @@
1
+ %w{ ubuntu debian fedora centos }.each do |os|
2
+ supports os
3
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Cookbook Name:: emacs
3
+ # Recipe:: default
4
+ #
5
+ # Copyright 2009, Opscode, Inc.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ package "emacs" do
20
+ action :upgrade
21
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "default",
3
+ "chef_type": "role",
4
+ "json_class": "Chef::Role",
5
+ "description": "The default role for all nodes.",
6
+ "run_list": [
7
+ "recipe[emacs]"
8
+ ]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "web",
3
+ "chef_type": "role",
4
+ "json_class": "Chef::Role",
5
+ "description": "The base role for nodes that serve HTTP traffic",
6
+ "run_list": [
7
+ "role[default]"
8
+ ]
9
+ }
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ require 'maestro'
6
+ require 'shoulda'
7
+ require 'ftools'
8
+ require 'fileutils'
@@ -0,0 +1,805 @@
1
+ require "helper"
2
+ require "base_aws"
3
+
4
+
5
+ # Integration tests for Maestro::Cloud::Aws
6
+ class TestAwsCloud < Test::Unit::TestCase
7
+
8
+ include BaseAws
9
+
10
+ context "Maestro::Cloud::Aws" do
11
+
12
+ #######################
13
+ # Setup
14
+ #######################
15
+ setup do
16
+ ENV[Maestro::MAESTRO_DIR_ENV_VAR] = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures')
17
+ Maestro.create_log_dirs
18
+ credentials = @credentials
19
+ @cloud = aws_cloud :maestro_aws_itest do
20
+ keypair_name credentials[:keypair_name]
21
+ keypair_file credentials[:keypair_file]
22
+ aws_account_id credentials[:aws_account_id]
23
+ aws_access_key credentials[:aws_access_key]
24
+ aws_secret_access_key credentials[:aws_secret_access_key]
25
+ chef_bucket credentials[:chef_bucket]
26
+
27
+ roles do
28
+ role "web" do
29
+ public_ports [80, 443]
30
+ end
31
+ end
32
+
33
+ nodes do
34
+ elb_node "lb-1" do
35
+ availability_zones ["us-east-1b"]
36
+ listeners [{:load_balancer_port => 80, :instance_port => 80, :protocol => "http"}]
37
+ ec2_nodes ["node-1", "node-2"]
38
+ health_check(:target => "TCP:80", :timeout => 15, :interval => 60, :unhealthy_threshold => 5, :healthy_threshold => 3)
39
+ end
40
+
41
+ ec2_node "node-1" do
42
+ roles ["web"]
43
+ ami "ami-bb709dd2"
44
+ ssh_user "ubuntu"
45
+ instance_type "m1.small"
46
+ availability_zone "us-east-1b"
47
+ end
48
+
49
+ ec2_node "node-2" do
50
+ roles ["web"]
51
+ ami "ami-bb709dd2"
52
+ ssh_user "ubuntu"
53
+ instance_type "m1.small"
54
+ availability_zone "us-east-1b"
55
+ end
56
+
57
+ rds_node "db-1" do
58
+ engine "MySQL5.1"
59
+ db_instance_class "db.m1.small"
60
+ master_username "root"
61
+ master_user_password "password"
62
+ port 3306
63
+ allocated_storage 5
64
+ availability_zone "us-east-1b"
65
+ preferred_maintenance_window "Sun:03:00-Sun:07:00"
66
+ preferred_backup_window "03:00-05:00"
67
+ backup_retention_period 7
68
+ db_parameters [{:name => "character_set_server", :value => "utf8"},
69
+ {:name => "collation_server", :value => "utf8_bin"},
70
+ {:name => "long_query_time", :value => "5"}]
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+
77
+ #######################
78
+ # Teardown
79
+ #######################
80
+
81
+ teardown do
82
+ # terminate ec2 instances
83
+ instances = @ec2.describe_instances
84
+ to_be_terminated = Array.new
85
+ to_be_watched = Array.new
86
+ @cloud.ec2_nodes.each_pair do |node_name, node|
87
+ instance = @cloud.find_ec2_node_instance(node_name, instances)
88
+ if !instance.nil?
89
+ to_be_terminated << instance.instanceId
90
+ to_be_watched << node_name
91
+ end
92
+ end
93
+ if !to_be_terminated.empty?
94
+ puts "Terminating AWS integration test EC2 instances"
95
+ @ec2.terminate_instances(:instance_id => to_be_terminated)
96
+ end
97
+ STDOUT.sync = true
98
+ print "Waiting for Nodes #{to_be_watched.inspect} to terminate..." if !to_be_watched.empty?
99
+ while !to_be_watched.empty?
100
+ instances = @ec2.describe_instances()
101
+ to_be_watched.each do |node_name|
102
+ instance = @cloud.find_ec2_node_instance(node_name, instances)
103
+ if instance.nil?
104
+ puts ""
105
+ puts "Node #{node_name} terminated"
106
+ to_be_watched.delete(node_name)
107
+ print "Waiting for Nodes #{to_be_watched.inspect} to terminate..." if !to_be_watched.empty?
108
+ else
109
+ print "."
110
+ end
111
+ end
112
+ sleep 5 if !to_be_watched.empty?
113
+ end
114
+
115
+ # delete ELBs
116
+ balancers = @elb.describe_load_balancers()
117
+ to_be_deleted = Array.new
118
+ @cloud.elb_nodes.each_pair do |node_name, node|
119
+ instance = @cloud.find_elb_node_instance(node_name, balancers)
120
+ if !instance.nil?
121
+ to_be_deleted << node
122
+ end
123
+ end
124
+ if !to_be_deleted.empty?
125
+ puts "Deleting AWS integration test ELB instances"
126
+ to_be_deleted.each do |node|
127
+ puts "Deleting AWS integration test ELB: #{node.name}"
128
+ @elb.delete_load_balancer(:load_balancer_name => node.load_balancer_name)
129
+ end
130
+ end
131
+
132
+ # delete ec2 security groups
133
+ @cloud.ec2_security_groups.each do |group_name|
134
+ @ec2.delete_security_group(:group_name => group_name)
135
+ end
136
+
137
+ # delete RDS instances
138
+ all_instances = @rds.describe_db_instances
139
+ wait_for = Hash.new
140
+ to_be_terminated = Array.new
141
+ to_be_watched = Array.new
142
+ @cloud.rds_nodes.each_pair do |node_name, node|
143
+ node_instance = @cloud.find_rds_node_instance(node.db_instance_identifier, all_instances)
144
+ if !node_instance.nil?
145
+ if node_instance.DBInstanceStatus.eql?("deleting")
146
+ to_be_watched << node_name
147
+ elsif (node_instance.DBInstanceStatus.eql?("creating") ||
148
+ node_instance.DBInstanceStatus.eql?("rebooting") ||
149
+ node_instance.DBInstanceStatus.eql?("modifying") ||
150
+ node_instance.DBInstanceStatus.eql?("resetting-mastercredentials") ||
151
+ node_instance.DBInstanceStatus.eql?("backing-up"))
152
+ wait_for[node_name] = node_instance.DBInstanceStatus
153
+ elsif (node_instance.DBInstanceStatus.eql?("available") ||
154
+ node_instance.DBInstanceStatus.eql?("failed") ||
155
+ node_instance.DBInstanceStatus.eql?("storage-full"))
156
+ to_be_terminated << node_name
157
+ end
158
+ end
159
+ end
160
+
161
+ print "Waiting for AWS integration test RDS Nodes #{wait_for.inspect} to finish work before deleting. This may take several minutes..." if !wait_for.empty?
162
+ while !wait_for.empty?
163
+ instances = @rds.describe_db_instances
164
+ wait_for.each_pair do |node_name, status|
165
+ node = @cloud.rds_nodes[node_name]
166
+ node_instance = @cloud.find_rds_node_instance(node.db_instance_identifier, instances)
167
+ if (node_instance.DBInstanceStatus.eql?("available") ||
168
+ node_instance.DBInstanceStatus.eql?("failed") ||
169
+ node_instance.DBInstanceStatus.eql?("storage-full"))
170
+ puts ""
171
+ wait_for.delete(node_name)
172
+ to_be_terminated << node_name
173
+ print "Waiting for AWS integration test RDS Nodes #{wait_for.inspect} to finish work before deleting. This may take several minutes..." if !wait_for.empty?
174
+ else
175
+ print "."
176
+ end
177
+ end
178
+ sleep 5 if !wait_for.empty?
179
+ end
180
+
181
+ puts "Deleting AWS integration test RDS instances" if !to_be_terminated.empty?
182
+ to_be_terminated.each do |node_name|
183
+ node = @cloud.rds_nodes[node_name]
184
+ now = DateTime.now
185
+ final_snapshot = node.db_instance_identifier + "-" + now.to_s
186
+ puts "Deleting RDS instance #{node_name}..."
187
+ @rds.delete_db_instance(:db_instance_identifier => node.db_instance_identifier, :skip_final_snapshot => true)
188
+ to_be_watched << node_name
189
+ end
190
+ STDOUT.sync = true
191
+ print "Waiting for AWS integration test Nodes #{to_be_watched.inspect} to delete. This may take several minutes..." if !to_be_watched.empty?
192
+ while !to_be_watched.empty?
193
+ instances = @rds.describe_db_instances
194
+ to_be_watched.each do |node_name|
195
+ node = @cloud.rds_nodes[node_name]
196
+ instance = @cloud.find_rds_node_instance(node.db_instance_identifier, instances)
197
+ if instance.nil?
198
+ puts ""
199
+ puts "Node #{node_name} deleted"
200
+ to_be_watched.delete(node_name)
201
+ print "Waiting for Nodes #{to_be_watched.inspect} to delete. This may take several minutes..." if !to_be_watched.empty?
202
+ else
203
+ print "."
204
+ end
205
+ end
206
+ sleep 5 if !to_be_watched.empty?
207
+ end
208
+
209
+ # delete DB parameter groups
210
+ @cloud.rds_nodes.each_pair do |node_name, node|
211
+ if !node.db_parameters.nil?
212
+ begin
213
+ @rds.delete_db_parameter_group(:db_parameter_group_name => node.db_parameter_group_name)
214
+ puts "Deleted AWS integration test DB parameter group: #{node.db_parameter_group_name}"
215
+ rescue AWS::Error => aws_error
216
+ if !aws_error.message.eql? "DBParameterGroup #{node.db_parameter_group_name} not found."
217
+ # it didn't exist
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ # delete DB security groups
224
+ @cloud.rds_nodes.each_pair do |node_name, node|
225
+ begin
226
+ @rds.delete_db_security_group(:db_security_group_name => node.db_security_group_name)
227
+ puts "Deleted AWS integration test DB security group: #{node.db_security_group_name}"
228
+ rescue AWS::Error => aws_error
229
+ if !aws_error.message.eql? "DBSecurityGroup #{node.db_security_group_name} not found."
230
+ # it didn't exist
231
+ end
232
+ end
233
+ end
234
+
235
+ # delete DB snapshots
236
+ @cloud.rds_nodes.each_pair do |node_name, node|
237
+ begin
238
+ snapshots = @rds.describe_db_snapshots(:db_instance_identifier => node.db_instance_identifier)
239
+ if !snapshots.DescribeDBSnapshotsResult.DBSnapshots.nil?
240
+ snapshots.DescribeDBSnapshotsResult.DBSnapshots.DBSnapshot.each do |snapshot|
241
+ if snapshot.respond_to?(:DBSnapshotIdentifier)
242
+ @rds.delete_db_snapshot(:db_snapshot_identifier => snapshot.DBSnapshotIdentifier)
243
+ puts "Deleted AWS integration test DB snapshot: #{snapshot.DBSnapshotIdentifier}"
244
+ end
245
+ end
246
+ end
247
+ rescue AWS::Error => aws_error
248
+ puts aws_error
249
+ end
250
+ end
251
+
252
+ FileUtils.rm_rf([Maestro.maestro_log_directory], :secure => true) if File.exists?(Maestro.maestro_log_directory)
253
+ ENV.delete Maestro::MAESTRO_DIR_ENV_VAR
254
+ end
255
+
256
+
257
+ #######################
258
+ # Tests
259
+ #######################
260
+
261
+ should "be able to connect to AWS" do
262
+ assert_nothing_raised do
263
+ @cloud.connect!
264
+ end
265
+ end
266
+
267
+ should "report status" do
268
+ assert_nothing_raised do
269
+ @cloud.connect!
270
+ # not running code path
271
+ @cloud.status
272
+ end
273
+ end
274
+
275
+ should "not have any nodes running" do
276
+ instances = @ec2.describe_instances
277
+ @cloud.ec2_nodes.each_pair do |node_name, node|
278
+ assert @cloud.find_ec2_node_instance(node.name, instances).nil?
279
+ end
280
+ balancers = @elb.describe_load_balancers
281
+ @cloud.elb_nodes.each_pair do |node_name, node|
282
+ assert @cloud.find_elb_node_instance(node.name, balancers).nil?
283
+ end
284
+ db_instances = @rds.describe_db_instances
285
+ @cloud.rds_nodes.each_pair do |node_name, node|
286
+ assert @cloud.find_rds_node_instance(node.name, balancers).nil?
287
+ end
288
+ end
289
+
290
+ should "not have any rds db parameter groups" do
291
+ @cloud.db_parameter_groups.each do |group_name|
292
+ begin
293
+ group = @rds.describe_db_parameter_groups(:db_parameter_group_name => group_name)
294
+ assert false
295
+ rescue AWS::Error => aws_error
296
+ assert aws_error.message.eql? "DBParameterGroup #{group_name} not found."
297
+ end
298
+ end
299
+ end
300
+
301
+ should "ensure rds db parameter groups" do
302
+ assert_nothing_raised do
303
+ @cloud.connect!
304
+ # doesn't exist code path
305
+ @cloud.ensure_rds_db_parameter_groups
306
+ assert_rds_db_parameter_groups
307
+ # already exists code path
308
+ @cloud.ensure_rds_db_parameter_groups
309
+ assert_rds_db_parameter_groups
310
+ end
311
+ end
312
+
313
+ should "not have any rds db security groups" do
314
+ @cloud.db_security_groups.each do |group_name|
315
+ begin
316
+ group = @rds.describe_db_security_groups(:db_security_group_name => group_name)
317
+ assert false
318
+ rescue AWS::Error => aws_error
319
+ assert aws_error.message.eql? "DBSecurityGroup #{group_name} not found."
320
+ end
321
+ end
322
+ end
323
+
324
+ should "ensure rds db security groups" do
325
+ assert_nothing_raised do
326
+ @cloud.connect!
327
+ # doesn't exist code path
328
+ @cloud.ensure_rds_db_security_groups
329
+ assert_rds_db_security_groups
330
+ # already exists code path
331
+ @cloud.ensure_rds_db_security_groups
332
+ assert_rds_db_security_groups
333
+ end
334
+ end
335
+
336
+ should "not have any ec2 security groups" do
337
+ cloud_security_groups = @cloud.ec2_security_groups
338
+ cloud_security_groups.each do |group_name|
339
+ security_group = @ec2.describe_security_groups(:group_name => [group_name])
340
+ assert security_group.nil?
341
+ end
342
+ end
343
+
344
+ should "ensure ec2 security groups" do
345
+ assert_nothing_raised do
346
+ @cloud.connect!
347
+ # doesn't exist code path
348
+ @cloud.ensure_ec2_security_groups
349
+ assert_ec2_security_groups_created
350
+ assert_role_ec2_security_groups
351
+ # already exists code path
352
+ @cloud.ensure_ec2_security_groups
353
+ assert_ec2_security_groups_created
354
+ assert_role_ec2_security_groups
355
+ end
356
+ end
357
+
358
+ should "ensure nodes running" do
359
+ assert_nothing_raised do
360
+ @cloud.connect!
361
+ @cloud.ensure_rds_security_groups
362
+ @cloud.ensure_rds_db_parameter_groups
363
+ @cloud.ensure_ec2_security_groups
364
+ @cloud.ensure_rds_db_security_groups
365
+ # not running code path
366
+ @cloud.ensure_nodes_running
367
+ assert_rds_nodes_running
368
+ assert_ec2_nodes_running
369
+ assert_elb_nodes_running
370
+ # already running code path
371
+ @cloud.ensure_nodes_running
372
+ assert_rds_nodes_running
373
+ assert_ec2_nodes_running
374
+ assert_elb_nodes_running
375
+ end
376
+ end
377
+
378
+ should "not find a bogus elastic ip allocated" do
379
+ assert_nothing_raised do
380
+ @cloud.connect!
381
+ assert !@cloud.elastic_ip_allocated?("127.0.0.1")
382
+ end
383
+ end
384
+
385
+ should "find an allocated elastic ip" do
386
+ assert_nothing_raised do
387
+ @cloud.connect!
388
+ elastic_ip = @ec2.allocate_address
389
+ assert @cloud.elastic_ip_allocated?(elastic_ip.publicIp)
390
+ end
391
+ end
392
+
393
+ should "ensure elastic ips" do
394
+ assert_nothing_raised do
395
+ @cloud.nodes.delete("lb-1")
396
+ @cloud.elb_nodes.delete("lb-1")
397
+ @cloud.nodes.delete("db-1")
398
+ @cloud.rds_nodes.delete("db-1")
399
+ # case 1: associate an Elastic IP to node_1
400
+ elastic_ip = @ec2.allocate_address
401
+ @cloud.nodes["node-1"].elastic_ip(elastic_ip.publicIp)
402
+ @cloud.connect!
403
+ @cloud.ensure_ec2_security_groups
404
+ @cloud.ensure_nodes_running
405
+ @cloud.ensure_elastic_ips
406
+ instances = @ec2.describe_instances
407
+ instance = @cloud.find_ec2_node_instance("node-1", instances)
408
+ instance_id = @cloud.elastic_ip_association(elastic_ip.publicIp)
409
+ assert instance_id.eql?(instance.instanceId)
410
+ # case 2: disassociate Elastic IP from node_1 and associate with node_2
411
+ @cloud.nodes["node-1"].elastic_ip(nil)
412
+ @cloud.nodes["node-2"].elastic_ip(elastic_ip.publicIp)
413
+ @cloud.ensure_elastic_ips
414
+ instances = @ec2.describe_instances
415
+ node_1_instance = @cloud.find_ec2_node_instance("node-1", instances)
416
+ node_2_instance = @cloud.find_ec2_node_instance("node-2", instances)
417
+ instance_id = @cloud.elastic_ip_association(elastic_ip.publicIp)
418
+ assert instance_id.eql?(node_2_instance.instanceId)
419
+ # case 3: Elastic IP already associated, do nothing
420
+ @cloud.ensure_elastic_ips
421
+ instance_id = @cloud.elastic_ip_association(elastic_ip.publicIp)
422
+ assert instance_id.eql?(node_2_instance.instanceId)
423
+ end
424
+ end
425
+
426
+ should "not find a bogus ebs volume allocated" do
427
+ assert_nothing_raised do
428
+ @cloud.connect!
429
+ assert !@cloud.ebs_volume_allocated?("vol-abcd1234")
430
+ end
431
+ end
432
+
433
+ should "find an ebs volume allocated" do
434
+ assert_nothing_raised do
435
+ @cloud.connect!
436
+ volume = @ec2.create_volume(:availability_zone => "us-east-1b", :size => "1")
437
+ to_be_watched = [volume.volumeId]
438
+ while !to_be_watched.empty?
439
+ volumes = @ec2.describe_volumes(:volume_id => to_be_watched[0])
440
+ if volumes.volumeSet.item[0].status.eql? "available"
441
+ to_be_watched.clear
442
+ end
443
+ sleep 5 if !to_be_watched.empty?
444
+ end
445
+ assert @cloud.ebs_volume_allocated?(volume.volumeId)
446
+ end
447
+ end
448
+
449
+ should "ensure ebs volumes" do
450
+ assert_nothing_raised do
451
+ @cloud.nodes.delete("lb-1")
452
+ @cloud.elb_nodes.delete("lb-1")
453
+ @cloud.nodes.delete("db-1")
454
+ @cloud.rds_nodes.delete("db-1")
455
+ # case 1: create an EBS volume and attach it to node-1
456
+ volume = @ec2.create_volume(:availability_zone => "us-east-1b", :size => "1")
457
+ to_be_watched = [volume.volumeId]
458
+ while !to_be_watched.empty?
459
+ volumes = @ec2.describe_volumes(:volume_id => to_be_watched[0])
460
+ if volumes.volumeSet.item[0].status.eql? "available"
461
+ to_be_watched.clear
462
+ end
463
+ sleep 5 if !to_be_watched.empty?
464
+ end
465
+ @cloud.nodes["node-1"].ebs_volume_id(volume.volumeId)
466
+ @cloud.nodes["node-1"].ebs_device("/dev/sdh")
467
+ @cloud.connect!
468
+ @cloud.ensure_ec2_security_groups
469
+ @cloud.ensure_nodes_running
470
+ @cloud.ensure_ebs_volumes
471
+ instances = @ec2.describe_instances
472
+ node_1_instance = @cloud.find_ec2_node_instance("node-1", instances)
473
+ node_2_instance = @cloud.find_ec2_node_instance("node-2", instances)
474
+ instance_id = @cloud.ebs_volume_association(volume.volumeId)
475
+ assert instance_id.eql?(node_1_instance.instanceId)
476
+ # case 2: detach EBS volume from node_1 and attach it to node_2
477
+ @cloud.nodes["node-1"].ebs_volume_id(nil)
478
+ @cloud.nodes["node-1"].ebs_device(nil)
479
+ @cloud.nodes["node-2"].ebs_volume_id(volume.volumeId)
480
+ @cloud.nodes["node-2"].ebs_device("/dev/sdh")
481
+ @cloud.ensure_ebs_volumes
482
+ instance_id = @cloud.ebs_volume_association(volume.volumeId)
483
+ assert instance_id.eql?(node_2_instance.instanceId)
484
+ # case 3: EBS Volumes already associated, do nothing
485
+ @cloud.ensure_ebs_volumes
486
+ instance_id = @cloud.ebs_volume_association(volume.volumeId)
487
+ assert instance_id.eql?(node_2_instance.instanceId)
488
+ end
489
+ end
490
+
491
+ should "start clean" do
492
+ assert_nothing_raised do
493
+ elastic_ip = @ec2.allocate_address
494
+ @cloud.nodes["node-1"].elastic_ip(elastic_ip.publicIp)
495
+ volume = @ec2.create_volume(:availability_zone => "us-east-1b", :size => "1")
496
+ to_be_watched = [volume.volumeId]
497
+ while !to_be_watched.empty?
498
+ volumes = @ec2.describe_volumes(:volume_id => to_be_watched[0])
499
+ if volumes.volumeSet.item[0].status.eql? "available"
500
+ to_be_watched.clear
501
+ end
502
+ sleep 5 if !to_be_watched.empty?
503
+ end
504
+ @cloud.nodes["node-2"].ebs_volume_id(volume.volumeId)
505
+ @cloud.nodes["node-2"].ebs_device("/dev/sdh")
506
+ @cloud.connect!
507
+ @cloud.start
508
+ assert_rds_db_parameter_groups
509
+ assert_ec2_security_groups_created
510
+ assert_role_ec2_security_groups
511
+ assert_rds_db_security_groups
512
+ assert_rds_nodes_running
513
+ assert_ec2_nodes_running
514
+ assert_elb_nodes_running
515
+ instances = @ec2.describe_instances
516
+ node_1_instance = @cloud.find_ec2_node_instance("node-1", instances)
517
+ node_2_instance = @cloud.find_ec2_node_instance("node-2", instances)
518
+ elastic_ip_instance_id = @cloud.elastic_ip_association(elastic_ip.publicIp)
519
+ assert elastic_ip_instance_id.eql?(node_1_instance.instanceId)
520
+ ebs_volume_instance_id = @cloud.ebs_volume_association(volume.volumeId)
521
+ assert ebs_volume_instance_id.eql?(node_2_instance.instanceId)
522
+ # running status code path
523
+ @cloud.status
524
+ end
525
+ end
526
+
527
+ should "upload Chef assets" do
528
+ assert_nothing_raised do
529
+ @cloud.connect!
530
+ @cloud.upload_chef_assets
531
+ bucket = AWS::S3::Bucket.find(@cloud.chef_bucket)
532
+ assert !bucket.nil?
533
+ assert !bucket[Maestro::MAESTRO_CHEF_ARCHIVE].nil?
534
+ end
535
+ end
536
+
537
+ should "configure nodes" do
538
+ assert_nothing_raised do
539
+ @cloud.nodes.delete("lb-1")
540
+ @cloud.elb_nodes.delete("lb-1")
541
+ @cloud.nodes.delete("db-1")
542
+ @cloud.rds_nodes.delete("db-1")
543
+ @cloud.connect!
544
+ @cloud.start
545
+ @cloud.configure
546
+ assert_ec2_security_groups_created
547
+ assert_role_ec2_security_groups
548
+ assert_ec2_nodes_running
549
+ end
550
+ end
551
+
552
+ should "ensure nodes terminated" do
553
+ assert_nothing_raised do
554
+ @cloud.connect!
555
+ @cloud.start
556
+ @cloud.ensure_nodes_terminated
557
+ assert_elb_nodes_not_running
558
+ assert_ec2_nodes_not_running
559
+ assert_rds_nodes_not_running
560
+ end
561
+ end
562
+
563
+ should "shutdown clean" do
564
+ assert_nothing_raised do
565
+ @cloud.connect!
566
+ @cloud.start
567
+ @cloud.shutdown
568
+ assert_elb_nodes_not_running
569
+ assert_ec2_nodes_not_running
570
+ assert_rds_nodes_not_running
571
+ end
572
+ end
573
+ end
574
+
575
+
576
+ ################################
577
+ # Assertion helper methods
578
+ ################################
579
+
580
+ # asserts that all of the Cloud's EC2 security groups have been created
581
+ def assert_ec2_security_groups_created
582
+ cloud_security_groups = @cloud.ec2_security_groups
583
+ cloud_security_groups.each do |group_name|
584
+ security_group = @ec2.describe_security_groups(:group_name => [group_name])
585
+ assert !security_group.nil?
586
+ end
587
+ end
588
+
589
+ # asserts that the Cloud's Role EC2 security groups have been configured correctly
590
+ def assert_role_ec2_security_groups
591
+ role_security_groups = @cloud.role_ec2_security_groups
592
+ role_security_groups.each do |group_name|
593
+ security_group = @ec2.describe_security_groups(:group_name => [group_name])
594
+ assert !security_group.nil?
595
+ if group_name.eql? @cloud.default_ec2_security_group
596
+ assert_default_ec2_security_group(security_group)
597
+ else
598
+ assert_role_ec2_security_group(security_group)
599
+ end
600
+ end
601
+ end
602
+
603
+ # asserts the default EC2 security group has been configured correctly
604
+ def assert_default_ec2_security_group(security_group)
605
+ assert !security_group.securityGroupInfo.item[0].ipPermissions.nil?
606
+ assert !security_group.securityGroupInfo.item[0].ipPermissions.item.nil?
607
+ assert !security_group.securityGroupInfo.item[0].ipPermissions.item.empty?
608
+ assert security_group.securityGroupInfo.item[0].ipPermissions.item.size == 4
609
+ ip_permissions = security_group.securityGroupInfo.item[0].ipPermissions.item
610
+ found_default_icmp = false
611
+ found_default_tcp = false
612
+ found_default_udp = false
613
+ found_ssh = false
614
+ ip_permissions.each do |permission|
615
+ if permission.groups.nil?
616
+ assert !permission.fromPort.nil?
617
+ assert permission.fromPort.eql?("22")
618
+ assert !permission.toPort.nil?
619
+ assert permission.toPort.eql?("22")
620
+ assert !permission.ipProtocol.nil?
621
+ assert permission.ipProtocol.eql?("tcp")
622
+ found_ssh = true
623
+ else
624
+ assert !permission.groups.item.nil?
625
+ assert permission.groups.item.size == 1
626
+ assert permission.groups.item[0].groupName.eql?(@cloud.default_ec2_security_group)
627
+ assert permission.groups.item[0].userId.gsub(/-/,'').eql?(@cloud.aws_account_id.gsub(/-/,''))
628
+ if permission.ipProtocol.eql?("icmp")
629
+ found_default_icmp = true
630
+ assert permission.fromPort.eql?("-1")
631
+ assert permission.toPort.eql?("-1")
632
+ elsif permission.ipProtocol.eql?("tcp")
633
+ found_default_tcp = true
634
+ assert permission.fromPort.eql?("1")
635
+ assert permission.toPort.eql?("65535")
636
+ elsif permission.ipProtocol.eql?("udp")
637
+ found_default_udp = true
638
+ assert permission.fromPort.eql?("1")
639
+ assert permission.toPort.eql?("65535")
640
+ end
641
+ end
642
+ end
643
+ assert found_default_icmp
644
+ assert found_default_tcp
645
+ assert found_default_udp
646
+ assert found_ssh
647
+ end
648
+
649
+ # asserts a Role EC2 security group is configured correctly
650
+ def assert_role_ec2_security_group(security_group)
651
+ role_security_groups = @cloud.role_ec2_security_groups
652
+ @cloud.roles.values.each do |role|
653
+ role_security_group_name = @cloud.role_ec2_security_group_name(role.name)
654
+ assert !role_security_group_name.nil?
655
+ role_security_group = role_security_groups.find {|group| group.eql? role_security_group_name}
656
+ assert !role_security_group.nil?
657
+ assert !security_group.securityGroupInfo.nil?
658
+ assert !security_group.securityGroupInfo.item.nil?
659
+ assert !security_group.securityGroupInfo.item.empty?
660
+ if !role.public_ports.nil?
661
+ role.public_ports.each do |port|
662
+ found_port = false
663
+ security_group.securityGroupInfo.item.each do |item|
664
+ if !item.ipPermissions.nil?
665
+ item.ipPermissions.item.each do |permission|
666
+ if permission.fromPort.eql?(port.to_s) && permission.toPort.eql?(port.to_s)
667
+ found_port = true
668
+ end
669
+ end
670
+ assert found_port
671
+ end
672
+ end
673
+ end
674
+ end
675
+ end
676
+ end
677
+
678
+ # asserts that the test Cloud's Ec2 Nodes are running and security groups applied correctly
679
+ def assert_ec2_nodes_running
680
+ instances = @ec2.describe_instances
681
+ @cloud.ec2_nodes.each_pair do |node_name, node|
682
+ instance = @cloud.find_ec2_node_instance(node_name, instances)
683
+ assert !instance.nil?
684
+ this_instance = @ec2.describe_instances(:instance_id => [instance.instanceId])
685
+ assert !this_instance.nil?
686
+ assert this_instance.reservationSet.item.size == 1
687
+ assert this_instance.reservationSet.item[0].groupSet.item.size == node.security_groups.size
688
+ node.security_groups.each do |node_group|
689
+ assert this_instance.reservationSet.item[0].groupSet.item.any? {|group| group.groupId.eql? node_group}
690
+ end
691
+ end
692
+ end
693
+
694
+ # asserts that the test Cloud's Nodes are not running
695
+ def assert_ec2_nodes_not_running
696
+ instances = @ec2.describe_instances
697
+ @cloud.ec2_nodes.each_pair do |node_name, node|
698
+ instance = @cloud.find_ec2_node_instance(node_name, instances)
699
+ assert instance.nil?
700
+ end
701
+ end
702
+
703
+ # asserts that the test Cloud's Elb Nodes are running and configured correctly
704
+ def assert_elb_nodes_running
705
+ elb_instances = @elb.describe_load_balancers
706
+ @cloud.elb_nodes.each_pair do |node_name, node|
707
+ elb_instance = @cloud.find_elb_node_instance(node_name, elb_instances)
708
+ assert !elb_instance.nil?
709
+ assert elb_instance.LoadBalancerName.eql? node.load_balancer_name
710
+ assert elb_instance.AvailabilityZones.member[0].eql? "us-east-1b"
711
+ assert elb_instance.Listeners.member[0].InstancePort.eql? "80"
712
+ assert elb_instance.Listeners.member[0].Protocol.eql? "HTTP"
713
+ assert elb_instance.Listeners.member[0].LoadBalancerPort.eql? "80"
714
+ assert elb_instance.HealthCheck.HealthyThreshold.eql? "3"
715
+ assert elb_instance.HealthCheck.Timeout.eql? "15"
716
+ assert elb_instance.HealthCheck.UnhealthyThreshold.eql? "5"
717
+ assert elb_instance.HealthCheck.Interval.eql? "60"
718
+ assert !elb_instance.Instances.nil?
719
+ assert !elb_instance.Instances.member.nil?
720
+ assert !elb_instance.Instances.member.empty?
721
+ registered_ec2_instance_ids = Array.new
722
+ elb_instance.Instances.member.each {|member| registered_ec2_instance_ids << member.InstanceId}
723
+ ec2_instances = @ec2.describe_instances
724
+ @cloud.ec2_nodes.each_pair do |ec2_node_name, ec2_node|
725
+ ec2_instance = @cloud.find_ec2_node_instance(ec2_node_name, ec2_instances)
726
+ assert registered_ec2_instance_ids.include?(ec2_instance.instanceId)
727
+ end
728
+ assert node.ec2_nodes.size == registered_ec2_instance_ids.size
729
+ end
730
+ end
731
+
732
+ # asserts that the test Cloud's Elb Nodes are not running
733
+ def assert_elb_nodes_not_running
734
+ instances = @elb.describe_load_balancers
735
+ @cloud.elb_nodes.each_pair do |node_name, node|
736
+ instance = @cloud.find_elb_node_instance(node_name, instances)
737
+ assert instance.nil?
738
+ end
739
+ end
740
+
741
+ # asserts that the test Cloud's Rds Nodes' db parameter groups are created and configured correctly
742
+ def assert_rds_db_parameter_groups
743
+ @cloud.rds_nodes.each_pair do |name, node|
744
+ params = Hash.new
745
+ node.db_parameters.each {|hash| params[hash[:name]] = hash[:value]}
746
+ begin
747
+ parameters = @rds.describe_db_parameters(:db_parameter_group_name => node.db_parameter_group_name)
748
+ assert !parameters.nil?
749
+ parameters.DescribeDBParametersResult.Parameters.Parameter.each do |p|
750
+ params.delete(p.ParameterName) if params.has_key?(p.ParameterName) && !p.ParameterValue.nil? && params[p.ParameterName].eql?(p.ParameterValue)
751
+ end
752
+ while !parameters.DescribeDBParametersResult.Marker.nil?
753
+ parameters = @rds.describe_db_parameters(:db_parameter_group_name => node.db_parameter_group_name, :marker => parameters.DescribeDBParametersResult.Marker)
754
+ assert !parameters.nil?
755
+ parameters.DescribeDBParametersResult.Parameters.Parameter.each do |p|
756
+ params.delete(p.ParameterName) if params.has_key?(p.ParameterName) && !p.ParameterValue.nil? && params[p.ParameterName].eql?(p.ParameterValue)
757
+ end
758
+ end
759
+ rescue AWS::Error => aws_error
760
+ assert false if aws_error.message.eql? "DBParameterGroup not found: #{node.db_parameter_group_name}"
761
+ end
762
+ assert params.empty?
763
+ end
764
+ end
765
+
766
+ # asserts that the test Cloud's Rds Nodes' db security groups are created and configured correctly
767
+ def assert_rds_db_security_groups
768
+ @cloud.rds_nodes.each_pair do |name, node|
769
+ begin
770
+ group = @rds.describe_db_security_groups(:db_security_group_name => node.db_security_group_name)
771
+ assert !group.nil?
772
+ assert group.DescribeDBSecurityGroupsResult.DBSecurityGroups.DBSecurityGroup.EC2SecurityGroups.EC2SecurityGroup.EC2SecurityGroupName.eql? @cloud.default_ec2_security_group
773
+ rescue AWS::Error => aws_error
774
+ assert false if aws_error.message.eql? "DBSecurityGroup not found: #{node.db_security_group_name}"
775
+ end
776
+ end
777
+ end
778
+
779
+ # asserts that the test Cloud's Rds Nodes are running and configured correctly
780
+ def assert_rds_nodes_running
781
+ db_instances = @rds.describe_db_instances
782
+ @cloud.rds_nodes.each_pair do |node_name, node|
783
+ rds_instance = @cloud.find_rds_node_instance(node.db_instance_identifier, db_instances)
784
+ assert !rds_instance.nil?
785
+ assert rds_instance.PreferredMaintenanceWindow.eql? node.preferred_maintenance_window.downcase
786
+ assert rds_instance.Engine.eql? node.engine.downcase
787
+ assert rds_instance.MasterUsername.eql? node.master_username
788
+ assert rds_instance.DBInstanceClass.eql? node.db_instance_class
789
+ assert rds_instance.BackupRetentionPeriod.eql? node.backup_retention_period.to_s
790
+ assert rds_instance.DBInstanceIdentifier.eql? node.db_instance_identifier
791
+ assert rds_instance.AllocatedStorage.eql? node.allocated_storage.to_s
792
+ assert rds_instance.AvailabilityZone.eql? node.availability_zone
793
+ assert rds_instance.PreferredBackupWindow.eql? node.preferred_backup_window
794
+ end
795
+ end
796
+
797
+ # asserts that the test Cloud's Rds Nodes are not running
798
+ def assert_rds_nodes_not_running
799
+ db_instances = @rds.describe_db_instances
800
+ @cloud.rds_nodes.each_pair do |node_name, node|
801
+ rds_instance = @cloud.find_rds_node_instance(node.db_instance_identifier, db_instances)
802
+ assert rds_instance.nil?
803
+ end
804
+ end
805
+ end