cloudformula 1.1.2

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +206 -0
  9. data/Rakefile +1 -0
  10. data/bin/cloudformula +12 -0
  11. data/cloudformula.gemspec +26 -0
  12. data/integration_tests/.ruby-gemset +1 -0
  13. data/integration_tests/.ruby-version +1 -0
  14. data/integration_tests/Gemfile +3 -0
  15. data/integration_tests/README.md +33 -0
  16. data/integration_tests/fixtures/minimal_cf_template.json +30 -0
  17. data/integration_tests/fixtures/minimal_cf_template_update.json +54 -0
  18. data/integration_tests/stack_create_update.rb +26 -0
  19. data/lib/cloudformula/cli.rb +113 -0
  20. data/lib/cloudformula/cloud_formation.rb +54 -0
  21. data/lib/cloudformula/help/create.txt +12 -0
  22. data/lib/cloudformula/help/generate.txt +10 -0
  23. data/lib/cloudformula/help/top.txt +20 -0
  24. data/lib/cloudformula/help/update.txt +12 -0
  25. data/lib/cloudformula/json_erb.rb +42 -0
  26. data/lib/cloudformula/string.rb +11 -0
  27. data/lib/cloudformula/template.rb +99 -0
  28. data/lib/cloudformula/validator.rb +172 -0
  29. data/lib/cloudformula/version.rb +3 -0
  30. data/lib/cloudformula.rb +23 -0
  31. data/spec/cloud_formula_spec.rb +18 -0
  32. data/spec/cloudformula/cloud_formation_spec.rb +55 -0
  33. data/spec/cloudformula/template_spec.rb +303 -0
  34. data/spec/fixtures/_partial.json.erb +1 -0
  35. data/spec/fixtures/with_custom_erb_validations.erb +4 -0
  36. data/spec/fixtures/with_erb_parameters.erb +3 -0
  37. data/spec/fixtures/with_erb_parameters.json.erb +4 -0
  38. data/spec/fixtures/with_erb_parameters_and_stack_options.json.erb +5 -0
  39. data/spec/fixtures/with_erb_parameters_answer.json +4 -0
  40. data/spec/fixtures/with_erb_parameters_answer.txt +3 -0
  41. data/spec/fixtures/with_erb_parameters_escaped_answer.json +4 -0
  42. data/spec/fixtures/with_erb_validations.json.erb +53 -0
  43. data/spec/fixtures/with_partial.json.erb +5 -0
  44. data/spec/fixtures/with_partial_answer.json +5 -0
  45. data/spec/fixtures/with_raw.json.erb +3 -0
  46. data/spec/fixtures/with_raw_answer.json +3 -0
  47. data/spec/fixtures/with_stack_options.json.erb +4 -0
  48. data/spec/fixtures/without_erb_parameters.erb +1 -0
  49. data/spec/fixtures/without_erb_parameters.json.erb +3 -0
  50. data/spec/spec_helper.rb +11 -0
  51. metadata +185 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24bc82972e9d49a352e41baa51b21706cd6542d5
