stemcell 0.2.9 → 0.4.4
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/.gitignore +1 -0
- data/LICENSE.txt +2 -2
- data/README.md +24 -12
- data/bin/necrosis +60 -26
- data/bin/stemcell +23 -180
- data/examples/stemcell.json +26 -0
- data/examples/stemcellrc +21 -2
- data/lib/stemcell/command_line.rb +222 -0
- data/lib/stemcell/errors.rb +25 -0
- data/lib/stemcell/launcher.rb +258 -0
- data/lib/stemcell/log_tailer.rb +143 -0
- data/lib/stemcell/metadata_launcher.rb +108 -0
- data/lib/stemcell/metadata_source.rb +139 -0
- data/lib/stemcell/option_parser.rb +292 -0
- data/lib/stemcell/templates/bootstrap.sh.erb +40 -3
- data/lib/stemcell/utility/deep_merge.rb +13 -0
- data/lib/stemcell/utility.rb +1 -0
- data/lib/stemcell/version.rb +1 -1
- data/lib/stemcell.rb +8 -187
- data/stemcell.gemspec +22 -17
- metadata +107 -13
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
|
3
|
+
module Stemcell
|
4
|
+
class OptionParser
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
attr_reader :defaults
|
8
|
+
attr_reader :version
|
9
|
+
attr_reader :banner
|
10
|
+
attr_reader :override_help
|
11
|
+
|
12
|
+
OPTION_DEFINITIONS = [
|
13
|
+
{
|
14
|
+
:name => 'local_chef_root',
|
15
|
+
:desc => "directory of your local chef repository",
|
16
|
+
:type => String,
|
17
|
+
:env => 'LOCAL_CHEF_ROOT'
|
18
|
+
},
|
19
|
+
{
|
20
|
+
:name => 'aws_creds',
|
21
|
+
:desc => "select the aws credentials to use, via the aws-creds gem",
|
22
|
+
:type => String,
|
23
|
+
:env => 'AWS_CREDS'
|
24
|
+
},
|
25
|
+
{
|
26
|
+
:name => 'aws_access_key',
|
27
|
+
:desc => "aws access key",
|
28
|
+
:type => String,
|
29
|
+
:env => 'AWS_ACCESS_KEY'
|
30
|
+
},
|
31
|
+
{
|
32
|
+
:name => 'aws_secret_key',
|
33
|
+
:desc => "aws secret key",
|
34
|
+
:type => String,
|
35
|
+
:env => 'AWS_SECRET_KEY'
|
36
|
+
},
|
37
|
+
{
|
38
|
+
:name => 'region',
|
39
|
+
:desc => "ec2 region to launch in",
|
40
|
+
:type => String,
|
41
|
+
:env => 'REGION'
|
42
|
+
},
|
43
|
+
{
|
44
|
+
:name => 'instance_type',
|
45
|
+
:desc => "machine type to launch",
|
46
|
+
:type => String,
|
47
|
+
:env => 'INSTANCE_TYPE'
|
48
|
+
},
|
49
|
+
{
|
50
|
+
:name => 'backing_store',
|
51
|
+
:desc => "select between the backing store templates",
|
52
|
+
:type => String,
|
53
|
+
:env => 'BACKING_STORE'
|
54
|
+
},
|
55
|
+
{
|
56
|
+
:name => 'image_id',
|
57
|
+
:desc => "ami to use for launch",
|
58
|
+
:type => String,
|
59
|
+
:env => 'IMAGE_ID'
|
60
|
+
},
|
61
|
+
{
|
62
|
+
:name => 'security_groups',
|
63
|
+
:desc => "comma-separated list of security groups to launch instance with",
|
64
|
+
:type => String,
|
65
|
+
:env => 'SECURITY_GROUPS'
|
66
|
+
},
|
67
|
+
{
|
68
|
+
:name => 'availability_zone',
|
69
|
+
:desc => "zone in which to launch instances",
|
70
|
+
:type => String,
|
71
|
+
:env => 'AVAILABILITY_ZONE'
|
72
|
+
},
|
73
|
+
{
|
74
|
+
:name => 'tags',
|
75
|
+
:desc => "comma-separated list of key=value pairs to apply",
|
76
|
+
:type => String,
|
77
|
+
:env => 'TAGS'
|
78
|
+
},
|
79
|
+
{
|
80
|
+
:name => 'key_name',
|
81
|
+
:desc => "aws ssh key name for the ubuntu user",
|
82
|
+
:type => String,
|
83
|
+
:env => 'KEY_NAME'
|
84
|
+
},
|
85
|
+
{
|
86
|
+
:name => 'iam_role',
|
87
|
+
:desc => "IAM role to associate with the instance",
|
88
|
+
:type => String,
|
89
|
+
:env => 'IAM_ROLE'
|
90
|
+
},
|
91
|
+
{
|
92
|
+
:name => 'placement_group',
|
93
|
+
:desc => "Placement group to associate with the instance",
|
94
|
+
:type => String,
|
95
|
+
:env => 'PLACEMENT_GROUP'
|
96
|
+
},
|
97
|
+
{
|
98
|
+
:name => 'ebs_optimized',
|
99
|
+
:desc => "launch an EBS-Optimized instance",
|
100
|
+
:type => String,
|
101
|
+
:env => 'EBS_OPTIMIZED'
|
102
|
+
},
|
103
|
+
{
|
104
|
+
:name => 'block_device_mappings',
|
105
|
+
:desc => 'block device mappings',
|
106
|
+
:type => String,
|
107
|
+
:env => 'BLOCK_DEVICE_MAPPINGS'
|
108
|
+
},
|
109
|
+
{
|
110
|
+
:name => 'ephemeral_devices',
|
111
|
+
:desc => "comma-separated list of block devices to map ephemeral devices to",
|
112
|
+
:type => String,
|
113
|
+
:env => 'EPHEMERAL_DEVICES'
|
114
|
+
},
|
115
|
+
{
|
116
|
+
:name => 'chef_data_bag_secret',
|
117
|
+
:desc => "path to secret file (or the string containing the secret)",
|
118
|
+
:type => String,
|
119
|
+
:env => 'CHEF_DATA_BAG_SECRET'
|
120
|
+
},
|
121
|
+
{
|
122
|
+
:name => 'chef_role',
|
123
|
+
:desc => "chef role of instance to be launched",
|
124
|
+
:type => String,
|
125
|
+
:env => 'CHEF_ROLE'
|
126
|
+
},
|
127
|
+
{
|
128
|
+
:name => 'chef_environment',
|
129
|
+
:desc => "chef environment in which this instance will run",
|
130
|
+
:type => String,
|
131
|
+
:env => 'CHEF_ENVIRONMENT'
|
132
|
+
},
|
133
|
+
{
|
134
|
+
:name => 'git_origin',
|
135
|
+
:desc => "git origin to use",
|
136
|
+
:type => String,
|
137
|
+
:env => 'GIT_ORIGIN'
|
138
|
+
},
|
139
|
+
{
|
140
|
+
:name => 'git_branch',
|
141
|
+
:desc => "git branch to run off",
|
142
|
+
:type => String,
|
143
|
+
:env => 'GIT_BRANCH'
|
144
|
+
},
|
145
|
+
{
|
146
|
+
:name => 'git_key',
|
147
|
+
:desc => "path to the git repo deploy key (or the key as a string)",
|
148
|
+
:type => String,
|
149
|
+
:env => 'GIT_KEY'
|
150
|
+
},
|
151
|
+
{
|
152
|
+
:name => 'count',
|
153
|
+
:desc => "number of instances to launch",
|
154
|
+
:type => Integer,
|
155
|
+
:env => 'COUNT'
|
156
|
+
},
|
157
|
+
{
|
158
|
+
:name => 'tail',
|
159
|
+
:desc => "interactively tail the initial converge",
|
160
|
+
:type => nil,
|
161
|
+
:env => 'TAIL',
|
162
|
+
:short => :t
|
163
|
+
},
|
164
|
+
{
|
165
|
+
:name => 'ssh_user',
|
166
|
+
:desc => "ssh username",
|
167
|
+
:type => String,
|
168
|
+
:env => 'SSH_USER',
|
169
|
+
:short => :u
|
170
|
+
},
|
171
|
+
{
|
172
|
+
:name => 'non_interactive',
|
173
|
+
:desc => "assumes an affirmative answer to all prompts",
|
174
|
+
:type => nil,
|
175
|
+
:env => 'NON_INTERACTIVE',
|
176
|
+
:short => :f
|
177
|
+
}
|
178
|
+
]
|
179
|
+
|
180
|
+
def initialize(config={})
|
181
|
+
@defaults = config[:defaults] || {}
|
182
|
+
@version = config[:version]
|
183
|
+
@banner = config[:banner]
|
184
|
+
@override_help = config[:override_help]
|
185
|
+
end
|
186
|
+
|
187
|
+
def parse!(args)
|
188
|
+
# The block passed to Trollop#options is evaluated in the binding of the
|
189
|
+
# trollop parser itself, it doesn't have access to the this instance.
|
190
|
+
# So use a value that can be captured instead!
|
191
|
+
_this = self
|
192
|
+
_defns = OPTION_DEFINITIONS
|
193
|
+
|
194
|
+
@options = Trollop::options(args) do
|
195
|
+
version _this.version if _this.version
|
196
|
+
banner _this.banner if _this.banner
|
197
|
+
|
198
|
+
_defns.each do |defn|
|
199
|
+
# Prioritize the environment variable, then the given default
|
200
|
+
default = ENV[defn[:env]] || _this.defaults[defn[:name]]
|
201
|
+
|
202
|
+
opt(
|
203
|
+
defn[:name],
|
204
|
+
defn[:desc],
|
205
|
+
:type => defn[:type],
|
206
|
+
:short => defn[:short],
|
207
|
+
:default => default)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Prevent trollop from showing its help screen
|
211
|
+
opt('help', 'help', :short => :l) if _this.override_help
|
212
|
+
end
|
213
|
+
|
214
|
+
# convert tags from string to ruby hash
|
215
|
+
if options['tags']
|
216
|
+
tags = {}
|
217
|
+
options['tags'].split(',').each do |tag_set|
|
218
|
+
key, value = tag_set.split('=')
|
219
|
+
tags[key] = value
|
220
|
+
end
|
221
|
+
options['tags'] = tags
|
222
|
+
end
|
223
|
+
|
224
|
+
# parse block_device_mappings to convert it from the standard CLI format
|
225
|
+
# to the EC2 Ruby API format.
|
226
|
+
# All of this is a bit hard to find so here are some docs links to
|
227
|
+
# understand
|
228
|
+
|
229
|
+
# CLI This format is documented by typing
|
230
|
+
# ec2-run-instances --help and looking at the -b option
|
231
|
+
# Basically, it's either
|
232
|
+
|
233
|
+
# none
|
234
|
+
# ephemeral<number>
|
235
|
+
# '[<snapshot-id>][:<size>[:<delete-on-termination>][:<type>[:<iops>]]]'
|
236
|
+
|
237
|
+
# Ruby API (that does call to the native API)
|
238
|
+
# gems/aws-sdk-1.17.0/lib/aws/ec2/instance_collection.rb
|
239
|
+
# line 91 + example line 57
|
240
|
+
|
241
|
+
if options['block_device_mappings']
|
242
|
+
block_device_mappings = []
|
243
|
+
options['block_device_mappings'].split(',').each do |device_set|
|
244
|
+
device,devparam = device_set.split('=')
|
245
|
+
|
246
|
+
mapping = {}
|
247
|
+
|
248
|
+
if devparam == 'none'
|
249
|
+
mapping = { :no_device => device }
|
250
|
+
else
|
251
|
+
mapping = { :device_name => device }
|
252
|
+
if devparam =~ /^ephemeral[0-3]/
|
253
|
+
mapping[:virtual_name] = devparam
|
254
|
+
else
|
255
|
+
# we have a more complex 'ebs' parameter
|
256
|
+
#'[<snapshot-id>][:<size>[:<delete-on-termination>][:<type>[:<iops>]]]'
|
257
|
+
|
258
|
+
mapping[:ebs] = {}
|
259
|
+
|
260
|
+
devparam = devparam.split ':'
|
261
|
+
|
262
|
+
# a bit ugly but short and won't change
|
263
|
+
# notice the to_i on volume_size parameter
|
264
|
+
mapping[:ebs][:snapshot_id] = devparam[0] unless devparam[0].blank?
|
265
|
+
mapping[:ebs][:volume_size] = devparam[1].to_i
|
266
|
+
|
267
|
+
# defaults to true - except if we have the exact string "false"
|
268
|
+
mapping[:ebs][:delete_on_termination] = (devparam[2] != "false")
|
269
|
+
|
270
|
+
# optional. notice the to_i on iops parameter
|
271
|
+
mapping[:ebs][:volume_type] = devparam[3] unless devparam[3].blank?
|
272
|
+
mapping[:ebs][:iops] = devparam[4].to_i if (devparam[4].to_i)
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
block_device_mappings.push mapping
|
278
|
+
end
|
279
|
+
|
280
|
+
options['block_device_mappings'] = block_device_mappings
|
281
|
+
end
|
282
|
+
|
283
|
+
# convert security_groups from comma seperated string to ruby array
|
284
|
+
options['security_groups'] &&= options['security_groups'].split(',')
|
285
|
+
# convert ephemeral_devices from comma separated string to ruby array
|
286
|
+
options['ephemeral_devices'] &&= options['ephemeral_devices'].split(',')
|
287
|
+
|
288
|
+
options
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
end
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
|
11
11
|
set -o pipefail
|
12
|
+
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
12
13
|
|
13
14
|
|
14
15
|
# ensure we were called by root
|
@@ -17,6 +18,29 @@ if [ $UID != 0 ]; then
|
|
17
18
|
exit 1
|
18
19
|
fi
|
19
20
|
|
21
|
+
## Only run once at a time
|
22
|
+
# http://stackoverflow.com/questions/959475/shell-fragment-to-make-sure-only-one-instance-a-shell-script-runs-at-any-given-t
|
23
|
+
# slightly modified to use $$
|
24
|
+
# to run it I must set +e
|
25
|
+
|
26
|
+
SCRIPTNAME=`basename $0`
|
27
|
+
PIDFILE=/var/run/${SCRIPTNAME}.pid
|
28
|
+
|
29
|
+
set +e
|
30
|
+
if [ -f ${PIDFILE} ]; then
|
31
|
+
#verify if the process is actually still running under this pid
|
32
|
+
OLDPID=`cat ${PIDFILE}`
|
33
|
+
RESULT=`ps -ef | grep ${OLDPID} | grep ${SCRIPTNAME}`
|
34
|
+
|
35
|
+
if [ -n "${RESULT}" ]; then
|
36
|
+
echo "Script already running! Exiting"
|
37
|
+
exit 255
|
38
|
+
fi
|
39
|
+
|
40
|
+
fi
|
41
|
+
### ---
|
42
|
+
set -e
|
43
|
+
|
20
44
|
|
21
45
|
# redirect stdout to /var/log/init
|
22
46
|
exec > /var/log/init
|
@@ -24,6 +48,10 @@ exec > /var/log/init
|
|
24
48
|
# redirect stderr to /var/log/init.err
|
25
49
|
exec 2> /var/log/init.err
|
26
50
|
|
51
|
+
#grab pid of this process and update the pid file with it
|
52
|
+
echo $$ > ${PIDFILE}
|
53
|
+
|
54
|
+
|
27
55
|
|
28
56
|
##
|
29
57
|
## settings
|
@@ -32,7 +60,7 @@ exec 2> /var/log/init.err
|
|
32
60
|
|
33
61
|
chef_version=11.4.0
|
34
62
|
chef_dir=/etc/chef
|
35
|
-
converge=/usr/local/bin/
|
63
|
+
converge=/usr/local/bin/first_converge
|
36
64
|
role=<%= opts['chef_role'] %>
|
37
65
|
environment=<%= opts['chef_environment'] %>
|
38
66
|
origin=<%= opts['git_origin'] %>
|
@@ -118,7 +146,10 @@ log_level :info
|
|
118
146
|
log_location STDOUT
|
119
147
|
EOF
|
120
148
|
|
121
|
-
|
149
|
+
encrypted_data_bag_secret_file=${chef_dir}/encrypted_data_bag_secret
|
150
|
+
echo -e "$data_bag_secret" > $encrypted_data_bag_secret_file
|
151
|
+
chmod 0400 $encrypted_data_bag_secret_file
|
152
|
+
|
122
153
|
echo "chef configured"
|
123
154
|
}
|
124
155
|
|
@@ -213,4 +244,10 @@ configure_chef_daemon
|
|
213
244
|
##
|
214
245
|
|
215
246
|
|
216
|
-
|
247
|
+
if [ -f ${PIDFILE} ]; then
|
248
|
+
rm ${PIDFILE}
|
249
|
+
fi
|
250
|
+
# I can delete the cron too
|
251
|
+
rm -f /etc/cron.d/ec2admin_get_callback1
|
252
|
+
|
253
|
+
echo "<%= last_bootstrap_line %>"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Hash
|
2
|
+
def deep_merge!(other_hash)
|
3
|
+
merge!(other_hash) do |key, oldval, newval|
|
4
|
+
# Coerce Chef-style attribute hashes to generic ones
|
5
|
+
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
|
6
|
+
newval = newval.to_hash if newval.respond_to?(:to_hash)
|
7
|
+
|
8
|
+
oldval.class.to_s == 'Hash' &&
|
9
|
+
newval.class.to_s == 'Hash' ?
|
10
|
+
oldval.deep_merge!(newval) : newval
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'stemcell/utility/deep_merge'
|
data/lib/stemcell/version.rb
CHANGED
data/lib/stemcell.rb
CHANGED
@@ -1,189 +1,10 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'aws-sdk'
|
1
|
+
require 'stemcell/utility'
|
2
|
+
require 'stemcell/errors'
|
4
3
|
|
5
|
-
|
4
|
+
require 'stemcell/command_line'
|
5
|
+
require 'stemcell/option_parser'
|
6
|
+
require 'stemcell/launcher'
|
7
|
+
require 'stemcell/log_tailer'
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(opts={})
|
10
|
-
@log = Logger.new(STDOUT)
|
11
|
-
@log.level = Logger::INFO unless ENV['DEBUG']
|
12
|
-
@log.debug "creating new stemcell object"
|
13
|
-
@log.debug "opts are #{opts.inspect}"
|
14
|
-
['aws_access_key',
|
15
|
-
'aws_secret_key',
|
16
|
-
'region',
|
17
|
-
].each do |req|
|
18
|
-
raise ArgumentError, "missing required param #{req}" unless opts[req]
|
19
|
-
instance_variable_set("@#{req}",opts[req])
|
20
|
-
end
|
21
|
-
|
22
|
-
@ec2_url = "ec2.#{@region}.amazonaws.com"
|
23
|
-
@timeout = 120
|
24
|
-
@start_time = Time.new
|
25
|
-
|
26
|
-
AWS.config({:access_key_id => @aws_access_key, :secret_access_key => @aws_secret_key})
|
27
|
-
@ec2 = AWS::EC2.new(:ec2_endpoint => @ec2_url)
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
def launch(opts={})
|
32
|
-
verify_required_options(opts,[
|
33
|
-
'image_id',
|
34
|
-
'security_groups',
|
35
|
-
'key_name',
|
36
|
-
'count',
|
37
|
-
'chef_role',
|
38
|
-
'chef_environment',
|
39
|
-
'chef_data_bag_secret',
|
40
|
-
'git_branch',
|
41
|
-
'git_key',
|
42
|
-
'git_origin',
|
43
|
-
'instance_type',
|
44
|
-
])
|
45
|
-
|
46
|
-
# attempt to accept keys as file paths
|
47
|
-
opts['git_key'] = try_file(opts['git_key'])
|
48
|
-
opts['chef_data_bag_secret'] = try_file(opts['chef_data_bag_secret'])
|
49
|
-
|
50
|
-
# generate tags and merge in any that were specefied as inputs
|
51
|
-
tags = {
|
52
|
-
'Name' => "#{opts['chef_role']}-#{opts['chef_environment']}",
|
53
|
-
'Group' => "#{opts['chef_role']}-#{opts['chef_environment']}",
|
54
|
-
'created_by' => ENV['USER'],
|
55
|
-
'stemcell' => VERSION,
|
56
|
-
}
|
57
|
-
tags.merge!(opts['tags']) if opts['tags']
|
58
|
-
|
59
|
-
# generate launch options
|
60
|
-
launch_options = {
|
61
|
-
:image_id => opts['image_id'],
|
62
|
-
:security_groups => opts['security_groups'],
|
63
|
-
:user_data => opts['user_data'],
|
64
|
-
:instance_type => opts['instance_type'],
|
65
|
-
:key_name => opts['key_name'],
|
66
|
-
:count => opts['count'],
|
67
|
-
}
|
68
|
-
|
69
|
-
# specify availability zone (optional)
|
70
|
-
launch_options[:availability_zone] = opts['availability_zone'] if opts['availability_zone']
|
71
|
-
|
72
|
-
# specify IAM role (optional)
|
73
|
-
launch_options[:iam_instance_profile] = opts['iam_role'] if opts['iam_role']
|
74
|
-
|
75
|
-
# specify an EBS-optimized instance (optional)
|
76
|
-
launch_options[:ebs_optimized] = true if opts['ebs_optimized']
|
77
|
-
|
78
|
-
# generate user data script to bootstrap instance, include in launch optsions
|
79
|
-
launch_options[:user_data] = render_template(opts)
|
80
|
-
|
81
|
-
# launch instances
|
82
|
-
instances = do_launch(launch_options)
|
83
|
-
|
84
|
-
# wait for aws to report instance stats
|
85
|
-
wait(instances)
|
86
|
-
|
87
|
-
# set tags on all instances launched
|
88
|
-
set_tags(instances, tags)
|
89
|
-
|
90
|
-
print_run_info(instances)
|
91
|
-
@log.info "launched instances successfully"
|
92
|
-
return instances
|
93
|
-
end
|
94
|
-
|
95
|
-
def find_instance(id)
|
96
|
-
return @ec2.instances[id]
|
97
|
-
end
|
98
|
-
|
99
|
-
def kill(instances,opts={})
|
100
|
-
return if instances.nil?
|
101
|
-
instances.each do |i|
|
102
|
-
begin
|
103
|
-
instance = find_instance(i)
|
104
|
-
@log.warn "Terminating instance #{instance.instance_id}"
|
105
|
-
instance.terminate
|
106
|
-
rescue AWS::EC2::Errors::InvalidInstanceID::NotFound => e
|
107
|
-
throw e unless opts[:ignore_not_found]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
def print_run_info(instances)
|
115
|
-
puts "here is the info for what's launched:"
|
116
|
-
instances.each do |instance|
|
117
|
-
puts "\tinstance_id: #{instance.instance_id}"
|
118
|
-
puts "\tpublic ip: #{instance.public_ip_address}"
|
119
|
-
puts
|
120
|
-
end
|
121
|
-
puts "install logs will be in /var/log/init and /var/log/init.err"
|
122
|
-
end
|
123
|
-
|
124
|
-
def wait(instances)
|
125
|
-
@log.info "Waiting up to #{@timeout} seconds for #{instances.count} instances (#{instances.inspect}):"
|
126
|
-
|
127
|
-
while true
|
128
|
-
sleep 5
|
129
|
-
if Time.now - @start_time > @timeout
|
130
|
-
kill(instances)
|
131
|
-
raise TimeoutError, "exceded timeout of #{@timeout}"
|
132
|
-
end
|
133
|
-
|
134
|
-
if instances.select{|i| i.status != :running }.empty?
|
135
|
-
break
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
@log.info "all instances in running state"
|
140
|
-
end
|
141
|
-
|
142
|
-
def verify_required_options(params,required_options)
|
143
|
-
@log.debug "params is #{params}"
|
144
|
-
@log.debug "required_options are #{required_options}"
|
145
|
-
required_options.each do |required|
|
146
|
-
raise ArgumentError, "you need to provide option #{required}" unless params.include?(required)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def do_launch(opts={})
|
151
|
-
@log.debug "about to launch instance(s) with options #{opts}"
|
152
|
-
@log.info "launching instances"
|
153
|
-
instances = @ec2.instances.create(opts)
|
154
|
-
instances = [instances] unless instances.class == Array
|
155
|
-
instances.each do |instance|
|
156
|
-
@log.info "launched instance #{instance.instance_id}"
|
157
|
-
end
|
158
|
-
return instances
|
159
|
-
end
|
160
|
-
|
161
|
-
def set_tags(instances=[],tags)
|
162
|
-
@log.info "setting tags on instance(s)"
|
163
|
-
instances.each do |instance|
|
164
|
-
instance.tags.set(tags)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def render_template(opts={})
|
169
|
-
this_file = File.expand_path __FILE__
|
170
|
-
base_dir = File.dirname this_file
|
171
|
-
template_file_path = File.join(base_dir,'stemcell','templates','bootstrap.sh.erb')
|
172
|
-
template_file = File.read(template_file_path)
|
173
|
-
erb_template = ERB.new(template_file)
|
174
|
-
generated_template = erb_template.result(binding)
|
175
|
-
@log.debug "genereated template is #{generated_template}"
|
176
|
-
return generated_template
|
177
|
-
end
|
178
|
-
|
179
|
-
# attempt to accept keys as file paths
|
180
|
-
def try_file(opt="")
|
181
|
-
begin
|
182
|
-
return File.read(opt)
|
183
|
-
rescue Object => e
|
184
|
-
return opt
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
end
|
189
|
-
end
|
9
|
+
require 'stemcell/metadata_launcher'
|
10
|
+
require 'stemcell/metadata_source'
|
data/stemcell.gemspec
CHANGED
@@ -1,22 +1,27 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
3
|
require 'stemcell/version'
|
5
4
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "stemcell"
|
7
|
+
s.version = Stemcell::VERSION
|
8
|
+
s.authors = ["Martin Rhoads", "Igor Serebryany", "Nelson Gauthier", "Patrick Viet"]
|
9
|
+
s.email = ["martin.rhoads@airbnb.com", "igor.serebryany@airbnb.com"]
|
10
|
+
s.description = %q{A tool for launching and bootstrapping EC2 instances}
|
11
|
+
s.summary = %q{no summary}
|
12
|
+
s.homepage = "https://github.com/airbnb/stemcell"
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
s.files = `git ls-files`.split($/)
|
15
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'aws-sdk', '~> 1.9'
|
20
|
+
s.add_runtime_dependency 'net-ssh', '~> 2.6'
|
21
|
+
s.add_runtime_dependency 'chef', '~> 11.4.0'
|
22
22
|
|
23
|
+
s.add_runtime_dependency 'trollop', '~> 2.0'
|
24
|
+
s.add_runtime_dependency 'aws-creds', '~> 0.2.2'
|
25
|
+
s.add_runtime_dependency 'colored', '~> 1.2'
|
26
|
+
s.add_runtime_dependency 'json', '~> 1.7.7'
|
27
|
+
end
|