ec2launcher 1.0.9 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 1.0.10
2
+
3
+ * Support multiple "gem" definitions in applications.
4
+ * Fixed module reference to InitOptions.
5
+ * Move run_with_backoff code into separate module for reusability.
6
+
1
7
  ## 1.0.9
2
8
 
3
9
  * Remove "default" environment.
data/bin/ec2launcher CHANGED
@@ -7,7 +7,7 @@ require 'erb'
7
7
  require 'ec2launcher'
8
8
  require "ec2launcher/init_options"
9
9
 
10
- opt_parser = InitOptions.new
10
+ opt_parser = EC2Launcher::InitOptions.new
11
11
  opt_parser.parse(ARGV)
12
12
 
13
13
  if opt_parser.command == "init"
@@ -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
- class ApplicationDSL
8
- attr_accessor :applications
8
+ module EC2Launcher
9
+ # Wrapper class to handle loading Application blocks.
10
+ class ApplicationDSL
11
+ attr_accessor :applications
9
12
 
10
- def initialize
11
- self.applications = []
12
- end
13
+ def initialize
14
+ self.applications = []
15
+ end
13
16
 
14
- def application(name, &block)
15
- application = Application.new(name)
16
- applications << application
17
- application.instance_eval &block
18
- application
19
- end
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
- def self.execute(dsl)
22
- new.tap do |context|
23
- context.instance_eval(dsl)
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
- class Application
29
- include EmailNotifications
31
+ # Represents a single application stack.
32
+ class Application
33
+ include EC2Launcher::EmailNotifications
34
+ include EC2Launcher::SecurityGroupHandler
30
35
 
31
- attr_reader :name
36
+ attr_reader :name
32
37
 
33
- def initialize(name)
34
- @name = name
35
- @email_notifications = nil
36
- end
38
+ def initialize(name)
39
+ @name = name
40
+ @email_notifications = nil
41
+ end
37
42
 
38
- def application(name)
39
- @name = name
40
- yield self
41
- self
42
- end
43
+ def application(name)
44
+ @name = name
45
+ yield self
46
+ self
47
+ end
43
48
 
44
- def ami_name(*ami_name)
45
- if ami_name.empty?
46
- @ami_name
47
- else
48
- if ami_name[0].kind_of? String
49
- @ami_name = /#{ami_name[0]}/
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
- @ami_name = ami_name[0]
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
- def availability_zone(*zone)
58
- if zone.empty?
59
- @availability_zone
60
- else
61
- @availability_zone = zone[0].to_s
62
- self
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
- def basename(*name)
67
- if name.empty?
68
- @basename
69
- else
70
- @basename = name[0]
71
- self
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
- def block_devices(*block_device_data)
76
- if block_device_data.empty?
77
- @block_devices
78
- else
79
- self
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
- def block_device(&block)
84
- @block_devices = [] if @block_devices.nil?
85
- device = BlockDevice.new
86
- device.instance_exec(&block)
87
- @block_devices << device
88
- end
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
- def elb(*elb)
91
- if elb.empty?
92
- @elb
93
- else
94
- @elb = Hash.new if @elb.nil?
95
- if elb[0].kind_of? Hash
96
- elb[0].keys.each {|key| @elb[key] = elb[0][key]}
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["default"] = elb[0].to_s
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
- def elb_for_environment(environment)
105
- elb_name = @elb[environment]
106
- elb_name ||= @elb["default"]
107
- elb_name
108
- end
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
- def environment_roles(*data)
111
- if data.empty?
112
- @environment_roles
113
- else
114
- @environment_roles = Hash.new if @environment_roles.nil?
115
- env_name = data[0]
116
- env_roles = data[1]
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
- environment_data = @environment_roles[env_name]
119
- environment_data ||= []
145
+ environment_data = @environment_roles[env_name]
146
+ environment_data ||= []
120
147
 
121
- if env_roles.kind_of? Array
122
- environment_data += env_roles
123
- else
124
- environment_data << env_roles
125
- end
126
- @environment_roles[env_name] = environment_data
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
- self
155
+ self
156
+ end
129
157
  end
130
- end
131
158
 
