stax 0.0.3 → 0.0.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.
- 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
|