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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0444c2ae4f050d73ba7cfd720eca476667102bf2
4
- data.tar.gz: 764f80db2e413eade8840ef8f04c33d93003e190
2
+ SHA256:
3
+ metadata.gz: 6fb67f05d66f75c3ee95f2f244189b9f0a24cf84c510280a02f17325b0f6c29b
4
+ data.tar.gz: e8a9c6ab1f3b869f36c688ffc7c9caf69799ed9fd9730f23beabd29a750ddfa2
5
5
  SHA512:
6
- metadata.gz: bb84fb5b04019ef607f8f4a6658414fdda2b3dbf641a31bbedadfb785ab20f1e76c8d039529c71a52d5f18419ead4c5594297bdfc7303efef8ccb4f8d0ef0191
7
- data.tar.gz: f65676b1870954bcc39c5d53a0ecd7b238d5b2fafd7bd2ea5761fc993dece0e33c7b8c2205ebf6a61021b148ba01f84f8856a523fd4d4c2fa6ff3bbfcea6fb41
6
+ metadata.gz: e31cf509ed73dbfc5045f5d2971d47894be9194f0a78d33812544ee5e854f15095c32ff3d4be868a168cf4d29475e9bf2bd03338f8f9043a6b43774bbecf3fa0
7
+ data.tar.gz: fad98ced6ce0cced2e8fa45405e6e3f5dd47d168ab5134a41c66e42cea280f2b36d9169b11d367aeb19e88cfbac3b093aa72541b619ca3de2a3b52c933dff9f2
data/README.md CHANGED
@@ -1,12 +1,71 @@
1
1
  # Stax
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/stax`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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
- Add this line to your application's Gemfile:
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
- $ bundle
76
+ ```
77
+ $ cd ops
78
+ $ bundle
79
+ $ bundle exec stax version
80
+ ```
18
81
 
19
- Or install it yourself as:
82
+ ## Usage
20
83
 
21
- $ gem install stax
84
+ Add each of your stacks to `ops/Staxfile`:
22
85
 
23
- ## Usage
86
+ ```ruby
87
+ stack :vpc
88
+ stack :db
89
+ stack :app
90
+ ```
24
91
 
25
- TODO: Write usage instructions here
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 dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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 install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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 https://github.com/[USERNAME]/stax. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
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 License](http://opensource.org/licenses/MIT).
231
+ The gem is available as open source under the terms of the [MIT
232
+ License](http://opensource.org/licenses/MIT).
@@ -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'
@@ -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
@@ -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