awsdsl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +167 -0
- data/README.md +212 -0
- data/Rakefile +10 -0
- data/awsdsl.gemspec +27 -0
- data/bin/awsdsl +4 -0
- data/lib/awsdsl/ami_builder.rb +98 -0
- data/lib/awsdsl/base_ami.rb +17 -0
- data/lib/awsdsl/cfn_builder.rb +360 -0
- data/lib/awsdsl/cfn_helpers.rb +131 -0
- data/lib/awsdsl/command_line.rb +48 -0
- data/lib/awsdsl/dsl/elasticache.rb +7 -0
- data/lib/awsdsl/dsl/load_balancer.rb +7 -0
- data/lib/awsdsl/dsl/role.rb +26 -0
- data/lib/awsdsl/dsl/role_profile.rb +31 -0
- data/lib/awsdsl/dsl/stack.rb +16 -0
- data/lib/awsdsl/dsl/subnet.rb +7 -0
- data/lib/awsdsl/dsl/vpc.rb +7 -0
- data/lib/awsdsl/dsl.rb +79 -0
- data/lib/awsdsl/ext/proc.rb +14 -0
- data/lib/awsdsl/ext/symbol.rb +13 -0
- data/lib/awsdsl/fn.rb +7 -0
- data/lib/awsdsl/loader.rb +24 -0
- data/lib/awsdsl/runner.rb +28 -0
- data/lib/awsdsl/version.rb +3 -0
- data/lib/awsdsl.rb +6 -0
- data/spec/awsdsl/cfn_builder_spec.rb +28 -0
- data/spec/awsdsl/loader_spec.rb +12 -0
- data/spec/fixtures/test_stack.rb +100 -0
- data/spec/spec_helper.rb +30 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ae154e947361276d4545c34fffffbc0eb07caf3e
|
4
|
+
data.tar.gz: b0a01325a610b74f8d8a9b38bf314a79e9f85324
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5537d5bfd37391d089dec9988d5af873d04c4b0a9e5a4ee748684a061a51d4b5e6535b06523d686b0f24a595b5efc85e235b96cded6251e8c1738119a4a0f929
|
7
|
+
data.tar.gz: eedb01fb25690bd8c417f9dadfc3cd6f7f65ea1088ace302a50bc8538e88d6920724b929151d316a6bd113f73a0b608b8606b877aa9bfe8619f479024873d851
|
data/.gitignore
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.rvmrc
|
6
|
+
.yardoc
|
7
|
+
/Gemfile.lock
|
8
|
+
_yardoc
|
9
|
+
/coverage
|
10
|
+
/doc/
|
11
|
+
/pkg
|
12
|
+
/spec/reports
|
13
|
+
/Berksfile*
|
14
|
+
tmp
|
15
|
+
*~
|
16
|
+
*.tar*
|
17
|
+
\#*
|
18
|
+
.DS_Store
|
19
|
+
/spec/knife.rb
|
20
|
+
/spec/*.pem
|
21
|
+
/features/config.yml
|
22
|
+
*.sw[op]
|
23
|
+
\.\#*
|
24
|
+
rerun.txt
|
25
|
+
.rspec
|
26
|
+
.kitchen
|
27
|
+
vendor/ruby
|
28
|
+
.ruby-version
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/josephglanville/gersberms
|
3
|
+
revision: b979728441156433c8a57d845a058c1b51e5e755
|
4
|
+
specs:
|
5
|
+
gersberms (0.0.1)
|
6
|
+
aws-sdk (~> 1.0)
|
7
|
+
berkshelf (~> 3.2.1)
|
8
|
+
net-scp (~> 1.2.1)
|
9
|
+
net-ssh (~> 2.9.1)
|
10
|
+
rake
|
11
|
+
thor (~> 0.19)
|
12
|
+
|
13
|
+
PATH
|
14
|
+
remote: .
|
15
|
+
specs:
|
16
|
+
awsdsl (0.0.1)
|
17
|
+
activesupport (~> 4)
|
18
|
+
aws-sdk (~> 1.0)
|
19
|
+
cfndsl
|
20
|
+
clamp (~> 0.6)
|
21
|
+
gersberms
|
22
|
+
netaddr
|
23
|
+
|
24
|
+
GEM
|
25
|
+
remote: https://rubygems.org/
|
26
|
+
specs:
|
27
|
+
activesupport (4.2.1)
|
28
|
+
i18n (~> 0.7)
|
29
|
+
json (~> 1.7, >= 1.7.7)
|
30
|
+
minitest (~> 5.1)
|
31
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
32
|
+
tzinfo (~> 1.1)
|
33
|
+
addressable (2.3.7)
|
34
|
+
aws-sdk (1.63.0)
|
35
|
+
aws-sdk-v1 (= 1.63.0)
|
36
|
+
aws-sdk-v1 (1.63.0)
|
37
|
+
json (~> 1.4)
|
38
|
+
nokogiri (>= 1.4.4)
|
39
|
+
berkshelf (3.2.3)
|
40
|
+
addressable (~> 2.3.4)
|
41
|
+
berkshelf-api-client (~> 1.2)
|
42
|
+
buff-config (~> 1.0)
|
43
|
+
buff-extensions (~> 1.0)
|
44
|
+
buff-shell_out (~> 0.1)
|
45
|
+
celluloid (~> 0.16.0)
|
46
|
+
celluloid-io (~> 0.16.1)
|
47
|
+
cleanroom (~> 1.0)
|
48
|
+
faraday (~> 0.9.0)
|
49
|
+
minitar (~> 0.5.4)
|
50
|
+
octokit (~> 3.0)
|
51
|
+
retryable (~> 2.0)
|
52
|
+
ridley (~> 4.0)
|
53
|
+
solve (~> 1.1)
|
54
|
+
thor (~> 0.19)
|
55
|
+
berkshelf-api-client (1.2.1)
|
56
|
+
faraday (~> 0.9.0)
|
57
|
+
buff-config (1.0.1)
|
58
|
+
buff-extensions (~> 1.0)
|
59
|
+
varia_model (~> 0.4)
|
60
|
+
buff-extensions (1.0.0)
|
61
|
+
buff-ignore (1.1.1)
|
62
|
+
buff-ruby_engine (0.1.0)
|
63
|
+
buff-shell_out (0.2.0)
|
64
|
+
buff-ruby_engine (~> 0.1.0)
|
65
|
+
celluloid (0.16.0)
|
66
|
+
timers (~> 4.0.0)
|
67
|
+
celluloid-io (0.16.2)
|
68
|
+
celluloid (>= 0.16.0)
|
69
|
+
nio4r (>= 1.1.0)
|
70
|
+
cfndsl (0.1.14)
|
71
|
+
clamp (0.6.4)
|
72
|
+
cleanroom (1.0.0)
|
73
|
+
coderay (1.1.0)
|
74
|
+
dep-selector-libgecode (1.0.2)
|
75
|
+
dep_selector (1.0.3)
|
76
|
+
dep-selector-libgecode (~> 1.0)
|
77
|
+
ffi (~> 1.9)
|
78
|
+
diff-lcs (1.2.5)
|
79
|
+
erubis (2.7.0)
|
80
|
+
faraday (0.9.1)
|
81
|
+
multipart-post (>= 1.2, < 3)
|
82
|
+
ffi (1.9.8)
|
83
|
+
hashie (2.1.2)
|
84
|
+
hitimes (1.2.2)
|
85
|
+
i18n (0.7.0)
|
86
|
+
json (1.8.2)
|
87
|
+
method_source (0.8.2)
|
88
|
+
mini_portile (0.6.2)
|
89
|
+
minitar (0.5.4)
|
90
|
+
minitest (5.6.0)
|
91
|
+
mixlib-authentication (1.3.0)
|
92
|
+
mixlib-log
|
93
|
+
mixlib-log (1.6.0)
|
94
|
+
multipart-post (2.0.0)
|
95
|
+
net-http-persistent (2.9.4)
|
96
|
+
net-scp (1.2.1)
|
97
|
+
net-ssh (>= 2.6.5)
|
98
|
+
net-ssh (2.9.2)
|
99
|
+
netaddr (1.5.0)
|
100
|
+
nio4r (1.1.0)
|
101
|
+
nokogiri (1.6.6.2)
|
102
|
+
mini_portile (~> 0.6.0)
|
103
|
+
octokit (3.8.0)
|
104
|
+
sawyer (~> 0.6.0, >= 0.5.3)
|
105
|
+
pry (0.10.1)
|
106
|
+
coderay (~> 1.1.0)
|
107
|
+
method_source (~> 0.8.1)
|
108
|
+
slop (~> 3.4)
|
109
|
+
rake (10.4.2)
|
110
|
+
retryable (2.0.1)
|
111
|
+
ridley (4.1.2)
|
112
|
+
addressable
|
113
|
+
buff-config (~> 1.0)
|
114
|
+
buff-extensions (~> 1.0)
|
115
|
+
buff-ignore (~> 1.1)
|
116
|
+
buff-shell_out (~> 0.1)
|
117
|
+
celluloid (~> 0.16.0)
|
118
|
+
celluloid-io (~> 0.16.1)
|
119
|
+
erubis
|
120
|
+
faraday (~> 0.9.0)
|
121
|
+
hashie (>= 2.0.2, < 3.0.0)
|
122
|
+
json (>= 1.7.7)
|
123
|
+
mixlib-authentication (>= 1.3.0)
|
124
|
+
net-http-persistent (>= 2.8)
|
125
|
+
retryable (>= 2.0.0)
|
126
|
+
semverse (~> 1.1)
|
127
|
+
varia_model (~> 0.4)
|
128
|
+
rspec (3.2.0)
|
129
|
+
rspec-core (~> 3.2.0)
|
130
|
+
rspec-expectations (~> 3.2.0)
|
131
|
+
rspec-mocks (~> 3.2.0)
|
132
|
+
rspec-core (3.2.2)
|
133
|
+
rspec-support (~> 3.2.0)
|
134
|
+
rspec-expectations (3.2.0)
|
135
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
136
|
+
rspec-support (~> 3.2.0)
|
137
|
+
rspec-mocks (3.2.1)
|
138
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
139
|
+
rspec-support (~> 3.2.0)
|
140
|
+
rspec-support (3.2.2)
|
141
|
+
sawyer (0.6.0)
|
142
|
+
addressable (~> 2.3.5)
|
143
|
+
faraday (~> 0.8, < 0.10)
|
144
|
+
semverse (1.2.1)
|
145
|
+
slop (3.6.0)
|
146
|
+
solve (1.2.1)
|
147
|
+
dep_selector (~> 1.0)
|
148
|
+
semverse (~> 1.1)
|
149
|
+
thor (0.19.1)
|
150
|
+
thread_safe (0.3.5)
|
151
|
+
timers (4.0.1)
|
152
|
+
hitimes
|
153
|
+
tzinfo (1.2.2)
|
154
|
+
thread_safe (~> 0.1)
|
155
|
+
varia_model (0.4.0)
|
156
|
+
buff-extensions (~> 1.0)
|
157
|
+
hashie (>= 2.0.2, < 3.0.0)
|
158
|
+
|
159
|
+
PLATFORMS
|
160
|
+
ruby
|
161
|
+
|
162
|
+
DEPENDENCIES
|
163
|
+
awsdsl!
|
164
|
+
gersberms!
|
165
|
+
pry
|
166
|
+
rake
|
167
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
AWS DSL
|
2
|
+
======
|
3
|
+
|
4
|
+
This project is an opinionated take on running applications on AWS.
|
5
|
+
It leverages [CloudFormation](http://aws.amazon.com/cloudformation/) and [Gersberms](https://github.com/josephglanville/gersberms) to build your application into an Amazon Machine Image and deploy it on bare EC2 servers.
|
6
|
+
|
7
|
+
In a nutshell you declare your infrastructure in a Stackfile, which is a Ruby DSL that describes CloudFormation resources and AMI build instructions.
|
8
|
+
|
9
|
+
It is in some ways analagous to OpsWorks however it's less intrusive and focuses on building immutable AMIs and replacing machines during updates rather than updating already running machines by re-running Chef.
|
10
|
+
|
11
|
+
That said it you are already using OpsWorks it would be easy to get started using AWS DSL.
|
12
|
+
|
13
|
+
Install
|
14
|
+
-------
|
15
|
+
|
16
|
+
For now I recommending using Bundler to install and manage AWS DSL.
|
17
|
+
Simply add this to your Gemfile
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'awsdsl', git: 'https://github.com/josephglanville/awsdsl'
|
21
|
+
```
|
22
|
+
|
23
|
+
I will publish the gem to RubyGems when I feel it's stabilized.
|
24
|
+
|
25
|
+
Getting Started
|
26
|
+
---------------
|
27
|
+
|
28
|
+
To get started with AWS DSL you need a few things.
|
29
|
+
|
30
|
+
* Your application deployable using Chef.
|
31
|
+
* Your Chef cookbooks managed with Berkshelf.
|
32
|
+
* Basic understanding of EC2, ELB and any other resources you might need.
|
33
|
+
|
34
|
+
Because AWS DSL abstracts away the vast majority of CloudFormation you shouldn't need an indepth understanding of CloudFormation but it doesn't hurt.
|
35
|
+
|
36
|
+
Simple Stackfile
|
37
|
+
----------------
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
stack 'static' do
|
41
|
+
description 'static files example'
|
42
|
+
|
43
|
+
vpc 'static' do
|
44
|
+
region 'ap-southeast-2'
|
45
|
+
subnet 'public' do
|
46
|
+
az 'a', 'b'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
role 'nginx' do
|
51
|
+
vpc 'static'
|
52
|
+
subnet 'public'
|
53
|
+
load_balancer 'static' do
|
54
|
+
listener port: 80
|
55
|
+
health_check target: 'HTTP:80/'
|
56
|
+
end
|
57
|
+
update_policy min_inservice: 0
|
58
|
+
instance_type 't2.micro'
|
59
|
+
key_pair 'joseph@reinteractive.net'
|
60
|
+
chef_provisioner runlist: 'nginx'
|
61
|
+
vars nginx: {
|
62
|
+
init_style: 'upstart'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
This simple stack declares a simple role along with provisioning a full VPC.
|
69
|
+
Said VPC includes 2 subnets across 2 AZs and an Internet Gateway to allow public addressing to work.
|
70
|
+
It runs the nginx::default recipe when preparing the AMI, providing the contents of vars as the node attributes.
|
71
|
+
|
72
|
+
Complex Stackfile
|
73
|
+
-----------------
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
stack 'logs' do
|
77
|
+
description 'logstash cluster'
|
78
|
+
ssl_cert_arn = 'arn:aws:iam::account_id::certificate'
|
79
|
+
zone_arn = 'arn:aws:route53:::hostedzone/zone_id'
|
80
|
+
snapshot_bucket_arn = 'arn:aws:s3:::snapshot_bucket'
|
81
|
+
cloudtrail_queue_arn = 'arn:aws:sqs:ap-southeast-2:account_id:queue'
|
82
|
+
|
83
|
+
role_profile 'es_comms' do
|
84
|
+
security_group 'sg-id'
|
85
|
+
subnets 'subnet-id'
|
86
|
+
vpc 'vpc-id'
|
87
|
+
end
|
88
|
+
|
89
|
+
role_profile 'es_bucket' do
|
90
|
+
policy_statement effect: 'Allow', action: 's3:ListBucket', resource: snapshot_bucket_arn
|
91
|
+
policy_statement effect: 'Allow',
|
92
|
+
action: %w(s3:GetObject s3:PutObjecs3:DeleteObject s3:DeleteObject),
|
93
|
+
resource: "#{snapshot_bucket_arn}/*"
|
94
|
+
end
|
95
|
+
|
96
|
+
role_profile 'ec2_discovery' do
|
97
|
+
policy_statement effect: 'Allow', action: 'ec2:DescribeInstances', resource: '*'
|
98
|
+
end
|
99
|
+
|
100
|
+
role 'logstash' do
|
101
|
+
include_profile 'ec2_discovery', 'es_comms'
|
102
|
+
load_balancer 'logstash' do
|
103
|
+
listener port: 80
|
104
|
+
listener port: 443, proto: 'HTTPS', cert: ssl_cert_arn
|
105
|
+
listener port: 9000, proto: 'TCP'
|
106
|
+
dns_record name: 'logstash.zone.com', zone: 'zone-id'
|
107
|
+
health_check target: 'HTTP:80/health'
|
108
|
+
end
|
109
|
+
policy_statement effect: 'Allow', action: 'sqs:*', resource: cloudtrail_queue_arn
|
110
|
+
min_size 2
|
111
|
+
max_size 4
|
112
|
+
tgt_size 2
|
113
|
+
update_policy pause_time: '5M'
|
114
|
+
instance_type 't2.micro'
|
115
|
+
chef_provisioner runlist: 'logstash'
|
116
|
+
end
|
117
|
+
|
118
|
+
role 'elasticsearch' do
|
119
|
+
include_profile 'ec2_discovery', 'es_bucket', 'es_comms'
|
120
|
+
load_balancer 'elasticsearch' do
|
121
|
+
listener port: 9200
|
122
|
+
health_check target: 'HTTP:9200/'
|
123
|
+
dns_record name: 'elasticsearch.zone.com', zone: 'zone-id'
|
124
|
+
security_group 'sg-id'
|
125
|
+
internal true
|
126
|
+
end
|
127
|
+
min_size 3
|
128
|
+
max_size 5
|
129
|
+
tgt_size 5
|
130
|
+
update_policy pause_time: '10M', min_inservice: 3
|
131
|
+
instance_type 't2.micro'
|
132
|
+
block_device name: '/dev/sda1', size: 20
|
133
|
+
chef_provisioner runlist: 'elasticsearch'
|
134
|
+
allow role: 'logstash', ports: 9200
|
135
|
+
allow role: 'utility', ports: 9200
|
136
|
+
allow role: 'elasticsearch', ports: 9200
|
137
|
+
end
|
138
|
+
|
139
|
+
role 'utility' do
|
140
|
+
include_profile 'es_bucket', 'es_comms'
|
141
|
+
policy_statement effect: 'Allow',
|
142
|
+
action: [
|
143
|
+
'route53:ChangeResourceRecordSets',
|
144
|
+
'route53:GetHostedZone',
|
145
|
+
'route53:ListResourceRecordSets'
|
146
|
+
],
|
147
|
+
resource: zone_arn
|
148
|
+
policy_statement effect: 'Allow', action: 'route53:ListHostedZones', resource: '*'
|
149
|
+
min_size 0
|
150
|
+
max_size 1
|
151
|
+
tgt_size 1
|
152
|
+
update_policy min_inservice: 0
|
153
|
+
instance_type 't2.micro'
|
154
|
+
chef_provisioner runlist: 'utility'
|
155
|
+
end
|
156
|
+
|
157
|
+
elasticache 'redis' do
|
158
|
+
vpc 'vpc-id'
|
159
|
+
subnet 'subnet-id'
|
160
|
+
engine 'redis'
|
161
|
+
node_type 't2.micro'
|
162
|
+
allow role: 'logstash'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
This much more complex example deploys a full Elasticsearch Logstash Kibana cluster. Along with standing up an Elasticache cluster running Redis.
|
168
|
+
It uses advanced features like Role Profiles (which are effectively Role mixins) and the powerful "allow" syntax which makes configuring security groups a breeze.
|
169
|
+
|
170
|
+
As you can see AWS DSL doesn't get in your way if you need to declare additional policy documents or do advanced things like setup SSL listeners or configure DNS records for your ELBs.
|
171
|
+
|
172
|
+
Command Line
|
173
|
+
------------
|
174
|
+
|
175
|
+
Once you have your Stackfile you will need to build the AMIs.
|
176
|
+
|
177
|
+
```
|
178
|
+
bundle exec awsdsl build
|
179
|
+
```
|
180
|
+
|
181
|
+
Then create your stack
|
182
|
+
|
183
|
+
```
|
184
|
+
bundle exec awsdsl create
|
185
|
+
```
|
186
|
+
|
187
|
+
When you build new AMIs or update settings in your Stackfile you can push updates like so
|
188
|
+
|
189
|
+
```
|
190
|
+
bundle exec awsdsl update
|
191
|
+
```
|
192
|
+
|
193
|
+
Philosophy
|
194
|
+
----------
|
195
|
+
|
196
|
+
AWS DSL was written to enable the versioning of infrastructure alongside code.
|
197
|
+
All infrastructure concerns are declared in the AWS DSL Stackfile including how to configure the application runtime environment which will be built into an AMI.
|
198
|
+
This is advantagous as updates to code that require infrastructure support can be done in unison.
|
199
|
+
|
200
|
+
AWS DSL thinks about your application in terms of Roles. A role is a singular purposed entity in your application and represents a build target and a scaling primitive.
|
201
|
+
You specify how to package your application into an AMI, tell it how many instances you want to run and any other considerations like security groups and away it goes.
|
202
|
+
|
203
|
+
To DRY up this process AWS DSL has Role Profiles. Role Profiles are analagous to mixins (or multiple inheritance if you must), anything you can put in a Role can be put in Role Profile and then you can mixin multiple Role Profiles into a Role with the include_profile keyword.
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
TODO
|
208
|
+
----
|
209
|
+
|
210
|
+
* cloud-init/cfn-init integration and environment variable system
|
211
|
+
* build AMIs seperately
|
212
|
+
* cfndsl section to allow arbitrary resource creation
|
data/Rakefile
ADDED
data/awsdsl.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'awsdsl/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'awsdsl'
|
8
|
+
spec.version = AWSDSL::VERSION
|
9
|
+
spec.authors = ['Joseph Glanville']
|
10
|
+
spec.email = ['jpg@jpg.id.au']
|
11
|
+
spec.summary = 'A simple DSL for deploying and running apps on AWS'
|
12
|
+
spec.description = 'A simple DSL for deploying and running apps on AWS'
|
13
|
+
spec.homepage = 'https://github.com/josephglanville/awsdsl'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'aws-sdk', '~> 1.0'
|
22
|
+
spec.add_dependency 'activesupport', '~> 4'
|
23
|
+
spec.add_dependency 'clamp', '~> 0.6'
|
24
|
+
spec.add_dependency 'cfndsl', '~> 0.1'
|
25
|
+
spec.add_dependency 'gersberms', '~> 1.0'
|
26
|
+
spec.add_dependency 'netaddr', '~> 1.5'
|
27
|
+
end
|
data/bin/awsdsl
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'gersberms'
|
2
|
+
require 'awsdsl/base_ami'
|
3
|
+
|
4
|
+
module AWSDSL
|
5
|
+
class AMIBuilder
|
6
|
+
def initialize(stack)
|
7
|
+
@stack = stack
|
8
|
+
end
|
9
|
+
|
10
|
+
def build
|
11
|
+
@stack.roles.each do |role|
|
12
|
+
build_ami(role)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def latest_amis
|
17
|
+
@stack.roles.each do |role|
|
18
|
+
role.ami = latest_ami(role).id
|
19
|
+
end
|
20
|
+
@stack
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.build(stack)
|
24
|
+
AMIBuilder.new(stack).build
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.latest_amis(stack)
|
28
|
+
AMIBuilder.new(stack).latest_amis
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_ami(role)
|
32
|
+
output_ami = ami_name(role)
|
33
|
+
# TODO(jpg): This needs to be better, also deep_merge
|
34
|
+
json = (@stack.vars || {}).merge(role.vars || {})
|
35
|
+
@builder = Gersberms::Gersberms.new base_ami: base_ami(role),
|
36
|
+
ami_name: output_ami,
|
37
|
+
json: json
|
38
|
+
begin
|
39
|
+
start_builder
|
40
|
+
role.file_provisioners.each do |provisioner|
|
41
|
+
@builder.options[:files] = provisioner
|
42
|
+
@builder.upload_files
|
43
|
+
end
|
44
|
+
role.chef_provisioners.each do |provisioner|
|
45
|
+
runlist = provisioner[:runlist]
|
46
|
+
runlist = [runlist] unless runlist.is_a? Array
|
47
|
+
@builder.options[:runlist] = runlist
|
48
|
+
@builder.run_chef
|
49
|
+
end
|
50
|
+
shutdown_builder
|
51
|
+
role.ami = @builder.image.id
|
52
|
+
rescue => e
|
53
|
+
@builder.destroy_instance
|
54
|
+
@builder.destroy_keypair
|
55
|
+
raise "Failed to build AMI for #{role.name}:\nError: #{e.message}\nBacktrace: #{e.backtrace.join("\n")}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_builder
|
60
|
+
@builder.preflight
|
61
|
+
@builder.create_keypair
|
62
|
+
@builder.create_instance
|
63
|
+
@builder.install_chef
|
64
|
+
@builder.upload_cookbooks
|
65
|
+
end
|
66
|
+
|
67
|
+
def shutdown_builder
|
68
|
+
@builder.stop_instance
|
69
|
+
@builder.create_ami
|
70
|
+
@builder.destroy_instance
|
71
|
+
@builder.destroy_keypair
|
72
|
+
end
|
73
|
+
|
74
|
+
def base_ami(role)
|
75
|
+
base = role.base_ami || 'ubuntu'
|
76
|
+
if BaseAMI::DISTROS.include?(base)
|
77
|
+
base = BaseAMI.find(base)
|
78
|
+
end
|
79
|
+
base
|
80
|
+
end
|
81
|
+
|
82
|
+
def ami_name(role)
|
83
|
+
last = latest_ami(role)
|
84
|
+
num = last.name.split('-').last.to_i + 1 if last
|
85
|
+
num ||= 1
|
86
|
+
"#{@stack.name}-#{role.name}-#{num}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def latest_ami(role)
|
90
|
+
ec2 = AWS::EC2.new
|
91
|
+
amis = ec2.images.with_owner('self').select do |i|
|
92
|
+
i.name.start_with?("#{@stack.name}-#{role.name}")
|
93
|
+
end
|
94
|
+
latest_num = amis.map { |i| i.name.split('-').last.to_i }.sort.last
|
95
|
+
amis.select { |i| i.name == "#{@stack.name}-#{role.name}-#{latest_num}" }.first
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module AWSDSL
|
2
|
+
module BaseAMI
|
3
|
+
DISTROS = %w(ubuntu)
|
4
|
+
|
5
|
+
def self.find(distro)
|
6
|
+
send(distro)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.ubuntu
|
10
|
+
ec2 = AWS::EC2.new
|
11
|
+
ami = ec2.images.with_owner('099720109477')
|
12
|
+
.filter('name', 'ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server*')
|
13
|
+
.sort_by(&:name).last
|
14
|
+
ami.id
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|