TerraformDevKit 0.3.8 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af5cf9e79681d4cb8a94d32caf3e9f4c3fe64f758e3b1fe5760d7fae1bff4a85
4
- data.tar.gz: 6ffb456ca318bceb0aa2f1697500e49793ea98113335375dc973e72a4bc0f513
3
+ metadata.gz: c12a82956adde25c164976def5145c9269da662266b5aaf3ebde1023b357829b
4
+ data.tar.gz: a6070b8c7dd20312bc467c01b8ef49d583b989eb861952f6091848e0c74e25d8
5
5
  SHA512:
6
- metadata.gz: 181e3ebee2bd0176b4efe9be86e28548b1ef579bb958e7bfc69bca6ada22700017d78540812fc0f5298fb25fb664670cda2dae982f5bbc6fae06f2918ba8c6d4
7
- data.tar.gz: 824de75407cd0587126a363811c2dc4cac046f00c9ede5d4d4eae91b4ef567005db406639384ac9ad00e1043811a0bc8ae63d84c372d7470008c8a86d07c79c7
6
+ metadata.gz: 62e75a9f4e1d3e0d9e0c9740c6dda68c055696bbafb79553aeff512d8f524daef5e90c338de1d4f7dc5ae3112561781864c1c925e8f061ec4e091868d59b06d3
7
+ data.tar.gz: e1f1d40206d0ac1fed224b6d679073a7eebc1bd095036836f02a3cef4c86fc2417bb656c2362ee53955cba8ab9929ccf8d2e0e0a523dab82edbbcba4a2da3613
@@ -1,5 +1,12 @@
1
+ version: "{build}-{branch}"
2
+
3
+ environment:
4
+ matrix:
5
+ - RUBY_VERSION: 25
6
+ - RUBY_VERSION: 26
7
+
1
8
  install:
2
- - set PATH=C:\Ruby21-x64\bin;%PATH%
9
+ - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
3
10
  - bundle install
4
11
 
