stemcell 0.2.9 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|