the-maestro 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +25 -0
- data/LICENSE +23 -0
- data/README.rdoc +378 -0
- data/Rakefile +116 -0
- data/VERSION +1 -0
- data/lib/maestro.rb +354 -0
- data/lib/maestro/cloud.rb +384 -0
- data/lib/maestro/cloud/aws.rb +1231 -0
- data/lib/maestro/dsl_property.rb +15 -0
- data/lib/maestro/log4r/console_formatter.rb +18 -0
- data/lib/maestro/log4r/file_formatter.rb +24 -0
- data/lib/maestro/node.rb +123 -0
- data/lib/maestro/operating_system.rb +53 -0
- data/lib/maestro/operating_system/cent_os.rb +23 -0
- data/lib/maestro/operating_system/debian.rb +40 -0
- data/lib/maestro/operating_system/fedora.rb +23 -0
- data/lib/maestro/operating_system/ubuntu.rb +100 -0
- data/lib/maestro/role.rb +36 -0
- data/lib/maestro/tasks.rb +52 -0
- data/lib/maestro/validator.rb +32 -0
- data/rails/init.rb +1 -0
- data/test/integration/base_aws.rb +156 -0
- data/test/integration/fixtures/config/maestro/cookbooks/emacs/metadata.json +41 -0
- data/test/integration/fixtures/config/maestro/cookbooks/emacs/metadata.rb +3 -0
- data/test/integration/fixtures/config/maestro/cookbooks/emacs/recipes/default.rb +21 -0
- data/test/integration/fixtures/config/maestro/roles/default.json +9 -0
- data/test/integration/fixtures/config/maestro/roles/web.json +9 -0
- data/test/integration/helper.rb +8 -0
- data/test/integration/test_aws_cloud.rb +805 -0
- data/test/integration/test_cent_os.rb +104 -0
- data/test/integration/test_debian.rb +119 -0
- data/test/integration/test_fedora.rb +104 -0
- data/test/integration/test_ubuntu.rb +149 -0
- data/test/unit/fixtures/invalid-clouds-not-a-directory/config/maestro/clouds +1 -0
- data/test/unit/fixtures/invalid-cookbooks-not-a-directory/config/maestro/cookbooks +0 -0
- data/test/unit/fixtures/invalid-maestro-not-a-directory/config/maestro +0 -0
- data/test/unit/fixtures/invalid-missing-cookbooks/config/maestro/clouds/valid.yml +21 -0
- data/test/unit/fixtures/invalid-missing-roles/config/maestro/clouds/valid.yml +21 -0
- data/test/unit/fixtures/invalid-roles-not-a-directory/config/maestro/roles +1 -0
- data/test/unit/fixtures/ssh/id_rsa-maestro-test-keypair +27 -0
- data/test/unit/helper.rb +6 -0
- data/test/unit/test_aws_cloud.rb +133 -0
- data/test/unit/test_aws_ec2_node.rb +76 -0
- data/test/unit/test_aws_elb_node.rb +221 -0
- data/test/unit/test_aws_rds_node.rb +380 -0
- data/test/unit/test_cent_os.rb +28 -0
- data/test/unit/test_cloud.rb +142 -0
- data/test/unit/test_debian.rb +62 -0
- data/test/unit/test_fedora.rb +28 -0
- data/test/unit/test_invalid_mode.rb +11 -0
- data/test/unit/test_maestro.rb +140 -0
- data/test/unit/test_node.rb +50 -0
- data/test/unit/test_operating_system.rb +19 -0
- data/test/unit/test_rails_mode.rb +77 -0
- data/test/unit/test_role.rb +59 -0
- data/test/unit/test_standalone_mode.rb +75 -0
- data/test/unit/test_ubuntu.rb +95 -0
- data/the-maestro.gemspec +150 -0
- 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,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,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
|