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