bora 0.9.4 → 1.0.0.beta1
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 +4 -4
- data/README.md +144 -115
- data/bora.gemspec +1 -0
- data/exe/bora +5 -0
- data/lib/bora.rb +41 -4
- data/lib/bora/cfn/event.rb +35 -0
- data/lib/bora/cfn/output.rb +24 -0
- data/lib/bora/cfn/stack.rb +168 -0
- data/lib/bora/cfn/stack_status.rb +33 -0
- data/lib/bora/cfn/status.rb +43 -0
- data/lib/bora/cfn_param_resolver.rb +3 -3
- data/lib/bora/cli.rb +81 -0
- data/lib/bora/stack.rb +125 -118
- data/lib/bora/stack_tasks.rb +132 -0
- data/lib/bora/tasks.rb +8 -3
- data/lib/bora/template.rb +37 -0
- data/lib/bora/version.rb +2 -2
- metadata +29 -10
- data/lib/bora/event.rb +0 -31
- data/lib/bora/output.rb +0 -20
- data/lib/bora/rake_tasks.rb +0 -107
- data/lib/bora/stack_status.rb +0 -29
- data/lib/bora/status.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0274ebd129ad2e2c1e96f6c93accc024628bb960
|
4
|
+
data.tar.gz: 0b5b69a386da693777967af8692b4ba87cecb648
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3de20877d9013f467d4fe4a95656586234f6bf9383d40db73b8bb4f118224449683ceb8ba1701a585bc28f8fa51089c6227dc1d34d6c9b142622948f1eddc256
|
7
|
+
data.tar.gz: 48257c8f06b1d56a3d952b40135ae72b7c72ddd472b5da9b0c2a845676e9cdc1e1bb05ec969fd05f48cd313e2110d242e8b2fbd4a8be5270caa1fa6946090109
|
data/README.md
CHANGED
@@ -1,169 +1,198 @@
|
|
1
1
|
# Bora
|
2
2
|
|
3
|
-
This gem contains
|
4
|
-
|
3
|
+
This Ruby gem contains a command line utility and [rake](https://github.com/ruby/rake) tasks
|
4
|
+
that help you define and work with [CloudFormation](https://aws.amazon.com/cloudformation/) stacks.
|
5
|
+
|
6
|
+
In a single YAML file you define your templates,
|
7
|
+
the stack instances built from those templates (eg: dev, uat, staging, prod, etc),
|
8
|
+
and the parameters for those stacks. Parameters can even refer to outputs of other stacks.
|
9
|
+
Templates can be written with plain CloudFormation JSON or
|
10
|
+
[cfndsl](https://github.com/stevenjack/cfndsl).
|
11
|
+
|
12
|
+
Given this config, Bora then provides commands (or Rake tasks) to work with those stacks
|
13
|
+
(create, update, delete, diff, etc).
|
5
14
|
|
6
15
|
|
7
16
|
## Installation
|
8
17
|
|
9
|
-
|
18
|
+
If you're using Bundler, add this line to your application's `Gemfile`:
|
10
19
|
|
11
20
|
```ruby
|
12
21
|
gem 'bora'
|
13
22
|
```
|
14
23
|
|
15
|
-
And then
|
24
|
+
And then run `bundle install`.
|
16
25
|
|
17
|
-
|
26
|
+
Alternatively, install directly with `gem install bora`.
|
18
27
|
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install bora
|
22
28
|
|
23
29
|
## Usage
|
24
30
|
|
25
31
|
### Quick Start
|
26
32
|
|
27
|
-
|
33
|
+
Create a file `bora.yml` in your home directory, something like this:
|
34
|
+
```yaml
|
35
|
+
templates:
|
36
|
+
example:
|
37
|
+
template_file: example.json
|
38
|
+
stacks:
|
39
|
+
uat:
|
40
|
+
params:
|
41
|
+
InstanceType: t2.micro
|
42
|
+
prod:
|
43
|
+
params:
|
44
|
+
InstanceType: m4.xlarge
|
45
|
+
```
|
46
|
+
|
47
|
+
Now run `bora apply example-uat` to create your "uat" stack.
|
48
|
+
Bora will wait until the stack is complete (or failed),
|
49
|
+
and return stack events to you as they happen.
|
50
|
+
To get a full list of available commands, run `bora help`.
|
51
|
+
|
52
|
+
Alternatively if you prefer using Rake, add this to your `Rakefile`:
|
28
53
|
|
29
54
|
```ruby
|
30
55
|
require 'bora'
|
31
|
-
|
32
|
-
Bora::Tasks.new("example", "example.json")
|
56
|
+
Bora.new.rake_tasks
|
33
57
|
```
|
34
58
|
|
35
|
-
|
36
|
-
|
37
|
-
```shell
|
38
|
-
rake stack:example:apply # Creates (or updates) the 'example' stack
|
39
|
-
rake stack:example:current_template # Shows the current template for 'example' stack
|
40
|
-
rake stack:example:delete # Deletes the 'example' stack
|
41
|
-
rake stack:example:diff # Diffs the new template with the 'example' stack's current template
|
42
|
-
rake stack:example:events # Outputs the latest events from the 'example' stack
|
43
|
-
rake stack:example:new_template # Shows the new template for 'example' stack
|
44
|
-
rake stack:example:outputs # Shows the outputs from the 'example' stack
|
45
|
-
rake stack:example:recreate # Recreates (deletes then creates) the 'example' stack
|
46
|
-
rake stack:example:status # Displays the current status of the 'example' stack
|
47
|
-
rake stack:example:validate # Checks the 'example' stack's template for validity
|
48
|
-
```
|
59
|
+
Then run `rake stack:example-uat:apply`.
|
60
|
+
To get a full list of available tasks run `rake -T`.
|
49
61
|
|
50
|
-
You can add as many templates as you like into your Rakefile, simply define an instance of `Bora::Tasks` for each one.
|
51
62
|
|
52
|
-
|
63
|
+
## File Format Reference
|
53
64
|
|
54
|
-
|
55
|
-
The available options are shown below with their default values.
|
65
|
+
The example below is a `bora.yml` file showing all available options:
|
56
66
|
|
57
|
-
```
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
* `colorize` - A boolean that controls whether console output is colored or not
|
67
|
+
```yaml
|
68
|
+
# A map defining all the CloudFormation templates available.
|
69
|
+
# A "template" is effectively a single CloudFormation JSON (or cfndsl template).
|
70
|
+
templates:
|
71
|
+
# A template named "app"
|
72
|
+
app:
|
73
|
+
# This template is a plain old CloudFormation JSON file
|
74
|
+
template_file: app.json
|
66
75
|
|
76
|
+
# Optional. An array of "capabilities" to be passed to the CloudFormation API
|
77
|
+
# (see CloudFormation docs for more details)
|
78
|
+
capabilities: [CAPABILITY_IAM]
|
67
79
|
|
68
|
-
|
69
|
-
|
80
|
+
# A map defining all the "stacks" associated with this template
|
81
|
+
# for example, "uat" and "prod"
|
82
|
+
stacks:
|
83
|
+
# The "uat" stack
|
84
|
+
uat:
|
85
|
+
# The CloudFormation parameters to pass into the stack
|
86
|
+
params:
|
87
|
+
InstanceType: t2.micro
|
88
|
+
AMI: ami-11032472
|
89
|
+
|
90
|
+
# The "prod" stack
|
91
|
+
prod:
|
92
|
+
# Optional. The stack name to use in CloudFormation
|
93
|
+
# If you don't supply this, the name will be the template
|
94
|
+
# name concatenated with the stack name as defined in this file,
|
95
|
+
# eg: "app-prod".
|
96
|
+
stack_name: prod-application-stack
|
97
|
+
params:
|
98
|
+
InstanceType: m4.xlarge
|
99
|
+
AMI: ami-11032472
|
70
100
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
101
|
+
# A template named "web"
|
102
|
+
web:
|
103
|
+
# This template is using cfndsl. Bora treats any template ending in
|
104
|
+
# ".rb" as a cfndsl template.
|
105
|
+
template_file: "web.rb"
|
106
|
+
stacks:
|
107
|
+
uat:
|
108
|
+
# The CloudFormation parameters to pass into the stack.
|
109
|
+
# You can define both cfndsl parameters and traditional CloudFormation
|
110
|
+
# parameters here. Cfndsl will receive all of them, but only those
|
111
|
+
# actually defined in the "Parameters" section of the template will be
|
112
|
+
# passed through to CloudFormation when the stack is applied.
|
113
|
+
params:
|
114
|
+
dns_zone: example.com
|
79
115
|
|
80
|
-
|
116
|
+
# You can use complex data structures with cfndsl parameters:
|
117
|
+
users:
|
118
|
+
- id: joe
|
119
|
+
name: Joe Bloggs
|
120
|
+
- id: mary
|
121
|
+
name: Mary Bloggs
|
81
122
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
Bora::Tasks.new("example", "example.json") do |t|
|
87
|
-
CfnDsl::RakeTask.new do |cfndsl_task|
|
88
|
-
cfndsl_task.cfndsl_opts = {
|
89
|
-
files: [{
|
90
|
-
filename: "example.rb",
|
91
|
-
output: "example.json"
|
92
|
-
}]
|
93
|
-
}
|
94
|
-
end
|
95
|
-
end
|
96
|
-
```
|
123
|
+
# You can refer to outputs of other stacks using "${}" notation too.
|
124
|
+
# See below for further details.
|
125
|
+
app_url: http://${app-uat/outputs/Domain}/api
|
97
126
|
|
98
|
-
|
99
|
-
|
127
|
+
# Traditional CloudFormation parameters
|
128
|
+
InstanceType: t2.micro
|
129
|
+
AMI: ami-11032472
|
100
130
|
|
101
|
-
|
102
|
-
Bora::Tasks.new("example", "example.json") do |t|
|
103
|
-
task :generate do |t, args|
|
104
|
-
arg1, arg2 = args.extras
|
105
|
-
# Generate your template and write it into "example.json" here
|
106
|
-
end
|
107
|
-
end
|
131
|
+
prod: {}
|
108
132
|
```
|
109
|
-
|
110
|
-
|
133
|
+
|
134
|
+
## Parameter Substitution
|
135
|
+
|
136
|
+
Bora supports looking up parameter values from other stacks and interpolating them into input parameters.
|
137
|
+
At present you can only look up the outputs of other stacks,
|
138
|
+
however in the future it may support looking up stack parameters or resources.
|
139
|
+
Other future possibilities include looking up values from other services,
|
140
|
+
for example AMI IDs.
|
141
|
+
|
142
|
+
The format is as follows:
|
143
|
+
|
144
|
+
`${<stack_name>/outputs/<output_name>}`
|
145
|
+
|
146
|
+
For example:
|
147
|
+
```yaml
|
148
|
+
params:
|
149
|
+
api_url: http://${api-stack/outputs/Domain}/api
|
111
150
|
```
|
112
151
|
|
152
|
+
This will look up the `Domain` output from the stack named `api-stack` and substitute it into the `api_url` parameter.
|
113
153
|
|
114
|
-
### API
|
115
154
|
|
116
|
-
|
155
|
+
## Command Line
|
117
156
|
|
118
|
-
|
119
|
-
require 'bora'
|
157
|
+
Run `bora help` to see all available commands.
|
120
158
|
|
121
|
-
|
122
|
-
|
123
|
-
puts event
|
124
|
-
end
|
159
|
+
`bora help [command]` will show you help for a particular command,
|
160
|
+
eg: `bora help apply`.
|
125
161
|
|
126
|
-
puts "Update #{result ? "succeeded" : "failed"}"
|
127
|
-
```
|
128
162
|
|
129
|
-
|
130
|
-
You can define and configure your stacks through YAML too.
|
131
|
-
This interface is currently experimental,
|
132
|
-
but longer term is likely to become the primary way to use this gem.
|
133
|
-
Sample usage is shown below (subject to change).
|
163
|
+
## Rake Tasks
|
134
164
|
|
135
|
-
Rakefile
|
165
|
+
To use the rake tasks, simply put this in your `Rakefile`:
|
136
166
|
```ruby
|
137
|
-
require
|
138
|
-
Bora
|
167
|
+
require 'bora'
|
168
|
+
Bora.new.rake_tasks
|
139
169
|
```
|
140
170
|
|
141
|
-
|
142
|
-
```yaml
|
143
|
-
templates:
|
144
|
-
app:
|
145
|
-
template_file: templates/test.json
|
146
|
-
stacks:
|
147
|
-
dev: {}
|
148
|
-
uat: {}
|
171
|
+
To get a full list of available tasks run `rake -T`.
|
149
172
|
|
150
|
-
web:
|
151
|
-
# Templates ending in ".rb" are treated as cfndsl templates
|
152
|
-
template_file: templates/test.rb
|
153
|
-
capabilities: [CAPABILITY_IAM]
|
154
|
-
stacks:
|
155
|
-
dev:
|
156
|
-
# Set stack name explicitly (otherwise would default to "web-dev")
|
157
|
-
stack_name: foo-dev
|
158
|
-
params:
|
159
|
-
# Look up a value from the outputs of the "app-dev"stack
|
160
|
-
app_sg: ${app-dev/outputs/AppSecurityGroup}
|
161
|
-
uat:
|
162
|
-
params:
|
163
|
-
app_sg: foouatdev
|
164
173
|
|
174
|
+
## Overriding Stack Parameters from the Command Line
|
175
|
+
|
176
|
+
Some commands accept a list of parameters that will override those defined in the YAML file.
|
177
|
+
|
178
|
+
If you are using the Bora command line, you can pass these parameters like this:
|
179
|
+
|
180
|
+
```bash
|
181
|
+
$ bora apply web-uat --params 'instance_type=t2.micro' 'ami=ami-11032472'
|
165
182
|
```
|
166
183
|
|
184
|
+
For rake, he equivalent is:
|
185
|
+
```bash
|
186
|
+
$ rake stack:web-uat:apply[instance_type=t2.micro,ami=ami-11032472]
|
187
|
+
```
|
188
|
+
|
189
|
+
|
190
|
+
## Related Projects
|
191
|
+
The following projects provided inspiration for Bora:
|
192
|
+
* [CfnDsl](https://github.com/stevenjack/cfndsl) - A Ruby DSL for CloudFormation templates
|
193
|
+
* [CloudFormer](https://github.com/kunday/cloudformer) - Rake tasks for CloudFormation
|
194
|
+
* [Cumulus](https://github.com/cotdsa/cumulus) - A Python YAML based tool for working with CloudFormation
|
195
|
+
|
167
196
|
|
168
197
|
## Development
|
169
198
|
|
data/bora.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency "colorize", "~> 0.7"
|
23
23
|
spec.add_dependency "diffy", "~> 3.0"
|
24
24
|
spec.add_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_dependency "thor", "~> 0.19"
|
25
26
|
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.11"
|
27
28
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/exe/bora
ADDED
data/lib/bora.rb
CHANGED
@@ -1,6 +1,43 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "colorize"
|
1
3
|
require "bora/version"
|
2
|
-
require "bora/
|
3
|
-
require "bora/event"
|
4
|
-
require "bora/stack"
|
4
|
+
require "bora/template"
|
5
5
|
require "bora/tasks"
|
6
|
-
|
6
|
+
|
7
|
+
class Bora
|
8
|
+
DEFAULT_CONFIG_FILE = "bora.yml"
|
9
|
+
|
10
|
+
def initialize(config_file_or_hash: DEFAULT_CONFIG_FILE, colorize: true)
|
11
|
+
@templates = {}
|
12
|
+
config = load_config(config_file_or_hash)
|
13
|
+
String.disable_colorization = !colorize
|
14
|
+
config['templates'].each do |template_name, template_config|
|
15
|
+
@templates[template_name] = Template.new(template_name, template_config)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def template(name)
|
20
|
+
@templates[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def stack(stack_name)
|
24
|
+
t = @templates.find { |_, template| template.stack(stack_name) != nil }
|
25
|
+
t ? t[1].stack(stack_name) : nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def rake_tasks
|
29
|
+
@templates.each { |_, t| t.rake_tasks }
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def load_config(config)
|
36
|
+
if config.class == String
|
37
|
+
return YAML.load_file(config)
|
38
|
+
elsif config.class == Hash
|
39
|
+
return config
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'bora/cfn/status'
|
2
|
+
|
3
|
+
class Bora
|
4
|
+
module Cfn
|
5
|
+
|
6
|
+
class Event
|
7
|
+
def initialize(event)
|
8
|
+
@event = event
|
9
|
+
@status = Status.new(@event.resource_status)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(sym, *args, &block)
|
13
|
+
@event.send(sym, *args, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def status_success?
|
17
|
+
@status.success?
|
18
|
+
end
|
19
|
+
|
20
|
+
def status_failure?
|
21
|
+
@status.failure?
|
22
|
+
end
|
23
|
+
|
24
|
+
def status_complete?
|
25
|
+
status_success? || status_failure?
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
status_reason = @event.resource_status_reason ? " - #{@event.resource_status_reason}" : ""
|
30
|
+
"#{@event.timestamp.getlocal} - #{@event.resource_type} - #{@event.logical_resource_id} - #{@status}#{status_reason}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Bora
|
2
|
+
module Cfn
|
3
|
+
|
4
|
+
class Output
|
5
|
+
def initialize(output)
|
6
|
+
@output = output
|
7
|
+
end
|
8
|
+
|
9
|
+
def key
|
10
|
+
@output.output_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def value
|
14
|
+
@output.output_value
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
desc = @output.description ? " (#{@output.description})" : ""
|
19
|
+
"#{@output.output_key} - #{@output.output_value} #{desc}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|