ec2launcher 1.0.9 → 1.0.10
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/CHANGELOG.md +6 -0
- data/bin/ec2launcher +1 -1
- data/lib/ec2launcher/application.rb +228 -195
- data/lib/ec2launcher/backoff_runner.rb +33 -0
- data/lib/ec2launcher/block_device.rb +72 -69
- data/lib/ec2launcher/email_notification.rb +49 -46
- data/lib/ec2launcher/environment.rb +10 -18
- data/lib/ec2launcher/init_options.rb +121 -119
- data/lib/ec2launcher/security_group_handler.rb +60 -0
- data/lib/ec2launcher/version.rb +1 -1
- data/lib/ec2launcher.rb +6 -25
- metadata +4 -2
data/CHANGELOG.md
CHANGED
data/bin/ec2launcher
CHANGED
@@ -3,260 +3,293 @@
|
|
3
3
|
#
|
4
4
|
require 'ec2launcher/block_device'
|
5
5
|
require 'ec2launcher/email_notification'
|
6
|
+
require 'ec2launcher/security_group_handler'
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
module EC2Launcher
|
9
|
+
# Wrapper class to handle loading Application blocks.
|
10
|
+
class ApplicationDSL
|
11
|
+
attr_accessor :applications
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
+
def initialize
|
14
|
+
self.applications = []
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def application(name, &block)
|
18
|
+
application = Application.new(name)
|
19
|
+
applications << application
|
20
|
+
application.instance_eval &block
|
21
|
+
application
|
22
|
+
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
def self.execute(dsl)
|
25
|
+
new.tap do |context|
|
26
|
+
context.instance_eval(dsl)
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
26
|
-
end
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
# Represents a single application stack.
|
32
|
+
class Application
|
33
|
+
include EC2Launcher::EmailNotifications
|
34
|
+
include EC2Launcher::SecurityGroupHandler
|
30
35
|
|
31
|
-
|
36
|
+
attr_reader :name
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
def initialize(name)
|
39
|
+
@name = name
|
40
|
+
@email_notifications = nil
|
41
|
+
end
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
def application(name)
|
44
|
+
@name = name
|
45
|
+
yield self
|
46
|
+
self
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
# Name of the AMI to use for new instances. Optional.
|
50
|
+
# Can be either a string or a regular expression.
|
51
|
+
#
|
52
|
+
# @param [Array, nil] Either an array of parameters or nil to return the AMI name.
|
53
|
+
def ami_name(*ami_name)
|
54
|
+
if ami_name.empty?
|
55
|
+
@ami_name
|
50
56
|
else
|
51
|
-
|
57
|
+
if ami_name[0].kind_of? String
|
58
|
+
@ami_name = /#{ami_name[0]}/
|
59
|
+
else
|
60
|
+
@ami_name = ami_name[0]
|
61
|
+
end
|
62
|
+
self
|
52
63
|
end
|
53
|
-
self
|
54
64
|
end
|
55
|
-
end
|
56
65
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
66
|
+
# Name of the availability zone to use for new instances. Optional.
|
67
|
+
# Must be one of EC2Launcher::AVAILABILITY_ZONES.
|
68
|
+
def availability_zone(*zone)
|
69
|
+
if zone.empty?
|
70
|
+
@availability_zone
|
71
|
+
else
|
72
|
+
@availability_zone = zone[0].to_s
|
73
|
+
self
|
74
|
+
end
|
63
75
|
end
|
64
|
-
end
|
65
76
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
77
|
+
# Defines a shorter name when building a host name for new instances. Optional.
|
78
|
+
# By default, new instances are named using the full application name. If you
|
79
|
+
# specify basename, it will be used instead. Typically, this is used if you
|
80
|
+
# want a shorter version of the full application name.
|
81
|
+
def basename(*name)
|
82
|
+
if name.empty?
|
83
|
+
@basename
|
84
|
+
else
|
85
|
+
@basename = name[0]
|
86
|
+
self
|
87
|
+
end
|
72
88
|
end
|
73
|
-
end
|
74
89
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
90
|
+
def block_devices(*block_device_data)
|
91
|
+
if block_device_data.empty?
|
92
|
+
@block_devices
|
93
|
+
else
|
94
|
+
self
|
95
|
+
end
|
80
96
|
end
|
81
|
-
end
|
82
97
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
98
|
+
def block_device(&block)
|
99
|
+
@block_devices = [] if @block_devices.nil?
|
100
|
+
device = EC2Launcher::BlockDevice.new
|
101
|
+
device.instance_exec(&block)
|
102
|
+
@block_devices << device
|
103
|
+
end
|
89
104
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
105
|
+
# Indicates the Amazon Elastic Load Balancer to which new instances should be
|
106
|
+
# attached after launch. Optional.
|
107
|
+
#
|
108
|
+
# The value can be either a String, indicating the name of the ELB, or a Hash
|
109
|
+
# that maps environment names to ELB names.
|
110
|
+
def elb(*elb)
|
111
|
+
if elb.empty?
|
112
|
+
@elb
|
97
113
|
else
|
98
|
-
@elb
|
114
|
+
@elb = Hash.new if @elb.nil?
|
115
|
+
if elb[0].kind_of? Hash
|
116
|
+
elb[0].keys.each {|key| @elb[key] = elb[0][key]}
|
117
|
+
else
|
118
|
+
@elb["default"] = elb[0].to_s
|
119
|
+
end
|
120
|
+
self
|
99
121
|
end
|
100
|
-
self
|
101
122
|
end
|
102
|
-
end
|
103
123
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
124
|
+
# Retrieves the ELB name for a given environment.
|
125
|
+
def elb_for_environment(environment)
|
126
|
+
elb_name = @elb[environment]
|
127
|
+
elb_name ||= @elb["default"]
|
128
|
+
elb_name
|
129
|
+
end
|
109
130
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
131
|
+
# Defines an Array of Chef roles that should be applied to new
|
132
|
+
# instances for a specific environment. Can be specified multiple times.
|
133
|
+
#
|
134
|
+
# Expects two parameters:
|
135
|
+
# * Name of an environment
|
136
|
+
# * Either the name of a single Chef role or an Array of Chef roles
|
137
|
+
def environment_roles(*data)
|
138
|
+
if data.empty?
|
139
|
+
@environment_roles
|
140
|
+
else
|
141
|
+
@environment_roles = Hash.new if @environment_roles.nil?
|
142
|
+
env_name = data[0]
|
143
|
+
env_roles = data[1]
|
117
144
|
|
118
|
-
|
119
|
-
|
145
|
+
environment_data = @environment_roles[env_name]
|
146
|
+
environment_data ||= []
|
120
147
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
148
|
+
if env_roles.kind_of? Array
|
149
|
+
environment_data += env_roles
|
150
|
+
else
|
151
|
+
environment_data << env_roles
|
152
|
+
end
|
153
|
+
@environment_roles[env_name] = environment_data
|
127
154
|
|
128
|
-
|
155
|
+
self
|
156
|
+
end
|
129
157
|
end
|
130
|
-
end
|
131
158
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
159
|
+
# Gems to install. Optional.
|
160
|
+
#
|
161
|
+
# Expects either a single String naming a gem to install or
|
162
|
+
# an Array of gems.
|
163
|
+
#
|
164
|
+
# Can be specified multiple times.
|
165
|
+
def gems(*gems)
|
166
|
+
if gems.empty?
|
167
|
+
@gems
|
168
|
+
else
|
169
|
+
@gems = [] if @gems.nil?
|
170
|
+
if gems[0].kind_of? Array
|
171
|
+
@gems += gems[0]
|
172
|
+
else
|
173
|
+
@gems << gems[0]
|
174
|
+
end
|
175
|
+
self
|
176
|
+
end
|
138
177
|
end
|
139
|
-
end
|
140
178
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
179
|
+
# Indicates that this application should inherit all of its settings from another named application.
|
180
|
+
# Optional.
|
181
|
+
def inherit(*inherit_type)
|
182
|
+
if inherit_type.empty?
|
183
|
+
@inherit_type
|
184
|
+
else
|
185
|
+
@inherit_type = inherit_type[0]
|
186
|
+
end
|
146
187
|
end
|
147
|
-
end
|
148
188
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
189
|
+
# The Amazon EC2 instance type that should be used for new instances.
|
190
|
+
# Must be one of EC2Launcher::INSTANCE_TYPES.
|
191
|
+
def instance_type(*type_name)
|
192
|
+
if type_name.empty?
|
193
|
+
@instance_type
|
194
|
+
else
|
195
|
+
@instance_type = type_name[0]
|
196
|
+
self
|
197
|
+
end
|
155
198
|
end
|
156
|
-
end
|
157
199
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
200
|
+
# Takes values from the other server type and merges them into this one
|
201
|
+
def merge(other_server)
|
202
|
+
@name = other_server.name
|
203
|
+
@ami_name = other_server.ami_name unless other_server.ami_name.nil?
|
204
|
+
@availability_zone = other_server.availability_zone unless other_server.availability_zone.nil?
|
205
|
+
@basename = other_server.basename unless other_server.basename.nil?
|
206
|
+
other_server.block_devices.each {|bd| @block_devices << bd } unless other_server.block_devices.nil?
|
207
|
+
other_server.elb.keys.each {|env_name| @elb[env_name] = other_server.elb[env_name] } unless other_server.elb.nil?
|
208
|
+
@instance_type = other_server.instance_type unless other_server.instance_type.nil?
|
209
|
+
@name_suffix = other_server.name_suffix unless other_server.name_suffix.nil?
|
210
|
+
other_server.roles.each {|role| @roles << role } unless other_server.roles.nil?
|
211
|
+
unless other_server.security_groups.nil?
|
212
|
+
other_server.security_groups.keys.each do |env_name|
|
213
|
+
unless @security_groups.has_key? env_name
|
214
|
+
@security_groups[env_name] = []
|
215
|
+
end
|
216
|
+
other_server.security_groups[env_name].each {|sg| @security_groups[env_name] << sg }
|
173
217
|
end
|
174
|
-
other_server.security_groups[env_name].each {|sg| @security_groups[env_name] << sg }
|
175
218
|
end
|
176
219
|
end
|
177
|
-
end
|
178
220
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
221
|
+
def name_suffix(*suffix)
|
222
|
+
if suffix.empty?
|
223
|
+
@name_suffix
|
224
|
+
else
|
225
|
+
@name_suffix = suffix[0]
|
226
|
+
end
|
184
227
|
end
|
185
|
-
end
|
186
228
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
229
|
+
def packages(*packages)
|
230
|
+
if packages.empty?
|
231
|
+
@packages
|
232
|
+
else
|
233
|
+
@packages = packages[0]
|
234
|
+
self
|
235
|
+
end
|
193
236
|
end
|
194
|
-
end
|
195
237
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
else
|
200
|
-
@roles = [] if @roles.nil?
|
201
|
-
if roles[0].kind_of? Array
|
202
|
-
@roles += roles[0]
|
238
|
+
def roles(*roles)
|
239
|
+
if roles.empty?
|
240
|
+
@roles
|
203
241
|
else
|
204
|
-
@roles = []
|
205
|
-
|
242
|
+
@roles = [] if @roles.nil?
|
243
|
+
if roles[0].kind_of? Array
|
244
|
+
@roles += roles[0]
|
245
|
+
else
|
246
|
+
@roles = []
|
247
|
+
@roles << roles[0]
|
248
|
+
end
|
249
|
+
self
|
206
250
|
end
|
207
|
-
self
|
208
251
|
end
|
209
|
-
end
|
210
252
|
|
211
|
-
|
212
|
-
|
213
|
-
|
253
|
+
def roles_for_environment(environment)
|
254
|
+
roles = []
|
255
|
+
roles += @roles unless @roles.nil?
|
214
256
|
|
215
|
-
|
216
|
-
|
257
|
+
unless @environment_roles.nil? || @environment_roles[environment].nil?
|
258
|
+
roles += @environment_roles[environment]
|
259
|
+
end
|
260
|
+
roles
|
261
|
+
end
|
262
|
+
|
263
|
+
# Retrieves the list of Security Group names for the specified environment.
|
264
|
+
#
|
265
|
+
# @return [Array] Returns the list of security groups for the environment. Returns
|
266
|
+
# the security groups for the "defaukt" environment if the requested
|
267
|
+
# environment is undefined. Returns an empty Array if both the
|
268
|
+
# requested environment and "default" environment are undefined.
|
269
|
+
def security_groups_for_environment(environment)
|
270
|
+
groups = @security_groups[environment]
|
271
|
+
groups ||= @security_groups["default"]
|
272
|
+
groups ||= []
|
273
|
+
groups
|
217
274
|
end
|
218
|
-
roles
|
219
|
-
end
|
220
275
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
else
|
225
|
-
@security_groups = Hash.new if @security_groups.nil?
|
226
|
-
if groups[0].kind_of? Array
|
227
|
-
@security_groups["default"] = groups[0]
|
228
|
-
elsif groups[0].kind_of? Hash
|
229
|
-
groups[0].keys.each {|key| @security_groups[key] = [ groups[0][key] ] }
|
276
|
+
def subnet(*subnet)
|
277
|
+
if subnet.empty?
|
278
|
+
@subnet
|
230
279
|
else
|
231
|
-
@
|
280
|
+
@subnet = subnet[0]
|
281
|
+
self
|
232
282
|
end
|
233
|
-
self
|
234
283
|
end
|
235
|
-
end
|
236
|
-
|
237
|
-
def security_groups_for_environment(environment)
|
238
|
-
groups = @security_groups[environment]
|
239
|
-
groups ||= @security_groups["default"]
|
240
|
-
groups ||= []
|
241
|
-
groups
|
242
|
-
end
|
243
284
|
|
244
|
-
|
245
|
-
|
246
|
-
@subnet
|
247
|
-
else
|
248
|
-
@subnet = subnet[0]
|
285
|
+
def load(dsl)
|
286
|
+
self.instance_eval(dsl)
|
249
287
|
self
|
250
288
|
end
|
251
|
-
end
|
252
|
-
|
253
|
-
def load(dsl)
|
254
|
-
self.instance_eval(dsl)
|
255
|
-
self
|
256
|
-
end
|
257
289
|
|
258
|
-
|
259
|
-
|
260
|
-
|
290
|
+
def self.load(dsl)
|
291
|
+
env = Application.new.instance_eval(dsl)
|
292
|
+
env
|
293
|
+
end
|
261
294
|
end
|
262
295
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 Sean Laurent
|
3
|
+
#
|
4
|
+
require 'aws-sdk'
|
5
|
+
|
6
|
+
module EC2Launcher
|
7
|
+
# Helper module to run AWS requests.
|
8
|
+
module BackoffRunner
|
9
|
+
# Runs an AWS request inside a Ruby block with an exponential backoff in case
|
10
|
+
# we exceed the allowed AWS RequestLimit.
|
11
|
+
#
|
12
|
+
# @param [Integer] max_time maximum amount of time to sleep before giving up.
|
13
|
+
# @param [Integer] sleep_time the initial amount of time to sleep before retrying.
|
14
|
+
# @param [message] message message to display if we get an exception.
|
15
|
+
# @param [Block] block Ruby code block to execute.
|
16
|
+
def run_with_backoff(max_time, sleep_time, message, &block)
|
17
|
+
if sleep_time > max_time
|
18
|
+
puts "AWS::EC2::Errors::RequestLimitExceeded ... failed #{message}"
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
yield
|
24
|
+
rescue AWS::EC2::Errors::RequestLimitExceeded
|
25
|
+
puts "AWS::EC2::Errors::RequestLimitExceeded ... retrying #{message} in #{sleep_time} seconds"
|
26
|
+
sleep sleep_time
|
27
|
+
run_with_backoff(max_time, sleep_time * 2, message, &block)
|
28
|
+
end
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -1,91 +1,94 @@
|
|
1
1
|
#
|
2
2
|
# Copyright (c) 2012 Sean Laurent
|
3
3
|
#
|
4
|
-
class BlockDevice
|
5
|
-
attr_reader :mount_point
|
6
|
-
attr_reader :name
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
5
|
+
module EC2Launcher
|
6
|
+
class BlockDevice
|
7
|
+
attr_reader :mount_point
|
8
|
+
attr_reader :name
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
def initialize()
|
11
|
+
@count = 1
|
12
|
+
@group = "root"
|
13
|
+
@user = "root"
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
@count
|
21
|
-
else
|
22
|
-
@count = block_count[0]
|
23
|
-
self
|
16
|
+
def is_raid?()
|
17
|
+
@raid_level.nil?
|
24
18
|
end
|
25
|
-
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
def count(*block_count)
|
21
|
+
if block_count.empty?
|
22
|
+
@count
|
23
|
+
else
|
24
|
+
@count = block_count[0]
|
25
|
+
self
|
26
|
+
end
|
33
27
|
end
|
34
|
-
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
29
|
+
def group(*group)
|
30
|
+
if group.empty?
|
31
|
+
@group
|
32
|
+
else
|
33
|
+
@group = group[0]
|
34
|
+
self
|
35
|
+
end
|
42
36
|
end
|
43
|
-
end
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
38
|
+
def mount(*mount)
|
39
|
+
if mount.empty?
|
40
|
+
@mount
|
41
|
+
else
|
42
|
+
@mount_point = mount[0]
|
43
|
+
self
|
44
|
+
end
|
51
45
|
end
|
52
|
-
end
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
47
|
+
def name(*name)
|
48
|
+
if name.empty?
|
49
|
+
@name
|
50
|
+
else
|
51
|
+
@name = name[0]
|
52
|
+
self
|
53
|
+
end
|
60
54
|
end
|
61
|
-
end
|
62
55
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
56
|
+
def owner(*owner)
|
57
|
+
if owner.empty?
|
58
|
+
@owner
|
59
|
+
else
|
60
|
+
@owner = owner[0]
|
61
|
+
self
|
62
|
+
end
|
69
63
|
end
|
70
|
-
end
|
71
64
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
65
|
+
def raid_level(*raid_level)
|
66
|
+
if raid_level.empty?
|
67
|
+
@raid_level
|
68
|
+
else
|
69
|
+
@raid_level = raid_level[0]
|
70
|
+
self
|
71
|
+
end
|
78
72
|
end
|
79
|
-
end
|
80
73
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
74
|
+
def size(*volume_size)
|
75
|
+
if volume_size.empty?
|
76
|
+
@size
|
77
|
+
else
|
78
|
+
@size = volume_size[0].to_i
|
79
|
+
self
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_json(*a)
|
84
|
+
{
|
85
|
+
"name" => @name,
|
86
|
+
"count" => @count,
|
87
|
+
"raid_level" => @raid_level,
|
88
|
+
"mount_point" => @mount_point,
|
89
|
+
"owner" => @owner,
|
90
|
+
"group" => @group
|
91
|
+
}.to_json(*a)
|
92
|
+
end
|
90
93
|
end
|
91
94
|
end
|
@@ -1,62 +1,65 @@
|
|
1
1
|
#
|
2
2
|
# Copyright (c) 2012 Sean Laurent
|
3
3
|
#
|
4
|
-
module EmailNotifications
|
5
|
-
attr_reader :email_notifications
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@email_notifications = notifications
|
11
|
-
end
|
12
|
-
end
|
5
|
+
module EC2Launcher
|
6
|
+
module EmailNotifications
|
7
|
+
attr_reader :email_notifications
|
13
8
|
|
14
|
-
|
15
|
-
|
9
|
+
def email_notification(&block)
|
10
|
+
notifications = EmailNotification.new
|
11
|
+
notifications.instance_exec(&block)
|
12
|
+
@email_notifications = notifications
|
13
|
+
end
|
16
14
|
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
@from
|
21
|
-
else
|
22
|
-
@from = from[0]
|
23
|
-
self
|
16
|
+
class EmailNotification
|
17
|
+
def initialize()
|
24
18
|
end
|
25
|
-
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
def from(*from)
|
21
|
+
if from.empty?
|
22
|
+
@from
|
23
|
+
else
|
24
|
+
@from = from[0]
|
25
|
+
self
|
26
|
+
end
|
33
27
|
end
|
34
|
-
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
29
|
+
def to(*to)
|
30
|
+
if to.empty?
|
31
|
+
@to
|
32
|
+
else
|
33
|
+
@to = to[0]
|
34
|
+
self
|
35
|
+
end
|
42
36
|
end
|
43
|
-
end
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
38
|
+
def ses_access_key(*ses_access_key)
|
39
|
+
if ses_access_key.empty?
|
40
|
+
@ses_access_key
|
41
|
+
else
|
42
|
+
@ses_access_key = ses_access_key[0]
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def ses_secret_key(*ses_secret_key)
|
48
|
+
if ses_secret_key.empty?
|
49
|
+
@ses_secret_key
|
50
|
+
else
|
51
|
+
@ses_secret_key = ses_secret_key[0]
|
52
|
+
self
|
53
|
+
end
|
51
54
|
end
|
52
|
-
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
def to_json(*a)
|
57
|
+
{
|
58
|
+
"from" => @from,
|
59
|
+
"to" => @to,
|
60
|
+
"ses_access_key" => @ses_access_key,
|
61
|
+
"ses_secret_key" => @ses_secret_key
|
62
|
+
}.to_json(*a)
|
63
|
+
end
|
61
64
|
end
|
62
|
-
end
|
65
|
+
end
|
@@ -2,10 +2,12 @@
|
|
2
2
|
# Copyright (c) 2012 Sean Laurent
|
3
3
|
#
|
4
4
|
require 'ec2launcher/email_notification'
|
5
|
+
require 'ec2launcher/security_group_handler'
|
5
6
|
|
6
7
|
module EC2Launcher
|
7
8
|
class Environment
|
8
|
-
include EmailNotifications
|
9
|
+
include EC2Launcher::EmailNotifications
|
10
|
+
include EC2Launcher::SecurityGroupHandler
|
9
11
|
|
10
12
|
attr_reader :name
|
11
13
|
attr_reader :precommands
|
@@ -19,8 +21,7 @@ module EC2Launcher
|
|
19
21
|
@precommands = []
|
20
22
|
@postcommands = []
|
21
23
|
@roles = []
|
22
|
-
@security_groups =
|
23
|
-
|
24
|
+
@security_groups = {}
|
24
25
|
end
|
25
26
|
|
26
27
|
def environment(name)
|
@@ -157,20 +158,6 @@ module EC2Launcher
|
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
160
|
-
def security_groups(*security_groups)
|
161
|
-
if security_groups.empty?
|
162
|
-
@security_groups
|
163
|
-
else
|
164
|
-
@security_groups = [] if @security_groups.nil?
|
165
|
-
if security_groups[0].kind_of? Array
|
166
|
-
@security_groups += security_groups[0]
|
167
|
-
else
|
168
|
-
@security_groups << security_groups[0]
|
169
|
-
end
|
170
|
-
self
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
161
|
def short_name(*short_name)
|
175
162
|
if short_name.empty?
|
176
163
|
@short_name
|
@@ -198,7 +185,12 @@ module EC2Launcher
|
|
198
185
|
@roles += other_env.roles unless other_env.roles.nil?
|
199
186
|
@precommands += other_env.precommands unless other_env.precommands.nil?
|
200
187
|
@postcommands += other_env.postcommands unless other_env.postcommands.nil?
|
201
|
-
|
188
|
+
unless other_env.security_groups.nil?
|
189
|
+
other_env.security_groups.keys.each do |key|
|
190
|
+
@security_groups[key] = [] if @security_groups[key].nil?
|
191
|
+
@security_groups[key] += other_env.security_groups[key]
|
192
|
+
end
|
193
|
+
end
|
202
194
|
|
203
195
|
@aliases = other_env.aliases.nil? ? nil : other_env.aliases
|
204
196
|
|
@@ -7,160 +7,162 @@ require 'ostruct'
|
|
7
7
|
|
8
8
|
require 'ec2launcher/defaults'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
module EC2Launcher
|
11
|
+
class InitOptions
|
12
|
+
attr_reader :command
|
13
|
+
attr_reader :options
|
14
|
+
attr_reader :location
|
14
15
|
|
15
|
-
|
16
|
+
SUB_COMMANDS = %w{init launch}
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def initialize
|
19
|
+
@opts = OptionParser.new do |opts|
|
20
|
+
opts.banner = "Usage: ec2launcher [COMMAND]
|
20
21
|
|
21
|
-
where [COMMAND] is one of:
|
22
|
+
where [COMMAND] is one of:
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
init [LOCATION] Initialize a repository in the specified directory.
|
25
|
+
launch [OPTIONS] Launch a new instance.
|
25
26
|
|
26
|
-
and [OPTIONS] include:
|
27
|
+
and [OPTIONS] include:
|
27
28
|
|
28
|
-
"
|
29
|
-
|
29
|
+
"
|
30
|
+
opts.separator "Query options:"
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
opts.on("-l", "--list", "Show environments and applications.") do
|
33
|
+
@options.list = true
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
opts.on("-s", "--show-defaults", "Show settings, but do not launch any instances.") do
|
37
|
+
@options.show_defaults = true
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
opts.separator ""
|
41
|
+
opts.separator "Required Launch options:"
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
opts.on("-e", "--environment ENV", "The environment for the server.") do |env|
|
44
|
+
@options.environ = env
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
opts.on("-a", "--application NAME", "The name of the application class for the new server.") do |app_name|
|
48
|
+
@options.application = app_name
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
opts.separator ""
|
52
|
+
opts.separator "Additional launch options:"
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
opts.on("--command [CMD]", "Additional command to run during launch sequence.") do |command|
|
55
|
+
@options.commands << command
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
opts.on("-c", "--clone HOST", "Clone the latest snapshots from a specific host.") do |clone_host|
|
59
|
+
@options.clone_host = clone_host
|
60
|
+
end
|
60
61
|
|
61
|
-
|
62
|
-
|
62
|
+
opts.separator ""
|
63
|
+
opts.separator "Overrides:"
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
opts.on("-d", "--directory DIRECTORY", "Location of configuration directory. Defaults to current directory.") do |directory|
|
66
|
+
@options.directory = directory
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
opts.on("-h", "--hostname NAME", "The name for the new server.") do |hostname|
|
70
|
+
@options.hostname = hostname
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
73
|
+
opts.on("-u", "--chef-validation-url", "URL for the Chef validation pem file.") do |chef_validation_url|
|
74
|
+
@options.chef_validation_url = chef_validation_url
|
75
|
+
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
opts.on("--ami AMI_ID", "AMI id") do |ami_id|
|
78
|
+
@options.ami_id = ami_id
|
79
|
+
end
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
opts.on("-z", "--availability-zone ZONE", EC2Launcher::AVAILABILITY_ZONES, "AWS availability zone (#{EC2Launcher::AVAILABILITY_ZONES.join(', ')}).") do |zone|
|
82
|
+
@options.zone = zone
|
83
|
+
end
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
85
|
+
opts.on("-i", "--instance-type TYPE", EC2Launcher::INSTANCE_TYPES, "EC2 instance type (#{EC2Launcher::INSTANCE_TYPES.join(', ')}).") do |instance_type|
|
86
|
+
@options.instance_type = instance_type
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
opts.on("-v", "--volume-size SIZE", Integer, "EBS volume size in GB. Defaults to #{EC2Launcher::DEFAULT_VOLUME_SIZE} GB") do |volume_size|
|
90
|
+
@options.volume_size = volume_size
|
91
|
+
end
|
91
92
|
|
92
|
-
|
93
|
-
|
93
|
+
opts.separator ""
|
94
|
+
opts.separator "AWS Security Options:"
|
94
95
|
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
opts.on("--access-key KEY", "Amazon access key. Overrides AWS_ACCESS_KEY environment variable.") do |access_key|
|
97
|
+
@options.access_key = access_key
|
98
|
+
end
|
98
99
|
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
opts.on("--secret SECRET", "Amazon secret access key. Overrides AWS_SECRET_ACCESS_KEY environment variable.") do |secret|
|
101
|
+
@options.secret = secret
|
102
|
+
end
|
102
103
|
|
103
|
-
|
104
|
-
|
104
|
+
opts.separator ""
|
105
|
+
opts.separator "Common options:"
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
107
|
+
# No argument, shows at tail. This will print an options summary.
|
108
|
+
# Try it and see!
|
109
|
+
opts.on_tail("-?", "--help", "Show this message") do
|
110
|
+
puts opts
|
111
|
+
exit
|
112
|
+
end
|
113
|
+
end
|
112
114
|
end
|
113
|
-
end
|
114
115
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
116
|
+
def parse(args)
|
117
|
+
@command = args.shift
|
118
|
+
unless SUB_COMMANDS.include?(@command)
|
119
|
+
puts "Missing command! " if @command.nil?
|
120
|
+
puts "Invalid command: #{@command}" unless @command.nil? || @command == "-?" || @command == "--help"
|
121
|
+
puts @opts
|
122
|
+
exit 1
|
123
|
+
end
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
@options = OpenStruct.new
|
126
|
+
@options.list = false
|
127
|
+
@options.show_defaults = false
|
127
128
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
@options.environ = nil
|
130
|
+
@options.application = nil
|
131
|
+
@options.commands = []
|
132
|
+
@options.clone_host = nil
|
132
133
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
134
|
+
@options.ami_id = nil
|
135
|
+
@options.hostname = nil
|
136
|
+
@options.zone = nil
|
137
|
+
@options.instance_type = nil
|
138
|
+
@options.volume_size = nil
|
138
139
|
|
139
|
-
|
140
|
+
@options.directory = "./"
|
140
141
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
142
|
+
if @command == "init"
|
143
|
+
unless args.length >= 1
|
144
|
+
puts "Missing location!"
|
145
|
+
puts
|
146
|
+
help
|
147
|
+
exit 1
|
148
|
+
end
|
149
|
+
@location = args[0]
|
150
|
+
else
|
151
|
+
@opts.parse!(args)
|
152
|
+
|
153
|
+
if (@options.environ.nil? || @options.application.nil?) && ! @options.list
|
154
|
+
puts "Missing a required parameter: #{@options.environ.nil? ? '--environment' : '--application'}"
|
155
|
+
puts
|
156
|
+
help
|
157
|
+
exit 1
|
158
|
+
end
|
157
159
|
end
|
158
|
-
end
|
159
160
|
|
160
|
-
|
161
|
-
|
161
|
+
@options
|
162
|
+
end
|
162
163
|
|
163
|
-
|
164
|
-
|
164
|
+
def help
|
165
|
+
puts @opts
|
166
|
+
end
|
165
167
|
end
|
166
|
-
end
|
168
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 Sean Laurent
|
3
|
+
#
|
4
|
+
module EC2Launcher
|
5
|
+
# Helper module for all objects that support EC2 Security Groups.
|
6
|
+
module SecurityGroupHandler
|
7
|
+
|
8
|
+
# Add or retrieve security groups. Defines the @security_groups instance variable,
|
9
|
+
# which contains a Hash of environment names to Arrays of security group names.
|
10
|
+
# May define a "default" environment.
|
11
|
+
#
|
12
|
+
# Can be defined several different ways:
|
13
|
+
# * String - Adds the named security group to the "default" environment.
|
14
|
+
# * Array - Adds the entire array of security groups to the "default" environment.
|
15
|
+
# * Hash - Keys are environment names (Strings) to security groups. Values of the
|
16
|
+
# hash can be either a String or an Array. Both are appended to any
|
17
|
+
# security groups already defined for the environment.
|
18
|
+
#
|
19
|
+
# @param [Array, nil] groups Array of security_group definitions. See above. Returns
|
20
|
+
# the entire Hash of security groups if empty.
|
21
|
+
#
|
22
|
+
# @return [Hash, self] Either returns the Hash of security groups (if groups parameter is empty)
|
23
|
+
# or returns a reference to self.
|
24
|
+
def security_groups(*groups)
|
25
|
+
if groups.empty?
|
26
|
+
@security_groups
|
27
|
+
else
|
28
|
+
@security_groups = Hash.new if @security_groups.nil?
|
29
|
+
if groups[0].kind_of? Array
|
30
|
+
@security_groups[:default] = [] if @security_groups[:default].nil?
|
31
|
+
@security_groups[:default] += groups[0]
|
32
|
+
elsif groups[0].kind_of? Hash
|
33
|
+
groups[0].keys.each do |env_name|
|
34
|
+
@security_groups[env_name] = [] if @security_groups[env_name].nil?
|
35
|
+
if groups[0][env_name].kind_of? Array
|
36
|
+
@security_groups[env_name] += groups[0][env_name]
|
37
|
+
else
|
38
|
+
@security_groups[env_name] << groups[0][env_name]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
@security_groups[:default] = [] if @security_groups[:default].nil?
|
43
|
+
@security_groups[:default] << groups[0].to_s
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieves the list of security groups for a given environment. Returns the
|
50
|
+
# default set of security groups if the environment isn't defined. Returns
|
51
|
+
# an empty array if the default security group is empty.
|
52
|
+
def security_groups_for_environment(environment)
|
53
|
+
if @security_groups[environment].nil?
|
54
|
+
# Environment not found - check default
|
55
|
+
return @security_groups[:default].nil? ? [] : @security_groups[:default]
|
56
|
+
end
|
57
|
+
@security_groups[environment]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/ec2launcher/version.rb
CHANGED
data/lib/ec2launcher.rb
CHANGED
@@ -9,12 +9,14 @@ require 'aws-sdk'
|
|
9
9
|
require "ec2launcher/version"
|
10
10
|
require "ec2launcher/config"
|
11
11
|
require "ec2launcher/defaults"
|
12
|
+
require "ec2launcher/backoff_runner"
|
12
13
|
|
13
14
|
require 'ec2launcher/application'
|
14
15
|
require 'ec2launcher/environment'
|
15
16
|
require 'ec2launcher/block_device_builder'
|
16
17
|
|
17
18
|
module EC2Launcher
|
19
|
+
|
18
20
|
class AmiDetails
|
19
21
|
attr_reader :ami_name, :ami_id
|
20
22
|
|
@@ -25,28 +27,7 @@ module EC2Launcher
|
|
25
27
|
end
|
26
28
|
|
27
29
|
class Launcher
|
28
|
-
|
29
|
-
# we exceed the allowed AWS RequestLimit.
|
30
|
-
#
|
31
|
-
# @param [Integer] max_time maximum amount of time to sleep before giving up.
|
32
|
-
# @param [Integer] sleep_time the initial amount of time to sleep before retrying.
|
33
|
-
# @param [message] message message to display if we get an exception.
|
34
|
-
# @param [Block] block Ruby code block to execute.
|
35
|
-
def run_with_backoff(max_time, sleep_time, message, &block)
|
36
|
-
if sleep_time > max_time
|
37
|
-
puts "AWS::EC2::Errors::RequestLimitExceeded ... failed #{message}"
|
38
|
-
return false
|
39
|
-
end
|
40
|
-
|
41
|
-
begin
|
42
|
-
yield
|
43
|
-
rescue AWS::EC2::Errors::RequestLimitExceeded
|
44
|
-
puts "AWS::EC2::Errors::RequestLimitExceeded ... retrying #{message} in #{sleep_time} seconds"
|
45
|
-
sleep sleep_time
|
46
|
-
run_with_backoff(max_time, sleep_time * 2, message, &block)
|
47
|
-
end
|
48
|
-
true
|
49
|
-
end
|
30
|
+
include BackoffRunner
|
50
31
|
|
51
32
|
def launch(options)
|
52
33
|
@options = options
|
@@ -79,7 +60,7 @@ module EC2Launcher
|
|
79
60
|
filename = File.join(app_dir, application_name)
|
80
61
|
next if File.directory?(filename)
|
81
62
|
|
82
|
-
apps = ApplicationDSL.execute(File.read(filename)).applications
|
63
|
+
apps = EC2Launcher::ApplicationDSL.execute(File.read(filename)).applications
|
83
64
|
apps.each do |new_application|
|
84
65
|
@applications[new_application.name] = new_application
|
85
66
|
validate_application(filename, new_application)
|
@@ -169,7 +150,7 @@ module EC2Launcher
|
|
169
150
|
# SECURITY GROUPS
|
170
151
|
##############################
|
171
152
|
security_groups = []
|
172
|
-
security_groups += @environment.
|
153
|
+
security_groups += @environment.security_groups_for_environment(@environment.name) unless @environment.security_groups_for_environment(@environment.name).nil?
|
173
154
|
security_groups += @application.security_groups_for_environment(@environment.name)
|
174
155
|
|
175
156
|
# Build mapping of existing security group names to security group objects
|
@@ -673,7 +654,7 @@ rm -f /tmp/runurl"
|
|
673
654
|
# Load configuration file
|
674
655
|
config_filename = File.join(@options.directory, "config.rb")
|
675
656
|
abort("Unable to find 'config.rb' in '#{@options.directory}'") unless File.exists?(config_filename)
|
676
|
-
ConfigDSL.execute(File.read(config_filename)).config
|
657
|
+
EC2Launcher::ConfigDSL.execute(File.read(config_filename)).config
|
677
658
|
end
|
678
659
|
|
679
660
|
# Load and parse an environment file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ec2launcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- ec2launcher.gemspec
|
46
46
|
- lib/ec2launcher.rb
|
47
47
|
- lib/ec2launcher/application.rb
|
48
|
+
- lib/ec2launcher/backoff_runner.rb
|
48
49
|
- lib/ec2launcher/block_device.rb
|
49
50
|
- lib/ec2launcher/block_device_builder.rb
|
50
51
|
- lib/ec2launcher/config.rb
|
@@ -52,6 +53,7 @@ files:
|
|
52
53
|
- lib/ec2launcher/email_notification.rb
|
53
54
|
- lib/ec2launcher/environment.rb
|
54
55
|
- lib/ec2launcher/init_options.rb
|
56
|
+
- lib/ec2launcher/security_group_handler.rb
|
55
57
|
- lib/ec2launcher/version.rb
|
56
58
|
- startup-scripts/setup.rb
|
57
59
|
- startup-scripts/setup_instance.rb
|