bora 0.9.4 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 051a9f9d9023048aa57a43f69a4fa40694976b39
4
- data.tar.gz: 015a4c14851b8448634116b8363455f728a75117
3
+ metadata.gz: 0274ebd129ad2e2c1e96f6c93accc024628bb960
4
+ data.tar.gz: 0b5b69a386da693777967af8692b4ba87cecb648
5
5
  SHA512:
6
- metadata.gz: 2ddc7cc56506aa31329a61310c6315c5f3b61164c0153ad3e56851e23c91bf21b056c2031e9d6598d1b6f546cebb0589f4ffc2232050bee049e9c1db22ccb1be
7
- data.tar.gz: 80ef8eb03d677a97fbf49fbf21625fc9adc8e2c104326dea6337e13462daf6e0ec0f07b95013e34962741154138ca6eb7417e81d07d2cc183fd22afd8397ad2a
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 Ruby [rake](https://github.com/ruby/rake) tasks that help you work with [CloudFormation](https://aws.amazon.com/cloudformation/) stacks.
4
- You don't need to use it with rake though - you can use the underlying classes in any Ruby app.
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
- Add this line to your application's Gemfile:
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 execute:
24
+ And then run `bundle install`.
16
25
 
17
- $ bundle
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
- Add this to your `Rakefile`
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
- This will give you the following rake tasks
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
- ### Task Options
63
+ ## File Format Reference
53
64
 
54
- When you define the Bora tasks, you can pass in a number of options that control how Bora works and what gets passed to CloudFormation.
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
- ```ruby
58
- Bora::Tasks.new("example", "example.json") do |t|
59
- t.stack_options = {}
60
- t.colorize = true
61
- end
62
- ```
63
- * `example.json` - this is a URL to your template. It can be anything openable by Ruby's [`open-uri`](http://ruby-doc.org/stdlib-2.3.0/libdoc/open-uri/rdoc/OpenURI.html) library (eg: a local file or http/https URL), or an `s3://` URL. This parameter is optional - if you don't supply it, you *must* specify either `template_body` or `template_url` in the `stack_options` (see below).
64
- * `stack_options` - A hash of options that are passed directly to the CloudFormation [`create_stack`](http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudFormation/Client.html#create_stack-instance_method) and [`update_stack`](http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudFormation/Client.html#update_stack-instance_method) APIs. If you specified a template URL in the constructor you don't need to supply `template_body` or `template_url here (you will get an error if you do).
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
- ### Dynamically Generated Templates
69
- If you are generating your templates dynamically using a DSL such as [cfndsl](https://github.com/stevenjack/cfndsl) you can easily hook this into the Bora tasks by defining a `generate` task within the Bora::Tasks constructor.
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
- ```ruby
72
- Bora::Tasks.new("example", "example.json") do |t|
73
- desc "Generates the template"
74
- task :generate do
75
- # Generate your template and write it into "example.json" here
76
- end
77
- end
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
- `cfndsl` comes with a rake task that you can use by embedding it inside the Bora task definition:
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
- ```ruby
83
- require 'bora'
84
- require 'cfndsl/rake_task'
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
- If you need to pass parameters from the rake command line through to your generate method,
99
- you can do so by using Rake's [`args.extras`](http://ruby-doc.org/stdlib-2.2.2/libdoc/rake/rdoc/Rake/TaskArguments.html#method-i-extras) functionality:
127
+ # Traditional CloudFormation parameters
128
+ InstanceType: t2.micro
129
+ AMI: ami-11032472
100
130
 
101
- ```ruby
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
- ```shell
110
- $ rake stack:example:apply[arg1_value, arg2_value]
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
- You can use this gem without using Rake. Most of the logic is implemented in [stack.rb](https://github.com/ampedandwired/bora/blob/master/lib/bora/stack.rb) and is fairly self-explanatory.
155
+ ## Command Line
117
156
 
118
- ```ruby
119
- require 'bora'
157
+ Run `bora help` to see all available commands.
120
158
 
121
- stack = Bora::Stack.new("my-stack")
122
- result = stack.update({template_body: File.read("example.json")}) do |event|
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
- ### YAML Configuration - Experimental
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 "bora"
138
- Bora::RakeTasks.new('templates.yml')
167
+ require 'bora'
168
+ Bora.new.rake_tasks
139
169
  ```
140
170
 
141
- templates.yml:
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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bora/cli'
4
+
5
+ Bora::Cli.start(ARGV)
data/lib/bora.rb CHANGED
@@ -1,6 +1,43 @@
1
+ require "yaml"
2
+ require "colorize"
1
3
  require "bora/version"
2
- require "bora/status"
3
- require "bora/event"
4
- require "bora/stack"
4
+ require "bora/template"
5
5
  require "bora/tasks"
6
- require "bora/rake_tasks"
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