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.
- 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
|