4
+ data.tar.gz: dbb0c9f3404d9c5509ca5cb7d3ecc7b06a494336
5
+ SHA512:
6
+ metadata.gz: 8cd142edaa41c09a9edbb576dba107de8d27e94c23ccec3afe6783437a42d876abb958544d57051d040b5f8902ee82a7ed1012fa4c9d6422247694beaf7f1602
7
+ data.tar.gz: b9093780e5397256d3481725703315e4ebcb21343ea98803575abecacb742a9f47b6ca813296ccee8cc8fe2d23bd9aabd3e7e3b5ed65963bd3ac217c55835df5
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .idea/
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ cloudformula
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p195
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloudformula.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kabam
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # CloudFormula
2
+
3
+ CloudFormula is a tool which generates and uploads CloudFormation templates from ERB templates.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cloudformula'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cloudformula
18
+
19
+ ## API
20
+
21
+ [API Documentation](http://rubydoc.info/github/Kabam/cloudformula/master/frames)
22
+
23
+ ## CLI
24
+
25
+ This gem includes a command-line interface which can generate templates, create and update CloudFormation stacks.
26
+ Type `cloudformula --help` in a console for documentation.
27
+
28
+ ## Basic usage
29
+
30
+ Generate a template:
31
+
32
+ ```ruby
33
+ template = CloudFormula::Template.new('/path/to/template', { :param1 => 'foo', :param2 => 'bar' })
34
+ template.generate
35
+ ```
36
+
37
+ You can then pass the template to `create_stack`:
38
+
39
+ ```ruby
40
+ stack = CloudFormula.create_stack('us-east-1', 'stack-name', template)
41
+ ```
42
+
43
+ ...or to `update_stack`:
44
+
45
+ ```ruby
46
+ CloudFormula.update_stack('us-east-1', 'stack-name', template)
47
+ ```
48
+
49
+ TODO - see api documentation
50
+
51
+ ## Creating templates
52
+
53
+ Templates are Ruby ERB, which means you have the full power of Ruby available.
54
+
55
+ ### Escaping content
56
+
57
+ Normally, variables output in your ERB templates using `<%= %>` will be automatically JSON-escaped. You can change
58
+ this behavior by using the `raw` helper. For instance:
59
+
60
+ ```ruby
61
+ <%= raw @foo %>
62
+ ```
63
+
64
+ Note this only works for Strings. If `@foo` is an object that will be implicitly converted to a String, it will still
65
+ be JSON-escaped.
66
+
67
+ ### Partials
68
+
69
+ Basic partial functionality is available. You can include a partial inside a template as follows:
70
+
71
+ ```ruby
72
+ <%= render 'path/to/template.json.erb', { :param1 => 'value1', :param2 => 'value2', ... } %>
73
+ ```
74
+
75
+ ERB variables are not automatically passed through to partials - you'll have to explicitly pass them.
76
+
77
+ ### Validation
78
+
79
+ Templates are responsible for their own validations. There are several pre-defined helpers you can use for common
80
+ cases. For the sake of familiarity, we've partially reproduced the helpers in ActiveRecord. See the
81
+ [ActiveRecord Validation Helpers documentation](http://edgeguides.rubyonrails.org/active_record_validations.html#validation-helpers)
82
+ for information on how to use the helpers.
83
+
84
+ Use pre-defined helpers by creating a Hash named `@validations` in your ERB template. For example:
85
+
86
+ ```ruby
87
+ <%
88
+ @validations = {
89
+ :param1 => { :presence => true },
90
+ ...
91
+ }
92
+ %>
93
+ {
94
+ "AWSTemplateFormatVersion" : "2010-09-09",
95
+ ...
96
+ }
97
+ ```
98
+
99
+ #### exclusion
100
+
101
+ Ensures a value is not in the exclusion list.
102
+
103
+ ```ruby
104
+ :exclusion => ['val1']
105
+ ```
106
+
107
+ #### inclusion
108
+
109
+ Ensures a value is in the inclusion list
110
+
111
+ ```ruby
112
+ :inclusion => ['val1', 'val2']
113
+ ```
114
+
115
+ #### length
116
+
117
+ Ensures value length
118
+
119
+ ```ruby
120
+ :length => {
121
+ :minimum => n,
122
+ :maximum => n,
123
+ :in|:within => x..y,
124
+ :is => n
125
+ }
126
+ ```
127
+
128
+ #### format
129
+
130
+ Ensures a value matches the regex
131
+
132
+ ```ruby
133
+ :format => /regex/
134
+ ```
135
+
136
+ #### numericality
137
+
138
+ Ensures numeric value constraints
139
+
140
+ ```ruby
141
+ :numericality => {
142
+ :only_integer => true|false,
143
+ :equal_to => n,
144
+ :greater_than => n,
145
+ :greater_than_or_equal_to => n,
146
+ :less_than => n,
147
+ :less_than_or_equal_to => n,
148
+ :even => true|false,
149
+ :odd => true|false
150
+ }
151
+ ```
152
+
153
+ #### presence
154
+
155
+ Ensures a value exists and has a length greater than 0
156
+
157
+ ```ruby
158
+ :presence => true|false
159
+ ```
160
+
161
+ #### Custom validations
162
+
163
+ You can also write custom validations by raising errors:
164
+
165
+ ```ruby
166
+ <%
167
+ # Validate parameters
168
+ raise 'Required parameter "@foo" is missing.' if @foo.nil? || @foo.empty?
169
+ %>
170
+ ```
171
+
172
+ ### Reserved keywords
173
+
174
+ `parameters` and `source` are reserved keywords and cannot be used as ERB or CloudFormation parameter names.
175
+
176
+ ### CloudFormation stack options
177
+
178
+ You can specify default stack options within a template. Refer to
179
+ [AWS::CloudFormation::StackCollection.create](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/CloudFormation/StackCollection.html#create-instance_method)
180
+ at "Options Hash (options)" for a list of options you can set. Note that "parameters" will be overwritten by parameters
181
+ supplied to the create|update_stack methods.
182
+
183
+ Example of setting the default to not rollback the stack in case of failure:
184
+ ```erb
185
+ <%
186
+ @stack_options = { :disable_rollback => true }
187
+ %>
188
+ {
189
+ ...template...
190
+ }
191
+ ```
192
+
193
+ You can override stack_options if needed by providing a Hash for the override_options parameter when calling
194
+ CloudFormula::CloudFormation#create_stack or CloudFormula::CloudFormation#update_stack
195
+
196
+ ### Security concerns
197
+
198
+ This gem should not be used with untrusted templates or parameters.
199
+
200
+ ## Contributing
201
+
202
+ 1. Fork it
203
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
204
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
205
+ 4. Push to the branch (`git push origin my-new-feature`)
206
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/cloudformula ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'cloudformula'
7
+ require 'cloudformula/cli'
8
+
9
+ args = ARGV.dup
10
+ ARGV.clear
11
+
12
+ CloudFormula::CLI.run(args)
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloudformula/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cloudformula'
8
+ spec.version = CloudFormula::VERSION
9
+ spec.authors = ['Kabam']
10
+ spec.summary = 'CloudFormula is a tool which generates and uploads CloudFormation templates from ERB templates.'
11
+ spec.description = spec.summary
12
+ spec.homepage = 'https://github.com/Kabam/cloudformula'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'aws-sdk', '~> 1.21.0'
21
+ spec.add_dependency 'trollop', '~> 2.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec', '~> 2.14'
26
+ end
@@ -0,0 +1 @@
1
+ cloudformula_integration
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p195
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'cloudformula', :path => '../'
@@ -0,0 +1,33 @@
1
+ # CloudFormula Integration Tests
2
+
3
+ These are simple scripts that fully test functionality of the gem from a user / developer perspective.
4
+
5
+ ## Setup
6
+
7
+ Provide your AWS security credentials via ENV:
8
+
9
+ ```shell
10
+ export AWS_ACCESS_KEY_ID='...'
11
+ export AWS_SECRET_ACCESS_KEY='...'
12
+ ```
13
+
14
+ Install gems:
15
+
16
+ ```shell
17
+ cd integration_tests
18
+ bundle
19
+ ```
20
+
21
+ ## Running
22
+
23
+ Run all tests from the `integration_tests` directory. These tests may be packaged in the future, but for now they can
24
+ be run individually as follows.
25
+
26
+ ```shell
27
+ ruby testfile.rb
28
+ ```
29
+
30
+ ### stack_create_update.rb
31
+
32
+ This test creates, updates, and deletes an AWS CloudFormation stack. If this test fails, you must manually delete
33
+ the CloudFormation stack "stackalong-cassidy" before attempting to run it again.
@@ -0,0 +1,30 @@
1
+ {
2
+ "AWSTemplateFormatVersion" : "2010-09-09",
3
+ "Description" : "Minimal stack for testing",
4
+
5
+ "Resources" : {
6
+ "CfnRole" : {
7
+ "Type" : "AWS::IAM::Role",
8
+ "Properties" : {
9
+ "AssumeRolePolicyDocument" : {
10
+ "Statement" : [ {
11
+ "Effect" : "Allow",
12
+ "Principal" : { "Service" : [ "ec2.amazonaws.com" ] },
13
+ "Action" : [ "sts:AssumeRole" ]
14
+ } ]
15
+ },
16
+ "Path" : "/",
17
+ "Policies" : [ {
18
+ "PolicyName" : "root",
19
+ "PolicyDocument" : {
20
+ "Statement" : [ {
21
+ "Effect" : "Allow",
22
+ "Action" : "cloudformation:DescribeStackResource",
23
+ "Resource" : "*"
24
+ } ]
25
+ }
26
+ } ]
27
+ }
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "AWSTemplateFormatVersion" : "2010-09-09",
3
+ "Description" : "Minimal stack for testing",
4
+
5
+ "Resources" : {
6
+ "CfnRole" : {
7
+ "Type" : "AWS::IAM::Role",
8
+ "Properties" : {
9
+ "AssumeRolePolicyDocument" : {
10
+ "Statement" : [ {
11
+ "Effect" : "Allow",
12
+ "Principal" : { "Service" : [ "ec2.amazonaws.com" ] },
13
+ "Action" : [ "sts:AssumeRole" ]
14
+ } ]
15
+ },
16
+ "Path" : "/",
17
+ "Policies" : [ {
18
+ "PolicyName" : "root",
19
+ "PolicyDocument" : {
20
+ "Statement" : [ {
21
+ "Effect" : "Allow",
22
+ "Action" : "cloudformation:DescribeStackResource",
23
+ "Resource" : "*"
24
+ } ]
25
+ }
26
+ } ]
27
+ }
28
+ },
29
+
30
+ "CfnRole2" : {
31
+ "Type" : "AWS::IAM::Role",
32
+ "Properties" : {
33
+ "AssumeRolePolicyDocument" : {
34
+ "Statement" : [ {
35
+ "Effect" : "Allow",
36
+ "Principal" : { "Service" : [ "ec2.amazonaws.com" ] },
37
+ "Action" : [ "sts:AssumeRole" ]
38
+ } ]
39
+ },
40
+ "Path" : "/",
41
+ "Policies" : [ {
42
+ "PolicyName" : "root",
43
+ "PolicyDocument" : {
44
+ "Statement" : [ {
45
+ "Effect" : "Allow",
46
+ "Action" : "cloudformation:DescribeStackResource",
47
+ "Resource" : "*"
48
+ } ]
49
+ }
50
+ } ]
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,26 @@
1
+ require 'bundler/setup'
2
+ require 'cloudformula'
3
+
4
+ template_path = File.expand_path('fixtures/minimal_cf_template.json', File.dirname(__FILE__))
5
+ template = CloudFormula::Template.new(template_path)
6
+
7
+ puts 'Creating...'
8
+ stack = CloudFormula.create_stack('us-west-2', 'stackalong-cassidy', template)
9
+ puts stack.inspect
10
+
11
+ puts 'Waiting for creation to complete...'
12
+ sleep(1.0) while stack.status == 'CREATE_IN_PROGRESS'
13
+
14
+ template_path = File.expand_path('fixtures/minimal_cf_template_update.json', File.dirname(__FILE__))
15
+ template = CloudFormula::Template.new(template_path)
16
+
17
+ puts 'Updating...'
18
+ CloudFormula.update_stack('us-west-2', 'stackalong-cassidy', template)
19
+
20
+ puts 'Waiting for update to complete...'
21
+ sleep(1.0) while stack.status == 'UPDATE_IN_PROGRESS' || stack.status == 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS'
22
+
23
+ puts 'Deleting...'
24
+ stack.delete
25
+
26
+ puts 'Done.'
@@ -0,0 +1,113 @@
1
+ require 'json'
2
+ require 'trollop'
3
+
4
+ module CloudFormula
5
+ class CLI
6
+ def self.run(args)
7
+ parse_opts = parse_options args
8
+ subcommand = parse_opts[:subcommand]
9
+ opts = parse_opts[:opts]
10
+
11
+ template = CloudFormula.template opts[:template], parse_parameters(opts[:parameters])
12
+
13
+ begin
14
+ case subcommand
15
+ when 'generate'
16
+ puts template.generate
17
+ when 'create'
18
+ stack = CloudFormula.create_stack opts[:region], opts[:'stack-name'], template
19
+ puts stack.inspect
20
+ when 'update'
21
+ CloudFormula.update_stack opts[:region], opts[:'stack-name'], template
22
+ end
23
+ rescue Exception => e
24
+ puts "ERROR: #{e.message}\n\nStack trace:\n#{e.backtrace.join("\n from ")}"
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # Parses and validates command-line arguments. Returns the subcommand and its options.
31
+ # @param [Array] args Command-line arguments (ARGV)
32
+ # @return [Hash] { :subcommand => "command", :opts => Hash } @see Trollop::Parser#parse for opts structure
33
+ def self.parse_options(args)
34
+ parse_top_level args
35
+ parse_subcommand args
36
+ end
37
+
38
+ # Top-level parser includes global help and subcommand pass-through
39
+ # @param [Array] args Command-line arguments (ARGV)
40
+ # @return [Hash] @see Trollop::Parser#parse
41
+ def self.parse_top_level(args)
42
+ Trollop::options args do
43
+ banner CloudFormula::CLI.banner(:top)
44
+ stop_on %w(generate create update)
45
+ end
46
+ end
47
+
48
+ # Subcommand parser includes subcommand help and parameter validation
49
+ # @param [Array] args Command-line arguments (ARGV)
50
+ # @return [Hash] { :subcommand => "command", :opts => Hash } @see Trollop::Parser#parse for opts structure
51
+ def self.parse_subcommand(args)
52
+ cmd = args.shift # get the subcommand
53
+ Trollop::die "unknown command #{cmd.inspect}" unless ['generate', 'create', 'update'].include?(cmd)
54
+ cmd_opts = Trollop::options args do
55
+ banner CloudFormula::CLI.banner(cmd.to_sym)
56
+ CloudFormula::CLI.opt_definitions(cmd).each { |item| opt *item }
57
+ end
58
+ validate_global_opts cmd_opts
59
+ { :subcommand => cmd, :opts => cmd_opts }
60
+ end
61
+
62
+ # Returns an array defining the command-line options
63
+ # Each item in the array can be passed directly to `opt` in a parser. Example:
64
+ # Trollop::options(args) do
65
+ # opt_definitions(subcommand).each { |item| opt *item }
66
+ # end
67
+ # @param [String] subcommand The definitions will be customized appropriately for this value.
68
+ # @return [Array]
69
+ def self.opt_definitions(subcommand)
70
+ defs = [
71
+ [:parameters, 'Parameters to supply to the template in JSON format. {"var1":"foo","var2":"bar"}', :type => :string],
72
+ [:template, 'Path to the .json or .json.erb template', :type => :string, :required => true]
73
+ ]
74
+ case subcommand
75
+ when 'create', 'update'
76
+ defs += [
77
+ [:'stack-name', 'Name of the CloudFormation stack', :type => :string, :required => true],
78
+ [:region, 'AWS region. us-east-1, us-west-2, etc. Default us-east-1', :type => :string]
79
+ ]
80
+ end
81
+ defs
82
+ end
83
+
84
+ # Returns the appropriate help banner for the given subcommand
85
+ # @param [Symbol] subcommand
86
+ # @return [String]
87
+ def self.banner(subcommand)
88
+ case subcommand
89
+ when :top, :generate, :create, :update
90
+ file = File.open(File.expand_path("#{subcommand}.txt", "#{File.dirname(__FILE__)}/help"), 'rb')
91
+ file.read
92
+ else
93
+ ''
94
+ end
95
+ end
96
+
97
+ # Validates options required for all subcommands.
98
+ # Returns the parsed --parameters value as either a Hash or an Array
99
+ # @param [Hash] opts
100
+ # @return [Array|Hash] @see JSON.parse
101
+ def self.validate_global_opts(opts)
102
+ Trollop.die :template, "file #{opts[:template]} not found or cannot be read" unless File.file?(opts[:template]) && File.readable?(opts[:template])
103
+ parse_parameters opts[:parameters]
104
+ end
105
+
106
+ def self.parse_parameters(parameters)
107
+ return {} if parameters.nil? || parameters.empty?
108
+ parsed = JSON.parse parameters, :symbolize_names => true rescue Trollop.die :parameters, 'must be valid JSON'
109
+ Trollop.die :parameters, 'must be a JSON hash' unless parsed.class == Hash
110
+ parsed
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,54 @@
1
+ require 'aws-sdk'
2
+
3
+ module CloudFormula
4
+ class CloudFormation
5
+ class << self
6
+ # Convenience method which creates a new CloudFormation stack.
7
+ #
8
+ # @param [String] region A valid AWS region, e.g. "us-east-1", "us-west-2", etc
9
+ # @param [String] stack_name
10
+ # @param [CloudFormula::Template] template
11
+ # @param [Hash] override_options Optional. Stack options to override. See README.md "CloudFormation stack options" for details
12
+ # @param [Hash] parameters Optional. Parameters to supply to the template.
13
+ # @param [String] aws_access_key_id Optional. If not supplied, AWS_ACCESS_KEY_ID must exist in ENV
14
+ # @param [String] aws_secret_key Optional. If not supplied, AWS_SECRET_ACCESS_KEY must exist in ENV
15
+ # @return [AWS::CloudFormation::Stack]
16
+ def create_stack(region, stack_name, template, override_options = {}, parameters = {}, aws_access_key_id = nil, aws_secret_key = nil)
17
+ validate template
18
+ cfm = get_cloud_formation region, aws_access_key_id, aws_secret_key
19
+ cfm.stacks.create(stack_name, template.generate, template.aws_options(override_options, parameters))
20
+ end
21
+
22
+ # Convenience method which updates an existing CloudFormation stack.
23
+ #
24
+ # @param [String] region A valid AWS region, e.g. "us-east-1", "us-west-2", etc
25
+ # @param [String] stack_name
26
+ # @param [CloudFormula::Template] template
27
+ # @param [Hash] parameters Optional. Parameters to supply to the template.
28
+ # @param [String] aws_access_key_id Optional. If not supplied, AWS_ACCESS_KEY_ID must exist in ENV
29
+ # @param [String] aws_secret_key Optional. If not supplied, AWS_SECRET_ACCESS_KEY must exist in ENV
30
+ # @return nil
31
+ def update_stack(region, stack_name, template, parameters = {}, aws_access_key_id = nil, aws_secret_key = nil)
32
+ validate template
33
+ cfm = get_cloud_formation region, aws_access_key_id, aws_secret_key
34
+ stack = cfm.stacks[stack_name]
35
+ stack.update({ :template => template.generate, :parameters => parameters })
36
+ end
37
+
38
+ private
39
+
40
+ # @param [CloudFormula::Template] template
41
+ def validate(template)
42
+ raise ArgumentError.new("Invalid template: #{template.inspect}") if !template.is_a?(CloudFormula::Template)
43
+ end
44
+
45
+ def get_cloud_formation(region, aws_access_key_id = nil, aws_secret_key = nil)
46
+ options = { :region => region }
47
+ options[:access_key_id] = aws_access_key_id unless aws_access_key_id.nil?
48
+ options[:secret_access_key] = aws_secret_key unless aws_secret_key.nil?
49
+ AWS::CloudFormation.new options
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,12 @@
1
+ NAME
2
+ cloudformula create
3
+
4
+ DESCRIPTION
5
+ Dynamically generates an AWS CloudFormation template and uses it to create a
6
+ new CloudFormation stack.
7
+
8
+ USAGE
9
+ cloudformula create --stack-name '<stackname>' --template '<template>' \\
10
+ --parameters '<params>' [--region '<region>']
11
+
12
+ OPTIONS
@@ -0,0 +1,10 @@
1
+ NAME
2
+ cloudformula generate
3
+
4
+ DESCRIPTION
5
+ Dynamically generates an AWS CloudFormation template and outputs the result.
6
+
7
+ USAGE
8
+ cloudformula generate --template '<template>' --parameters '<params>'
9
+
10
+ OPTIONS