stackr 1.0.0
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 +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
|
+
[](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: []
|