dh-proteus 0.1.3
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 +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
|