furnish-aws 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +59 -0
- data/Rakefile +32 -0
- data/furnish-aws.gemspec +30 -0
- data/lib/furnish/aws/version.rb +6 -0
- data/lib/furnish/provisioners/aws.rb +101 -0
- data/lib/furnish/provisioners/ec2.rb +372 -0
- data/lib/furnish/provisioners/security_group.rb +380 -0
- data/test/helper.rb +61 -0
- data/test/test_aws_base_class.rb +30 -0
- data/test/test_ec2.rb +225 -0
- data/test/test_security_group.rb +322 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MDdmMmNkNDA4ZTc1NzZiNTQ0MWY0NmRjMTg2ZGZlOTUxOTQ2ZjU0Yw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MzkwNjE1M2Q5YzAyNDgzMGI5MWRhODg1ZmJjN2I1MGY2N2E0ZGNiMw==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDUyYTljYTBlMmI3YjRlNTI3ZTA2NjkyMDU5ZGE3MzRkZDMyMzYwMTE0YTI5
|
10
|
+
YTVkMTYwZTExODNmYWEyNGZjNDg0NGVjNDkzZDAyZjUzMmE5MWU4MmQxMjdi
|
11
|
+
OTZkMzkyNmVlN2ViZTM1ZjAyZGE2N2YyYTQ1Y2JiNTliOTJmZDY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjMzMGE5NTNhMzAwNWUyYWZmYTdmNTk4ODk1MzlmZTNjZmEwZjdmYTZhMDMy
|
14
|
+
MzI3NWVhZGEyZTNkN2FhNDFiMTY4NmYwMzhlOGY2MzRiMDZhNTNjMDg5Yzg2
|
15
|
+
YjJiYWIyZjY4OTYxY2JkMjMzN2Y4ZmMwMDQ3MmU0NWYzNWY5Zjg=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Erik Hollensbe
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# AWS provisioners for Furnish
|
2
|
+
|
3
|
+
Several AWS-related provisioners for Furnish:
|
4
|
+
|
5
|
+
* EC2 Instances
|
6
|
+
* Security Groups
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
For usage, see the documentation for each Provisioner class. For general
|
11
|
+
Furnish usage, see the [Furnish documentation](http://rubydoc.info/gems/furnish/).
|
12
|
+
|
13
|
+
## Testing
|
14
|
+
|
15
|
+
Tests do not use mocks. They are built in a way to do a minimum amount of API
|
16
|
+
traffic, but traffic does happen, machines and other resources get provisioned,
|
17
|
+
and yes, your card will be charged as a result of that.
|
18
|
+
|
19
|
+
Acknowledging that and still willing to test, there are two things you need to
|
20
|
+
do to get tests to run:
|
21
|
+
|
22
|
+
* `touch .acknowledged` in the root of the repo - this more or less indicates
|
23
|
+
you read the above.
|
24
|
+
* open `.aws-env` and put some ruby in it that sets your AWS credentials. This
|
25
|
+
will be evaluated at harness time. This is to ensure you don't do something
|
26
|
+
stupid to production.
|
27
|
+
|
28
|
+
e.g.:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
ENV["AWS_ACCESS_KEY_ID"]="aaaaah"
|
32
|
+
ENV["AWS_SECRET_ACCESS_KEY"]="it's a space herpe"
|
33
|
+
```
|
34
|
+
|
35
|
+
If you don't do these the test suite will not run. Both files are in
|
36
|
+
`.gitignore` so you can feel comfortable knowing they won't be committed.
|
37
|
+
|
38
|
+
## Installation
|
39
|
+
|
40
|
+
Add this line to your application's Gemfile:
|
41
|
+
|
42
|
+
gem 'furnish-aws'
|
43
|
+
|
44
|
+
And then execute:
|
45
|
+
|
46
|
+
$ bundle
|
47
|
+
|
48
|
+
Or install it yourself as:
|
49
|
+
|
50
|
+
$ gem install furnish-aws
|
51
|
+
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rdoc/task'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.test_files = FileList["test/test_*.rb"]
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
RDoc::Task.new do |rdoc|
|
12
|
+
rdoc.title = "AWS provisioners for Furnish"
|
13
|
+
rdoc.main = "README.md"
|
14
|
+
rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
|
15
|
+
rdoc.rdoc_files -= ["lib/furnish/aws/version.rb"]
|
16
|
+
if ENV["RDOC_COVER"]
|
17
|
+
rdoc.options << "-C"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "run tests with coverage report"
|
22
|
+
task "test:coverage" do
|
23
|
+
ENV["COVERAGE"] = "1"
|
24
|
+
Rake::Task["test"].invoke
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "run rdoc with coverage report"
|
28
|
+
task :rdoc_cov do
|
29
|
+
# ugh
|
30
|
+
ENV["RDOC_COVER"] = "1"
|
31
|
+
ruby "-S rake rerdoc"
|
32
|
+
end
|
data/furnish-aws.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'furnish/aws/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "furnish-aws"
|
8
|
+
gem.version = Furnish::AWS::VERSION
|
9
|
+
gem.authors = ["Erik Hollensbe"]
|
10
|
+
gem.email = ["erik+github@hollensbe.org"]
|
11
|
+
gem.description = %q{AWS provisioners for Furnish}
|
12
|
+
gem.summary = %q{AWS provisioners for Furnish}
|
13
|
+
gem.homepage = "https://github.com/chef-workflow/furnish-aws"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'furnish', '~> 0.1.0'
|
21
|
+
gem.add_dependency 'aws-sdk', '~> 1.8.5'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'rake'
|
24
|
+
gem.add_development_dependency 'minitest', '~> 4.5.0'
|
25
|
+
gem.add_development_dependency 'guard-minitest'
|
26
|
+
gem.add_development_dependency 'guard-rake', '~> 0.0.8'
|
27
|
+
gem.add_development_dependency 'rdoc', '~> 4'
|
28
|
+
gem.add_development_dependency 'rb-fsevent'
|
29
|
+
gem.add_development_dependency 'simplecov'
|
30
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'aws'
|
2
|
+
require 'furnish/logger'
|
3
|
+
require 'furnish/provisioners/api'
|
4
|
+
|
5
|
+
module Furnish # :nodoc:
|
6
|
+
module Provisioner # :nodoc:
|
7
|
+
#
|
8
|
+
# AWS base class, handles AWS API basics, argument checking, general
|
9
|
+
# utility methods.
|
10
|
+
#
|
11
|
+
# See attributes and #new for contraints that apply to all
|
12
|
+
# provisioners that inherit from it.
|
13
|
+
#
|
14
|
+
class AWS < API
|
15
|
+
include Furnish::Logger::Mixins
|
16
|
+
|
17
|
+
##
|
18
|
+
# :attr: access_key
|
19
|
+
#
|
20
|
+
# The AWS access key ID. Required.
|
21
|
+
#
|
22
|
+
furnish_property :access_key,
|
23
|
+
"AWS access key ID",
|
24
|
+
String
|
25
|
+
|
26
|
+
##
|
27
|
+
# :attr: secret_key
|
28
|
+
#
|
29
|
+
# The AWS secret key ID. Required.
|
30
|
+
#
|
31
|
+
furnish_property :secret_key,
|
32
|
+
"AWS secret key ID",
|
33
|
+
String
|
34
|
+
|
35
|
+
##
|
36
|
+
# :attr: region
|
37
|
+
#
|
38
|
+
# The AWS region. Not required. The default is nil. Inheriting
|
39
|
+
# provisioners may affect the default and/or make it required.
|
40
|
+
#
|
41
|
+
furnish_property :region,
|
42
|
+
"AWS region -- default is nil, may be defaulted or required by subclasses.",
|
43
|
+
String
|
44
|
+
|
45
|
+
#
|
46
|
+
# Construct a new provisioner. Hash for arguments is required, values
|
47
|
+
# supplied will be injected into attributes. If a hash is not supplied,
|
48
|
+
# an ArgumentError is raised.
|
49
|
+
#
|
50
|
+
# The AWS #access_key and #secret_key are required, and also raise an
|
51
|
+
# ArgumentError unless supplied.
|
52
|
+
#
|
53
|
+
# In some provisioners, a region may also be required. In those, an...
|
54
|
+
# ArgumentError will be raise if it isn't supplied.
|
55
|
+
#
|
56
|
+
# For further constraints, please see provisioners that inherit from this
|
57
|
+
# class.
|
58
|
+
#
|
59
|
+
def initialize(args)
|
60
|
+
super
|
61
|
+
check_aws_settings
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Ensures #access_key and #secret_key are set. Used by initializer.
|
66
|
+
#
|
67
|
+
def check_aws_settings
|
68
|
+
unless access_key and secret_key
|
69
|
+
raise ArgumentError, "AWS credentials must be provided to the provisioner."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Ensures the region is set. Is not used by default in the initializer,
|
75
|
+
# but is common to many provisioners.
|
76
|
+
#
|
77
|
+
def check_region
|
78
|
+
unless region
|
79
|
+
raise ArgumentError, "AWS region must be provided to the provisioner."
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# constructs an AWS::EC2 object. if a region is supplied, constraints it
|
85
|
+
# to the region and a AWS::EC2::Region object will be returned.
|
86
|
+
#
|
87
|
+
# these have the same interface for most things, and things that require
|
88
|
+
# a region in AWS::EC2 are defaulted to us-east-1. See the aws-sdk
|
89
|
+
# documentation for more details.
|
90
|
+
#
|
91
|
+
def ec2
|
92
|
+
ec2 = ::AWS::EC2.new(
|
93
|
+
:access_key_id => access_key,
|
94
|
+
:secret_access_key => secret_key
|
95
|
+
)
|
96
|
+
|
97
|
+
return region ? ec2.regions[region] : ec2
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,372 @@
|
|
1
|
+
require 'furnish/provisioners/aws'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module Furnish # :nodoc:
|
5
|
+
module Provisioner # :nodoc:
|
6
|
+
#
|
7
|
+
# EC2 Instance Provisioner. See attributes and #new for argument
|
8
|
+
# descriptions.
|
9
|
+
#
|
10
|
+
# If security groups are supplied to this provisioner as a part of a
|
11
|
+
# provisioner group (in #startup's argument list), they are appended to any
|
12
|
+
# list of security group id's already provided. This allows you to
|
13
|
+
# provision a security group with the instances without knowing about
|
14
|
+
# either beforehand.
|
15
|
+
#
|
16
|
+
# On startup, IP addresses of the instances are returned to the next
|
17
|
+
# provisioner in the provisioner group.
|
18
|
+
#
|
19
|
+
# It would be in your best interest to familiarize yourself with some of
|
20
|
+
# aws-sdk's API when trying to understand some of these parameters. Here is
|
21
|
+
# some documentation:
|
22
|
+
#
|
23
|
+
# http://rdoc.info/gems/aws-sdk
|
24
|
+
#
|
25
|
+
class EC2 < AWS
|
26
|
+
##
|
27
|
+
# :attr: region
|
28
|
+
#
|
29
|
+
# See Furnish::Provisioner::AWS#region. nil by default, required in this
|
30
|
+
# provisioner. If #availability_zone is supplied, the region will be
|
31
|
+
# extracted from what is set as the AZ.
|
32
|
+
#
|
33
|
+
|
34
|
+
##
|
35
|
+
# :attr: provision_timeout
|
36
|
+
#
|
37
|
+
# Give up after this long for the requested instances to be in a running
|
38
|
+
# state. Default is 300 seconds.
|
39
|
+
#
|
40
|
+
furnish_property :provision_timeout,
|
41
|
+
"Give up after this long for the instance to come alive after requesting from the API. Default is 300 seconds.",
|
42
|
+
Integer
|
43
|
+
|
44
|
+
##
|
45
|
+
# :attr: poll_interval
|
46
|
+
#
|
47
|
+
# Wait this long between instance status requests. Default is one second.
|
48
|
+
# Fractional values are OK.
|
49
|
+
#
|
50
|
+
furnish_property :poll_interval,
|
51
|
+
"Wait this long between instance status requests. Default is 1 second, fractional values OK.",
|
52
|
+
Numeric
|
53
|
+
|
54
|
+
##
|
55
|
+
# :attr: key_name
|
56
|
+
#
|
57
|
+
# The SSH key name for EC2. Must already exists. Required. No Default.
|
58
|
+
#
|
59
|
+
furnish_property :key_name,
|
60
|
+
"EC2 SSH Key Name, must already exist. Required. No Default.",
|
61
|
+
String
|
62
|
+
|
63
|
+
##
|
64
|
+
# :attr: availability_zone
|
65
|
+
#
|
66
|
+
# EC2 availability zone. If not provided, EC2 will pick based on the
|
67
|
+
# region. If provided, region will be determined from this value.
|
68
|
+
#
|
69
|
+
furnish_property :availability_zone,
|
70
|
+
"EC2 Availability Zone. If provided and no region supplied, region will be determined from this. If this is not supplied, AZ will be EC2's choosing based on the region.",
|
71
|
+
String
|
72
|
+
|
73
|
+
##
|
74
|
+
# :attr: image_id
|
75
|
+
#
|
76
|
+
# The AMI identifier. Required. No default.
|
77
|
+
#
|
78
|
+
furnish_property :image_id,
|
79
|
+
"AMI Identifier. Required. No Default.",
|
80
|
+
String
|
81
|
+
|
82
|
+
##
|
83
|
+
# :attr: kernel_id
|
84
|
+
#
|
85
|
+
# The AKI identifier. No default, EC2 picks based on the AMI if not
|
86
|
+
# provided.
|
87
|
+
#
|
88
|
+
furnish_property :kernel_id,
|
89
|
+
"AKI Identifier. If not supplied, left to EC2 to sort out.",
|
90
|
+
String
|
91
|
+
|
92
|
+
##
|
93
|
+
# :attr: ramdisk_id
|
94
|
+
#
|
95
|
+
# The ARI identifier. No default, EC2 picks based on the AMI and possibly
|
96
|
+
# the AKI if not provided.
|
97
|
+
#
|
98
|
+
furnish_property :ramdisk_id,
|
99
|
+
"ARI Identifier. If not supplied, left to EC2 to sort out.",
|
100
|
+
String
|
101
|
+
|
102
|
+
##
|
103
|
+
# :attr: count
|
104
|
+
#
|
105
|
+
# The number of instances to provision. Default is 1.
|
106
|
+
#
|
107
|
+
furnish_property :count,
|
108
|
+
"Number of instances to allocate. Required, Default is 1.",
|
109
|
+
Integer
|
110
|
+
|
111
|
+
##
|
112
|
+
# :attr: instance_type
|
113
|
+
#
|
114
|
+
# The size or "flavor" of instance(s). Required, default is 'c1.medium'.
|
115
|
+
#
|
116
|
+
furnish_property :instance_type,
|
117
|
+
"Size or 'flavor' of instance(s). Required, default is 'c1.medium'."
|
118
|
+
String
|
119
|
+
|
120
|
+
##
|
121
|
+
# :attr: security_group_ids
|
122
|
+
#
|
123
|
+
# Array of security group identifiers (not *group names*) that these
|
124
|
+
# instances will be bound to. Appends any incoming group id's from
|
125
|
+
# previous provisioners. At least one must exist at provisioning time, or
|
126
|
+
# the provision will fail.
|
127
|
+
#
|
128
|
+
furnish_property :security_group_ids,
|
129
|
+
"list of group identifiers (not names) that these instances will be bound to. Appends any incoming group id's from previous provisioners. At least one must exist at provisioning time.",
|
130
|
+
Array
|
131
|
+
|
132
|
+
##
|
133
|
+
# :attr: block_device_mappings
|
134
|
+
#
|
135
|
+
# Mapping for block device information. Passed directly to
|
136
|
+
# AWS::EC2::InstanceCollection#create -- see its documentation for more
|
137
|
+
# information.
|
138
|
+
#
|
139
|
+
furnish_property :block_device_mappings,
|
140
|
+
"block device information. Passed directly to AWS::EC2::InstanceCollection#create's argument list. Not required, no default."
|
141
|
+
|
142
|
+
##
|
143
|
+
# :attr: user_data
|
144
|
+
#
|
145
|
+
# Mapping for user data. Passed directly to
|
146
|
+
# AWS::EC2::InstanceCollection#create -- see its documentation for more
|
147
|
+
# information.
|
148
|
+
#
|
149
|
+
furnish_property :user_data,
|
150
|
+
"user data. Passed directly to AWS::EC2::InstanceCollection#create's argument list. Not required, no default."
|
151
|
+
|
152
|
+
##
|
153
|
+
# :attr: monitoring_enabled
|
154
|
+
#
|
155
|
+
# Mapping for configuring monitoring. Passed directly to
|
156
|
+
# AWS::EC2::InstanceCollection#create -- see its documentation for more
|
157
|
+
# information.
|
158
|
+
#
|
159
|
+
furnish_property :monitoring_enabled,
|
160
|
+
"enable montioring. Passed directly to AWS::EC2::InstanceCollection#create's argument list. Not required, no default."
|
161
|
+
|
162
|
+
|
163
|
+
##
|
164
|
+
#
|
165
|
+
# After provisioning, instance identifiers managed by this provisioner
|
166
|
+
# will be set here.
|
167
|
+
#
|
168
|
+
attr_reader :instance_ids
|
169
|
+
|
170
|
+
##
|
171
|
+
# Arguments that are passed through straight to
|
172
|
+
# AWS::EC2::InstanceCollection#create.
|
173
|
+
#
|
174
|
+
PASSTHROUGH_ATTRS = %w[
|
175
|
+
key_name
|
176
|
+
availability_zone
|
177
|
+
image_id
|
178
|
+
kernel_id
|
179
|
+
ramdisk_id
|
180
|
+
count
|
181
|
+
block_device_mappings
|
182
|
+
instance_type
|
183
|
+
user_data
|
184
|
+
security_group_ids
|
185
|
+
monitoring_enabled
|
186
|
+
]
|
187
|
+
|
188
|
+
#
|
189
|
+
#
|
190
|
+
# Create a new EC2 Provisioner. Inherits the initializer from
|
191
|
+
# Furnish::Provisioner::AWS.new, see the documentation there for more
|
192
|
+
# information.
|
193
|
+
#
|
194
|
+
# Additionally, see the attribute listing for argument documentation and
|
195
|
+
# their defaults.
|
196
|
+
#
|
197
|
+
# Required arguments that do not have defaults. ArgumentError will be
|
198
|
+
# raised if they are not supplied at construction time:
|
199
|
+
#
|
200
|
+
# * #access_key (from Furnish::Provisioner::AWS)
|
201
|
+
# * #secret_key (from Furnish::Provisioner::AWS)
|
202
|
+
# * #instance_type
|
203
|
+
# * #image_id
|
204
|
+
# * #region (also see #availability_zone)
|
205
|
+
# * #key_name
|
206
|
+
#
|
207
|
+
# Additionally see #security_group_ids, #poll_interval and
|
208
|
+
# #provision_timeout for some detail on special logic surrounding the
|
209
|
+
# operation of this provisioner.
|
210
|
+
#
|
211
|
+
def initialize(args)
|
212
|
+
super
|
213
|
+
|
214
|
+
check_region
|
215
|
+
check_ec2_args
|
216
|
+
|
217
|
+
@count ||= 1
|
218
|
+
@instance_type ||= "c1.medium"
|
219
|
+
@security_group_ids ||= []
|
220
|
+
@provision_wait ||= 300
|
221
|
+
@poll_interval ||= 1
|
222
|
+
@instance_ids = []
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# Overload of Furnish::Provisioner::AWS#check_region that also deals with
|
227
|
+
# availability zones.
|
228
|
+
def check_region
|
229
|
+
if availability_zone and !region
|
230
|
+
@region = availability_zone.sub(/\D+$/, '')
|
231
|
+
end
|
232
|
+
|
233
|
+
if availability_zone and region and !availability_zone.start_with?(region)
|
234
|
+
raise ArgumentError,
|
235
|
+
"region and availability zone are both supplied and do not match: [r: #{region}, a: #{availability_zone}]"
|
236
|
+
end
|
237
|
+
|
238
|
+
super
|
239
|
+
end
|
240
|
+
|
241
|
+
#
|
242
|
+
# Validate additional required EC2-specific arguments like #key_name and
|
243
|
+
# #image_id.
|
244
|
+
#
|
245
|
+
def check_ec2_args
|
246
|
+
unless key_name
|
247
|
+
raise ArgumentError, "key_name is required for instance provision"
|
248
|
+
end
|
249
|
+
|
250
|
+
unless image_id
|
251
|
+
raise ArgumentError, "an AMI image_id must be provided"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# generates options to pass to AWS::EC2::InstanceCollection#create from
|
257
|
+
# our attributes.
|
258
|
+
#
|
259
|
+
def launch_options
|
260
|
+
options = { }
|
261
|
+
|
262
|
+
PASSTHROUGH_ATTRS.each { |x| options[x.to_sym] = send(x) if send(x) }
|
263
|
+
|
264
|
+
return options
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# puts the return value from ec2.instances.create into a normal array no
|
269
|
+
# matter what it returns. See the method comments for more information.
|
270
|
+
#
|
271
|
+
def coerce_instances(instances)
|
272
|
+
# if count == 1, returns a single instance id.
|
273
|
+
instances = [instances] unless instances.kind_of?(Array)
|
274
|
+
|
275
|
+
# XXX there are versions of aws-sdk that respond to #kind_of?(Array)
|
276
|
+
# but aren't actually arrays. This will always give us an array.
|
277
|
+
|
278
|
+
tmp_instances = []
|
279
|
+
instances.each { |i| tmp_instances.push(i) }
|
280
|
+
return tmp_instances
|
281
|
+
end
|
282
|
+
|
283
|
+
#
|
284
|
+
# Polls the EC2 API waiting for instances to enter the state passed.
|
285
|
+
# Uses #poll_interval to determine how long to wait between status
|
286
|
+
# requests, and #provision_timeout to determine how long to wait before
|
287
|
+
# giving up.
|
288
|
+
#
|
289
|
+
def wait_for_instances(instances, state)
|
290
|
+
Timeout.timeout(provision_timeout) do
|
291
|
+
not_running = instances.dup
|
292
|
+
|
293
|
+
until not_running.empty?
|
294
|
+
instance = not_running.shift
|
295
|
+
|
296
|
+
status = instance.status rescue nil
|
297
|
+
|
298
|
+
if status
|
299
|
+
unless status == state
|
300
|
+
if_debug(3) do
|
301
|
+
puts "instance #{instance.id} is not in #{state} state yet."
|
302
|
+
end
|
303
|
+
|
304
|
+
not_running.push(instance)
|
305
|
+
end
|
306
|
+
else
|
307
|
+
if_debug(3) do
|
308
|
+
puts "API server doesn't think #{instance.id} exists yet."
|
309
|
+
end
|
310
|
+
|
311
|
+
not_running.push(instance)
|
312
|
+
end
|
313
|
+
|
314
|
+
sleep poll_interval
|
315
|
+
end
|
316
|
+
end
|
317
|
+
rescue TimeoutError
|
318
|
+
raise "instances timed out waiting for ec2 API"
|
319
|
+
end
|
320
|
+
|
321
|
+
#
|
322
|
+
# Provision instance(s).
|
323
|
+
#
|
324
|
+
# If a security group id is supplied to this method, it will be
|
325
|
+
# permanently appended to #security_group_ids and used during instance
|
326
|
+
# creation.
|
327
|
+
#
|
328
|
+
# Regardless of the above behavior, if no security group ids exist, will
|
329
|
+
# raise RuntimeError (EC2 requires instances to be in at least one
|
330
|
+
# security group).
|
331
|
+
#
|
332
|
+
# Records the instance id's, waits for them to all enter the `:running`
|
333
|
+
# state, and then returns a list of their public IP addresses to the next
|
334
|
+
# provisioner.
|
335
|
+
#
|
336
|
+
def startup(args={})
|
337
|
+
if args[:security_group_ids]
|
338
|
+
@security_group_ids += args[:security_group_ids]
|
339
|
+
end
|
340
|
+
|
341
|
+
if security_group_ids.empty?
|
342
|
+
raise "no security groups supplied either at construction or provisioning time, cannot request instances."
|
343
|
+
end
|
344
|
+
|
345
|
+
instances = coerce_instances(ec2.instances.create(launch_options))
|
346
|
+
@instance_ids = instances.map(&:id)
|
347
|
+
wait_for_instances(instances, :running)
|
348
|
+
|
349
|
+
return({ :security_group_ids => @security_group_ids, :ips => Set[*instances.map(&:ip_address)], :ec2_instance_ids => @instance_ids })
|
350
|
+
end
|
351
|
+
|
352
|
+
#
|
353
|
+
# All instances are told to terminate, the method then waits for all of
|
354
|
+
# them to enter the terminated state, then returns true.
|
355
|
+
#
|
356
|
+
def shutdown(args={})
|
357
|
+
instances = instance_ids.map { |i| ec2.instances[i] }
|
358
|
+
instances.each { |i| i.terminate rescue nil }
|
359
|
+
wait_for_instances(instances, :terminated)
|
360
|
+
|
361
|
+
return { }
|
362
|
+
end
|
363
|
+
|
364
|
+
#
|
365
|
+
# Furnish reporter -- includes image id, number of servers, and instance id's.
|
366
|
+
#
|
367
|
+
def report
|
368
|
+
["ami #{image_id}; #{count} servers; instance_ids: #{instance_ids.inspect}"]
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|