132
- def gems(*gems)
133
- if gems.empty?
134
- @gems
135
- else
136
- @gems = gems[0]
137
- self
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
- def inherit(*inherit_type)
142
- if inherit_type.empty?
143
- @inherit_type
144
- else
145
- @inherit_type = inherit_type[0]
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
- def instance_type(*type_name)
150
- if type_name.empty?
151
- @instance_type
152
- else
153
- @instance_type = type_name[0]
154
- self
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
- # Takes values from the other server type and merges them into this one
159
- def merge(other_server)
160
- @name = other_server.name
161
- @ami_name = other_server.ami_name unless other_server.ami_name.nil?
162
- @availability_zone = other_server.availability_zone unless other_server.availability_zone.nil?
163
- @basename = other_server.basename unless other_server.basename.nil?
164
- other_server.block_devices.each {|bd| @block_devices << bd } unless other_server.block_devices.nil?
165
- other_server.elb.keys.each {|env_name| @elb[env_name] = other_server.elb[env_name] } unless other_server.elb.nil?
166
- @instance_type = other_server.instance_type unless other_server.instance_type.nil?
167
- @name_suffix = other_server.name_suffix unless other_server.name_suffix.nil?
168
- other_server.roles.each {|role| @roles << role } unless other_server.roles.nil?
169
- unless other_server.security_groups.nil?
170
- other_server.security_groups.keys.each do |env_name|
171
- unless @security_groups.has_key? env_name
172
- @security_groups[env_name] = []
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
- def name_suffix(*suffix)
180
- if suffix.empty?
181
- @name_suffix
182
- else
183
- @name_suffix = suffix[0]
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
- def packages(*packages)
188
- if packages.empty?
189
- @packages
190
- else
191
- @packages = packages[0]
192
- self
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
- def roles(*roles)
197
- if roles.empty?
198
- @roles
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
- @roles << roles[0]
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
- def roles_for_environment(environment)
212
- roles = []
213
- roles += @roles unless @roles.nil?
253
+ def roles_for_environment(environment)
254
+ roles = []
255
+ roles += @roles unless @roles.nil?
214
256
 
215
- unless @environment_roles.nil? || @environment_roles[environment].nil?
216
- roles += @environment_roles[environment]
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
- def security_groups(*groups)
222
- if groups.empty?
223
- @security_groups
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
- @security_groups["default"] = [ groups[0].to_s ]
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
- def subnet(*subnet)
245
- if subnet.empty?
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
- def self.load(dsl)
259
- env = Application.new.instance_eval(dsl)
260
- env
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
- def initialize()
9
- @count = 1
10
- @group = "root"
11
- @user = "root"
12
- end
5
+ module EC2Launcher
6
+ class BlockDevice
7
+ attr_reader :mount_point
8
+ attr_reader :name
13
9
 
14
- def is_raid?()
15
- @raid_level.nil?
16
- end
10
+ def initialize()
11
+ @count = 1
12
+ @group = "root"
13
+ @user = "root"
14
+ end
17
15
 
18
- def count(*block_count)
19
- if block_count.empty?
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
- def group(*group)
28
- if group.empty?
29
- @group
30
- else
31
- @group = group[0]
32
- self
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
- def mount(*mount)
37
- if mount.empty?
38
- @mount
39
- else
40
- @mount_point = mount[0]
41
- self
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
- def name(*name)
46
- if name.empty?
47
- @name
48
- else
49
- @name = name[0]
50
- self
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
- def owner(*owner)
55
- if owner.empty?
56
- @owner
57
- else
58
- @owner = owner[0]
59
- self
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
- def raid_level(*raid_level)
64
- if raid_level.empty?
65
- @raid_level
66
- else
67
- @raid_level = raid_level[0]
68
- self
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
- def size(*volume_size)
73
- if volume_size.empty?
74
- @size
75
- else
76
- @size = volume_size[0].to_i
77
- self
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
- def to_json(*a)
82
- {
83
- "name" => @name,
84
- "count" => @count,
85
- "raid_level" => @raid_level,
86
- "mount_point" => @mount_point,
87
- "owner" => @owner,
88
- "group" => @group
89
- }.to_json(*a)
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
- def email_notification(&block)
8
- notifications = EmailNotification.new
9
- notifications.instance_exec(&block)
10
- @email_notifications = notifications
11
- end
12
- end
5
+ module EC2Launcher
6
+ module EmailNotifications
7
+ attr_reader :email_notifications
13
8
 
