ec2launcher 1.0.10 → 1.0.11
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 +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