ec2launcher 1.0.10 → 1.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/lib/ec2launcher/block_device_builder.rb +63 -41
- data/lib/ec2launcher/dsl/application.rb +297 -0
- data/lib/ec2launcher/dsl/block_device.rb +96 -0
- data/lib/ec2launcher/dsl/config.rb +78 -0
- data/lib/ec2launcher/dsl/email_notification.rb +67 -0
- data/lib/ec2launcher/dsl/environment.rb +221 -0
- data/lib/ec2launcher/hostname_generator.rb +117 -0
- data/lib/ec2launcher/init_options.rb +18 -7
- data/lib/ec2launcher/version.rb +1 -1
- data/lib/ec2launcher.rb +111 -148
- metadata +7 -6
- data/lib/ec2launcher/application.rb +0 -295
- data/lib/ec2launcher/block_device.rb +0 -94
- data/lib/ec2launcher/config.rb +0 -76
- data/lib/ec2launcher/email_notification.rb +0 -65
- data/lib/ec2launcher/environment.rb +0 -219
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 Sean Laurent
|
3
|
+
#
|
4
|
+
|
5
|
+
module EC2Launcher
|
6
|
+
module DSL
|
7
|
+
module EmailNotifications
|
8
|
+
attr_reader :email_notifications
|
9
|
+
|
10
|
+
def email_notification(&block)
|
11
|
+
notifications = EC2Launcher::DSL::EmailNotification.new
|
12
|
+
notifications.instance_exec(&block)
|
13
|
+
@email_notifications = notifications
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class EmailNotification
|
18
|
+
def initialize()
|
19
|
+
end
|
20
|
+
|
21
|
+
def from(*from)
|
22
|
+
if from.empty?
|
23
|
+
@from
|
24
|
+
else
|
25
|
+
@from = from[0]
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to(*to)
|
31
|
+
if to.empty?
|
32
|
+
@to
|
33
|
+
else
|
34
|
+
@to = to[0]
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ses_access_key(*ses_access_key)
|
40
|
+
if ses_access_key.empty?
|
41
|
+
@ses_access_key
|
42
|
+
else
|
43
|
+
@ses_access_key = ses_access_key[0]
|
44
|
+
self
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def ses_secret_key(*ses_secret_key)
|
49
|
+
if ses_secret_key.empty?
|
50
|
+
@ses_secret_key
|
51
|
+
else
|
52
|
+
@ses_secret_key = ses_secret_key[0]
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_json(*a)
|
58
|
+
{
|
59
|
+
"from" => @from,
|
60
|
+
"to" => @to,
|
61
|
+
"ses_access_key" => @ses_access_key,
|
62
|
+
"ses_secret_key" => @ses_secret_key
|
63
|
+
}.to_json(*a)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 Sean Laurent
|
3
|
+
#
|
4
|
+
require 'ec2launcher/dsl/email_notification'
|
5
|
+
require 'ec2launcher/security_group_handler'
|
6
|
+
|
7
|
+
module EC2Launcher
|
8
|
+
module DSL
|
9
|
+
class Environment
|
10
|
+
include EC2Launcher::DSL::EmailNotifications
|
11
|
+
include EC2Launcher::SecurityGroupHandler
|
12
|
+
|
13
|
+
attr_reader :name
|
14
|
+
attr_reader :precommands
|
15
|
+
attr_reader :postcommands
|
16
|
+
|
17
|
+
def initialize()
|
18
|
+
@aliases = []
|
19
|
+
@email_notifications = nil
|
20
|
+
@gems = []
|
21
|
+
@packages = []
|
22
|
+
@precommands = []
|
23
|
+
@postcommands = []
|
24
|
+
@roles = []
|
25
|
+
@security_groups = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def environment(name)
|
29
|
+
@name = name
|
30
|
+
yield self
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def aws_keyfile(*aws_keyfile)
|
35
|
+
if aws_keyfile.empty?
|
36
|
+
@aws_keyfile
|
37
|
+
else
|
38
|
+
@aws_keyfile = aws_keyfile[0]
|
39
|
+
self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def aliases(*aliases)
|
44
|
+
if aliases.empty?
|
45
|
+
@aliases
|
46
|
+
else
|
47
|
+
if aliases[0].kind_of? String
|
48
|
+
@aliases = [ aliases[0] ]
|
49
|
+
else
|
50
|
+
@aliases = aliases[0]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def ami_name(*ami_name)
|
56
|
+
if ami_name.empty?
|
57
|
+
@ami_name
|
58
|
+
else
|
59
|
+
if ami_name[0].kind_of? String
|
60
|
+
@ami_name = /#{ami_name[0]}/
|
61
|
+
else
|
62
|
+
@ami_name = ami_name[0]
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
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
|
75
|
+
end
|
76
|
+
|
77
|
+
def chef_server_url(*server_url)
|
78
|
+
if server_url.empty?
|
79
|
+
@chef_server_url
|
80
|
+
else
|
81
|
+
@chef_server_url = server_url[0]
|
82
|
+
self
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def chef_validation_pem_url(*chef_validation_pem_url)
|
87
|
+
if chef_validation_pem_url.empty?
|
88
|
+
@chef_validation_pem_url
|
89
|
+
else
|
90
|
+
@chef_validation_pem_url = chef_validation_pem_url[0]
|
91
|
+
self
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def domain_name(*domain_name)
|
96
|
+
if domain_name.empty?
|
97
|
+
@domain_name
|
98
|
+
else
|
99
|
+
@domain_name = domain_name[0]
|
100
|
+
self
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def gems(*gems)
|
105
|
+
if gems.empty?
|
106
|
+
@gems
|
107
|
+
else
|
108
|
+
@gems = gems[0]
|
109
|
+
self
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def inherit(*inherit_type)
|
114
|
+
if inherit_type.empty?
|
115
|
+
@inherit_type
|
116
|
+
else
|
117
|
+
@inherit_type = inherit_type[0]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def key_name(*key)
|
122
|
+
if key.empty?
|
123
|
+
@key_name
|
124
|
+
else
|
125
|
+
@key_name = key[0]
|
126
|
+
self
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def packages(*packages)
|
131
|
+
if packages.empty?
|
132
|
+
@packages
|
133
|
+
else
|
134
|
+
@packages = packages[0]
|
135
|
+
self
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def precommand(*command)
|
140
|
+
@precommands << command[0]
|
141
|
+
end
|
142
|
+
|
143
|
+
def postcommand(*command)
|
144
|
+
@postcommands << command[0]
|
145
|
+
end
|
146
|
+
|
147
|
+
def roles(*roles)
|
148
|
+
if roles.empty?
|
149
|
+
@roles
|
150
|
+
else
|
151
|
+
@roles = [] if @roles.nil?
|
152
|
+
if roles[0].kind_of? Array
|
153
|
+
@roles += roles[0]
|
154
|
+
else
|
155
|
+
@roles = []
|
156
|
+
@roles << roles[0]
|
157
|
+
end
|
158
|
+
self
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def short_name(*short_name)
|
163
|
+
if short_name.empty?
|
164
|
+
@short_name
|
165
|
+
else
|
166
|
+
@short_name = short_name[0]
|
167
|
+
self
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def subnet(*subnet)
|
172
|
+
if subnet.empty?
|
173
|
+
@subnet
|
174
|
+
else
|
175
|
+
@subnet = subnet[0]
|
176
|
+
self
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Takes values from the other environment and merges them into this one
|
181
|
+
def merge(other_env)
|
182
|
+
@name =other_env.name
|
183
|
+
|
184
|
+
@gems += other_env.gems unless other_env.gems.nil?
|
185
|
+
@packages += other_env.packages unless other_env.packages.nil?
|
186
|
+
@roles += other_env.roles unless other_env.roles.nil?
|
187
|
+
@precommands += other_env.precommands unless other_env.precommands.nil?
|
188
|
+
@postcommands += other_env.postcommands unless other_env.postcommands.nil?
|
189
|
+
unless other_env.security_groups.nil?
|
190
|
+
other_env.security_groups.keys.each do |key|
|
191
|
+
@security_groups[key] = [] if @security_groups[key].nil?
|
192
|
+
@security_groups[key] += other_env.security_groups[key]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
@aliases = other_env.aliases.nil? ? nil : other_env.aliases
|
197
|
+
|
198
|
+
@ami_name = other_env.ami_name unless other_env.ami_name.nil?
|
199
|
+
@aws_keyfile = other_env.aws_keyfile unless other_env.aws_keyfile.nil?
|
200
|
+
@availability_zone = other_env.availability_zone unless other_env.availability_zone.nil?
|
201
|
+
@chef_server_url = other_env.chef_server_url unless other_env.chef_server_url.nil?
|
202
|
+
@chef_validation_pem_url = other_env.chef_validation_pem_url unless other_env.chef_validation_pem_url.nil?
|
203
|
+
@domain_name = other_env.domain_name unless other_env.domain_name.nil?
|
204
|
+
@email_notifications = other_env.email_notifications unless other_env.email_notifications.nil?
|
205
|
+
@key_name = other_env.key_name unless other_env.key_name.nil?
|
206
|
+
@subnet = other_env.subnet unless other_env.subnet.nil?
|
207
|
+
@short_name = other_env.short_name unless other_env.short_name.nil?
|
208
|
+
end
|
209
|
+
|
210
|
+
def load(dsl)
|
211
|
+
self.instance_eval(dsl)
|
212
|
+
self
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.load(dsl)
|
216
|
+
env = EC2Launcher::DSL::Environment.new.instance_eval(dsl)
|
217
|
+
env
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 Sean Laurent
|
3
|
+
#
|
4
|
+
require 'rubygems'
|
5
|
+
require 'aws-sdk'
|
6
|
+
|
7
|
+
module EC2Launcher
|
8
|
+
# Helper class to generate sequential, numbered host names
|
9
|
+
class HostnameGenerator
|
10
|
+
#
|
11
|
+
# @param [AWS::EC2] ec2 EC2 object used to query for existing instances
|
12
|
+
# @param [EC2Launcher::Environment] environment Environment to use for generating host names
|
13
|
+
# @param [EC2Launcher::Application] application Application to use for generating hostn ames
|
14
|
+
def initialize(ec2, environment, application)
|
15
|
+
@ec2 = ec2
|
16
|
+
@server_name_cache = nil
|
17
|
+
|
18
|
+
@prefix = application.basename
|
19
|
+
@prefix ||= application.name
|
20
|
+
|
21
|
+
@env_suffix = environment.short_name
|
22
|
+
@env_suffix ||= environment.name
|
23
|
+
|
24
|
+
@suffix = @env_suffix
|
25
|
+
unless application.name_suffix.nil?
|
26
|
+
@suffix = "#{application.name_suffix}.#{@env_suffix}"
|
27
|
+
end
|
28
|
+
|
29
|
+
@host_number_regex = Regexp.new("#{@prefix}(\\d+)[.]#{@suffix.gsub(/[.]/, "[.]")}.*")
|
30
|
+
|
31
|
+
# Load and cache instance names
|
32
|
+
load_instances(@prefix, @suffix)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Generates a new host name and automatically caches it
|
36
|
+
# so that future requests don't use the same name.
|
37
|
+
def generate_hostname()
|
38
|
+
# Find next host number
|
39
|
+
host_number = get_next_host_number()
|
40
|
+
|
41
|
+
# Build short host name
|
42
|
+
short_name = "#{@prefix}#{host_number}.#{@suffix}"
|
43
|
+
|
44
|
+
# Cache the new hostname
|
45
|
+
@server_name_cache << short_name
|
46
|
+
|
47
|
+
short_name
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_long_name(short_hostname, domain_name = nil)
|
51
|
+
hostname = short_hostname
|
52
|
+
unless domain_name.nil?
|
53
|
+
hostname += ".#{domain_name}"
|
54
|
+
end
|
55
|
+
|
56
|
+
hostname
|
57
|
+
end
|
58
|
+
|
59
|
+
def generate_short_name(long_name, domain_name = nil)
|
60
|
+
short_hostname = long_name
|
61
|
+
unless domain_name.nil?
|
62
|
+
short_hostname = long_name.gsub(/.#{domain_name}/, '')
|
63
|
+
end
|
64
|
+
short_hostname
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Loads and caches instance host names
|
70
|
+
def load_instances(prefix, suffix)
|
71
|
+
@server_name_cache = []
|
72
|
+
AWS.memoize do
|
73
|
+
server_instances = @ec2.instances.filter("tag:Name", "#{prefix}*#{suffix}*")
|
74
|
+
server_instances.each do |i|
|
75
|
+
next if i.status == :terminated
|
76
|
+
@server_name_cache << i.tags[:Name]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Determines the next available number for a host name
|
82
|
+
def get_next_host_number()
|
83
|
+
highest_server_number = 0
|
84
|
+
lowest_server_number = 32768
|
85
|
+
|
86
|
+
server_numbers = []
|
87
|
+
|
88
|
+
@server_name_cache.each do |server_name|
|
89
|
+
unless @host_number_regex.match(server_name).nil?
|
90
|
+
server_num = $1.to_i
|
91
|
+
server_numbers << server_num
|
92
|
+
end
|
93
|
+
end
|
94
|
+
highest_server_number = server_numbers.max
|
95
|
+
|
96
|
+
# If the highest number server is less than 10, just add
|
97
|
+
# 1 to it. Otherwise, find the first available
|
98
|
+
# server number starting at 1.
|
99
|
+
host_number = 0
|
100
|
+
if highest_server_number.nil?
|
101
|
+
host_number = 1
|
102
|
+
elsif highest_server_number < 10
|
103
|
+
host_number = highest_server_number + 1
|
104
|
+
else
|
105
|
+
# Try to start over with 1 and find the
|
106
|
+
# first available host number
|
107
|
+
server_number_set = Set.new(server_numbers)
|
108
|
+
host_number = 1
|
109
|
+
while server_number_set.include?(host_number) do
|
110
|
+
host_number += 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
host_number
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -51,26 +51,30 @@ module EC2Launcher
|
|
51
51
|
opts.separator ""
|
52
52
|
opts.separator "Additional launch options:"
|
53
53
|
|
54
|
-
opts.on("--command [CMD]", "Additional command to run during launch sequence.") do |command|
|
54
|
+
opts.on("--command [CMD]", String, "Additional command to run during launch sequence.") do |command|
|
55
55
|
@options.commands << command
|
56
56
|
end
|
57
57
|
|
58
|
-
opts.on("
|
58
|
+
opts.on("--clone HOST", String, "Clone the latest snapshots from a specific host.") do |clone_host|
|
59
59
|
@options.clone_host = clone_host
|
60
60
|
end
|
61
61
|
|
62
|
+
opts.on("-c", "--count COUNT", Integer, "Number of new instances to launch.") do |count|
|
63
|
+
@options.count = count
|
64
|
+
end
|
65
|
+
|
62
66
|
opts.separator ""
|
63
67
|
opts.separator "Overrides:"
|
64
68
|
|
65
|
-
opts.on("-d", "--directory DIRECTORY", "Location of configuration directory. Defaults to current directory.") do |directory|
|
69
|
+
opts.on("-d", "--directory DIRECTORY", String, "Location of configuration directory. Defaults to current directory.") do |directory|
|
66
70
|
@options.directory = directory
|
67
71
|
end
|
68
72
|
|
69
|
-
opts.on("-h", "--hostname NAME", "The name for the new server.") do |hostname|
|
73
|
+
opts.on("-h", "--hostname NAME", String, "The name for the new server.") do |hostname|
|
70
74
|
@options.hostname = hostname
|
71
75
|
end
|
72
76
|
|
73
|
-
opts.on("-u", "--chef-validation-url", "URL for the Chef validation pem file.") do |chef_validation_url|
|
77
|
+
opts.on("-u", "--chef-validation-url", String, "URL for the Chef validation pem file.") do |chef_validation_url|
|
74
78
|
@options.chef_validation_url = chef_validation_url
|
75
79
|
end
|
76
80
|
|
@@ -93,11 +97,11 @@ module EC2Launcher
|
|
93
97
|
opts.separator ""
|
94
98
|
opts.separator "AWS Security Options:"
|
95
99
|
|
96
|
-
opts.on("--access-key KEY", "Amazon access key. Overrides AWS_ACCESS_KEY environment variable.") do |access_key|
|
100
|
+
opts.on("--access-key KEY", String, "Amazon access key. Overrides AWS_ACCESS_KEY environment variable.") do |access_key|
|
97
101
|
@options.access_key = access_key
|
98
102
|
end
|
99
103
|
|
100
|
-
opts.on("--secret SECRET", "Amazon secret access key. Overrides AWS_SECRET_ACCESS_KEY environment variable.") do |secret|
|
104
|
+
opts.on("--secret SECRET", String, "Amazon secret access key. Overrides AWS_SECRET_ACCESS_KEY environment variable.") do |secret|
|
101
105
|
@options.secret = secret
|
102
106
|
end
|
103
107
|
|
@@ -130,6 +134,7 @@ module EC2Launcher
|
|
130
134
|
@options.application = nil
|
131
135
|
@options.commands = []
|
132
136
|
@options.clone_host = nil
|
137
|
+
@options.count = 1
|
133
138
|
|
134
139
|
@options.ami_id = nil
|
135
140
|
@options.hostname = nil
|
@@ -156,6 +161,12 @@ module EC2Launcher
|
|
156
161
|
help
|
157
162
|
exit 1
|
158
163
|
end
|
164
|
+
|
165
|
+
if ! @options.hostname.nil? && @options.count > 1
|
166
|
+
puts "Cannot specify both a hostname ['#{@options.hostname}'] and multiple instances [#{@options.count}]."
|
167
|
+
puts
|
168
|
+
exit 1
|
169
|
+
end
|
159
170
|
end
|
160
171
|
|
161
172
|
@options
|
data/lib/ec2launcher/version.rb
CHANGED