stemcell 0.0.11 → 0.2.0
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/README.md +75 -11
- data/bin/stemcell +47 -14
- data/examples/stemcellrc +10 -0
- data/lib/stemcell/templates/bootstrap.sh.erb +80 -66
- data/lib/stemcell/version.rb +1 -1
- data/lib/stemcell.rb +81 -53
- metadata +3 -2
data/README.md
CHANGED
@@ -1,28 +1,92 @@
|
|
1
|
-
# Stemcell
|
1
|
+
# Stemcell #
|
2
2
|
|
3
|
-
Stemcell launches instances
|
3
|
+
Stemcell launches instances in EC2.
|
4
|
+
These instances are created to your specification, with knobs like AMI, instance type, and region exposed.
|
5
|
+
The instances are bootstrapped with chef-solo, using a specified git repo and branch as the source of roles and recipes.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add this line to your application's Gemfile:
|
8
10
|
|
9
|
-
|
11
|
+
```bash
|
12
|
+
gem 'stemcell'
|
13
|
+
```
|
10
14
|
|
11
15
|
And then execute:
|
12
16
|
|
13
|
-
|
17
|
+
```bash
|
18
|
+
$ bundle
|
19
|
+
```
|
14
20
|
|
15
21
|
Or install it yourself as:
|
16
22
|
|
17
|
-
|
23
|
+
```bash
|
24
|
+
$ gem install stemcell
|
25
|
+
```
|
26
|
+
|
27
|
+
## Configuration
|
28
|
+
|
29
|
+
You should create an rc file for stemcell with your standard options
|
30
|
+
(and place it in the root dir as .stemcellrc?). You can see an example
|
31
|
+
in examples/stemcellrc. You can get most of the options from your
|
32
|
+
.chef/knife.rb but you will need to get the new chef deploy key so
|
33
|
+
that instances that you launch can download code.
|
18
34
|
|
19
35
|
## Usage
|
20
36
|
|
37
|
+
### Include your base config:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
$ source .stemcellrc
|
41
|
+
```
|
42
|
+
|
43
|
+
### Simple launch:
|
44
|
+
|
45
|
+
```bash
|
46
|
+
$ ./bin/stemcell --chef-role $your_chef_role --git-branch $your_chef_branch
|
47
|
+
```
|
48
|
+
|
49
|
+
This will cause instance(s) to be launched and their ip's and instance
|
50
|
+
id to be printed to the screen.
|
51
|
+
|
52
|
+
### More options:
|
53
|
+
|
54
|
+
```bash
|
55
|
+
$ ./bin/stemcell --help
|
56
|
+
```
|
57
|
+
|
58
|
+
### Watching install:
|
59
|
+
|
60
|
+
```bash
|
61
|
+
$ ssh unbutu@$IP 'tail -f /var/log/init*'
|
62
|
+
```
|
63
|
+
|
64
|
+
### Terminating:
|
65
|
+
|
66
|
+
This still needs to be completed. For now, you can kill using the
|
67
|
+
amazon cli tools or the web ui.
|
68
|
+
|
69
|
+
## Automation ##
|
70
|
+
|
71
|
+
This README presents `stemcell` as a tool for administrators to use to create instances.
|
72
|
+
However, we designed `stemcell` to be easily useful for automated systems which control server infrastructure.
|
73
|
+
These automated systems can call out to `stemcell` on the command-line or use the ruby classes directly.
|
74
|
+
|
75
|
+
To see how we manage our cloud, check out the rest of SmartStack, especially Cortex, our auto-scaling system.
|
76
|
+
|
77
|
+
## Similar Tools ##
|
78
|
+
|
79
|
+
There are a few additional tools which bootstrap EC2 instances with chef-solo.
|
80
|
+
If you're using chef-server, obvious answer is [knife-ec2](https://github.com/opscode/knife-ec2).
|
81
|
+
Unless you're working on a big team where lots of people edit cookbooks simultaneously, we strongly recommend this approach!
|
82
|
+
It's especially excellent when paired with [hosted chef](http://www.opscode.com/hosted-chef/), which makes getting off the ground with configuration management fast and easy.
|
21
83
|
|
22
|
-
|
84
|
+
If you want to use knife-ec2 with chef-solo, you could use [knife solo](http://matschaffer.github.com/knife-solo/).
|
85
|
+
Another approach which is great for interactive usage involves [using fabric to bootstrap chef](http://unfoldthat.com/2012/06/02/quick-deploy-chef-solo-fabric.html)([with gist](https://gist.github.com/va1en0k/2859812)).
|
23
86
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
87
|
+
Finally, we couldn't resist doing a bit of code archeology.
|
88
|
+
People have been using chef with EC2 for a long time!
|
89
|
+
One early article is [this one](http://web.archive.org/web/20110404114025/http://probablyinteractive.com/2009/3/29/Amazon%20EC2%20+%20Chef%20=%20Mmmmm.html), which isn't even on the web anymore.
|
90
|
+
However, it's spawned some recently-active tools like [this](https://github.com/conormullen/chef-bootstrap) and [this](https://github.com/grempe/chef-solo-bootstrap).
|
91
|
+
Similar approaches are mentioned [here](http://www.opinionatedprogrammer.com/2011/06/chef-solo-tutorial-managing-a-single-server-with-chef/), with code [herh](https://github.com/ciastek/ubuntu-chef-solo) or [here](https://github.com/riywo/ubuntu-chef-solo) (with accompanying [blog post](http://weblog.riywo.com/post/35976125760))
|
92
|
+
[This article](http://illuminatedcomputing.com/posts/2012/02/simple-chef-solo-tutorial/), also mentions many worthwhile predecessors.
|
data/bin/stemcell
CHANGED
@@ -30,22 +30,22 @@ END_OF_BANNER
|
|
30
30
|
:default => ENV['REGION'] ? ENV['REGION'] : 'us-east-1',
|
31
31
|
)
|
32
32
|
|
33
|
-
opt('
|
34
|
-
'
|
33
|
+
opt('instance_type',
|
34
|
+
'machine type to launch',
|
35
35
|
:type => String,
|
36
|
-
:default => ENV['
|
36
|
+
:default => ENV['INSTANCE_TYPE'] ? ENV['INSTANCE_TYPE'] : 'm1.small',
|
37
37
|
)
|
38
38
|
|
39
|
-
opt('
|
39
|
+
opt('image_id',
|
40
40
|
'ami to use for launch',
|
41
41
|
:type => String,
|
42
|
-
:default => ENV['
|
42
|
+
:default => ENV['IMAGE_ID'] ? ENV['IMAGE_ID'] : 'ami-d726abbe',
|
43
43
|
)
|
44
44
|
|
45
|
-
opt('
|
46
|
-
'security
|
45
|
+
opt('security_groups',
|
46
|
+
'security groups to launch instance with',
|
47
47
|
:type => String,
|
48
|
-
:default => ENV['
|
48
|
+
:default => ENV['SECURITY_GROUPS'] ? ENV['SECURITY_GROUPS'] : 'default',
|
49
49
|
)
|
50
50
|
|
51
51
|
opt('availability-zone',
|
@@ -55,7 +55,7 @@ END_OF_BANNER
|
|
55
55
|
)
|
56
56
|
|
57
57
|
opt('tags',
|
58
|
-
'
|
58
|
+
'comma-separated list of key=value pairs to apply',
|
59
59
|
:type => String,
|
60
60
|
:default => ENV['TAGS'],
|
61
61
|
)
|
@@ -78,6 +78,12 @@ END_OF_BANNER
|
|
78
78
|
:default => ENV['CHEF_ROLE'],
|
79
79
|
)
|
80
80
|
|
81
|
+
opt('chef_environment',
|
82
|
+
'chef environment in which this instance will run',
|
83
|
+
:type => String,
|
84
|
+
:default => ENV['CHEF_ENVIRONMENT'],
|
85
|
+
)
|
86
|
+
|
81
87
|
opt('git_origin',
|
82
88
|
'git origin to use',
|
83
89
|
:type => String,
|
@@ -87,7 +93,7 @@ END_OF_BANNER
|
|
87
93
|
opt('git_branch',
|
88
94
|
'git branch to run off',
|
89
95
|
:type => String,
|
90
|
-
:default => ENV['GIT_BRANCH'],
|
96
|
+
:default => ENV['GIT_BRANCH'] ? ENV['GIT_BRANCH'] : 'production',
|
91
97
|
)
|
92
98
|
|
93
99
|
opt('git_key',
|
@@ -98,8 +104,8 @@ END_OF_BANNER
|
|
98
104
|
|
99
105
|
opt('count',
|
100
106
|
'number of instances to launch',
|
101
|
-
:type =>
|
102
|
-
:default => ENV['COUNT'],
|
107
|
+
:type => Integer,
|
108
|
+
:default => ENV['COUNT'] ? ENV['COUNT'] : 1,
|
103
109
|
)
|
104
110
|
|
105
111
|
end
|
@@ -108,6 +114,8 @@ required_parameters = [
|
|
108
114
|
'aws_access_key',
|
109
115
|
'aws_secret_key',
|
110
116
|
'chef_role',
|
117
|
+
'chef_environment',
|
118
|
+
'chef_data_bag_secret',
|
111
119
|
'git_branch',
|
112
120
|
'git_key',
|
113
121
|
'git_origin',
|
@@ -120,6 +128,7 @@ by the #{arg.upcase.gsub('-','_')} environment variable" if
|
|
120
128
|
options[arg].nil? or ! options[arg]
|
121
129
|
end
|
122
130
|
|
131
|
+
|
123
132
|
# convert tags from string to ruby hash
|
124
133
|
tags = {}
|
125
134
|
if options['tags']
|
@@ -130,6 +139,30 @@ if options['tags']
|
|
130
139
|
end
|
131
140
|
options['tags'] = tags
|
132
141
|
|
133
|
-
stemcell = Stemcell::Stemcell.new(options)
|
134
|
-
stemcell.launch({'count' => options['count']})
|
135
142
|
|
143
|
+
# convert security_groups from comma seperated string to ruby array
|
144
|
+
options['security_groups'] = options['security_groups'].split(',')
|
145
|
+
|
146
|
+
|
147
|
+
# create stemcell object
|
148
|
+
stemcell = Stemcell::Stemcell.new({
|
149
|
+
'aws_access_key' => options['aws_access_key'],
|
150
|
+
'aws_secret_key' => options['aws_secret_key'],
|
151
|
+
'region' => options['region'],
|
152
|
+
})
|
153
|
+
|
154
|
+
|
155
|
+
# launch instance(s)
|
156
|
+
stemcell.launch({
|
157
|
+
'instance_type' => options['instance_type'],
|
158
|
+
'image_id' => options['image_id'],
|
159
|
+
'security_groups' => options['security_groups'],
|
160
|
+
'chef_role' => options['chef_role'],
|
161
|
+
'chef_environment' => options['chef_environment'],
|
162
|
+
'chef_data_bag_secret' => options['chef_data_bag_secret'],
|
163
|
+
'git_branch' => options['git_branch'],
|
164
|
+
'git_key' => options['git_key'],
|
165
|
+
'git_origin' => options['git_origin'],
|
166
|
+
'key_name' => options['key_name'],
|
167
|
+
'count' => options['count'],
|
168
|
+
})
|
data/examples/stemcellrc
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# these options need to be set
|
2
|
+
export AWS_ACCESS_KEY=your_aws_access_key
|
3
|
+
export AWS_SECRET_KEY=your_aws_secret_key
|
4
|
+
export KEY_NAME=your_aws_ssh_key_name
|
5
|
+
export CHEF_DATA_BAG_SECRET=/path/to/encrypted/data/bag/secret
|
6
|
+
export GIT_KEY=/path/to/chef/deploy/key
|
7
|
+
|
8
|
+
# these options should not need to be set
|
9
|
+
export SECURITY_GROUP=default
|
10
|
+
export GIT_ORIGIN=git@github.com:airbnb/chef.git
|
@@ -31,12 +31,14 @@ exec 2> /var/log/init.err
|
|
31
31
|
|
32
32
|
|
33
33
|
chef_version=11.4.0
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
chef_dir=/etc/chef
|
35
|
+
converge=/usr/local/bin/converge
|
36
|
+
role=<%= opts['chef_role'] %>
|
37
|
+
environment=<%= opts['chef_environment'] %>
|
38
|
+
origin=<%= opts['git_origin'] %>
|
39
|
+
branch=<%= opts['git_branch'] %>
|
40
|
+
git_key='<%= opts["git_key"] %>'
|
41
|
+
data_bag_secret='<%= opts["chef_data_bag_secret"] %>'
|
40
42
|
|
41
43
|
|
42
44
|
##
|
@@ -69,8 +71,6 @@ install_chef() {
|
|
69
71
|
if ! which chef-solo > /dev/null ; then
|
70
72
|
echo installing chef via omnibus...
|
71
73
|
curl -L --silent https://www.opscode.com/chef/install.sh | sudo bash -s -- -v $chef_version 1>&2
|
72
|
-
# TODO(mkr): this is a hack to get chef-solo-search working
|
73
|
-
/opt/chef/embedded/bin/gem install --no-ri --no-rdoc treetop
|
74
74
|
else
|
75
75
|
echo chef is already installed
|
76
76
|
fi
|
@@ -78,80 +78,86 @@ install_chef() {
|
|
78
78
|
|
79
79
|
|
80
80
|
update_repo() {
|
81
|
-
|
82
|
-
|
81
|
+
echo -e "$git_key" > $keyfile
|
82
|
+
chmod 0400 $keyfile
|
83
|
+
|
84
|
+
echo "ssh -i $keyfile -o StrictHostKeyChecking=no \$1 \$2" > $git_wrapper
|
85
|
+
chmod 0500 $git_wrapper
|
86
|
+
|
83
87
|
mkdir -p $(dirname $repo_dir)
|
84
|
-
if [ -d $repo_dir ]; then
|
85
|
-
echo
|
86
|
-
(cd $repo_dir && GIT_SSH=$git_wrapper git fetch && git reset --hard origin/$branch && git clean -fdx)
|
87
|
-
else
|
88
|
-
echo -e "$git_key" > $keyfile
|
89
|
-
chmod 0400 $keyfile
|
90
|
-
echo "ssh -i $keyfile -o StrictHostKeyChecking=no \$1 \$2" > $git_wrapper
|
91
|
-
chmod 0500 $git_wrapper
|
92
|
-
echo downloading cookbook repo...
|
88
|
+
if [ ! -d $repo_dir ]; then
|
89
|
+
echo "downloading cookbook repo..."
|
93
90
|
GIT_SSH=$git_wrapper git clone --branch $branch --depth 1 $origin $repo_dir
|
91
|
+
else
|
92
|
+
echo "cookbook repo already exists"
|
94
93
|
fi
|
95
|
-
|
94
|
+
|
95
|
+
echo "done updating code"
|
96
96
|
}
|
97
97
|
|
98
98
|
|
99
99
|
configure_chef() {
|
100
|
-
echo
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
"
|
111
|
-
|
112
|
-
}
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
cat<<EOF>/etc/chef/solo.json
|
117
|
-
{
|
118
|
-
"run_list": "role[$role]",
|
119
|
-
"branch": "$branch",
|
120
|
-
"role": "$role"
|
121
|
-
}
|
122
|
-
EOF
|
123
|
-
fi
|
100
|
+
echo "saving current branch ($branch), role ($role), and environment ($environment)"
|
101
|
+
echo "$branch" > $branchfile
|
102
|
+
chmod 644 $branchfile
|
103
|
+
|
104
|
+
echo "$role" > $rolefile
|
105
|
+
chmod 644 $rolefile
|
106
|
+
|
107
|
+
echo "$environment" > $envfile
|
108
|
+
chmod 644 $envfile
|
109
|
+
|
110
|
+
echo "configuring chef solo..."
|
111
|
+
cat<<EOF > ${chef_dir}/solo.rb
|
112
|
+
repo_dir = "${repo_dir}"
|
113
|
+
cookbook_path "#{repo_dir}/cookbooks"
|
114
|
+
role_path "#{repo_dir}/roles"
|
115
|
+
data_bag_path "#{repo_dir}/data_bags"
|
124
116
|
|
125
|
-
cat<<EOF>/etc/chef/solo.rb
|
126
117
|
log_level :info
|
127
118
|
log_location STDOUT
|
128
|
-
cookbook_path "$repo_dir/cookbooks"
|
129
|
-
role_path "$repo_dir/roles"
|
130
|
-
data_bag_path "$repo_dir/data_bags"
|
131
|
-
json_attribs '/etc/chef/solo.json'
|
132
119
|
EOF
|
133
|
-
|
134
|
-
echo
|
120
|
+
|
121
|
+
echo -e "$data_bag_secret" > ${chef_dir}/encrypted_data_bag_secret
|
122
|
+
echo "chef configured"
|
135
123
|
}
|
136
124
|
|
137
125
|
|
138
126
|
configure_converger() {
|
139
|
-
cat<<EOF
|
140
|
-
#!/bin/bash -
|
127
|
+
cat<<EOF > $converge
|
128
|
+
#!/bin/bash -eu
|
129
|
+
|
130
|
+
repo="${repo_dir}"
|
131
|
+
branch=\`cat ${branchfile}\`
|
132
|
+
role=\`cat ${rolefile}\`
|
133
|
+
env=\`cat ${envfile}\`
|
134
|
+
|
135
|
+
cd \${repo}
|
136
|
+
|
137
|
+
runlist="role[\${role}]"
|
138
|
+
if [ -f "roles/\${env}.rb" ]
|
139
|
+
then
|
140
|
+
runlist+=",role[\${env}]"
|
141
|
+
fi
|
141
142
|
|
142
|
-
echo doing a hard reset to origin
|
143
|
-
|
144
|
-
|
145
|
-
git reset --hard origin/$branch
|
143
|
+
echo doing a hard reset to origin/\${branch}
|
144
|
+
GIT_SSH=${git_wrapper} git fetch
|
145
|
+
git reset --hard origin/\${branch}
|
146
146
|
git clean -fdx
|
147
|
-
|
147
|
+
|
148
|
+
json="{\"role\": \"\${role}\", \"env\": \"\${env}\", \"branch\": \"\${branch}\"}"
|
149
|
+
json_file=\`tempfile\`
|
150
|
+
echo \$json > \$json_file
|
151
|
+
trap "{ rm -f '\$json_file' ; }" EXIT
|
152
|
+
|
153
|
+
echo "running chef-solo with runlist \$runlist and json \$json (in file \$json_file)"
|
154
|
+
chef-solo -o "\${runlist}" -j \${json_file} "\$@"
|
148
155
|
EOF
|
149
|
-
chmod
|
156
|
+
chmod 544 $converge
|
150
157
|
}
|
151
158
|
|
152
|
-
|
153
159
|
configure_chef_daemon() {
|
154
|
-
cat<<EOF
|
160
|
+
cat<<EOF > /etc/init/chef-solo.conf
|
155
161
|
description "chef-solo"
|
156
162
|
author "Martin Rhoads"
|
157
163
|
start on networking
|
@@ -164,12 +170,11 @@ EOF
|
|
164
170
|
|
165
171
|
|
166
172
|
run_chef() {
|
167
|
-
echo
|
168
|
-
|
169
|
-
echo
|
173
|
+
echo "doing initial chef run..."
|
174
|
+
$converge 1>&2
|
175
|
+
echo "initial chef run complete"
|
170
176
|
}
|
171
177
|
|
172
|
-
|
173
178
|
start_chef_daemon() {
|
174
179
|
start chef-solo
|
175
180
|
}
|
@@ -179,8 +184,16 @@ start_chef_daemon() {
|
|
179
184
|
## main
|
180
185
|
##
|
181
186
|
|
187
|
+
#some derived vars
|
188
|
+
repo_dir=${chef_dir}/src
|
189
|
+
keyfile=${chef_dir}/git_key
|
190
|
+
envfile=${chef_dir}/environment
|
191
|
+
rolefile=${chef_dir}/role
|
192
|
+
branchfile=${chef_dir}/branch
|
193
|
+
git_wrapper=${chef_dir}/git_wrapper
|
182
194
|
|
183
|
-
echo starting chef bootstrapping...
|
195
|
+
echo "starting chef bootstrapping..."
|
196
|
+
mkdir -p ${chef_dir}
|
184
197
|
update
|
185
198
|
install curl
|
186
199
|
install git
|
@@ -190,6 +203,7 @@ update_repo
|
|
190
203
|
configure_chef
|
191
204
|
configure_converger
|
192
205
|
run_chef
|
206
|
+
|
193
207
|
configure_chef_daemon
|
194
208
|
# start_chef_daemon
|
195
209
|
|
data/lib/stemcell/version.rb
CHANGED
data/lib/stemcell.rb
CHANGED
@@ -8,65 +8,81 @@ module Stemcell
|
|
8
8
|
class Stemcell
|
9
9
|
def initialize(opts={})
|
10
10
|
@log = Logger.new(STDOUT)
|
11
|
+
@log.level = Logger::INFO unless ENV['DEBUG']
|
12
|
+
@log.debug "creating new stemcell object"
|
11
13
|
@log.debug "opts are #{opts.inspect}"
|
12
14
|
['aws_access_key',
|
13
15
|
'aws_secret_key',
|
14
16
|
'region',
|
15
|
-
'machine_type',
|
16
|
-
'image',
|
17
|
-
'security_group',
|
18
|
-
|
19
|
-
'chef_role',
|
20
|
-
'git_branch',
|
21
|
-
'git_key',
|
22
|
-
'git_origin',
|
23
|
-
'key_name',
|
24
17
|
].each do |req|
|
25
18
|
raise ArgumentError, "missing required param #{req}" unless opts[req]
|
26
19
|
instance_variable_set("@#{req}",opts[req])
|
27
20
|
end
|
28
21
|
|
29
|
-
@zone = opts.include?('availability_zone') ? opts['availability_zone'] : nil
|
30
22
|
@ec2_url = "ec2.#{@region}.amazonaws.com"
|
31
23
|
@timeout = 120
|
32
24
|
@start_time = Time.new
|
33
25
|
|
34
|
-
@tags = {
|
35
|
-
'Name' => "#{@chef_role}-#{@git_branch}",
|
36
|
-
'Group' => "#{@chef_role}-#{@git_branch}",
|
37
|
-
'created_by' => ENV['USER'],
|
38
|
-
'stemcell' => VERSION,
|
39
|
-
}
|
40
|
-
@tags.merge!(opts['tags']) if opts['tags']
|
41
|
-
|
42
|
-
begin
|
43
|
-
@git_key_contents = File.read(@git_key)
|
44
|
-
rescue Object => e
|
45
|
-
@git_key_contents = @git_key # assume content is passed in
|
46
|
-
end
|
47
|
-
|
48
|
-
if opts.include?('chef_data_bag_secret')
|
49
|
-
begin
|
50
|
-
@chef_data_bag_secret = File.read(opts['chef_data_bag_secret'])
|
51
|
-
rescue Object => e
|
52
|
-
@chef_data_bag_secret = opts['chef_data_bag_secret'] # assume secret is passed in
|
53
|
-
end
|
54
|
-
else
|
55
|
-
@chef_data_bag_secret = ''
|
56
|
-
end
|
57
|
-
|
58
26
|
AWS.config({:access_key_id => @aws_access_key, :secret_access_key => @aws_secret_key})
|
59
27
|
@ec2 = AWS::EC2.new(:ec2_endpoint => @ec2_url)
|
60
28
|
@ec2_region = @ec2.regions[@region]
|
61
|
-
@user_data = render_template
|
62
29
|
end
|
63
30
|
|
31
|
+
|
64
32
|
def launch(opts={})
|
65
|
-
|
66
|
-
|
33
|
+
verify_required_options(opts,[
|
34
|
+
'image_id',
|
35
|
+
'security_groups',
|
36
|
+
'key_name',
|
37
|
+
'count',
|
38
|
+
'chef_role',
|
39
|
+
'chef_environment',
|
40
|
+
'chef_data_bag_secret',
|
41
|
+
'git_branch',
|
42
|
+
'git_key',
|
43
|
+
'git_origin',
|
44
|
+
'instance_type',
|
45
|
+
])
|
46
|
+
|
47
|
+
# attempt to accept keys as file paths
|
48
|
+
opts['git_key'] = try_file(opts['git_key'])
|
49
|
+
opts['chef_data_bag_secret'] = try_file(opts['chef_data_bag_secret'])
|
50
|
+
|
51
|
+
# generate tags and merge in any that were specefied as in inputs
|
52
|
+
tags = {
|
53
|
+
'Name' => "#{opts['chef_role']}-#{opts['chef_environment']}",
|
54
|
+
'Group' => "#{opts['chef_role']}-#{opts['chef_environment']}",
|
55
|
+
'created_by' => ENV['USER'],
|
56
|
+
'stemcell' => VERSION,
|
57
|
+
}
|
58
|
+
tags.merge!(opts['tags']) if opts['tags']
|
59
|
+
|
60
|
+
# generate user data script to boot strap instance based on the
|
61
|
+
# opts that we were passed.
|
62
|
+
user_data = render_template(opts)
|
63
|
+
|
64
|
+
launch_options = {
|
65
|
+
:image_id => opts['image_id'],
|
66
|
+
:security_groups => opts['security_groups'],
|
67
|
+
:user_data => opts['user_data'],
|
68
|
+
:instance_type => opts['instance_type'],
|
69
|
+
:key_name => opts['key_name'],
|
70
|
+
:count => opts['count'],
|
71
|
+
:user_data => user_data,
|
72
|
+
}
|
73
|
+
launch_options.merge({:availability_zone => opts['availability_zone']}) if opts['availability_zone']
|
74
|
+
|
75
|
+
# launch instances
|
76
|
+
instances = do_launch(launch_options)
|
77
|
+
|
78
|
+
# wait for aws to report instance stats
|
67
79
|
wait(instances)
|
68
|
-
|
80
|
+
|
81
|
+
# set tags on all instances launched
|
82
|
+
set_tags(instances, tags)
|
83
|
+
|
69
84
|
print_run_info(instances)
|
85
|
+
@log.info "launched instances successfully"
|
70
86
|
return instances
|
71
87
|
end
|
72
88
|
|
@@ -83,7 +99,7 @@ module Stemcell
|
|
83
99
|
end
|
84
100
|
|
85
101
|
def wait(instances)
|
86
|
-
@log.info "Waiting for #{instances.count} instances (#{instances.inspect}):"
|
102
|
+
@log.info "Waiting up to #{@timeout} seconds for #{instances.count} instances (#{instances.inspect}):"
|
87
103
|
|
88
104
|
while true
|
89
105
|
sleep 5
|
@@ -100,18 +116,18 @@ module Stemcell
|
|
100
116
|
@log.info "all instances in running state"
|
101
117
|
end
|
102
118
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
}
|
111
|
-
options[:availability_zone] = @zone if @zone
|
112
|
-
options[:count] = opts['count'] if opts.include?('count')
|
119
|
+
def verify_required_options(params,required_options)
|
120
|
+
@log.debug "params is #{params}"
|
121
|
+
@log.debug "required_options are #{required_options}"
|
122
|
+
required_options.each do |required|
|
123
|
+
raise ArgumentError, "you need to provide option #{required}" unless params[required]
|
124
|
+
end
|
125
|
+
end
|
113
126
|
|
114
|
-
|
127
|
+
def do_launch(opts={})
|
128
|
+
@log.debug "about to launch instance(s) with options #{opts}"
|
129
|
+
@log.info "launching instances"
|
130
|
+
instances = @ec2_region.instances.create(opts)
|
115
131
|
instances = [instances] unless instances.class == Array
|
116
132
|
instances.each do |instance|
|
117
133
|
@log.info "launched instance #{instance.instance_id}"
|
@@ -119,19 +135,21 @@ module Stemcell
|
|
119
135
|
return instances
|
120
136
|
end
|
121
137
|
|
122
|
-
def set_tags(instances=[])
|
138
|
+
def set_tags(instances=[],tags)
|
139
|
+
@log.info "setting tags on instance(s)"
|
123
140
|
instances.each do |instance|
|
124
|
-
instance.tags.set(
|
141
|
+
instance.tags.set(tags)
|
125
142
|
end
|
126
143
|
end
|
127
144
|
|
128
|
-
def render_template
|
145
|
+
def render_template(opts={})
|
129
146
|
this_file = File.expand_path __FILE__
|
130
147
|
base_dir = File.dirname this_file
|
131
148
|
template_file_path = File.join(base_dir,'stemcell','templates','bootstrap.sh.erb')
|
132
149
|
template_file = File.read(template_file_path)
|
133
150
|
erb_template = ERB.new(template_file)
|
134
151
|
generated_template = erb_template.result(binding)
|
152
|
+
@log.debug "genereated template is #{generated_template}"
|
135
153
|
return generated_template
|
136
154
|
end
|
137
155
|
|
@@ -142,5 +160,15 @@ module Stemcell
|
|
142
160
|
instance.delete
|
143
161
|
end
|
144
162
|
end
|
163
|
+
|
164
|
+
# attempt to accept keys as file paths
|
165
|
+
def try_file(opt="")
|
166
|
+
begin
|
167
|
+
return File.read(opt)
|
168
|
+
rescue Object => e
|
169
|
+
return opt
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
145
173
|
end
|
146
174
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stemcell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
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: 2013-02-
|
12
|
+
date: 2013-02-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: trollop
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- README.md
|
58
58
|
- Rakefile
|
59
59
|
- bin/stemcell
|
60
|
+
- examples/stemcellrc
|
60
61
|
- lib/stemcell.rb
|
61
62
|
- lib/stemcell/templates/bootstrap.sh.erb
|
62
63
|
- lib/stemcell/version.rb
|