14
- class EmailNotification
15
- def initialize()
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
- def from(*from)
19
- if from.empty?
20
- @from
21
- else
22
- @from = from[0]
23
- self
16
+ class EmailNotification
17
+ def initialize()
24
18
  end
25
- end
26
19
 
27
- def to(*to)
28
- if to.empty?
29
- @to
30
- else
31
- @to = to[0]
32
- self
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
- def ses_access_key(*ses_access_key)
37
- if ses_access_key.empty?
38
- @ses_access_key
39
- else
40
- @ses_access_key = ses_access_key[0]
41
- self
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
- def ses_secret_key(*ses_secret_key)
46
- if ses_secret_key.empty?
47
- @ses_secret_key
48
- else
49
- @ses_secret_key = ses_secret_key[0]
50
- self
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
- def to_json(*a)
55
- {
56
- "from" => @from,
57
- "to" => @to,
58
- "ses_access_key" => @ses_access_key,
59
- "ses_secret_key" => @ses_secret_key
60
- }.to_json(*a)
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
- @security_groups += other_env.security_groups unless other_env.security_groups.nil?
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
- class InitOptions
11
- attr_reader :command
12
- attr_reader :options
13
- attr_reader :location
10
+ module EC2Launcher
11
+ class InitOptions
12
+ attr_reader :command
13
+ attr_reader :options
14
+ attr_reader :location
14
15
 
15
- SUB_COMMANDS = %w{init launch}
16
+ SUB_COMMANDS = %w{init launch}
16
17
 
17
- def initialize
18
- @opts = OptionParser.new do |opts|
19
- opts.banner = "Usage: ec2launcher [COMMAND]
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
- init [LOCATION] Initialize a repository in the specified directory.
24
- launch [OPTIONS] Launch a new instance.
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
- opts.separator "Query options:"
29
+ "
30
+ opts.separator "Query options:"
30
31
 
31
- opts.on("-l", "--list", "Show environments and applications.") do
32
- @options.list = true
33
- end
32
+ opts.on("-l", "--list", "Show environments and applications.") do
33
+ @options.list = true
34
+ end
34
35
 
35
- opts.on("-s", "--show-defaults", "Show settings, but do not launch any instances.") do
36
- @options.show_defaults = true
37
- end
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
- opts.separator ""
40
- opts.separator "Required Launch options:"
40
+ opts.separator ""
41
+ opts.separator "Required Launch options:"
41
42
 
42
- opts.on("-e", "--environment ENV", "The environment for the server.") do |env|
43
- @options.environ = env
44
- end
43
+ opts.on("-e", "--environment ENV", "The environment for the server.") do |env|
44
+ @options.environ = env
45
+ end
45
46
 
46
- opts.on("-a", "--application NAME", "The name of the application class for the new server.") do |app_name|
47
- @options.application = app_name
48
- end
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
- opts.separator ""
51
- opts.separator "Additional launch options:"
51
+ opts.separator ""
52
+ opts.separator "Additional launch options:"
52
53
 
53
- opts.on("--command [CMD]", "Additional command to run during launch sequence.") do |command|
54
- @options.commands << command
55
- end
54
+ opts.on("--command [CMD]", "Additional command to run during launch sequence.") do |command|
55
+ @options.commands << command
56
+ end
56
57
 
57
- opts.on("-c", "--clone HOST", "Clone the latest snapshots from a specific host.") do |clone_host|
58
- @options.clone_host = clone_host
59
- end
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
- opts.separator ""
62
- opts.separator "Overrides:"
62
+ opts.separator ""
63
+ opts.separator "Overrides:"
63
64
 
64
- opts.on("-d", "--directory DIRECTORY", "Location of configuration directory. Defaults to current directory.") do |directory|
65
- @options.directory = directory
66
- end
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
- opts.on("-h", "--hostname NAME", "The name for the new server.") do |hostname|
69
- @options.hostname = hostname
70
- end
69
+ opts.on("-h", "--hostname NAME", "The name for the new server.") do |hostname|
70
+ @options.hostname = hostname
71
+ end
71
72
 
