stackr 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +86 -0
- data/Rakefile +6 -0
- data/exe/stackr +5 -0
- data/lib/stackr/cli.rb +193 -0
- data/lib/stackr/cloudformation.rb +160 -0
- data/lib/stackr/errors.rb +10 -0
- data/lib/stackr/template.rb +34 -0
- data/lib/stackr/template_helpers.rb +16 -0
- data/lib/stackr/version.rb +3 -0
- data/lib/stackr.rb +6 -0
- data/stackr.gemspec +29 -0
- data/templates/generator.rb.tt +42 -0
- data/templates/project/.env.example +19 -0
- data/templates/project/.gitignore +2 -0
- data/templates/project/.ruby-gemset.tt +1 -0
- data/templates/project/Gemfile +3 -0
- data/templates/project/includes/environment_map.rb +15 -0
- data/templates/project/templates/.keep +0 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3ed539b980b7fb3b579691b37b7fd4e730cb3c58
|
4
|
+
data.tar.gz: 09df285fe2c3db0b1b1f658bd65d88c856291798
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 56a2d4cc0a7a07c1c57b6217abc091a12b593d1713467d8978a560389bd991590029b0c19cbadfa7efcce302d0abb3b0800ff977a027b8f258f787659f340aa2
|
7
|
+
data.tar.gz: 357b0e3ca89f0a0126c1ef54592161fd61153fa6bc13abcfaafe7f3ffaff1de23e0abe8e36b2059daeeabad6928d66ff9fdddf7d386a502b1fb5f5c044431ce5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
stackr
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Leaf Software Solutions
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# Stackr
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/LeafSoftware/stackr.svg?branch=master)](https://travis-ci.org/LeafSoftware/stackr)
|
4
|
+
|
5
|
+
Create CloudFormation templates using ruby DSL and launch them with a CLI.
|
6
|
+
|
7
|
+
## TODO
|
8
|
+
|
9
|
+
* Add options to create-template to add boilerplate stuff like vpc, instance, etc
|
10
|
+
* Add tests for cli for all the exception handling
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'stackr'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install stackr
|
27
|
+
|
28
|
+
## Configuration
|
29
|
+
|
30
|
+
You must configure your AWS credentials to use stackr. Refer to the ruby [aws-sdk](http://docs.aws.amazon.com/sdkforruby/api/).
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
```
|
35
|
+
Commands:
|
36
|
+
stackr create-project PROJECT_NAME # create stackr project
|
37
|
+
stackr create-stack TEMPLATE # create a stack from TEMPLATE
|
38
|
+
stackr create-template TEMPLATE # create a new template generator
|
39
|
+
stackr delete-stack STACK # delete the stack named STACK
|
40
|
+
stackr generate-template TEMPLATE # write the template json file
|
41
|
+
stackr help [COMMAND] # Describe available commands
|
42
|
+
stackr list-stacks # list all stacks
|
43
|
+
stackr update-stack TEMPLATE # update the stack created from TEMPLATE
|
44
|
+
stackr validate-template TEMPLATE # Verify template and parameters
|
45
|
+
stackr version # show version
|
46
|
+
```
|
47
|
+
|
48
|
+
1. Create a project with ```stackr create-project myproject```
|
49
|
+
2. Change directory into your project ```cd myproject```
|
50
|
+
3. ```cp .env.example .env``` and edit ```.env```
|
51
|
+
5. ```source .env```
|
52
|
+
2. Create a template with ```stackr create-template mytemplate```
|
53
|
+
3. Edit the new template in ```templates/mytemplate.rb``` adding parameters, resources, outputs, etc. See [cloudformation-ruby-dsl](https://github.com/bazaarvoice/cloudformation-ruby-dsl) for tips
|
54
|
+
4. Run ```stackr generate-template mytemplate``` and review the json document created at ```templates/mytemplate.json```
|
55
|
+
5. Create a CloudFormation stack from your template using ```stackr create-stack mytemplate```
|
56
|
+
6. List all of your stacks with ```stackr list-stacks```
|
57
|
+
7. Tear your stack down with ```stackr delete-stack mytemplate```
|
58
|
+
|
59
|
+
## Parameter Mapping
|
60
|
+
|
61
|
+
Many times you want to include secrets as stack parameters. These secrets do not belong in your source code. So we hand them in as environment variables.
|
62
|
+
|
63
|
+
You can set up a mapping between stack parameters and environment variables using the template parameter_map method.
|
64
|
+
|
65
|
+
This example tells stackr to fill in the "Environment" stack parameter with the contents of $ENVIRONMENT when creating or updating the stack.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
t.parameter_map = {
|
69
|
+
'Environment' => 'ENVIRONMENT'
|
70
|
+
}
|
71
|
+
```
|
72
|
+
|
73
|
+
You can use a ```.env``` file for your environment variables. It's included in the project .gitignore file.
|
74
|
+
|
75
|
+
## Environment Map
|
76
|
+
|
77
|
+
You may want to use the same template to launch stacks in different environments (e.g. 'dev', 'prd', 'test'). You can edit ```includes/environment_map.rb``` to configure your different environments. This is useful when you are creating resources in different VPCs or Regions for different environments.
|
78
|
+
|
79
|
+
## Contributing
|
80
|
+
|
81
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/LeafSoftware/stackr.
|
82
|
+
|
83
|
+
|
84
|
+
## License
|
85
|
+
|
86
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/stackr
ADDED
data/lib/stackr/cli.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Stackr
|
4
|
+
class Cli < Thor
|
5
|
+
|
6
|
+
no_commands do
|
7
|
+
def project_path=(name)
|
8
|
+
@project_path = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def project_path
|
12
|
+
@project_path || File.basename(Dir.getwd)
|
13
|
+
end
|
14
|
+
|
15
|
+
def templates_path=(path)
|
16
|
+
@templates_path = path
|
17
|
+
end
|
18
|
+
|
19
|
+
def templates_path()
|
20
|
+
@templates_path || 'templates'
|
21
|
+
end
|
22
|
+
|
23
|
+
def json_template_path(name)
|
24
|
+
File.join(templates_path, "#{name}.json")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Only load the template the first time
|
28
|
+
def load_template(name)
|
29
|
+
if @template.nil?
|
30
|
+
template_file = File.join(templates_path, "#{name}.rb")
|
31
|
+
@template = Stackr::Template.load(template_file)
|
32
|
+
end
|
33
|
+
@template
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
## version
|
38
|
+
desc 'version', 'show version'
|
39
|
+
def version
|
40
|
+
say Stackr::VERSION
|
41
|
+
end
|
42
|
+
|
43
|
+
## project
|
44
|
+
include Thor::Actions
|
45
|
+
def self.source_root
|
46
|
+
File.join(File.dirname(__FILE__),'..','..')
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'create-project PROJECT_NAME', 'create stackr project'
|
50
|
+
def create_project(name)
|
51
|
+
@project_name = name
|
52
|
+
|
53
|
+
say "Creating stackr project: #{name}\n"
|
54
|
+
directory 'templates/project', name
|
55
|
+
end
|
56
|
+
|
57
|
+
## create-template
|
58
|
+
desc 'create-template TEMPLATE', 'create a new template generator'
|
59
|
+
def create_template(name)
|
60
|
+
@template_name = name
|
61
|
+
say "Creating template generator #{name}\n"
|
62
|
+
template 'templates/generator.rb.tt', File.join(templates_path, "#{name}.rb")
|
63
|
+
end
|
64
|
+
|
65
|
+
## generate-template
|
66
|
+
desc 'generate-template TEMPLATE', 'write the template json file'
|
67
|
+
def generate_template(template_name)
|
68
|
+
template = load_template(template_name)
|
69
|
+
if !template
|
70
|
+
say "There is no template named \'#{template_name}\'."
|
71
|
+
return
|
72
|
+
end
|
73
|
+
|
74
|
+
json_file = json_template_path(template_name)
|
75
|
+
|
76
|
+
say "Writing #{template_name} to #{json_file}\n"
|
77
|
+
File.open(json_file, 'w') do |f|
|
78
|
+
f.write(template.body)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
## validate-template
|
83
|
+
desc 'validate-template TEMPLATE', 'Verify template and parameters'
|
84
|
+
def validate_template(template_name)
|
85
|
+
template = load_template(template_name)
|
86
|
+
if !template
|
87
|
+
say "There is no template named \'#{template_name}\'."
|
88
|
+
return
|
89
|
+
end
|
90
|
+
|
91
|
+
launcher = Stackr::CloudFormation.new
|
92
|
+
begin
|
93
|
+
launcher.validate_template(template)
|
94
|
+
rescue Aws::S3::Errors::ServiceError => e
|
95
|
+
say e.message
|
96
|
+
return false
|
97
|
+
else
|
98
|
+
say "Template #{template_name} validates."
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
## create-stack
|
104
|
+
desc 'create-stack TEMPLATE', 'create a stack from TEMPLATE'
|
105
|
+
option :name,
|
106
|
+
aliases: '-n',
|
107
|
+
desc: 'Stack name, defaults to template name'
|
108
|
+
|
109
|
+
option :disable_rollback,
|
110
|
+
type: :boolean,
|
111
|
+
default: false,
|
112
|
+
desc: 'disable rollback of failed stacks'
|
113
|
+
|
114
|
+
# TODO: log parameters and their values
|
115
|
+
def create_stack(template_name)
|
116
|
+
return if !validate_template(template_name)
|
117
|
+
|
118
|
+
template = load_template(template_name)
|
119
|
+
if !template
|
120
|
+
say "There is no template named \'#{template_name}\'."
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
name = options[:name] || template.name
|
125
|
+
launcher = Stackr::CloudFormation.new
|
126
|
+
say "Creating CloudFormation stack #{name} from template #{template.name}\n"
|
127
|
+
begin
|
128
|
+
launcher.create_stack(template, options)
|
129
|
+
rescue Stackr::StackAlreadyExistsError => e
|
130
|
+
say e.message
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
## update-stack
|
135
|
+
desc 'update-stack TEMPLATE', 'update the stack created from TEMPLATE'
|
136
|
+
option :name,
|
137
|
+
aliases: '-n',
|
138
|
+
desc: 'Stack name, defaults to template name'
|
139
|
+
|
140
|
+
# TODO: log parameters and their values
|
141
|
+
def update_stack(template_name)
|
142
|
+
return if !validate_template(template_name)
|
143
|
+
|
144
|
+
template = load_template(template_name)
|
145
|
+
if !template
|
146
|
+
say "There is no template named \'#{template_name}\'."
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
name = options[:name] || template.name
|
151
|
+
say "Updating CloudFormation stack #{name} from template #{template.name}\n"
|
152
|
+
launcher = Stackr::CloudFormation.new
|
153
|
+
begin
|
154
|
+
launcher.update_stack(template, options)
|
155
|
+
rescue Stackr::StackMissingError => e
|
156
|
+
say e.message
|
157
|
+
rescue Stackr::StackUpdateNotRequiredError => e
|
158
|
+
say e.message
|
159
|
+
rescue Stackr::InsufficientCapabilitiesError => e
|
160
|
+
say e.message
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
## delete-stack
|
165
|
+
desc 'delete-stack STACK', 'delete the stack named STACK'
|
166
|
+
def delete_stack(stack_name)
|
167
|
+
say "Deleting CloudFormation stack #{stack_name}\n"
|
168
|
+
launcher = Stackr::CloudFormation.new
|
169
|
+
begin
|
170
|
+
launcher.delete_stack(stack_name)
|
171
|
+
rescue Stackr::StackMissingError => e
|
172
|
+
say e.message
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
## list-stacks
|
177
|
+
desc 'list-stacks', 'list all stacks'
|
178
|
+
def list_stacks
|
179
|
+
launcher = Stackr::CloudFormation.new
|
180
|
+
rows = []
|
181
|
+
launcher.list_stacks.each do |stack|
|
182
|
+
rows << [
|
183
|
+
stack.name,
|
184
|
+
stack.stack_status,
|
185
|
+
stack.creation_time ? stack.creation_time.iso8601 : '',
|
186
|
+
stack.last_updated_time ? stack.last_updated_time.iso8601 : ''
|
187
|
+
]
|
188
|
+
end
|
189
|
+
print_table rows
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'stackr'
|
3
|
+
|
4
|
+
module Stackr
|
5
|
+
class CloudFormation
|
6
|
+
|
7
|
+
# Is this template too big to include in API calls?
|
8
|
+
# If so, we better upload it to S3
|
9
|
+
def is_too_big?(template_str)
|
10
|
+
template_str.bytesize > 51200
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
# Is this template too big for CloudFormation
|
15
|
+
def is_way_too_big?(template_str)
|
16
|
+
template_str.bytesize > 460800
|
17
|
+
end
|
18
|
+
|
19
|
+
# Requires TEMPLATE_BUCKET environment variable to be set.
|
20
|
+
# If TEMPLATE_PREFIX environment variable is set, templates will be uploaded
|
21
|
+
# using that prefix.
|
22
|
+
def upload_to_s3(template_str, name)
|
23
|
+
s3 = Aws::S3::Resource.new
|
24
|
+
if ENV['TEMPLATE_BUCKET'].nil?
|
25
|
+
raise Stackr::MissingTemplateBucketError, 'Please set TEMPLATE_BUCKET environment variable before uploading templates to S3.'
|
26
|
+
end
|
27
|
+
bucket = s3.bucket(ENV['TEMPLATE_BUCKET'])
|
28
|
+
key = "#{name}.json"
|
29
|
+
if ENV['TEMPLATE_PREFIX']
|
30
|
+
key = "#{ENV['TEMPLATE_PREFIX']}/#{key}"
|
31
|
+
end
|
32
|
+
s3_object = bucket.object(key)
|
33
|
+
s3_object.put(body: template_str)
|
34
|
+
return s3_object.public_url
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return proper argument for CloudFormation api calls.
|
38
|
+
# If template is too big, upload it to s3 first and return
|
39
|
+
# {template_url: s3_url}
|
40
|
+
# otherwise return
|
41
|
+
# {template_body: template_contents}
|
42
|
+
# But if we've already uploaded the template to s3 this run,
|
43
|
+
# (because we validated it and then ran a create-stack),
|
44
|
+
# don't upload it a second time, just return the s3 url.
|
45
|
+
def template_argument(template)
|
46
|
+
|
47
|
+
if is_way_too_big? template.body
|
48
|
+
raise Stackr::TemplateTooBigError, "Template #{template.name} is too big for CloudFormation."
|
49
|
+
end
|
50
|
+
|
51
|
+
if is_too_big? template.body
|
52
|
+
if template.url.nil?
|
53
|
+
template.url = upload_to_s3(template.body, template.name)
|
54
|
+
end
|
55
|
+
return {template_url: template.url}
|
56
|
+
end
|
57
|
+
|
58
|
+
return {template_body: template.body}
|
59
|
+
end
|
60
|
+
|
61
|
+
def stack_parameters(parameter_map)
|
62
|
+
parameter_map.map { |k,v| {parameter_key: k, parameter_value: ENV[v]} }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Raise an error if the template does not validate
|
66
|
+
# takes a Stackr::Template
|
67
|
+
def validate_template(template)
|
68
|
+
cfn = Aws::CloudFormation::Client.new
|
69
|
+
|
70
|
+
opts = template_argument(template)
|
71
|
+
begin
|
72
|
+
resp = cfn.validate_template(opts)
|
73
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
74
|
+
raise Stackr::TemplateValidationError, e.message
|
75
|
+
end
|
76
|
+
|
77
|
+
# validate parameters
|
78
|
+
# Make sure each parameter w/o a default has a value
|
79
|
+
resp.parameters.each do |param|
|
80
|
+
param_name = param[:parameter_key]
|
81
|
+
env_var = template.parameter_map[param_name]
|
82
|
+
|
83
|
+
if param[:default_value].nil? && ENV[env_var].nil?
|
84
|
+
raise Stackr::MissingParameterError, "Required parameter #{param_name} (#{env_var}) not specified."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Takes a Stackr::Template
|
90
|
+
def create_stack(template, options)
|
91
|
+
cfn = Aws::CloudFormation::Resource.new
|
92
|
+
stack_name = options[:name] || template.name
|
93
|
+
|
94
|
+
opts = {
|
95
|
+
stack_name: options[:name] || template.name,
|
96
|
+
parameters: stack_parameters(template.parameter_map),
|
97
|
+
disable_rollback: options[:disable_rollback],
|
98
|
+
capabilities: template.capabilities
|
99
|
+
}
|
100
|
+
|
101
|
+
# are we using template_body or template_url?
|
102
|
+
opts.merge!( template_argument(template) )
|
103
|
+
|
104
|
+
# Trap error raised when stack already exists
|
105
|
+
begin
|
106
|
+
cfn.create_stack(opts)
|
107
|
+
rescue Aws::CloudFormation::Errors::AlreadyExistsException => e
|
108
|
+
raise Stackr::StackAlreadyExistsError, e.message
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Takes a Stackr::Template
|
113
|
+
def update_stack(template, options)
|
114
|
+
cfn = Aws::CloudFormation::Resource.new
|
115
|
+
stack_name = options[:name] || template.name
|
116
|
+
stack = cfn.stack(stack_name)
|
117
|
+
if !stack
|
118
|
+
raise Stackr::StackMissingError, "Stack #{stack_name} does not exist."
|
119
|
+
end
|
120
|
+
|
121
|
+
opts = {
|
122
|
+
parameters: stack_parameters(template.parameter_map),
|
123
|
+
capabilities: template.capabilities
|
124
|
+
}
|
125
|
+
|
126
|
+
# are we using template_body or template_url?
|
127
|
+
opts.merge!( template_argument(template) )
|
128
|
+
|
129
|
+
begin
|
130
|
+
stack.update(opts)
|
131
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
132
|
+
case e.message
|
133
|
+
when 'No updates are to be performed.'
|
134
|
+
raise Stackr::StackUpdateNotRequiredError, "Stack [#{stack_name}] requires no updates."
|
135
|
+
when "Stack [#{stack_name}] does not exist"
|
136
|
+
raise Stackr::StackMissingError, e.message
|
137
|
+
else
|
138
|
+
raise e
|
139
|
+
end
|
140
|
+
rescue Aws::CloudFormation::Errors::InsufficientCapabilitiesException => e
|
141
|
+
raise Stackr::InsufficientCapabilitiesError, "#{e.message}\nPlease add them to your template and run update again."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def delete_stack(stack_name)
|
146
|
+
cfn = Aws::CloudFormation::Resource.new
|
147
|
+
stack = cfn.stack(stack_name)
|
148
|
+
if stack.exists?
|
149
|
+
stack.delete
|
150
|
+
else
|
151
|
+
raise Stackr::StackMissingError, "Stack [#{stack_name}] does not exist."
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def list_stacks
|
156
|
+
cfn = Aws::CloudFormation::Resource.new
|
157
|
+
cfn.stacks
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Stackr
|
2
|
+
class StackMissingError < StandardError; end
|
3
|
+
class StackAlreadyExistsError < StandardError; end
|
4
|
+
class StackUpdateNotRequiredError < StandardError; end
|
5
|
+
class InsufficientCapabilitiesError < StandardError; end
|
6
|
+
class TemplateValidationError < StandardError; end
|
7
|
+
class MissingParameterError < StandardError; end
|
8
|
+
class TemplateTooBigError < StandardError; end
|
9
|
+
class MissingTemplateBucketError < StandardError; end
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'stackr/template_helpers'
|
3
|
+
|
4
|
+
include Stackr::TemplateHelpers
|
5
|
+
|
6
|
+
module Stackr
|
7
|
+
class Template
|
8
|
+
attr_accessor :name, :parameter_map, :template_dsl, :capabilities
|
9
|
+
attr_accessor :includes_path, :url
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@capabilities = []
|
13
|
+
@parameter_map = {}
|
14
|
+
@includes_path = 'includes'
|
15
|
+
end
|
16
|
+
|
17
|
+
# eval the contents in the context of this class
|
18
|
+
def self.load(template_file)
|
19
|
+
return nil if !File.exist?(template_file)
|
20
|
+
eval File.read(template_file)
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate
|
24
|
+
JSON.pretty_generate(template_dsl)
|
25
|
+
end
|
26
|
+
|
27
|
+
def body
|
28
|
+
if @body.nil?
|
29
|
+
@body = generate()
|
30
|
+
end
|
31
|
+
@body
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'cloudformation-ruby-dsl/cfntemplate'
|
2
|
+
|
3
|
+
module Stackr
|
4
|
+
module TemplateHelpers
|
5
|
+
# Put all helper methods that you want to add to the DSL here.
|
6
|
+
# TODO: Make something that loads project helpers too.
|
7
|
+
|
8
|
+
def find_in_env(name)
|
9
|
+
find_in_map('EnvironmentMap', ref('Environment'), name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def include_file(filepath, locals={})
|
13
|
+
interpolate(file(filepath), locals)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/stackr.rb
ADDED
data/stackr.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stackr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "stackr"
|
8
|
+
spec.version = Stackr::VERSION
|
9
|
+
spec.authors = ["Chris Chalfant"]
|
10
|
+
spec.email = ["cchalfant@leafsoftwaresolutions.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Framework for managing CloudFormation stacks}
|
13
|
+
spec.description = %q{Framework for managing CloudFormation stacks}
|
14
|
+
spec.homepage = "https://github.com/LeafSoftware/stackr"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "aws-sdk"
|
23
|
+
spec.add_dependency "cloudformation-ruby-dsl"
|
24
|
+
spec.add_dependency "thor"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Stackr::Template.new.tap do |t|
|
2
|
+
|
3
|
+
t.name = '<%= @template_name %>' # Name for this stack
|
4
|
+
|
5
|
+
# Map CloudFormation Stack parameters to environment variables
|
6
|
+
# Use this to hand in secrets that don't belong in the environment map
|
7
|
+
# like database passwords.
|
8
|
+
t.parameter_map = {
|
9
|
+
'Environment' => 'ENVIRONMENT'
|
10
|
+
}
|
11
|
+
|
12
|
+
# Uncomment if you have IAM resources in this template
|
13
|
+
# t.capabilities = ['CAPABILITY_IAM']
|
14
|
+
# Or, if you have custom names for IAM resources, this instead
|
15
|
+
# t.capabilities = ['CAPABILITY_NAMED_IAM']
|
16
|
+
|
17
|
+
##
|
18
|
+
## CloudFormation template stuff goes here
|
19
|
+
## See https://github.com/bazaarvoice/cloudformation-ruby-dsl
|
20
|
+
##
|
21
|
+
t.template_dsl = template do
|
22
|
+
value AWSTemplateFormatVersion: '2010-09-09'
|
23
|
+
value Description: 'Template for <%= @template_name %>'
|
24
|
+
|
25
|
+
parameter 'Environment',
|
26
|
+
Description: 'Launch stack in this environment',
|
27
|
+
Type: 'String',
|
28
|
+
Default: 'dev',
|
29
|
+
AllowedValues: ['dev'] # Add new environment strings as necessary
|
30
|
+
|
31
|
+
# Add other parameters here.
|
32
|
+
|
33
|
+
# Add in the environment map in includes/environment_map.rb
|
34
|
+
# You can edit the map to include environment specific things
|
35
|
+
# like Vpc, Subnets, Security Groups, etc.
|
36
|
+
mapping 'EnvironmentMap', File.join(includes_path, 'environment_map.rb')
|
37
|
+
|
38
|
+
# Add resources here.
|
39
|
+
|
40
|
+
# Add outputs here.
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# example .env file
|
3
|
+
# cp .env.example .env
|
4
|
+
#
|
5
|
+
# Set environment variables for all template parameters here.
|
6
|
+
# This .env file is in gitignore to help keep application
|
7
|
+
# secrets from leaking out into source code control.
|
8
|
+
#
|
9
|
+
# I suggest something like https://github.com/kennethreitz/autoenv
|
10
|
+
# to automagically set your environment when you cd into the project
|
11
|
+
# directory.
|
12
|
+
export ENVIRONMENT=dev
|
13
|
+
|
14
|
+
# If templates are too big the CloudFormation API, they must be uploaded
|
15
|
+
# to s3. Set this variable to the bucket you want to use.
|
16
|
+
# export TEMPLATE_BUCKET=mytemplatebucket
|
17
|
+
|
18
|
+
# You can prefix the templates in the bucket as well.
|
19
|
+
# export TEMPLATE_PREFIX=stackr-templates
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @project_name %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
'Mappings' => {
|
3
|
+
'EnvironmentMap' => {
|
4
|
+
# In your template, use find_in_env('VpcId').
|
5
|
+
# It will use the Environment parameter to determine
|
6
|
+
# which VpcId to return.
|
7
|
+
dev: {
|
8
|
+
# VpcId: '' # Your Dev VPC
|
9
|
+
},
|
10
|
+
test: {
|
11
|
+
# VpcId: '' # Your Test VPC
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stackr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Chalfant
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cloudformation-ruby-dsl
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description: Framework for managing CloudFormation stacks
|
98
|
+
email:
|
99
|
+
- cchalfant@leafsoftwaresolutions.com
|
100
|
+
executables:
|
101
|
+
- stackr
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".rspec"
|
107
|
+
- ".ruby-gemset"
|
108
|
+
- ".ruby-version"
|
109
|
+
- ".travis.yml"
|
110
|
+
- Gemfile
|
111
|
+
- LICENSE.txt
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- exe/stackr
|
115
|
+
- lib/stackr.rb
|
116
|
+
- lib/stackr/cli.rb
|
117
|
+
- lib/stackr/cloudformation.rb
|
118
|
+
- lib/stackr/errors.rb
|
119
|
+
- lib/stackr/template.rb
|
120
|
+
- lib/stackr/template_helpers.rb
|
121
|
+
- lib/stackr/version.rb
|
122
|
+
- stackr.gemspec
|
123
|
+
- templates/generator.rb.tt
|
124
|
+
- templates/project/.env.example
|
125
|
+
- templates/project/.gitignore
|
126
|
+
- templates/project/.ruby-gemset.tt
|
127
|
+
- templates/project/Gemfile
|
128
|
+
- templates/project/includes/environment_map.rb
|
129
|
+
- templates/project/templates/.keep
|
130
|
+
homepage: https://github.com/LeafSoftware/stackr
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.5.1
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Framework for managing CloudFormation stacks
|
154
|
+
test_files: []
|