dh-proteus 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +80 -0
- data/README.md +414 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/proteus +5 -0
- data/bin/proteus_testing +8 -0
- data/bin/setup +8 -0
- data/build.sh +28 -0
- data/dh-proteus.gemspec +50 -0
- data/lib/core_ext/hash.rb +14 -0
- data/lib/proteus.rb +9 -0
- data/lib/proteus/app.rb +86 -0
- data/lib/proteus/backend/backend.rb +41 -0
- data/lib/proteus/commands/apply.rb +53 -0
- data/lib/proteus/commands/clean.rb +22 -0
- data/lib/proteus/commands/destroy.rb +51 -0
- data/lib/proteus/commands/graph.rb +22 -0
- data/lib/proteus/commands/import.rb +75 -0
- data/lib/proteus/commands/move.rb +28 -0
- data/lib/proteus/commands/output.rb +29 -0
- data/lib/proteus/commands/plan.rb +55 -0
- data/lib/proteus/commands/remove.rb +71 -0
- data/lib/proteus/commands/render.rb +36 -0
- data/lib/proteus/commands/taint.rb +35 -0
- data/lib/proteus/common.rb +72 -0
- data/lib/proteus/config/config.rb +47 -0
- data/lib/proteus/context_management/context.rb +31 -0
- data/lib/proteus/context_management/helpers.rb +14 -0
- data/lib/proteus/generate.rb +18 -0
- data/lib/proteus/generators/context.rb +57 -0
- data/lib/proteus/generators/environment.rb +42 -0
- data/lib/proteus/generators/init.rb +40 -0
- data/lib/proteus/generators/module.rb +69 -0
- data/lib/proteus/generators/templates/config/config.yaml.erb +22 -0
- data/lib/proteus/generators/templates/context/main.tf.erb +7 -0
- data/lib/proteus/generators/templates/context/variables.tf.erb +3 -0
- data/lib/proteus/generators/templates/environment/terraform.tfvars.erb +6 -0
- data/lib/proteus/generators/templates/module/io.tf.erb +1 -0
- data/lib/proteus/generators/templates/module/module.tf.erb +1 -0
- data/lib/proteus/generators/templates/module/validator.rb.erb +9 -0
- data/lib/proteus/global_commands/validate.rb +45 -0
- data/lib/proteus/helpers.rb +91 -0
- data/lib/proteus/helpers/path_helpers.rb +90 -0
- data/lib/proteus/helpers/string_helpers.rb +13 -0
- data/lib/proteus/init.rb +16 -0
- data/lib/proteus/modules/manager.rb +53 -0
- data/lib/proteus/modules/terraform_module.rb +184 -0
- data/lib/proteus/templates/partial.rb +123 -0
- data/lib/proteus/templates/template_binding.rb +62 -0
- data/lib/proteus/validators/base_validator.rb +24 -0
- data/lib/proteus/validators/validation_dsl.rb +172 -0
- data/lib/proteus/validators/validation_error.rb +9 -0
- data/lib/proteus/validators/validation_helpers.rb +9 -0
- data/lib/proteus/version.rb +7 -0
- metadata +260 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b0d31f4cca8dbbd403180b503384d70fc284b2b95df51a095e71f629b4942210
|
4
|
+
data.tar.gz: 0b57912e38c3c4b3f80d57b4c865bd89113f2f0934fb0996582f4d3a4ed1efaf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2a3c50683b3358058ed6651459152bc166f6452533bd8ca5c6d5a4a1c03c1ec821a39ebc40d210c5dfe5d32f6055156e8af5617cea9579964f8de49d75279bc7
|
7
|
+
data.tar.gz: 1e9ea31e800c1ead1ce9ec95ef460365848dae61a2f34fb6584821a31a1745d0a6f78f255e0b328a062ce79f652c8036cd7005e3c1924ddfaac50128f6183be0
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/_yardoc/
|
4
|
+
/coverage/
|
5
|
+
/doc/
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/tmp/
|
9
|
+
|
10
|
+
# rspec failure tracking
|
11
|
+
.rspec_status
|
12
|
+
|
13
|
+
terraform.tfvars
|
14
|
+
*.tfplan
|
15
|
+
terraform.tfstate*
|
16
|
+
graph.png
|
17
|
+
.terraform
|
18
|
+
.DS_Store
|
19
|
+
.idea
|
20
|
+
|
21
|
+
release
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
proteus (0.1.3)
|
5
|
+
activesupport (~> 5.1.1)
|
6
|
+
aws-sdk-elasticsearchservice (~> 1.4.0)
|
7
|
+
aws-sdk-rds (~> 1.11.0)
|
8
|
+
aws-sdk-route53 (~> 1.7.0)
|
9
|
+
erubis (~> 2.7.0)
|
10
|
+
hcl-checker (~> 1.0.5)
|
11
|
+
thor (~> 0.20.0)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: https://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activesupport (5.1.1)
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
+
i18n (~> 0.7)
|
19
|
+
minitest (~> 5.1)
|
20
|
+
tzinfo (~> 1.1)
|
21
|
+
aws-partitions (1.60.0)
|
22
|
+
aws-sdk-core (3.15.0)
|
23
|
+
aws-partitions (~> 1.0)
|
24
|
+
aws-sigv4 (~> 1.0)
|
25
|
+
jmespath (~> 1.0)
|
26
|
+
aws-sdk-elasticsearchservice (1.4.0)
|
27
|
+
aws-sdk-core (~> 3)
|
28
|
+
aws-sigv4 (~> 1.0)
|
29
|
+
aws-sdk-rds (1.11.0)
|
30
|
+
aws-sdk-core (~> 3)
|
31
|
+
aws-sigv4 (~> 1.0)
|
32
|
+
aws-sdk-route53 (1.7.0)
|
33
|
+
aws-sdk-core (~> 3)
|
34
|
+
aws-sigv4 (~> 1.0)
|
35
|
+
aws-sigv4 (1.0.2)
|
36
|
+
coderay (1.1.1)
|
37
|
+
concurrent-ruby (1.0.5)
|
38
|
+
diff-lcs (1.3)
|
39
|
+
erubis (2.7.0)
|
40
|
+
hcl-checker (1.0.5)
|
41
|
+
i18n (0.8.1)
|
42
|
+
jmespath (1.3.1)
|
43
|
+
method_source (0.8.2)
|
44
|
+
minitest (5.10.3)
|
45
|
+
pry (0.10.4)
|
46
|
+
coderay (~> 1.1.0)
|
47
|
+
method_source (~> 0.8.1)
|
48
|
+
slop (~> 3.4)
|
49
|
+
rake (10.5.0)
|
50
|
+
rspec (3.8.0)
|
51
|
+
rspec-core (~> 3.8.0)
|
52
|
+
rspec-expectations (~> 3.8.0)
|
53
|
+
rspec-mocks (~> 3.8.0)
|
54
|
+
rspec-core (3.8.0)
|
55
|
+
rspec-support (~> 3.8.0)
|
56
|
+
rspec-expectations (3.8.3)
|
57
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
58
|
+
rspec-support (~> 3.8.0)
|
59
|
+
rspec-mocks (3.8.0)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.8.0)
|
62
|
+
rspec-support (3.8.0)
|
63
|
+
slop (3.6.0)
|
64
|
+
thor (0.20.0)
|
65
|
+
thread_safe (0.3.6)
|
66
|
+
tzinfo (1.2.3)
|
67
|
+
thread_safe (~> 0.1)
|
68
|
+
|
69
|
+
PLATFORMS
|
70
|
+
ruby
|
71
|
+
|
72
|
+
DEPENDENCIES
|
73
|
+
bundler (~> 2.0)
|
74
|
+
proteus!
|
75
|
+
pry
|
76
|
+
rake (~> 10.0)
|
77
|
+
rspec (~> 3.0)
|
78
|
+
|
79
|
+
BUNDLED WITH
|
80
|
+
2.0.1
|
data/README.md
ADDED
@@ -0,0 +1,414 @@
|
|
1
|
+
# Terraform tool
|
2
|
+
|
3
|
+
This repository contains a wrapper application (proteus) around Terraform that facilitates management of resources.
|
4
|
+
|
5
|
+
The incentive for a Terraform wrapper is that Terraform in its current state cannot iteratively
|
6
|
+
declare module includes in a loop. Writing all the module includes manually would inevitably lead to
|
7
|
+
large manifest files.
|
8
|
+
These files would be difficult if not impossible to maintain without human error.
|
9
|
+
|
10
|
+
Furthermore, the abstraction from Terraform that the configuration format of the wrapper provides, enables
|
11
|
+
people who are not familiar with Terraform's configuration format to configure resources easily.
|
12
|
+
|
13
|
+
## Setup
|
14
|
+
|
15
|
+
### Requirements
|
16
|
+
* Ruby >= 2.5.5
|
17
|
+
* Terraform >= 0.11.14
|
18
|
+
* AWS IAM profile credentials
|
19
|
+
|
20
|
+
### Prerequisites
|
21
|
+
|
22
|
+
#### Install Terraform
|
23
|
+
```
|
24
|
+
brew install terraform
|
25
|
+
```
|
26
|
+
|
27
|
+
If you would still like to use Terraform `< 0.12`:
|
28
|
+
```
|
29
|
+
# Install Terraform 0.11.14 (for Homebrew users)
|
30
|
+
mkdir -p /usr/local/Cellar/terraform/0.11.14/bin \
|
31
|
+
&& curl -o /tmp/terraform.zip "https://releases.hashicorp.com/terraform/0.11.14/terraform_0.11.14_darwin_amd64.zip" \
|
32
|
+
&& unzip -o /tmp/terraform.zip -d "/usr/local/Cellar/terraform/0.11.14/bin" \
|
33
|
+
&& rm -f /tmp/terraform.zip \
|
34
|
+
&& brew switch terraform 0.11.14
|
35
|
+
&& brew pin terraform
|
36
|
+
```
|
37
|
+
|
38
|
+
#### Install proteus
|
39
|
+
```
|
40
|
+
gem source --add https://YOUR_USER:YOUR_PASSWORD@nexus.usehurrier.com/repository/infra-gems
|
41
|
+
gem install proteus
|
42
|
+
```
|
43
|
+
|
44
|
+
#### Set up proteus root path
|
45
|
+
```
|
46
|
+
cd /path/to/your/repository
|
47
|
+
|
48
|
+
# initialize a project scaffolding
|
49
|
+
proteus init
|
50
|
+
```
|
51
|
+
|
52
|
+
The above directory will be a valid `proteus` root. Should you want to be able to call `proteus` from anywhere on your system,
|
53
|
+
set it as an environment variable like so:
|
54
|
+
```
|
55
|
+
export PROTEUS_ROOT=/path/to/your/repository
|
56
|
+
```
|
57
|
+
|
58
|
+
#### AWS profile configuration
|
59
|
+
Create profiles and credentials for your environments:
|
60
|
+
|
61
|
+
`$HOME/.aws/config`:
|
62
|
+
|
63
|
+
```
|
64
|
+
[profile staging]
|
65
|
+
region = eu-west-1
|
66
|
+
[profile production]
|
67
|
+
region = eu-west-1
|
68
|
+
```
|
69
|
+
|
70
|
+
`$HOME/.aws/credentials`:
|
71
|
+
```
|
72
|
+
[staging]
|
73
|
+
aws_access_key_id = YOUR_ACCESS_KEY_ID
|
74
|
+
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
|
75
|
+
|
76
|
+
[production]
|
77
|
+
aws_access_key_id = YOUR_ACCESS_KEY_ID
|
78
|
+
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
|
79
|
+
```
|
80
|
+
|
81
|
+
#### State
|
82
|
+
Terraform state is managed remotely in an S3 bucket. Make sure to create that bucket and enable versioning.
|
83
|
+
|
84
|
+
### Customize configuration
|
85
|
+
Having set up all of the above, it is time to modify the configuration to your needs.
|
86
|
+
|
87
|
+
```
|
88
|
+
vim $PROTEUS_ROOT/config/config.yaml
|
89
|
+
```
|
90
|
+
|
91
|
+
Should the term "environments" be unclear, please keep on reading. Otherwise:
|
92
|
+
|
93
|
+
## TL;DR usage
|
94
|
+
```
|
95
|
+
# Run "plan"
|
96
|
+
./proteus [environment] plan
|
97
|
+
|
98
|
+
# Validate output
|
99
|
+
|
100
|
+
# Run "apply"
|
101
|
+
./proteus [environment] apply
|
102
|
+
```
|
103
|
+
|
104
|
+
Check the `example` context and its demo module in `contexts/example`.
|
105
|
+
|
106
|
+
## Contexts and Environments
|
107
|
+
Environments and contexts define a scope for Terraform configuration. While contexts are defined by creating a directory in the contexts directory,
|
108
|
+
environments are defined using a Terraform variables file in the `environments` directory of a context.
|
109
|
+
|
110
|
+
### State
|
111
|
+
Any tuple of the form (context, environment) has its own state. No state will be shared between tuples.
|
112
|
+
That means: (default, production) will not have any shared resources with (default, staging). Neither will (foo, production) share any state with
|
113
|
+
(bar, production.)
|
114
|
+
|
115
|
+
### Conventions
|
116
|
+
* Valid environment names are are [snake case](https://en.wikipedia.org/wiki/Snake_case) and lowercase.
|
117
|
+
* files in `environments` need to comply with the following format: `terraform.environment_name.tfvars`
|
118
|
+
|
119
|
+
Once an environment gets defined using the above conventions, `proteus` will pick it up as a scope for its subcommands.
|
120
|
+
|
121
|
+
**Note:** You do not need to touch any code for the environment to be available in the command line interface.
|
122
|
+
|
123
|
+
## Modules
|
124
|
+
There are two types of modules: Standard Terraform modules and modules that are managed by `proteus`.
|
125
|
+
Each module without `proteus` configuration behaves as a standard module.
|
126
|
+
|
127
|
+
### Conventions
|
128
|
+
* Module names are [snake case](https://en.wikipedia.org/wiki/Snake_case) and lowercase. `foo_bar` is a correct module name while `FooBar` and `Foo_Bar` are both invalid names.
|
129
|
+
* Input and output variables go inside of a file called `io.tf` within the root of the module
|
130
|
+
* Group resources in separate files in the root of the module: Route53 related resources should be described in a file called `route53.tf`; IAM
|
131
|
+
specific resources go in a file called `iam.tf`. This way resource declarations are easy to find.
|
132
|
+
* Be verbose: We're not using MS-DOS FAT here. There is no need to shorten resource names.
|
133
|
+
|
134
|
+
**Note:** Use the generators provided by `proteus` for creating a scaffolding for contexts, modules and environments.
|
135
|
+
|
136
|
+
### Standard modules
|
137
|
+
Standard modules can be implemented exactly as described in the [Terraform documentation](https://www.terraform.io/docs/modules/index.html).
|
138
|
+
They need to be included in a Terraform manifest in the root of a context. `proteus` will not use these modules for generating any code.
|
139
|
+
|
140
|
+
#### Structure
|
141
|
+
```
|
142
|
+
modules/route53
|
143
|
+
├── io.tf # Definition of input and output variables
|
144
|
+
└── route53.tf # route53 resources
|
145
|
+
```
|
146
|
+
|
147
|
+
### Managed modules
|
148
|
+
Managed modules extend the functionality of standard modules with a YAML configuration format, validators and templates.
|
149
|
+
|
150
|
+
#### Conventions
|
151
|
+
* Standard module conventions apply
|
152
|
+
* Singular (that means non-repeated resources) go into Terraform manifests in `config/global_resources`. Manifests in this directory
|
153
|
+
can be either standard Terraform manifests or ERB templates
|
154
|
+
* YAML Configuration files have to named exactly as an existing environment (with `.yaml` suffix)
|
155
|
+
* Template names are snake case (lowercase)
|
156
|
+
* The validator is located in the module's config root and named `validator.rb`
|
157
|
+
|
158
|
+
**Note: If your module does not contain a configuration file for your environment, it will be ignored.**
|
159
|
+
|
160
|
+
#### Structure
|
161
|
+
```
|
162
|
+
modules/rds
|
163
|
+
├── config # proteus confguration directory
|
164
|
+
│ ├── README.md
|
165
|
+
│ ├── global_resources # resources which only get applied once
|
166
|
+
│ │ ├── parameter_groups.tf
|
167
|
+
│ │ ├── rds.tf
|
168
|
+
│ │ └── vpc.tf
|
169
|
+
│ ├── production_ap.yaml # data for environment production_ap
|
170
|
+
│ ├── production_eu.yaml # data for environment production_eu
|
171
|
+
│ ├── production_us.yaml # ...
|
172
|
+
│ ├── qa.yaml # ...
|
173
|
+
│ ├── staging.yaml # ...
|
174
|
+
│ ├── templates
|
175
|
+
│ │ ├── _parameter_group.tf.erb # partial template for parameter groups
|
176
|
+
│ │ ├── _route53.tf.erb # partial template for route53 configuration
|
177
|
+
│ │ ├── defaults
|
178
|
+
│ │ │ └── parameter_group.yaml # default data for paramater group partial
|
179
|
+
│ │ └── rds.tf.erb # main template of the module
|
180
|
+
│ └── validator.rb # Class ensuring correct format of data
|
181
|
+
├── io.tf # Definition of input and output variables
|
182
|
+
└── rds.tf # rds resources of the module
|
183
|
+
```
|
184
|
+
|
185
|
+
#### Configuration format and templates
|
186
|
+
Configuration is implemented in YAML files in the root of the `config` directory modules.
|
187
|
+
The configuration format has only one required key: `template_data`.
|
188
|
+
|
189
|
+
*Example:*
|
190
|
+
```
|
191
|
+
global_resources:
|
192
|
+
# refers to module_name/config/global_resources/your_global_resource_template.tf.erb
|
193
|
+
your_global_resource_template:
|
194
|
+
key0: value0
|
195
|
+
key1: value1
|
196
|
+
template_data:
|
197
|
+
# refers to module_name/config/templates/your_template.tf.erb
|
198
|
+
your_template:
|
199
|
+
foo: bar
|
200
|
+
hue:
|
201
|
+
- hue
|
202
|
+
- hue
|
203
|
+
- hue
|
204
|
+
```
|
205
|
+
Each key in `template_data` refers to a template name. Each key in a template section of the `template_data`
|
206
|
+
Hash is available as an instance variable in the corresponding ERB template.
|
207
|
+
|
208
|
+
For the above example:
|
209
|
+
The template file is `your_template.tf.erb`. In the template `@foo` is available as a String value and
|
210
|
+
`@hue` is available as an Array.
|
211
|
+
The same gets applied to the global resource template `your_global_resource_template.tf.erb`: `@key0` and `@key1` are available
|
212
|
+
as instance variables within the corresponding template.
|
213
|
+
|
214
|
+
#### Partial templates
|
215
|
+
In addition to standard templates which are used to render collections as a whole, modules support templates which can be used
|
216
|
+
on single records within collections. This comes in handy if data related to a single record has to be rendered as it keeps
|
217
|
+
templates short and YAML configuration logically structured.
|
218
|
+
|
219
|
+
An example use case for this is RDS hosts and Route 53 records where a single database can have multiple Route 53 records.
|
220
|
+
|
221
|
+
Consider the following configuration data for an RDS host within the RDS module:
|
222
|
+
```
|
223
|
+
template_data:
|
224
|
+
rds:
|
225
|
+
instances:
|
226
|
+
- instance_identifier: "dashboard"
|
227
|
+
instance_class: "db.m4.xlarge"
|
228
|
+
engine_version: "9.6.5"
|
229
|
+
engine: "postgres"
|
230
|
+
allocated_storage: 500
|
231
|
+
|
232
|
+
partials:
|
233
|
+
route53:
|
234
|
+
- app: "dashboard"
|
235
|
+
countries:
|
236
|
+
- "au"
|
237
|
+
- "bd"
|
238
|
+
- "bn"
|
239
|
+
- "hk"
|
240
|
+
- "kr"
|
241
|
+
- "my"
|
242
|
+
- "ph"
|
243
|
+
- "pk"
|
244
|
+
- "th"
|
245
|
+
- "tw"
|
246
|
+
```
|
247
|
+
For this configuration, a template called `rds.tf.erb` will be loaded. Within the template all data within the scope of the key `rds` will
|
248
|
+
be available as instance variables. That means, data within in `instances` will be present in the template as `@instances`.
|
249
|
+
|
250
|
+
When iterating over the instances in the main template, one can trigger rendering of a partial in the context of the current instance
|
251
|
+
if data for an existing partial template is present.
|
252
|
+
In the above example, the configuration for the instance with the identifier "dashboard" refers to a partial template called `route53` and defines some
|
253
|
+
data within the scope of the key `route53`.
|
254
|
+
|
255
|
+
The partial template can be rendered in the main template as follows:
|
256
|
+
```
|
257
|
+
<% @instances.each do |instance| %>
|
258
|
+
module "rds-<%= instance['instance_identifier'] %>" {
|
259
|
+
source = "./modules/rds"
|
260
|
+
...
|
261
|
+
instance_identifier = "<%= instance['instance_identifier'] %>"
|
262
|
+
}
|
263
|
+
|
264
|
+
# Render partial "_route53.tf.erb" if data is present
|
265
|
+
# within partials => route53
|
266
|
+
<%= render_partial(name: :route53, data: instance, force_rendering: false) %>
|
267
|
+
<% end %>
|
268
|
+
```
|
269
|
+
**Note:** The parameter `force_rendering` defines whether or not the partial will be rendered regardless of data being present or absent. The parameter
|
270
|
+
defaults to `true`.
|
271
|
+
|
272
|
+
**Partial default values**
|
273
|
+
|
274
|
+
If the data for a partial template is a Hash, defaults can be loaded from a YAML file in the `templates/defaults` directory.
|
275
|
+
All of the default values will be injected into the partial template, respecting the values set in the main YAML configuration.
|
276
|
+
Thus, the values in the main configuration act as overrides to the defaults.
|
277
|
+
|
278
|
+
For further information about how partial defaults work, please refer to the `example` module. You can render the module and check its
|
279
|
+
output by running the following command:
|
280
|
+
|
281
|
+
`./proteus context example demo_env render && cat contexts/example/demo_module.tf`
|
282
|
+
|
283
|
+
|
284
|
+
### Default values
|
285
|
+
Each of the modules in this repository contains a file called `io.tf` which defines the input and output variables of the module.
|
286
|
+
For input variables, default values can be defined. These defaults can be overridden using the YAML configuration.
|
287
|
+
The following example is based on the `elasticache` module:
|
288
|
+
|
289
|
+
The module defines a variable named `node_type` in its `io.tf` manifest:
|
290
|
+
```
|
291
|
+
variable "node_type" {
|
292
|
+
default = "cache.t2.micro"
|
293
|
+
}
|
294
|
+
|
295
|
+
```
|
296
|
+
The configuration for the `staging` environment sets an override as follows:
|
297
|
+
```
|
298
|
+
...
|
299
|
+
elasticache:
|
300
|
+
instances:
|
301
|
+
- replication_group_id: sidekiq
|
302
|
+
node_type: cache.m3.medium # override for node_type
|
303
|
+
engine: "redis"
|
304
|
+
engine_version: "3.2.4"
|
305
|
+
availability_zones:
|
306
|
+
- eu-west-1a
|
307
|
+
number_cache_clusters: 1
|
308
|
+
...
|
309
|
+
```
|
310
|
+
|
311
|
+
Inside of the template `elasticache.tf.erb`, the method `render_defaults` gets called:
|
312
|
+
```
|
313
|
+
...
|
314
|
+
engine = "<%= instance['engine'] %>"
|
315
|
+
engine_version = "<%= instance['engine_version'] %>"
|
316
|
+
|
317
|
+
<%= render_defaults(instance) %>
|
318
|
+
environment = "${var.environment}"
|
319
|
+
|
320
|
+
vpc_id = "${module.vpc.id}"
|
321
|
+
...
|
322
|
+
|
323
|
+
```
|
324
|
+
`render_defaults` internally checks if the given context (in this case the data for an ElastiCache instance) defines
|
325
|
+
overrides for defaults defined in `io.tf` and, if overrides are present, renders them into the template.
|
326
|
+
|
327
|
+
|
328
|
+
#### Validators
|
329
|
+
The `proteus` library provides a simple DSL for validating module configuration. The DSL is available for validator classes.
|
330
|
+
|
331
|
+
#### Validator classes
|
332
|
+
The following conventions apply for validator classes:
|
333
|
+
* contained in `validator.rb` in config root of the respective module
|
334
|
+
* Class name: module name in [upper camel case](https://en.wikipedia.org/wiki/Camel_case)
|
335
|
+
* Validators inherit from `Proteus::Validators::BaseValidator`
|
336
|
+
* Validators override (and implement) exactly one method: `validate`
|
337
|
+
|
338
|
+
#### Validation DSL
|
339
|
+
The DSL provides the following keywords:
|
340
|
+
|
341
|
+
| Keyword | Description |
|
342
|
+
| ------------- |-------------|
|
343
|
+
| `within(key) { block }` | Ensures presence of `key` and data below `key` |
|
344
|
+
| `ensure_unique_values` | Ensures unique values in collections |
|
345
|
+
| `ensure_data_type(type)` | Checks if the current context is of type `type` |
|
346
|
+
| `ensure_uniqueness_across(key)` | Ensures uniqueness across a hierarchy |
|
347
|
+
| `each_key { block }` | Iterates over keys in the current context |
|
348
|
+
| `ensure_keys(*keys)` | Checks for presence of all provided keys in the current context |
|
349
|
+
| `each { block }` | Iterates over elements of a collection |
|
350
|
+
| `ensure_presence(key)` | Checks if `key` is present in the current context |
|
351
|
+
| `ensure_value(key, options)` | Ensures a value is in a set of predefined values or range or matches a regular expression |
|
352
|
+
| `in_case(key, has_value: [...]) { block }` | Optionally executes `block` if the value of `key` is in `has_value` |
|
353
|
+
|
354
|
+
Here's an example for data and the corresponding validator:
|
355
|
+
|
356
|
+
**Data:**
|
357
|
+
```yaml
|
358
|
+
template_data:
|
359
|
+
my_template:
|
360
|
+
countries:
|
361
|
+
country_a:
|
362
|
+
apps:
|
363
|
+
- foo
|
364
|
+
- bar
|
365
|
+
- baz
|
366
|
+
country_b:
|
367
|
+
apps:
|
368
|
+
- foo
|
369
|
+
- bar
|
370
|
+
- baz
|
371
|
+
```
|
372
|
+
|
373
|
+
**Validator method:**
|
374
|
+
```ruby
|
375
|
+
|
376
|
+
def validate
|
377
|
+
within :template_data do # fails if template_data is absent
|
378
|
+
|
379
|
+
ensure_data_type Hash # fails is template_data is not a Hash
|
380
|
+
|
381
|
+
within :my_template do # fails if my_template is absent
|
382
|
+
|
383
|
+
ensure_data_type Hash # fails if my_template is not a Hash
|
384
|
+
|
385
|
+
within: countries do # fails if countries is absent
|
386
|
+
each_key do # iterates over countries
|
387
|
+
within :apps do # fails if any country is missing the apps key
|
388
|
+
ensure_data_type Array # fails if apps is not an Array
|
389
|
+
ensure_unique_values # fails if apps has duplicate values
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
```
|
398
|
+
|
399
|
+
## Generators
|
400
|
+
TBD
|
401
|
+
|
402
|
+
## Development
|
403
|
+
|
404
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
405
|
+
|
406
|
+
## To be done
|
407
|
+
* Error message if running apply without plan being present
|
408
|
+
* Tests
|
409
|
+
* Code documentation (YARD)
|
410
|
+
* Slack deployment?
|
411
|
+
* Disable Slack notifications for dry runs
|
412
|
+
* Enforcing validators?
|
413
|
+
* Prettier output
|
414
|
+
* Rubocop
|