stax 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +205 -13
- data/lib/stax.rb +6 -1
- data/lib/stax/aws/asg.rb +1 -0
- data/lib/stax/aws/cfn.rb +64 -3
- data/lib/stax/aws/codebuild.rb +41 -0
- data/lib/stax/aws/codepipeline.rb +44 -0
- data/lib/stax/aws/dynamodb.rb +10 -0
- data/lib/stax/aws/ec2.rb +11 -0
- data/lib/stax/aws/ecr.rb +17 -0
- data/lib/stax/aws/ecs.rb +5 -5
- data/lib/stax/aws/route53.rb +51 -0
- data/lib/stax/base.rb +18 -0
- data/lib/stax/cfer.rb +43 -33
- data/lib/stax/cli.rb +3 -5
- data/lib/stax/meta.rb +18 -0
- data/lib/stax/mixin/asg.rb +1 -1
- data/lib/stax/mixin/codebuild.rb +98 -0
- data/lib/stax/mixin/codepipeline.rb +125 -0
- data/lib/stax/mixin/dynamodb.rb +17 -1
- data/lib/stax/mixin/ec2.rb +6 -1
- data/lib/stax/mixin/ecr.rb +68 -0
- data/lib/stax/mixin/ecs.rb +98 -33
- data/lib/stax/mixin/ecs/deploy.rb +49 -0
- data/lib/stax/mixin/logs.rb +73 -2
- data/lib/stax/stack.rb +8 -6
- data/lib/stax/stack/cfn.rb +49 -8
- data/lib/stax/stack/changeset.rb +88 -0
- data/lib/stax/stack/crud.rb +143 -26
- data/lib/stax/stack/imports.rb +34 -0
- data/lib/stax/stack/outputs.rb +4 -2
- data/lib/stax/stack/parameters.rb +1 -1
- data/lib/stax/stack/resources.rb +3 -3
- data/lib/stax/staxfile.rb +14 -4
- data/lib/stax/version.rb +1 -1
- metadata +12 -4
- data/lib/stax/asg.rb +0 -140
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6fb67f05d66f75c3ee95f2f244189b9f0a24cf84c510280a02f17325b0f6c29b
|
4
|
+
data.tar.gz: e8a9c6ab1f3b869f36c688ffc7c9caf69799ed9fd9730f23beabd29a750ddfa2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e31cf509ed73dbfc5045f5d2971d47894be9194f0a78d33812544ee5e854f15095c32ff3d4be868a168cf4d29475e9bf2bd03338f8f9043a6b43774bbecf3fa0
|
7
|
+
data.tar.gz: fad98ced6ce0cced2e8fa45405e6e3f5dd47d168ab5134a41c66e42cea280f2b36d9169b11d367aeb19e88cfbac3b093aa72541b619ca3de2a3b52c933dff9f2
|
data/README.md
CHANGED
@@ -1,12 +1,71 @@
|
|
1
1
|
# Stax
|
2
2
|
|
3
|
-
|
3
|
+
Stax is a highly-opionated framework for managing Cloudformation
|
4
|
+
stacks along with all the crappy glue code you need around them.
|
4
5
|
|
5
|
-
|
6
|
+
Stax is built as a set of ruby classes, with configuration based
|
7
|
+
around sub-classing and monkey-patching.
|
8
|
+
|
9
|
+
For now, Stax reads template files written using the
|
10
|
+
[cfer](https://github.com/seanedwards/cfer) ruby wrapper. It should be
|
11
|
+
straightforward to change to raw json/yaml, or a different wrapper by
|
12
|
+
re-implementing the methods in `lib/stax/stack/crud.rb`.
|
13
|
+
|
14
|
+
## Concepts
|
15
|
+
|
16
|
+
### Application
|
17
|
+
|
18
|
+
You have an application in a git repo. Our example will be called
|
19
|
+
`website`. The Stax infrastructure code and cloudformation templates
|
20
|
+
live in the same repo as the application code.
|
21
|
+
|
22
|
+
### Branch
|
23
|
+
|
24
|
+
You can check out any branch of your repo and deploy a
|
25
|
+
fully-operational infrastructure using Stax.
|
26
|
+
|
27
|
+
Example branches might be `prod`, `stg`, `dev`, or perhaps your model
|
28
|
+
is `release/37`, `feature/38`, `hotfix/1234`, etc.
|
29
|
+
|
30
|
+
### Stacks
|
31
|
+
|
32
|
+
Each deployable branch consists of one or more actual cloudformation
|
33
|
+
stacks. For example, our website app may consist of stacks `vpc`, `db`
|
34
|
+
and `app`.
|
35
|
+
|
36
|
+
In our experience it is better to build multiple coupled
|
37
|
+
cloudformation stacks, handling discrete parts of an application
|
38
|
+
infrastructure, rather than having a single giant template.
|
39
|
+
|
40
|
+
These stacks are connected via their outputs and input parameters. For
|
41
|
+
example, `vpc` stack outputs its subnet IDs, which are passed as
|
42
|
+
parameters to the `app` stack. Stax is designed to handle this wiring
|
43
|
+
for us.
|
44
|
+
|
45
|
+
### Extensions
|
46
|
+
|
47
|
+
Stax is intended to be modified to handle all the hackery needed in
|
48
|
+
real-world app deployments. Each stack is modeled by subclassing the
|
49
|
+
`Stax::Stack` class, and you are encouraged to monkey-patch methods,
|
50
|
+
for example to perform extra work before/after creating or destroying
|
51
|
+
stacks.
|
6
52
|
|
7
53
|
## Installation
|
8
54
|
|
9
|
-
|
55
|
+
We like to keep all infrastructure code in a subdirectory `ops` of
|
56
|
+
application repos, but you can use any layout you like.
|
57
|
+
|
58
|
+
Example directory structure:
|
59
|
+
|
60
|
+
```
|
61
|
+
ops/
|
62
|
+
├── Gemfile
|
63
|
+
├── Staxfile
|
64
|
+
├── cf/
|
65
|
+
├── lib/
|
66
|
+
```
|
67
|
+
|
68
|
+
Add this line to your `ops/Gemfile`:
|
10
69
|
|
11
70
|
```ruby
|
12
71
|
gem 'stax'
|
@@ -14,27 +73,160 @@ gem 'stax'
|
|
14
73
|
|
15
74
|
And then execute:
|
16
75
|
|
17
|
-
|
76
|
+
```
|
77
|
+
$ cd ops
|
78
|
+
$ bundle
|
79
|
+
$ bundle exec stax version
|
80
|
+
```
|
18
81
|
|
19
|
-
|
82
|
+
## Usage
|
20
83
|
|
21
|
-
|
84
|
+
Add each of your stacks to `ops/Staxfile`:
|
22
85
|
|
23
|
-
|
86
|
+
```ruby
|
87
|
+
stack :vpc
|
88
|
+
stack :db
|
89
|
+
stack :app
|
90
|
+
```
|
24
91
|
|
25
|
-
|
92
|
+
Run stax to see it has created subcommands for each of your stacks:
|
93
|
+
|
94
|
+
```
|
95
|
+
$ bundle exec stax
|
96
|
+
Commands:
|
97
|
+
stax app # app stack
|
98
|
+
stax create # meta create task
|
99
|
+
stax db # db stack
|
100
|
+
stax delete # meta delete task
|
101
|
+
stax help [COMMAND] # Describe available commands or one specific command
|
102
|
+
stax ls # list stacks for this branch
|
103
|
+
stax version # show version
|
104
|
+
stax vpc # vpc stack
|
105
|
+
|
106
|
+
```
|
107
|
+
|
108
|
+
with the standard create/update/delete tasks for each:
|
109
|
+
|
110
|
+
```
|
111
|
+
$ bundle exec stax vpc
|
112
|
+
Commands:
|
113
|
+
stax vpc create # create stack
|
114
|
+
stax vpc delete # delete stack
|
115
|
+
stax vpc events # show all events for stack
|
116
|
+
stax vpc exists # test if stack exists
|
117
|
+
stax vpc generate # generate cloudformation template
|
118
|
+
stax vpc help [COMMAND] # Describe subcommands or one specific subcommand
|
119
|
+
stax vpc id [LOGICAL_ID] # get physical ID from resource logical ID
|
120
|
+
stax vpc outputs # show stack outputs
|
121
|
+
stax vpc parameters # show stack input parameters
|
122
|
+
stax vpc protection # show/set termination protection for stack
|
123
|
+
stax vpc resources # list resources for this stack
|
124
|
+
stax vpc tail # tail stack events
|
125
|
+
stax vpc template # get template of existing stack from cloudformation
|
126
|
+
stax vpc update # update stack
|
127
|
+
```
|
128
|
+
|
129
|
+
## Cloudformation templates
|
130
|
+
|
131
|
+
Stax will load template files from the path relative to its `Staxfile`
|
132
|
+
as `cf/$stack.rb`, e.g. `cf/vpc.rb`. Modify this using the method `Stax::Stack::cfer_template`.
|
133
|
+
See `examples` for a typical setup.
|
134
|
+
|
135
|
+
Simply control stacks using the relevant subcommands:
|
136
|
+
|
137
|
+
```
|
138
|
+
$ stax vpc create
|
139
|
+
$ stax vpc update
|
140
|
+
$ stax vpc delete
|
141
|
+
```
|
142
|
+
|
143
|
+
By default Stax will name stacks as `$app-$branch-$stack`. For our
|
144
|
+
example we will have e.g. `website-master-vpc`, `website-master-db`,
|
145
|
+
etc.
|
146
|
+
|
147
|
+
To change this scheme modify the methods `Stax::Base::stack_prefix`
|
148
|
+
and/or `Stax::Stack::stack_name`.
|
149
|
+
|
150
|
+
## Stack parameters
|
151
|
+
|
152
|
+
For any given stack, subclass `Stax::Stack` and return define a hash of
|
153
|
+
parameters from the method `cfer_parameters`. For example:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
module Stax
|
157
|
+
class App < Stack
|
158
|
+
no_commands do
|
159
|
+
|
160
|
+
def cfer_parameters
|
161
|
+
{
|
162
|
+
vpc: stack(:vpc).stack_name, # how to reference other stacks
|
163
|
+
db: stack(:db).stack_name,
|
164
|
+
ami: 'ami-e582d29f',
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
Note, `Stax::Stack` objects are subclassed from
|
174
|
+
[Thor](https://github.com/erikhuda/thor), and any non-CLI command
|
175
|
+
methods must be defined inside a `no_commands` block. See examples for
|
176
|
+
clearer illustration of this.
|
177
|
+
|
178
|
+
## Adding and modifying tasks
|
179
|
+
|
180
|
+
A strong underlying assumption of Stax is that you will always need
|
181
|
+
extra non-cloudformation glue code to handle edge-cases in your
|
182
|
+
infrastructure. This is handled by sub-classing and monkey-patching
|
183
|
+
`Stax::Stack`.
|
184
|
+
|
185
|
+
For example, in our `Stax::App` class:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
module Stax
|
189
|
+
class App < Stack
|
190
|
+
|
191
|
+
desc 'create', 'create stack'
|
192
|
+
def create
|
193
|
+
ensure_stack :vpc, :db # make sure vpc and db stacks are created first
|
194
|
+
super # create the stack
|
195
|
+
notify_slack() # define and call any extra code you need
|
196
|
+
end
|
197
|
+
|
198
|
+
desc 'delete', 'delete stack'
|
199
|
+
def delete
|
200
|
+
super # delete the stack
|
201
|
+
cleanup_code() # do some extra work
|
202
|
+
notify_slack() # etc
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
26
208
|
|
27
209
|
## Development
|
28
210
|
|
29
|
-
After checking out the repo, run `bin/setup` to install
|
211
|
+
After checking out the repo, run `bin/setup` to install
|
212
|
+
dependencies. You can also run `bin/console` for an interactive prompt
|
213
|
+
that will allow you to experiment.
|
30
214
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake
|
215
|
+
To install this gem onto your local machine, run `bundle exec rake
|
216
|
+
install`. To release a new version, update the version number in
|
217
|
+
`version.rb`, and then run `bundle exec rake release`, which will
|
218
|
+
create a git tag for the version, push git commits and tags, and push
|
219
|
+
the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
220
|
|
33
221
|
## Contributing
|
34
222
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
36
|
-
|
223
|
+
Bug reports and pull requests are welcome on GitHub at
|
224
|
+
https://github.com/[USERNAME]/stax. This project is intended to be a
|
225
|
+
safe, welcoming space for collaboration, and contributors are expected
|
226
|
+
to adhere to the [Contributor
|
227
|
+
Covenant](http://contributor-covenant.org) code of conduct.
|
37
228
|
|
38
229
|
## License
|
39
230
|
|
40
|
-
The gem is available as open source under the terms of the [MIT
|
231
|
+
The gem is available as open source under the terms of the [MIT
|
232
|
+
License](http://opensource.org/licenses/MIT).
|
data/lib/stax.rb
CHANGED
@@ -14,8 +14,10 @@ require 'stax/cfer'
|
|
14
14
|
require 'stax/stack'
|
15
15
|
require 'stax/stack/cfn'
|
16
16
|
require 'stax/stack/crud'
|
17
|
+
require 'stax/stack/changeset'
|
17
18
|
require 'stax/stack/parameters'
|
18
19
|
require 'stax/stack/outputs'
|
20
|
+
require 'stax/stack/imports'
|
19
21
|
require 'stax/stack/resources'
|
20
22
|
|
21
23
|
require 'stax/mixin/ec2'
|
@@ -25,6 +27,7 @@ require 'stax/mixin/sg'
|
|
25
27
|
require 'stax/mixin/s3'
|
26
28
|
require 'stax/mixin/asg'
|
27
29
|
require 'stax/mixin/ecs'
|
30
|
+
require 'stax/mixin/ecr'
|
28
31
|
require 'stax/mixin/sqs'
|
29
32
|
require 'stax/mixin/kms'
|
30
33
|
require 'stax/mixin/ssm'
|
@@ -35,4 +38,6 @@ require 'stax/mixin/lambda'
|
|
35
38
|
require 'stax/mixin/dynamodb'
|
36
39
|
require 'stax/mixin/logs'
|
37
40
|
require 'stax/mixin/apigw'
|
38
|
-
require 'stax/mixin/firehose'
|
41
|
+
require 'stax/mixin/firehose'
|
42
|
+
require 'stax/mixin/codebuild'
|
43
|
+
require 'stax/mixin/codepipeline'
|
data/lib/stax/aws/asg.rb
CHANGED
@@ -16,6 +16,7 @@ module Stax
|
|
16
16
|
|
17
17
|
def instances(names)
|
18
18
|
ids = describe(names).map(&:instances).flatten.map(&:instance_id)
|
19
|
+
return [] if ids.empty? # below call will return all instances in a/c if this empty
|
19
20
|
paginate(:auto_scaling_instances) do |token|
|
20
21
|
client.describe_auto_scaling_instances(instance_ids: ids, next_token: token)
|
21
22
|
end
|
data/lib/stax/aws/cfn.rb
CHANGED
@@ -13,6 +13,7 @@ module Stax
|
|
13
13
|
]
|
14
14
|
|
15
15
|
COLORS = {
|
16
|
+
## stack status
|
16
17
|
CREATE_COMPLETE: :green,
|
17
18
|
DELETE_COMPLETE: :green,
|
18
19
|
UPDATE_COMPLETE: :green,
|
@@ -21,6 +22,10 @@ module Stax
|
|
21
22
|
UPDATE_FAILED: :red,
|
22
23
|
ROLLBACK_IN_PROGRESS: :red,
|
23
24
|
ROLLBACK_COMPLETE: :red,
|
25
|
+
## resource action
|
26
|
+
Add: :green,
|
27
|
+
Modify: :yellow,
|
28
|
+
Remove: :red,
|
24
29
|
}
|
25
30
|
|
26
31
|
class << self
|
@@ -55,8 +60,6 @@ module Stax
|
|
55
60
|
paginate(:stack_events) do |token|
|
56
61
|
client.describe_stack_events(stack_name: name, next_token: token)
|
57
62
|
end
|
58
|
-
rescue ::Aws::CloudFormation::Errors::ValidationError => e
|
59
|
-
puts e.message
|
60
63
|
end
|
61
64
|
|
62
65
|
def id(name, id)
|
@@ -72,7 +75,7 @@ module Stax
|
|
72
75
|
end
|
73
76
|
|
74
77
|
def exists?(name)
|
75
|
-
Cfn.describe(name) && true
|
78
|
+
Aws::Cfn.describe(name) && true
|
76
79
|
rescue ::Aws::CloudFormation::Errors::ValidationError
|
77
80
|
false
|
78
81
|
end
|
@@ -87,14 +90,72 @@ module Stax
|
|
87
90
|
outputs(name)[key]
|
88
91
|
end
|
89
92
|
|
93
|
+
## list of this stack output exports
|
94
|
+
def exports(name)
|
95
|
+
describe(name).outputs.select(&:export_name)
|
96
|
+
end
|
97
|
+
|
98
|
+
## list of stacks that import from this one
|
99
|
+
def imports(name)
|
100
|
+
paginate(:imports) do |next_token|
|
101
|
+
client.list_imports(export_name: name, next_token: next_token)
|
102
|
+
end
|
103
|
+
rescue ::Aws::CloudFormation::Errors::ValidationError
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
|
107
|
+
def validate(opt)
|
108
|
+
client.validate_template(opt)
|
109
|
+
end
|
110
|
+
|
111
|
+
def create(opt)
|
112
|
+
client.create_stack(opt)&.stack_id
|
113
|
+
end
|
114
|
+
|
115
|
+
def update(opt)
|
116
|
+
client.update_stack(opt)&.stack_id
|
117
|
+
end
|
118
|
+
|
90
119
|
def delete(name)
|
91
120
|
client.delete_stack(stack_name: name)
|
92
121
|
end
|
93
122
|
|
123
|
+
def cancel(name)
|
124
|
+
client.cancel_update_stack(stack_name: name)
|
125
|
+
end
|
126
|
+
|
94
127
|
def protection(name, enable)
|
95
128
|
client.update_termination_protection(stack_name: name, enable_termination_protection: enable)
|
96
129
|
end
|
97
130
|
|
131
|
+
def get_policy(opt)
|
132
|
+
client.get_stack_policy(opt).stack_policy_body
|
133
|
+
end
|
134
|
+
|
135
|
+
def set_policy(opt)
|
136
|
+
client.set_stack_policy(opt)
|
137
|
+
end
|
138
|
+
|
139
|
+
def list_change_sets(name)
|
140
|
+
paginate(:summaries) do |next_token|
|
141
|
+
client.list_change_sets(stack_name: name, next_token: next_token)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def changes(opt)
|
146
|
+
paginate(:changes) do |next_token|
|
147
|
+
client.describe_change_set(opt.merge(next_token: next_token))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def changeset(opt)
|
152
|
+
client.create_change_set(opt)
|
153
|
+
end
|
154
|
+
|
155
|
+
def execute(opt)
|
156
|
+
client.execute_change_set(opt)
|
157
|
+
end
|
158
|
+
|
98
159
|
end
|
99
160
|
|
100
161
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Codebuild < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::CodeBuild::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def projects(names)
|
12
|
+
client.batch_get_projects(names: names).projects
|
13
|
+
end
|
14
|
+
|
15
|
+
## returns ids of num most recent builds for project
|
16
|
+
def builds_for_project(name, num = 100)
|
17
|
+
count = 0
|
18
|
+
next_token = nil
|
19
|
+
builds = []
|
20
|
+
loop do
|
21
|
+
r = client.list_builds_for_project(project_name: name, next_token: next_token)
|
22
|
+
builds += r.ids
|
23
|
+
break if (count += r.ids.count) >= num
|
24
|
+
break if (next_token = r.next_token).nil?
|
25
|
+
end
|
26
|
+
builds.first(num)
|
27
|
+
end
|
28
|
+
|
29
|
+
def builds(ids)
|
30
|
+
client.batch_get_builds(ids: ids).builds
|
31
|
+
end
|
32
|
+
|
33
|
+
def start(opt)
|
34
|
+
client.start_build(opt).build
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Codepipeline < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::CodePipeline::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def stages(name)
|
12
|
+
client.get_pipeline(name: name).pipeline.stages
|
13
|
+
end
|
14
|
+
|
15
|
+
def executions(name, num = nil)
|
16
|
+
opt = {pipeline_name: name, max_results: num}
|
17
|
+
token = nil
|
18
|
+
summaries = []
|
19
|
+
loop do
|
20
|
+
s = client.list_pipeline_executions(opt.merge(next_token: token))
|
21
|
+
summaries += s.pipeline_execution_summaries
|
22
|
+
break if (token = s.next_token).nil?
|
23
|
+
break if summaries.count >= num
|
24
|
+
end
|
25
|
+
summaries.first(num)
|
26
|
+
end
|
27
|
+
|
28
|
+
def execution(name, id)
|
29
|
+
client.get_pipeline_execution(pipeline_name: name, pipeline_execution_id: id).pipeline_execution
|
30
|
+
end
|
31
|
+
|
32
|
+
def state(name)
|
33
|
+
client.get_pipeline_state(name: name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def start(name)
|
37
|
+
client.start_pipeline_execution(name: name).pipeline_execution_id
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|