5
12
  build: off
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
- --format documentation
2
- --color
1
+ --format documentation
2
+ --color
@@ -1,8 +1,10 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.1.6
5
- before_install: gem install bundler -v 1.14.6
6
- os:
7
- - linux
8
- - osx
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5
5
+ - 2.6
6
+ - 2.7
7
+ before_install: gem install bundler
8
+ os:
9
+ - linux
10
+ - osx
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in TerraformDevKit.gemspec
4
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in TerraformDevKit.gemspec
4
+ gemspec
data/README.md CHANGED
@@ -1,239 +1,239 @@
1
- # TerraformDevKit
2
-
3
- [![Build Status](https://travis-ci.org/vistaprint/TerraformDevKit.svg?branch=master)](https://travis-ci.org/vistaprint/TerraformDevKit) [![Build status](https://ci.appveyor.com/api/projects/status/74s4yd7dmuwg5tmn/branch/master?svg=true)](https://ci.appveyor.com/project/Vistaprint/terraformdevkit/branch/master)
4
-
5
- Set of scripts to ease development and testing with [Terraform](https://www.terraform.io/).
6
-
7
- The script collection includes support for:
8
-
9
- * Managing AWS credentials
10
- * Backing up the state from a failed Terraform execution
11
- * Executing external commands
12
- * Simple configuration management
13
- * Simple reading and writing to AWS DynamoDB
14
- * Multiplatform tools
15
- * Making simple HTTP requests
16
- * Retrying a block of code
17
- * Terraform environment management
18
- * Locally installing Terraform
19
- * Filtering Terraform logging messages
20
-
21
- Most of these scripts exist to provide support to a module development and testing environment for Terraform: [TerraformModules](https://github.com/vistaprint/TerraformModules). But, they might be useful for other purposes too.
22
-
23
- Currently this repository is tightly coupled with AWS and has not been tested to work with other providers. We are actively working to change this and hope to have a more generic solution soon. If you would like to see support for your favourite cloud provider please have submit a pull request implementing support and we will be more than happy to merge your changes in.
24
-
25
- ## Installation
26
-
27
- Add this line to your application's Gemfile:
28
-
29
- ```ruby
30
- gem 'TerraformDevKit'
31
- ```
32
-
33
- And then execute:
34
-
35
- $ bundle
36
-
37
- Or install it yourself as:
38
-
39
- $ gem install TerraformDevKit
40
-
41
- ## Usage
42
-
43
- To use the library simply import it as:
44
-
45
- ```ruby
46
- require 'TerraformDevKit'
47
- ```
48
-
49
- ## Managing Terraform Environments
50
-
51
- TerraformDevKit provides a set of Rake tasks and Ruby scripts to ease the management of multiple Terraform environments. Three major environment types are supported: `dev`, `test` and `prod`.
52
-
53
- There might be many development environments (`dev`), each one with its own name. Development environments use a local Terraform backend. They are intented to be used by developers while adding features to the infrastructure.
54
-
55
- Testing (`test`) and production (`prod`) environment use a remote backend. Thus, the Terraform state file is not kept in the local disk, but on S3. This allows multiple developers to easily work on the same infrastructure instance. For safety reasons, operations that affect testing and production environments require manual user input. This is not the case for development environments.
56
-
57
- TerraformDevKit expects templated versions (using [Mustache](https://mustache.github.io/)) of the Terraform files. Such files might contain placeholders for several fields such as `Environment`, (AWS) `Region` or (AWS) `Profile`, among others. TerraformDevKit uses the template files to generate the final files that will be consumed by Terraform. As an example, for the production environment, the resulting files are placed in a directory named `envs/prod`.
58
-
59
- ### Configuration Files
60
-
61
- Configuration files must be placed in a directory named `config`. Each environment type requires a different configuration file. Thus, the following three files must be placed in the `config` directory:
62
-
63
- * `config-dev.yml`
64
- * `config-test.yml`
65
- * `config-prod.yml`
66
-
67
- The first one contains the configuration for **all** the development environments that are created. The other two contain the configuration for the test and production environments, respectively.
68
-
69
- A sample configuration files is shown next:
70
-
71
- ```yaml
72
- terraform-version: 0.11.0
73
- project-name: my super cool project
74
- aws:
75
- profile: myprofile
76
- region: eu-west-1
77
- ```
78
-
79
- The AWS profile **must not** be specified for test and production accounts, as users are required to manually type the profile name.
80
-
81
- ### A Minimal Rakefile
82
-
83
- ```ruby
84
- ROOT_PATH = File.dirname(File.expand_path(__FILE__))
85
-
86
- spec = Gem::Specification.find_by_name 'TerraformDevKit'
87
- load "#{spec.gem_dir}/tasks/devkit.rake"
88
-
89
- task :custom_test, [:env] do |_, args|
90
- # Add some tests here
91
- end
92
- ```
93
-
94
- #### Overrides
95
-
96
- It's possible to override the location of your config files by setting the variable `CONFIG_FILE` in the top level `Rakefile`
97
-
98
- ```ruby
99
- # %s will be substituted with the environment name.
100
- # File is exected to live in /c/path/to/root/config/config-dev.yml
101
- CONFIG_FILE = File.join(ROOT_PATH, 'config', 'config-%s.yml')
102
- ```
103
-
104
- ### Tasks and Hooks
105
-
106
- TerraformDevKit provides a set of generic tasks to perform:
107
-
108
- * `prepare`: prepares the environment
109
- * `plan`: shows the plan to create the infrastructure
110
- * `apply`: creates the infrastructure
111
- * `destroy`: destroys the infrastructure
112
- * `clean`: cleans the environment (after destroying the infrastructure)
113
- * `test`: tests a local environment
114
- * `preflight`: creates a temporary infrastructure and runs the test task
115
-
116
- Additionally, TerraformDevKit allows users to define a set of hooks that will be called during the different steps required to complete the previous list of tasks. The following hooks are available:
117
-
118
- * `pre_apply`: invoked before `apply` task runs
119
- * `post_apply`: invoked after `apply` task runs
120
- * `pre_destroy`: invoked before `destroy` task runs
121
- * `post_destroy`: invoked after `destroy` task runs
122
- * `custom_prepare`: invoked during the preparation process, before terraform is initialized and the environment folder is created
123
- * `custom_test`: invoked during as part of the `test` task, right after `apply` completes.
124
-
125
- ### Sample Terraform Templates
126
-
127
- The following file (`main.tf.mustache`) contains the infrastructure configuration (a single S3 bucket) as well as information related to the AWS provider.
128
-
129
- ```hcl
130
- locals {
131
- env = "{{Environment}}"
132
- }
133
-
134
- # See example below for how to configure a remote backend
135
-
136
- provider "aws" {
137
- profile = "{{Profile}}"
138
- region = "{{Region}}"
139
- }
140
-
141
- resource "aws_s3_bucket" "raw" {
142
- bucket = "foo-${local.env}"
143
- acl = "private"
144
-
145
- {{#LocalBackend}}
146
- force_destroy = true
147
- {{/LocalBackend}}
148
- {{^LocalBackend}}
149
- lifecycle {
150
- prevent_destroy = true
151
- }
152
- {{/LocalBackend}}
153
- }
154
- ```
155
-
156
- The config file requires a `project-name` to be set. This project name is then use to generate the S3 bucket and dynamodb lock table required by terraform to mamage remote state. To use the remote state feature of TerraformDevKit you must add the following section to your `main.tf.mustache` file:
157
-
158
- ```hcl
159
- terraform {
160
- {{#LocalBackend}}
161
- backend "local" {}
162
- {{/LocalBackend}}
163
- {{^LocalBackend}}
164
- backend "s3" {
165
- bucket = "{{ProjectName}}-{{Environment}}-state"
166
- key = "{{ProjectAcronym}}-{{Environment}}.tfstate"
167
- dynamodb_table = "{{ProjectAcronym}}-{{Environment}}-lock-table"
168
- encrypt = true
169
- profile = "{{Profile}}"
170
- region = "{{Region}}"
171
- }
172
- {{/LocalBackend}}
173
- }
174
- ```
175
-
176
- ### Injecting Additional Variables into Template Files
177
-
178
- In addition to the default variables that are passed to Mustache when rendering a template file, users can provide additional variables. To do so, users must register a procedure that receives the environment as a parameter and returns a map with the extra variables and their values. An example is shown next:
179
-
180
- ```ruby
181
- TDK::TerraformConfigManager.register_extra_vars_proc(
182
- proc do
183
- { SumoLogicEndpoint: TDK::Configuration.get('sumologic')['endpoint'] }
184
- end
185
- )
186
- ```
187
-
188
- ### Additional Template Sources
189
-
190
- TerraformDevKit looks for mustache templates in the root directory, and creates final versions of these files after injecting values such as the environment name or the project name.
191
-
192
- In addition to doing this for the infrastructure description files (`.tf` files), TerraformDevKit allows users to specify additional template sources. This feature could be used, for instance, to version other types of files whose content depends on values such as the environment name.
193
-
194
- The following example shows how to instruct TerraformDevKit to read templates from different source folders, and write the resulting files into their corresponding destination folders located within an environment folder (e.g., `envs/<env-name>/`):
195
-
196
- ```yaml
197
- terraform-version: 0.11.0
198
- project-name: my super cool project
199
- aws:
200
- profile: myprofile
201
- region: eu-west-1
202
- template-dirs:
203
- template-dest-folder: ../template-src-folder
204
- template-dest-folder: ../another-template-src-folder
205
- another-template-dest-folder: yet-another-template-src-folder
206
- ```
207
-
208
- ### Copying Files to the Environment Folder
209
-
210
- Sometimes it may be desirable to copy files that exist in a given folder into the environment folder. The `copy-files` field makes this possible. See the following configuration for an example:
211
-
212
- ```yaml
213
- terraform-version: 0.11.0
214
- project-name: my super cool project
215
- copy-files:
216
- dest-folder: ../src-folder
217
- another-dest-folder: ../../another-src-folder
218
- dest-file: src-file
219
- ```
220
-
221
- ### Updating Modules
222
-
223
- Terraform will get the necessary modules every time a new environment is created. Once the modules are cached, there is generally no need to keep updating the modules each time Terraform is executed. When using a module repository it is possible to select a specific version to use (as shown [here](https://www.terraform.io/docs/modules/sources.html#ref)). In such a case, Terraform will automatically update the modules whenever the version number is changed.
224
-
225
- When using local modules (e.g., during development process) it might be desirable to update the modules every time Terraform runs. This can be achieved by setting the environment variable `TF_DEVKIT_UPDATE_MODULES` to `true`.
226
-
227
- ## Development
228
-
229
- 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.
230
-
231
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
232
-
233
- ## Contributing
234
-
235
- Bug reports and pull requests are welcome on GitHub at https://github.com/vistaprint/TerraformDevKit.
236
-
237
- ## License
238
-
239
- The gem is available as open source under the terms of the [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0).
1
+ # TerraformDevKit
2
+
3
+ [![Build Status](https://travis-ci.org/vistaprint/TerraformDevKit.svg?branch=master)](https://travis-ci.org/vistaprint/TerraformDevKit) [![Build status](https://ci.appveyor.com/api/projects/status/74s4yd7dmuwg5tmn/branch/master?svg=true)](https://ci.appveyor.com/project/Vistaprint/terraformdevkit/branch/master)
4
+
5
+ Set of scripts to ease development and testing with [Terraform](https://www.terraform.io/).
6
+
7
+ The script collection includes support for:
8
+
9
+ * Managing AWS credentials
10
+ * Backing up the state from a failed Terraform execution
11
+ * Executing external commands
12
+ * Simple configuration management
13
+ * Simple reading and writing to AWS DynamoDB
14
+ * Multiplatform tools
15
+ * Making simple HTTP requests
16
+ * Retrying a block of code
17
+ * Terraform environment management
18
+ * Locally installing Terraform
19
+ * Filtering Terraform logging messages
20
+
21
+ Most of these scripts exist to provide support to a module development and testing environment for Terraform: [TerraformModules](https://github.com/vistaprint/TerraformModules). But, they might be useful for other purposes too.
22
+
23
+ Currently this repository is tightly coupled with AWS and has not been tested to work with other providers. We are actively working to change this and hope to have a more generic solution soon. If you would like to see support for your favourite cloud provider please have submit a pull request implementing support and we will be more than happy to merge your changes in.
24
+
25
+ ## Installation
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ ```ruby
30
+ gem 'TerraformDevKit'
31
+ ```
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install TerraformDevKit
40
+
41
+ ## Usage
42
+
43
+ To use the library simply import it as:
44
+
45
+ ```ruby
46
+ require 'TerraformDevKit'
47
+ ```
48
+
49
+ ## Managing Terraform Environments
50
+
51
+ TerraformDevKit provides a set of Rake tasks and Ruby scripts to ease the management of multiple Terraform environments. Three major environment types are supported: `dev`, `test` and `prod`.
52
+
53
+ There might be many development environments (`dev`), each one with its own name. Development environments use a local Terraform backend. They are intented to be used by developers while adding features to the infrastructure.
54
+
55
+ Testing (`test`) and production (`prod`) environment use a remote backend. Thus, the Terraform state file is not kept in the local disk, but on S3. This allows multiple developers to easily work on the same infrastructure instance. For safety reasons, operations that affect testing and production environments require manual user input. This is not the case for development environments.
56
+
57
+ TerraformDevKit expects templated versions (using [Mustache](https://mustache.github.io/)) of the Terraform files. Such files might contain placeholders for several fields such as `Environment`, (AWS) `Region` or (AWS) `Profile`, among others. TerraformDevKit uses the template files to generate the final files that will be consumed by Terraform. As an example, for the production environment, the resulting files are placed in a directory named `envs/prod`.
58
+
59
+ ### Configuration Files
60
+
61
+ Configuration files must be placed in a directory named `config`. Each environment type requires a different configuration file. Thus, the following three files must be placed in the `config` directory:
62
+
63
+ * `config-dev.yml`
64
+ * `config-test.yml`
65
+ * `config-prod.yml`
66
+
67
+ The first one contains the configuration for **all** the development environments that are created. The other two contain the configuration for the test and production environments, respectively.
68
+
69
+ A sample configuration files is shown next:
70
+
71
+ ```yaml
72
+ terraform-version: 0.12.24
73
+ project-name: my super cool project
74
+ aws:
75
+ profile: myprofile
76
+ region: eu-west-1
77
+ ```
78
+
79
+ The AWS profile **must not** be specified for test and production accounts, as users are required to manually type the profile name.
80
+
81
+ ### A Minimal Rakefile
82
+
83
+ ```ruby
84
+ ROOT_PATH = File.dirname(File.expand_path(__FILE__))
85
+
86
+ spec = Gem::Specification.find_by_name 'TerraformDevKit'
87
+ load "#{spec.gem_dir}/tasks/devkit.rake"
88
+
89
+ task :custom_test, [:env] do |_, args|
90
+ # Add some tests here
91
+ end
92
+ ```
93
+
94
+ #### Overrides
95
+
96
+ It's possible to override the location of your config files by setting the variable `CONFIG_FILE` in the top level `Rakefile`
97
+
98
+ ```ruby
99
+ # %s will be substituted with the environment name.
100
+ # File is exected to live in /c/path/to/root/config/config-dev.yml
101
+ CONFIG_FILE = File.join(ROOT_PATH, 'config', 'config-%s.yml')
102
+ ```
103
+
104
+ ### Tasks and Hooks
105
+
106
+ TerraformDevKit provides a set of generic tasks to perform:
107
+
108
+ * `prepare`: prepares the environment
109
+ * `plan`: shows the plan to create the infrastructure
110
+ * `apply`: creates the infrastructure
111
+ * `destroy`: destroys the infrastructure
112
+ * `clean`: cleans the environment (after destroying the infrastructure)
113
+ * `test`: tests a local environment
114
+ * `preflight`: creates a temporary infrastructure and runs the test task
115
+
116
+ Additionally, TerraformDevKit allows users to define a set of hooks that will be called during the different steps required to complete the previous list of tasks. The following hooks are available:
117
+
118
+ * `pre_apply`: invoked before `apply` task runs
119
+ * `post_apply`: invoked after `apply` task runs
120
+ * `pre_destroy`: invoked before `destroy` task runs
121
+ * `post_destroy`: invoked after `destroy` task runs
122
+ * `custom_prepare`: invoked during the preparation process, before terraform is initialized and the environment folder is created
123
+ * `custom_test`: invoked during as part of the `test` task, right after `apply` completes.
124
+
125
+ ### Sample Terraform Templates
126
+
127
+ The following file (`main.tf.mustache`) contains the infrastructure configuration (a single S3 bucket) as well as information related to the AWS provider.
128
+
129
+ ```hcl
130
+ locals {
131
+ env = "{{Environment}}"
132
+ }
133
+
134
+ # See example below for how to configure a remote backend
135
+
136
+ provider "aws" {
137
+ profile = "{{Profile}}"
138
+ region = "{{Region}}"
139
+ }
140
+
141
+ resource "aws_s3_bucket" "raw" {
142
+ bucket = "foo-${local.env}"
143
+ acl = "private"
144
+
145
+ {{#LocalBackend}}
146
+ force_destroy = true
147
+ {{/LocalBackend}}
148
+ {{^LocalBackend}}
149
+ lifecycle {
150
+ prevent_destroy = true
151
+ }
152
+ {{/LocalBackend}}
153
+ }
154
+ ```
155
+
156
+ The config file requires a `project-name` to be set. This project name is then use to generate the S3 bucket and dynamodb lock table required by terraform to mamage remote state. To use the remote state feature of TerraformDevKit you must add the following section to your `main.tf.mustache` file:
157
+
158
+ ```hcl
159
+ terraform {
160
+ {{#LocalBackend}}
161
+ backend "local" {}
162
+ {{/LocalBackend}}
163
+ {{^LocalBackend}}
164
+ backend "s3" {
165
+ bucket = "{{ProjectName}}-{{Environment}}-state"
166
+ key = "{{ProjectAcronym}}-{{Environment}}.tfstate"
167
+ dynamodb_table = "{{ProjectAcronym}}-{{Environment}}-lock-table"
168
+ encrypt = true
169
+ profile = "{{Profile}}"
170
+ region = "{{Region}}"
171
+ }
172
+ {{/LocalBackend}}
173
+ }
174
+ ```
175
+
176
+ ### Injecting Additional Variables into Template Files
177
+
178
+ In addition to the default variables that are passed to Mustache when rendering a template file, users can provide additional variables. To do so, users must register a procedure that receives the environment as a parameter and returns a map with the extra variables and their values. An example is shown next:
179
+
180
+ ```ruby
181
+ TDK::TerraformConfigManager.register_extra_vars_proc(
182
+ proc do
183
+ { SumoLogicEndpoint: TDK::Configuration.get('sumologic')['endpoint'] }
184
+ end
185
+ )
186
+ ```
187
+
188
+ ### Additional Template Sources
189
+
190
+ TerraformDevKit looks for mustache templates in the root directory, and creates final versions of these files after injecting values such as the environment name or the project name.
191
+
192
+ In addition to doing this for the infrastructure description files (`.tf` files), TerraformDevKit allows users to specify additional template sources. This feature could be used, for instance, to version other types of files whose content depends on values such as the environment name.
193
+
194
+ The following example shows how to instruct TerraformDevKit to read templates from different source folders, and write the resulting files into their corresponding destination folders located within an environment folder (e.g., `envs/<env-name>/`):
195
+
196
+ ```yaml
197
+ terraform-version: 0.12.24
198
+ project-name: my super cool project
199
+ aws:
200
+ profile: myprofile
201
+ region: eu-west-1
202
+ template-dirs:
203
+ template-dest-folder: ../template-src-folder
204
+ template-dest-folder: ../another-template-src-folder
205
+ another-template-dest-folder: yet-another-template-src-folder
206
+ ```
207
+
208
+ ### Copying Files to the Environment Folder
209
+
210
+ Sometimes it may be desirable to copy files that exist in a given folder into the environment folder. The `copy-files` field makes this possible. See the following configuration for an example:
211
+
212
+ ```yaml
213
+ terraform-version: 0.12.24
214
+ project-name: my super cool project
215
+ copy-files:
216
+ dest-folder: ../src-folder
217
+ another-dest-folder: ../../another-src-folder
218
+ dest-file: src-file
219
+ ```
220
+
221
+ ### Updating Modules
222
+
223
+ Terraform will get the necessary modules every time a new environment is created. Once the modules are cached, there is generally no need to keep updating the modules each time Terraform is executed. When using a module repository it is possible to select a specific version to use (as shown [here](https://www.terraform.io/docs/modules/sources.html#ref)). In such a case, Terraform will automatically update the modules whenever the version number is changed.
224
+
225
+ When using local modules (e.g., during development process) it might be desirable to update the modules every time Terraform runs. This can be achieved by setting the environment variable `TF_DEVKIT_UPDATE_MODULES` to `true`.
226
+
227
+ ## Development
228
+
229
+ 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.
230
+
231
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
232
+
233
+ ## Contributing
234
+
235
+ Bug reports and pull requests are welcome on GitHub at https://github.com/vistaprint/TerraformDevKit.
236
+
237
+ ## License
238
+
239
+ The gem is available as open source under the terms of the [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0).