rubycfn 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +1 -1
- data/README.md +219 -299
- data/bin/rubycfn +2 -0
- data/lib/rubycfn/version.rb +1 -1
- data/lib/rubycfn.rb +2 -0
- data/templates/.gitignore.erb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85ac61bfe1b03a66e40625c1b060e1bb95cbbe22
|
4
|
+
data.tar.gz: 9f15208720740bf70662579c0e59dd211ac94376
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8495e7b2a758bddddd44b2c343f4ed36208dcaa350549cdf635cd460da63fd68094fb294630e9dd187d0d296e9a7f2c0cc2dc9e93a639ef3bef1c14af8819e61
|
7
|
+
data.tar.gz: f7f4cace496dec204bf5ae8bc7ec0c1e149d351fab3d8ed05b3f28af500c4c5a3b847ea990588529ad1c8d06d02480ff53fd895baf649e8bc67bc654a7884513
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,12 @@
|
|
2
2
|
All notable changes to Rubycfn will be documented in this file.
|
3
3
|
This project uses [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
-
## 0.3.
|
5
|
+
## 0.3.8 (Next Release)
|
6
|
+
|
7
|
+
## 0.3.7
|
8
|
+
|
9
|
+
* Allow symbols to be passed to Ref as argument for Fn::GetAtt -- [@dennisvink][@dennisvink]
|
10
|
+
* Added .gitignore to default project template -- [@dennisvink][@dennisvink]
|
6
11
|
|
7
12
|
## 0.3.6
|
8
13
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,20 +1,59 @@
|
|
1
1
|
# RubyCfn
|
2
2
|
|
3
|
-
[RubyCfn](https://rubycfn.com/) is a light-weight tiny CloudFormation, Deployment Manager
|
3
|
+
[RubyCfn](https://rubycfn.com/) is a light-weight tiny CloudFormation, and Deployment Manager to make expressing
|
4
4
|
AWS templates as Ruby code a bit more pleasing to the eye.
|
5
5
|
|
6
6
|
You can find the [CloudFormation Compiler](https://rubycfn.com/) at https://rubycfn.com with examples.
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
a
|
8
|
+
## Philosophy
|
9
|
+
|
10
|
+
Standardisation is key to keep your engineering team agile. Time spent on projects that deviate from a standard implementation is time taken away from delivering value. Custom implementations are detrimental to a team’s velocity and scalability. It hinders knowledge sharing as a select few have knowledge about the specifics of such a custom implementation, and because the wheel is reinvented many times over proper testing is tedious at best. We’ve automated best practices and ensured that new projects automatically incorporate our principles. Our tooling has been built with cloud engineer happiness in mind.
|
11
11
|
|
12
12
|
## Quick start
|
13
13
|
|
14
14
|
Install Rubycfn:
|
15
|
-
|
15
|
+
```
|
16
|
+
gem install rubycfn
|
17
|
+
echo "resource :my_s3_bucket, type: 'AWS::S3::Bucket'" | rubycfn
|
18
|
+
```
|
19
|
+
|
20
|
+
The Rubycfn CLI can be piped to or takes a file name as argument. You can start
|
21
|
+
by cloning the [Rubycfn Examples Repository](https://github.com/dennisvink/rubycfn-examples/). To generate a CloudFormation template type:
|
22
|
+
|
23
|
+
`cat "3. Deploying a Serverless function.rb" | rubycfn`
|
24
|
+
|
25
|
+
or
|
26
|
+
|
27
|
+
`rubycfn "3. Deploying a Serverless function.rb"`
|
28
|
+
|
29
|
+
to generate the CloudFormation template for that example.
|
30
|
+
|
31
|
+
Now take this `template.rb` as an example:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
parameter :bucket_name,
|
35
|
+
description: "Bucket name"
|
36
|
+
|
37
|
+
resource :foobar,
|
38
|
+
type: "AWS::S3::Bucket" do |r|
|
39
|
+
r.property(:name) { :bucket_name.ref }
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
You can generate a CloudFormation template from this script in the following ways:
|
44
|
+
`cat template.rb | rubycfn`
|
45
|
+
|
46
|
+
or
|
47
|
+
|
48
|
+
`rubycfn template.rb`
|
49
|
+
|
50
|
+
Both commands will output the CloudFormation template without the need for you to set up a project.
|
51
|
+
|
52
|
+
## Setting up a Rubycfn project
|
53
|
+
|
54
|
+
For projects that extend beyond a simnple stack or those that require unit
|
55
|
+
testing you can create a Rubycfn project in the following way:
|
16
56
|
|
17
|
-
Starting a new Rubycfn project:
|
18
57
|
`rubycfn`
|
19
58
|
```$ rubycfn
|
20
59
|
__________ ____ __________________.___._________ _____________________
|
@@ -22,12 +61,14 @@ __________ ____ __________________.___._________ _____________________
|
|
22
61
|
| _/ | /| | _// | |/ \ \/ | __) | | _/
|
23
62
|
| | \ | / | | \\____ |\ \____| \ | | \
|
24
63
|
|____|_ /______/ |______ // ______| \______ /\___ / |______ /
|
25
|
-
\/ \/ \/ \/ \/ \/ [v0.3.
|
64
|
+
\/ \/ \/ \/ \/ \/ [v0.3.7]
|
26
65
|
Project name? example
|
27
66
|
Account ID? 1234567890
|
28
67
|
Select region EU (Frankfurt)
|
29
68
|
```
|
30
69
|
|
70
|
+
## Project commands
|
71
|
+
|
31
72
|
Installing project dependencies:
|
32
73
|
`bundle`
|
33
74
|
|
@@ -52,366 +93,245 @@ Checking difference between local and remote stack:
|
|
52
93
|
Deploying stack to AWS:
|
53
94
|
`rake apply`
|
54
95
|
|
55
|
-
##
|
96
|
+
## Anatomy of a Rubycfn project
|
56
97
|
|
57
|
-
|
58
|
-
|
59
|
-
## Overview of Rubycfn
|
60
|
-
|
61
|
-
RubyCfn is an abstraction layer around several Cloud templates such as CloudFormation (AWS). Rubycfn projects are set up for easy grouping of resources that have a mutual cohesion, and structured in such a way to make it easy for developers to quickly find what they need. Rubycfn is a so-called ‘DSL’ on top of these template formats and presents templates as code that is friendly to the eye and easy to read and understand. In addition of being an alternate representation of a template, Rubycfn allows you to combine template generation with programming logic making it far more versatile than what AWS offers in their templates. Last but not least Rubycfn enforces code quality by testing the generated templates against unit tests, checking if the expected resources and their configuration matches with what was actually generated, and by LINTing the templates and the underlying code that generates the templates.
|
62
|
-
|
63
|
-
Out of the box Rubycfn comes with a CI/CD pipeline. It’s a serverless pipeline running on Amazon Web Services (AWS), which you can fully configure using the complimentary `buildspec.yml`. The CI/CD pipeline is linked to a Github repository, and a change in this repository triggers the CI/CD pipeline to execute the steps you’ve defined in the buildspec.yml.
|
64
|
-
|
65
|
-
Typically a commit to your application GIT repository triggers the build process and the following things happen:
|
66
|
-
- Application code is checked out
|
67
|
-
- Infrastructure as code (Rubycfn project) is checked out
|
68
|
-
- Rubycfn project is ‘built’, kicking off unit tests against the underlying code, and against the resulting build artifact (template(s))
|
69
|
-
- Application (unit) tests are ran
|
70
|
-
- The build artifact is stored (versioned), so you can use it as input for your delivery pipeline
|
71
|
-
- The artifact may or may not include the application code. A part of the build process could - for example - also be that the application is dockerized and pushed to a docker registry.
|
72
|
-
- The resulting artifact is the complete recipe to deploy the application and associated resources to AWS
|
73
|
-
|
74
|
-
## Example code
|
75
|
-
|
76
|
-
You can find stack examples at [https://github.com/dennisvink/rubycfn-example](https://github.com/dennisvink/rubycfn-example/)
|
77
|
-
|
78
|
-
## Installing Gems
|
79
|
-
|
80
|
-
Type `bundle install` to install all dependencies that are listed in the Gemfile.
|
81
|
-
|
82
|
-
## Running specs
|
83
|
-
|
84
|
-
Type `rake` to run the tests. It tests if RubyCfn creates a valid
|
85
|
-
CloudFormation stack.
|
86
|
-
|
87
|
-
## Example usage
|
88
|
-
|
89
|
-
```ruby
|
90
|
-
require "rubycfn"
|
91
|
-
|
92
|
-
module DnsStack
|
93
|
-
extend ActiveSupport::Concern
|
94
|
-
include Rubycfn
|
98
|
+
A new Rubycfn project has the following structure:
|
95
99
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
100
|
+
```
|
101
|
+
total 112
|
102
|
+
drwxr-xr-x 14 dennis staff 448 Jul 15 20:43 .
|
103
|
+
drwxr-xr-x 17 dennis staff 544 Jul 15 20:43 ..
|
104
|
+
-rw-r--r-- 1 dennis staff 92 Jul 15 20:43 .env
|
105
|
+
-rw-r--r-- 1 dennis staff 325 Jul 15 20:43 .env.production
|
106
|
+
-rw-r--r-- 1 dennis staff 207 Jul 15 20:43 .env.rspec
|
107
|
+
-rw-r--r-- 1 dennis staff 298 Jul 15 20:43 .env.test
|
108
|
+
drwxr-xr-x 10 dennis staff 320 Jul 15 20:43 .git
|
109
|
+
-rw-r--r-- 1 dennis staff 1344 Jul 15 20:43 .rubocop.yml
|
110
|
+
-rw-r--r-- 1 dennis staff 502 Jul 15 20:43 Gemfile
|
111
|
+
-rw-r--r-- 1 dennis staff 27158 Jul 15 20:43 Gemfile.lock
|
112
|
+
-rw-r--r-- 1 dennis staff 998 Jul 15 20:43 Rakefile
|
113
|
+
drwxr-xr-x 2 dennis staff 64 Jul 15 20:43 build
|
114
|
+
drwxr-xr-x 7 dennis staff 224 Jul 15 20:43 lib
|
115
|
+
drwxr-xr-x 4 dennis staff 128 Jul 15 20:43 spec
|
116
|
+
```
|
113
117
|
|
114
|
-
|
115
|
-
amount: 1,
|
116
|
-
type: "AWS::SES::ConfigurationSet" do |r|
|
117
|
-
r.depends_on "HostedZone"
|
118
|
-
r.property(:name) { ["HostedZone".ref, "_SESConfigurationSet"].fnjoin }
|
119
|
-
end
|
118
|
+
Lets first discuss the files in the root folder.
|
120
119
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
120
|
+
```
|
121
|
+
.env Global environment variables, available in every environment
|
122
|
+
.env.production Environment variables available in production environment
|
123
|
+
.env.test Environment variables available in test environment
|
124
|
+
.env.rspec Environment variables available to unit tests
|
125
|
+
.rubocop.yml Ruby LINTer configuration to enforce good code style
|
126
|
+
Gemfile Ruby gem dependencies
|
127
|
+
Gemfile.lock Resolved gem dependencies
|
128
|
+
Rakefile Contains all Rubycfn rake tasks
|
129
|
+
```
|
127
130
|
|
128
|
-
|
129
|
-
end
|
131
|
+
#### .env
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
+
The `.env` file contains environment variables that are available, regardless of
|
134
|
+
the environment you're building for. For example:
|
133
135
|
|
134
136
|
```
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
{
|
140
|
-
"AWSTemplateFormatVersion": "2010-09-09",
|
141
|
-
"Parameters": {
|
142
|
-
"DomainName": {
|
143
|
-
"Default": "example.com",
|
144
|
-
"Description": "Domain name for the HostedZone",
|
145
|
-
"Type": "String"
|
146
|
-
}
|
147
|
-
},
|
148
|
-
"Resources": {
|
149
|
-
"HostedZone": {
|
150
|
-
"Properties": {
|
151
|
-
"HostedZoneConfig": {
|
152
|
-
"Comment": {
|
153
|
-
"Fn::Join": [
|
154
|
-
"",
|
155
|
-
[
|
156
|
-
"Hosted Zone for ",
|
157
|
-
{
|
158
|
-
"Ref": "DomainName"
|
159
|
-
}
|
160
|
-
]
|
161
|
-
]
|
162
|
-
}
|
163
|
-
},
|
164
|
-
"Name": {
|
165
|
-
"Ref": "DomainName"
|
166
|
-
}
|
167
|
-
},
|
168
|
-
"Type": "AWS::Route53::HostedZone"
|
169
|
-
},
|
170
|
-
"HostedZoneSes": {
|
171
|
-
"DependsOn": [
|
172
|
-
"HostedZone"
|
173
|
-
],
|
174
|
-
"Properties": {
|
175
|
-
"Name": {
|
176
|
-
"Fn::Join": [
|
177
|
-
"",
|
178
|
-
[
|
179
|
-
{
|
180
|
-
"Ref": "HostedZone"
|
181
|
-
},
|
182
|
-
"_SESConfigurationSet"
|
183
|
-
]
|
184
|
-
]
|
185
|
-
}
|
186
|
-
},
|
187
|
-
"Type": "AWS::SES::ConfigurationSet"
|
188
|
-
}
|
189
|
-
},
|
190
|
-
"Outputs": {
|
191
|
-
"HostedZoneId": {
|
192
|
-
"Description": "HostedZoneId",
|
193
|
-
"Export": {
|
194
|
-
"Name": {
|
195
|
-
"Fn::Join": [
|
196
|
-
":",
|
197
|
-
[
|
198
|
-
{
|
199
|
-
"Ref": "AWS::StackName"
|
200
|
-
},
|
201
|
-
"HostedZoneId"
|
202
|
-
]
|
203
|
-
]
|
204
|
-
}
|
205
|
-
},
|
206
|
-
"Value": {
|
207
|
-
"Ref": "HostedZone"
|
208
|
-
}
|
209
|
-
}
|
210
|
-
}
|
211
|
-
}
|
137
|
+
AWS_ACCOUNT_ID="1234567890"
|
138
|
+
AWS_REGION="eu-west-1"
|
139
|
+
ENVIRONMENT="rspec"
|
140
|
+
PROJECT_NAME="sample"
|
212
141
|
```
|
213
142
|
|
214
|
-
|
215
|
-
necessary as this defaults to 1 if you omit it. If you want multiple resources of
|
216
|
-
the same type just increase the amount. The resource names will be enumerated,
|
217
|
-
e.g. Resource, Resource2, Resource3, etc.
|
218
|
-
|
219
|
-
With |r, index| you will be have to access the index number of the generated
|
220
|
-
resource in the `index` variable.
|
143
|
+
#### .env.production and .env.test
|
221
144
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
resource :my_s3_bucket,
|
226
|
-
amount: 10,
|
227
|
-
type: "AWS::S3::Bucket" do |r, index|
|
145
|
+
The `.env.production` and `.env.test` files contain environment variables that
|
146
|
+
are specific to production or test respectively. For example `.env.test` can
|
147
|
+
contain something like this:
|
228
148
|
|
229
|
-
|
230
|
-
|
149
|
+
```
|
150
|
+
# ENV vars for test environment
|
151
|
+
CLOUD_TRAIL_MONITOR_SNS_RECIPIENTS="changeme@example.com,changemetoo@example.com"
|
152
|
+
ROOT_MONITOR_SNS_RECIPIENTS="changeme@example.com,changemetoo@example.com"
|
153
|
+
VPC_CIDR_BLOCK="10.100.0.0/16"
|
154
|
+
ARTIFACT_BUCKET="my-awesome-cloudformation-artifact-bucket"
|
155
|
+
STACK_NAME="test"
|
231
156
|
```
|
232
157
|
|
233
|
-
|
234
|
-
|
235
|
-
It is not necessary to start a Rubycfn project to generate CloudFormation templates.
|
236
|
-
The examples from the [Rubycfn CloudFormation Compiler](https://rubycfn.com/) can be used with the Rubycfn cli directly.
|
237
|
-
Take this `template.rb` as an example:
|
238
|
-
|
239
|
-
```ruby
|
240
|
-
parameter :bucket_name,
|
241
|
-
description: "Bucket name"
|
158
|
+
While .env.production can look something like this:
|
242
159
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
160
|
+
```
|
161
|
+
# ENV vars for production environment
|
162
|
+
CLOUD_TRAIL_MONITOR_SNS_RECIPIENTS="changeme@example.com,changemetoo@example.com"
|
163
|
+
ROOT_MONITOR_SNS_RECIPIENTS="changeme@example.com,changemetoo@example.com"
|
164
|
+
VPC_CIDR_BLOCK="10.200.0.0/16"
|
165
|
+
ARTIFACT_BUCKET="my-awesome-cloudformation-artifact-bucket-for-production"
|
166
|
+
STACK_NAME="production"
|
247
167
|
```
|
248
168
|
|
249
|
-
You can
|
250
|
-
`cat template.rb | rubycfn`
|
169
|
+
You can reuse these environment variables in your project code.
|
251
170
|
|
252
|
-
|
171
|
+
#### .env.rspec
|
253
172
|
|
254
|
-
`
|
173
|
+
The `.env.rspec` is used when running unit tests. It contains mock variables
|
174
|
+
so that you can test the resulting CloudFormation templates properly.
|
255
175
|
|
256
|
-
|
176
|
+
#### The missing .env.private file
|
257
177
|
|
258
|
-
|
178
|
+
There is one file that is not generated by default but does need mentioning:
|
179
|
+
the `.env.private` file. This is a special file that allows you to override
|
180
|
+
environment variables. An environment variable set in .env.private always takes
|
181
|
+
precedence over environment variables set in other .env* files.
|
259
182
|
|
260
|
-
|
261
|
-
If you supply an argument to .ref it'll be rendered as Fn::GetAtt. Last but
|
262
|
-
not least, calling Fn::Join is achieved by postpending .fnjoin to an array.
|
263
|
-
You can provide it with an argument for the separator. By default its "".
|
183
|
+
#### .rubocop.yml
|
264
184
|
|
265
|
-
|
185
|
+
The `.rubocop.yml` file contains configuration for the code linter. When running
|
186
|
+
`rubocop` from the root folder of your project it will error on code style
|
187
|
+
violations.
|
266
188
|
|
267
|
-
|
189
|
+
### Rubycfn project directories
|
268
190
|
|
269
|
-
|
191
|
+
As shown before, a Rubycfn project contains three directories:
|
270
192
|
|
271
|
-
|
193
|
+
```
|
194
|
+
drwxr-xr-x 2 dennis staff 64 Jul 15 20:43 build
|
195
|
+
drwxr-xr-x 7 dennis staff 224 Jul 15 20:43 lib
|
196
|
+
drwxr-xr-x 4 dennis staff 128 Jul 15 20:43 spec
|
197
|
+
```
|
272
198
|
|
273
|
-
|
199
|
+
#### build
|
274
200
|
|
275
|
-
|
276
|
-
|
201
|
+
The build directory is where your resulting CloudFormation templates will be
|
202
|
+
stored.
|
277
203
|
|
278
|
-
|
204
|
+
#### lib
|
279
205
|
|
280
|
-
|
206
|
+
The 'lib' directory contains all your stacks, modules and project libraries.
|
207
|
+
This directory is the most important, as this is the directory where you work
|
208
|
+
in. I will go into more detail on the `lib` directory in the next chapter.
|
281
209
|
|
282
|
-
|
210
|
+
#### spec
|
283
211
|
|
284
|
-
|
285
|
-
|
286
|
-
-rw-r--r-- 1 binx staff 246 Oct 17 16:06 Gemfile
|
287
|
-
-rw-r--r-- 1 binx staff 346 Oct 17 16:06 Rakefile
|
288
|
-
drwxr-xr-x 2 binx staff 64 Oct 17 16:06 build
|
289
|
-
drwxr-xr-x 3 binx staff 96 Oct 17 16:06 config
|
290
|
-
-rw-r--r-- 1 binx staff 15 Oct 17 16:06 format.vim
|
291
|
-
drwxr-xr-x 6 binx staff 192 Oct 17 16:06 lib
|
292
|
-
drwxr-xr-x 4 binx staff 128 Oct 17 16:06 spec
|
212
|
+
The `spec` directory contains all unit tests. They are executed with the
|
213
|
+
`rake spec` command.
|
293
214
|
|
294
|
-
|
215
|
+
## The lib directory
|
295
216
|
|
296
|
-
|
217
|
+
As mentioned the `lib` directory is the most important directory. When you
|
218
|
+
create a Rubycfn project it will contain the following by default:
|
297
219
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
220
|
+
```
|
221
|
+
total 8
|
222
|
+
drwxr-xr-x 7 dennis staff 224 Jul 15 20:43 .
|
223
|
+
drwxr-xr-x 16 dennis staff 512 Jul 15 21:08 ..
|
224
|
+
drwxr-xr-x 9 dennis staff 288 Jul 15 20:43 aws_helper
|
225
|
+
drwxr-xr-x 6 dennis staff 192 Jul 15 20:43 core
|
226
|
+
-rw-r--r-- 1 dennis staff 734 Jul 15 20:43 main.rb
|
227
|
+
drwxr-xr-x 5 dennis staff 160 Jul 15 20:43 shared_concerns
|
228
|
+
drwxr-xr-x 8 dennis staff 256 Jul 15 20:43 stacks
|
302
229
|
```
|
303
230
|
|
304
|
-
|
305
|
-
|
306
|
-
|
231
|
+
The `aws_helper` and `core` directories and the `main.rb` file contains helper
|
232
|
+
function and a lot of glue to make Rubycfn code compile and deploy. You should
|
233
|
+
never need to touch those files. In this section I'll focus on the
|
234
|
+
`shared_concerns` directory and the `stacks` directory.
|
307
235
|
|
308
|
-
|
236
|
+
To understand the purpose of the `shared_concerns` directory it's important to
|
237
|
+
understand that a stack consists of a parent stack file and modules. Lets say
|
238
|
+
you have a VPC stack: It will consist of a `vpc_stack.rb` file that includes
|
239
|
+
modules from the `vpc_stack/` directory. This modular approach keeps your
|
240
|
+
projects nice and tidy. By default, the shared_concerns directory contains a
|
241
|
+
global variables module, a shared methods module and a helper methods module.
|
309
242
|
|
310
|
-
|
243
|
+
The `shared_concerns/` directory also contain modules. The difference is that
|
244
|
+
these modules can be used in more than one stack. If you have resources or code
|
245
|
+
that you want to reuse cross stacks, create a shared concern.
|
311
246
|
|
312
|
-
The `
|
247
|
+
The `stacks` folder, by default, contains the following:
|
313
248
|
|
314
|
-
Example:
|
315
249
|
```
|
316
|
-
$ ls -al build/
|
317
250
|
total 24
|
318
|
-
drwxr-xr-x
|
319
|
-
drwxr-xr-x
|
320
|
-
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
251
|
+
drwxr-xr-x 8 dennis staff 256 Jul 15 20:43 .
|
252
|
+
drwxr-xr-x 7 dennis staff 224 Jul 15 20:43 ..
|
253
|
+
drwxr-xr-x 3 dennis staff 96 Jul 15 20:43 ecs_stack
|
254
|
+
-rw-r--r-- 1 dennis staff 254 Jul 15 20:43 ecs_stack.rb
|
255
|
+
drwxr-xr-x 3 dennis staff 96 Jul 15 20:43 parent_stack
|
256
|
+
-rw-r--r-- 1 dennis staff 259 Jul 15 20:43 parent_stack.rb
|
257
|
+
drwxr-xr-x 4 dennis staff 128 Jul 15 20:43 vpc_stack
|
258
|
+
-rw-r--r-- 1 dennis staff 248 Jul 15 20:43 vpc_stack.rb
|
326
259
|
```
|
327
|
-
context "Codebuild Service Role" do
|
328
|
-
let(:code_build_service_role) { resources["CodeBuildDemoServiceRole"] }
|
329
|
-
subject { code_build_service_role }
|
330
260
|
|
331
|
-
|
261
|
+
The default project creates three CloudFormation templates: a VPC stack, an
|
262
|
+
ECS stack and a parent stack. The parent stack is a CloudFormation stack that
|
263
|
+
contains all other stacks. When you deploy a Rubycfn project these other stacks
|
264
|
+
show up as `nested stacks`. The parent stack acts not only as a container for
|
265
|
+
all other stacks, but is also responsible for passing outputs from stacks as
|
266
|
+
parameters to another. For example: The VPC Id that is created in the VPC stack
|
267
|
+
can easily be passed to the ECS stack as a parameter. This nested stack approach
|
268
|
+
has an additional benefit: A change of output in stack X can trigger an update
|
269
|
+
in stack Y.
|
332
270
|
|
333
|
-
|
334
|
-
|
335
|
-
subject { code_build_service_role_properties }
|
271
|
+
The .rb files you see in the lib/stacks/ directory are the parent stack files.
|
272
|
+
Lets have a look at `vpc_stack.rb`:
|
336
273
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
context "Code build service role policy document" do
|
342
|
-
let(:policy_document) { code_build_service_role_properties["Policies"][0]["PolicyDocument"] }
|
343
|
-
subject { policy_document }
|
344
|
-
|
345
|
-
it { should have_key "Statement" }
|
274
|
+
```ruby
|
275
|
+
module VpcStack
|
276
|
+
extend ActiveSupport::Concern
|
277
|
+
include Rubycfn
|
346
278
|
|
347
|
-
|
348
|
-
|
349
|
-
|
279
|
+
included do
|
280
|
+
include Concerns::GlobalVariables
|
281
|
+
include Concerns::SharedMethods
|
282
|
+
include VpcStack::Main
|
350
283
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
end
|
355
|
-
end
|
284
|
+
description generate_stack_description("VpcStack")
|
285
|
+
end
|
286
|
+
end
|
356
287
|
```
|
357
288
|
|
358
|
-
|
289
|
+
On the first line we define the module name. It is important that the module
|
290
|
+
name ends with 'Stack' to make the compiler magic work. The code between
|
291
|
+
`include do` and `end` loads in two of the shared concerns, and includes the
|
292
|
+
VpcStack::Main module. Finally the description of the stack is set.
|
359
293
|
|
360
|
-
|
294
|
+
The `lib/stacks/vpc_stack/` directory contains a `vpc.rb` file, which is the
|
295
|
+
implementation of the VpcStack::Main module:
|
361
296
|
|
362
|
-
|
297
|
+
```ruby
|
298
|
+
require_relative "subnets"
|
363
299
|
|
364
|
-
|
365
|
-
module
|
366
|
-
module GlobalVariables
|
300
|
+
module VpcStack
|
301
|
+
module Main
|
367
302
|
extend ActiveSupport::Concern
|
368
303
|
|
369
304
|
included do
|
370
|
-
|
371
|
-
default: "test",
|
372
|
-
global: true,
|
373
|
-
value: ENV["ENVIRONMENT"]
|
305
|
+
# A lot of VPC code here
|
374
306
|
end
|
375
307
|
end
|
376
308
|
end
|
377
309
|
```
|
378
|
-
This module exposes a variable `environment`, which defaults to `test` if not set. It sources the value from the ENVIRONMENT environment variable. This variable can be used throughout your project at any place you see fit.
|
379
310
|
|
380
|
-
The
|
311
|
+
The first line is identical to the parent stack file and defines this module is
|
312
|
+
part of `VpcStack`. The second line defines the name of the module, in this case
|
313
|
+
`Main`. The code beteen `included do` and `end` is the implementation of this
|
314
|
+
module.
|
381
315
|
|
382
|
-
|
383
|
-
```
|
384
|
-
module DemoStack
|
385
|
-
extend ActiveSupport::Concern
|
386
|
-
include Rubycfn
|
316
|
+
## AWS Intrinsic functions
|
387
317
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
end
|
393
|
-
```
|
394
|
-
Our stack file consists of two modules: Main and CICD. When compiling the code, the combined result of the Main and CICD module will be written to the DemoStack json file in the build/ directory. Modularising stacks allows for separation of code by cohesion or any other logic you deem appropriate.
|
318
|
+
You can Ref by postpending the .ref method to any string or hash, e.g. :foobar.ref
|
319
|
+
If you supply an argument to .ref it'll be rendered as Fn::GetAtt. Last but
|
320
|
+
not least, calling Fn::Join is achieved by postpending .fnjoin to an array.
|
321
|
+
You can provide it with an argument for the separator. By default its "".
|
395
322
|
|
396
|
-
|
323
|
+
You can use the following methods in the same fashion:
|
397
324
|
|
398
|
-
|
325
|
+
fnsplit, fnbase64, fnjoin and fnselect
|
399
326
|
|
400
|
-
|
401
|
-
```
|
402
|
-
module DemoStack
|
403
|
-
module Main
|
404
|
-
extend ActiveSupport::Concern
|
405
|
-
included do
|
327
|
+
## Outputting to YAML
|
406
328
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
end
|
414
|
-
```
|
329
|
+
`brew install cfnflip`
|
330
|
+
|
331
|
+
or...
|
332
|
+
|
333
|
+
Paste the CloudFormation output in [cfnflip.com](https://cfnflip.com/) to
|
334
|
+
convert it to YAML format ;)
|
415
335
|
|
416
336
|
## Serverless Transforms
|
417
337
|
|
data/bin/rubycfn
CHANGED
@@ -71,6 +71,7 @@ dotenv_rspec = render('.env.rspec', { name: project_name }, path)
|
|
71
71
|
ecs_stack = render('ecs_stack.rb', {}, path)
|
72
72
|
ecs_stack_concern = render('ecs_Stack_concern.rb', { name: project_name }, path)
|
73
73
|
gemfile = render('Gemfile', { version: Rubycfn::VERSION }, path)
|
74
|
+
gitignore = render('.gitignore', {}, path)
|
74
75
|
global_variables = render('global_variables.rb', { name: project_name.downcase }, path)
|
75
76
|
helpers = render('helpers.rb', {}, path)
|
76
77
|
helper_methods = render('helper_methods.rb', {}, path)
|
@@ -109,6 +110,7 @@ File.open("#{project_path}/.env.rspec", 'w') { |file| file.write(dotenv_rspec) }
|
|
109
110
|
File.open("#{project_path}/lib/stacks/ecs_stack.rb", 'w') { |file| file.write(ecs_stack) }
|
110
111
|
File.open("#{project_path}/lib/stacks/ecs_stack/ecs_cluster.rb", 'w') { |file| file.write(ecs_stack_concern) }
|
111
112
|
File.open("#{project_path}/Gemfile", 'w') { |file| file.write(gemfile) }
|
113
|
+
File.open("#{project_path}/.gitignore", 'w') { |file| file.write(gitignore) }
|
112
114
|
File.open("#{project_path}/lib/shared_concerns/global_variables.rb", 'w') { |file| file.write(global_variables) }
|
113
115
|
File.open("#{project_path}/lib/aws_helper/helpers.rb", 'w') { |file| file.write(helpers) }
|
114
116
|
File.open("#{project_path}/lib/shared_concerns/helper_methods.rb", 'w') { |file| file.write(helper_methods) }
|
data/lib/rubycfn/version.rb
CHANGED
data/lib/rubycfn.rb
CHANGED
@@ -26,6 +26,7 @@ class Symbol
|
|
26
26
|
unless attr
|
27
27
|
return { Ref: self.to_s.split("_").map{ |e| e.capitalize }.join }
|
28
28
|
end
|
29
|
+
attr = attr.class == String ? attr : attr.to_s.split('_').map{|e| e.capitalize}.join
|
29
30
|
return { "Fn::GetAtt": [ self.to_s.split("_").map{ |e| e.capitalize }.join, attr ] }
|
30
31
|
end
|
31
32
|
end
|
@@ -51,6 +52,7 @@ class String
|
|
51
52
|
unless attr
|
52
53
|
return { Ref: self }
|
53
54
|
end
|
55
|
+
attr = attr.class == String ? attr : attr.to_s.split('_').map{|e| e.capitalize}.join
|
54
56
|
return { "Fn::GetAtt": [ self, attr ] }
|
55
57
|
end
|
56
58
|
|
data/templates/.gitignore.erb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubycfn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dennis Vink
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: neatjson
|