72
- opts.on("-u", "--chef-validation-url", "URL for the Chef validation pem file.") do |chef_validation_url|
73
- @options.chef_validation_url = chef_validation_url
74
- end
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
- opts.on("--ami AMI_ID", "AMI id") do |ami_id|
77
- @options.ami_id = ami_id
78
- end
77
+ opts.on("--ami AMI_ID", "AMI id") do |ami_id|
78
+ @options.ami_id = ami_id
79
+ end
79
80
 
80
- opts.on("-z", "--availability-zone ZONE", EC2Launcher::AVAILABILITY_ZONES, "AWS availability zone (#{EC2Launcher::AVAILABILITY_ZONES.join(', ')}).") do |zone|
81
- @options.zone = zone
82
- end
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
- opts.on("-i", "--instance-type TYPE", EC2Launcher::INSTANCE_TYPES, "EC2 instance type (#{EC2Launcher::INSTANCE_TYPES.join(', ')}).") do |instance_type|
85
- @options.instance_type = instance_type
86
- end
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
- opts.on("-v", "--volume-size SIZE", Integer, "EBS volume size in GB. Defaults to #{EC2Launcher::DEFAULT_VOLUME_SIZE} GB") do |volume_size|
89
- @options.volume_size = volume_size
90
- end
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
- opts.separator ""
93
- opts.separator "AWS Security Options:"
93
+ opts.separator ""
94
+ opts.separator "AWS Security Options:"
94
95
 
95
- opts.on("--access-key KEY", "Amazon access key. Overrides AWS_ACCESS_KEY environment variable.") do |access_key|
96
- @options.access_key = access_key
97
- end
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
- opts.on("--secret SECRET", "Amazon secret access key. Overrides AWS_SECRET_ACCESS_KEY environment variable.") do |secret|
100
- @options.secret = secret
101
- end
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
- opts.separator ""
104
- opts.separator "Common options:"
104
+ opts.separator ""
105
+ opts.separator "Common options:"
105
106
 
106
- # No argument, shows at tail. This will print an options summary.
107
- # Try it and see!
108
- opts.on_tail("-?", "--help", "Show this message") do
109
- puts opts
110
- exit
111
- end
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
- def parse(args)
116
- @command = args.shift
117
- unless SUB_COMMANDS.include?(@command)
118
- puts "Missing command! " if @command.nil?
119
- puts "Invalid command: #{@command}" unless @command.nil? || @command == "-?" || @command == "--help"
120
- puts @opts
121
- exit 1
122
- end
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
- @options = OpenStruct.new
125
- @options.list = false
126
- @options.show_defaults = false
125
+ @options = OpenStruct.new
126
+ @options.list = false
127
+ @options.show_defaults = false
127
128
 
128
- @options.environ = nil
129
- @options.application = nil
130
- @options.commands = []
131
- @options.clone_host = nil
129
+ @options.environ = nil
130
+ @options.application = nil
131
+ @options.commands = []
132
+ @options.clone_host = nil
132
133
 
133
- @options.ami_id = nil
134
- @options.hostname = nil
135
- @options.zone = nil
136
- @options.instance_type = nil
137
- @options.volume_size = nil
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
- @options.directory = "./"
140
+ @options.directory = "./"
140
141
 
141
- if @command == "init"
142
- unless args.length >= 1
143
- puts "Missing location!"
144
- puts
145
- help
146
- exit 1
147
- end
148
- @location = args[0]
149
- else
150
- @opts.parse!(args)
151
-
152
- if (@options.environ.nil? || @options.application.nil?) && ! @options.list
153
- puts "Missing a required parameter: #{@options.environ.nil? ? '--environment' : '--application'}"
154
- puts
155
- help
156
- exit 1
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
- @options
161
- end
161
+ @options
162
+ end
162
163
 
163
- def help
164
- puts @opts
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
@@ -2,5 +2,5 @@
2
2
  # Copyright (c) 2012 Sean Laurent
3
3
  #
4
4
  module Ec2launcher
5
- VERSION = "1.0.9"
5
+ VERSION = "1.0.10"
6
6
  end
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
- # Runs an AWS request inside a Ruby block with an exponential backoff in case
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.security_groups unless @environment.security_groups.nil?
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.9
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-18 00:00:00.000000000 Z
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