tfwrapper 0.2.0.beta1
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 +54 -0
- data/.rubocop.yml +22 -0
- data/ChangeLog.md +3 -0
- data/Gemfile +4 -0
- data/Guardfile +21 -0
- data/LICENSE +21 -0
- data/README.md +320 -0
- data/Rakefile +60 -0
- data/circle.yml +24 -0
- data/lib/tfwrapper/helpers.rb +81 -0
- data/lib/tfwrapper/raketasks.rb +357 -0
- data/lib/tfwrapper/version.rb +6 -0
- data/lib/tfwrapper.rb +8 -0
- data/spec/acceptance/acceptance_helpers.rb +99 -0
- data/spec/acceptance/acceptance_spec.rb +461 -0
- data/spec/acceptance/consulserver.rb +34 -0
- data/spec/fixtures/Rakefile +7 -0
- data/spec/fixtures/testOne.tf +22 -0
- data/spec/fixtures/testThree/Rakefile +32 -0
- data/spec/fixtures/testThree/bar/testThreeBar.tf +28 -0
- data/spec/fixtures/testThree/baz/testThreeBaz.tf +22 -0
- data/spec/fixtures/testThree/foo/testThreeFoo.tf +27 -0
- data/spec/fixtures/testTwo/Rakefile +9 -0
- data/spec/fixtures/testTwo/foo/bar/testTwo.tf +28 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/unit/helpers_spec.rb +143 -0
- data/spec/unit/raketasks_spec.rb +851 -0
- data/tfwrapper.gemspec +61 -0
- metadata +419 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d770015f26d1cc19424567299a4cf0d300c7c9d
|
4
|
+
data.tar.gz: 682aecc64e4c300ff15140221f7b69b47a7042d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0501b6c78f2c23afc807ab5f2942798929d177fdfd07230504135c5470360655bd5705c6b4086977b763f6337d0a5dc28d997c816d78519e1b0bef8c1243db4c
|
7
|
+
data.tar.gz: 2361518650e15fef68dab14983345195935b8a46bdd2fd88dc1c8a70813140a5f43ba57781efed2a0ce79e79867dda1e950e340bfe8e51d47feacf1e381ef449
|
data/.gitignore
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
/results/
|
13
|
+
/vendor/
|
14
|
+
.terraform
|
15
|
+
build.tfvars.json
|
16
|
+
|
17
|
+
# Used by dotenv library to load environment variables.
|
18
|
+
# .env
|
19
|
+
|
20
|
+
## Specific to RubyMotion:
|
21
|
+
.dat*
|
22
|
+
.repl_history
|
23
|
+
build/
|
24
|
+
*.bridgesupport
|
25
|
+
build-iPhoneOS/
|
26
|
+
build-iPhoneSimulator/
|
27
|
+
|
28
|
+
## Specific to RubyMotion (use of CocoaPods):
|
29
|
+
#
|
30
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
31
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
32
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
33
|
+
#
|
34
|
+
# vendor/Pods/
|
35
|
+
|
36
|
+
## Documentation cache and generated files:
|
37
|
+
/.yardoc/
|
38
|
+
/_yardoc/
|
39
|
+
/doc/
|
40
|
+
/rdoc/
|
41
|
+
|
42
|
+
## Environment normalization:
|
43
|
+
/.bundle/
|
44
|
+
/vendor/bundle
|
45
|
+
/lib/bundler/man/
|
46
|
+
|
47
|
+
# for a library or gem, you might want to ignore these files since the code is
|
48
|
+
# intended to run in multiple environments; otherwise, check them in:
|
49
|
+
Gemfile.lock
|
50
|
+
.ruby-version
|
51
|
+
.ruby-gemset
|
52
|
+
|
53
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
54
|
+
.rvmrc
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Metrics/AbcSize:
|
2
|
+
Max: 22
|
3
|
+
|
4
|
+
Metrics/BlockLength:
|
5
|
+
Max: 1000
|
6
|
+
|
7
|
+
Metrics/ClassLength:
|
8
|
+
Max: 500
|
9
|
+
|
10
|
+
Metrics/MethodLength:
|
11
|
+
Max: 100
|
12
|
+
|
13
|
+
# These are to compensate for some warnings that differ by
|
14
|
+
# rubocop/ruby version
|
15
|
+
Lint/UnneededDisable:
|
16
|
+
Exclude:
|
17
|
+
- 'lib/tfwrapper/raketasks.rb'
|
18
|
+
|
19
|
+
Style/MutableConstant:
|
20
|
+
Exclude:
|
21
|
+
- 'lib/tfwrapper/version.rb'
|
22
|
+
- 'spec/acceptance/acceptance_spec.rb'
|
data/ChangeLog.md
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :bundler do
|
4
|
+
watch('tfwrapper.gemspec')
|
5
|
+
end
|
6
|
+
|
7
|
+
guard :rubocop do
|
8
|
+
watch(/.+\.rb$/)
|
9
|
+
watch(%r{(?:.+\/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
10
|
+
end
|
11
|
+
|
12
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
13
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
14
|
+
watch(%r{^spec/.+_spec\.rb})
|
15
|
+
watch(%r{^spec/(.+)/.+_spec\.rb})
|
16
|
+
watch(%r{^lib\/tfwrapper/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
17
|
+
end
|
18
|
+
|
19
|
+
guard 'yard', port: '8808' do
|
20
|
+
watch(/README\.md/)
|
21
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Manheim
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
# tfwrapper
|
2
|
+
|
3
|
+
Build of master branch: [](https://circleci.com/gh/manheim/tfwrapper)
|
4
|
+
|
5
|
+
Documentation: [http://www.rubydoc.info/gems/tfwrapper/](http://www.rubydoc.info/gems/tfwrapper/)
|
6
|
+
|
7
|
+
tfwrapper provides Rake tasks for working with [Hashicorp Terraform](https://www.terraform.io/) 0.9+, ensuring proper initialization and passing in variables from the environment or Ruby, as well as optionally pushing some information to Consul. tfwrapper also attempts to detect and retry
|
8
|
+
failed runs due to AWS throttling or access denied errors.
|
9
|
+
|
10
|
+
## Overview
|
11
|
+
|
12
|
+
This Gem provides the following Rake tasks:
|
13
|
+
|
14
|
+
* __tf:init__ - run ``terraform init`` to pull down dependency modules and configure remote
|
15
|
+
state backend. This task also checks that any configured environment variables are set and
|
16
|
+
that the ``terraform`` version is compatible with this gem.
|
17
|
+
* __tf:plan__ - run ``terraform plan`` with all variables and configuration, and TF variables written to disk. You can specify
|
18
|
+
one or more optional [resource address](https://www.terraform.io/docs/internals/resource-addressing.html) targets to pass to
|
19
|
+
terraform with the ``-target`` flag as Rake task arguments, i.e. ``bundle exec rake tf:plan[aws_instance.foo[1]]`` or
|
20
|
+
``bundle exec rake tf:plan[aws_instance.foo[1],aws_instance.bar[2]]``; see the
|
21
|
+
[plan documentation](https://www.terraform.io/docs/commands/plan.html) for more information.
|
22
|
+
* __tf:apply__ - run ``terraform apply`` with all variables and configuration, and TF variables written to disk. You can specify
|
23
|
+
one or more optional [resource address](https://www.terraform.io/docs/internals/resource-addressing.html) targets to pass to
|
24
|
+
terraform with the ``-target`` flag as Rake task arguments, i.e. ``bundle exec rake tf:apply[aws_instance.foo[1]]`` or
|
25
|
+
``bundle exec rake tf:apply[aws_instance.foo[1],aws_instance.bar[2]]``; see the
|
26
|
+
[apply documentation](https://www.terraform.io/docs/commands/apply.html) for more information. This also runs a plan first.
|
27
|
+
* __tf:refresh__ - run ``terraform refresh``
|
28
|
+
* __tf:destroy__ - run ``terraform destroy`` with all variables and configuration, and TF variables written to disk. You can specify
|
29
|
+
one or more optional [resource address](https://www.terraform.io/docs/internals/resource-addressing.html) targets to pass to
|
30
|
+
terraform with the ``-target`` flag as Rake task arguments, i.e. ``bundle exec rake tf:destroy[aws_instance.foo[1]]`` or
|
31
|
+
``bundle exec rake tf:destroy[aws_instance.foo[1],aws_instance.bar[2]]``; see the
|
32
|
+
[destroy documentation](https://www.terraform.io/docs/commands/destroy.html) for more information.
|
33
|
+
* __tf:write_tf_vars__ - used as a prerequisite for other tasks; write Terraform variables to file on disk
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
__Note:__ tfwrapper only supports Ruby >= 2.0.0. The effort to maintain compatibility
|
38
|
+
with 1.9.3 is simply too high to justify.
|
39
|
+
|
40
|
+
Add to your ``Gemfile``:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
gem 'tfwrapper', '~> 0.1.0'
|
44
|
+
```
|
45
|
+
|
46
|
+
### Supported Terraform Versions
|
47
|
+
|
48
|
+
tfwrapper only supports terraform 0.9+. It has been tested up to 0.9.2.
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
To use the Terraform rake tasks, require the module in your Rakefile and use the
|
53
|
+
``install_tasks`` method to set up the tasks. ``install_tasks`` takes one mandatory parameter,
|
54
|
+
``tf_dir`` specifying the relative path (from the Rakefile) to the Terraform configuration.
|
55
|
+
|
56
|
+
For a directory layout like:
|
57
|
+
|
58
|
+
```
|
59
|
+
.
|
60
|
+
├── bar.tf
|
61
|
+
├── foo.tf
|
62
|
+
├── main.tf
|
63
|
+
└── Rakefile
|
64
|
+
```
|
65
|
+
|
66
|
+
The minimal ``Rakefile`` would be:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
require 'tfwrapper/raketasks'
|
70
|
+
|
71
|
+
TFWrapper::RakeTasks.install_tasks('.')
|
72
|
+
```
|
73
|
+
|
74
|
+
``rake -T`` output:
|
75
|
+
|
76
|
+
```
|
77
|
+
rake tf:apply[target] # Apply a terraform plan that will provision your resources; specify optional CSV targets
|
78
|
+
rake tf:destroy[target] # Destroy any live resources that are tracked by your state files; specify optional CSV targets
|
79
|
+
rake tf:init # Run terraform init with appropriate arguments
|
80
|
+
rake tf:plan[target] # Output the set plan to be executed by apply; specify optional CSV targets
|
81
|
+
rake tf:write_tf_vars # Write PWD/build.tfvars.json
|
82
|
+
```
|
83
|
+
|
84
|
+
You can also point ``tf_dir`` to an arbitrary directory relative to the Rakefile, such as when your terraform
|
85
|
+
configurations are nested below the Rakefile:
|
86
|
+
|
87
|
+
```
|
88
|
+
.
|
89
|
+
├── infrastructure
|
90
|
+
│ └── terraform
|
91
|
+
│ ├── bar.tf
|
92
|
+
│ ├── foo.tf
|
93
|
+
│ └── main.tf
|
94
|
+
├── lib
|
95
|
+
├── Rakefile
|
96
|
+
└── spec
|
97
|
+
```
|
98
|
+
|
99
|
+
Rakefile:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
require 'tfwrapper/raketasks'
|
103
|
+
|
104
|
+
TFWrapper::RakeTasks.install_tasks('infrastructure/terraform')
|
105
|
+
```
|
106
|
+
|
107
|
+
### Environment Variables to Terraform Variables
|
108
|
+
|
109
|
+
If you wish to bind the values of environment variables to Terraform variables, you can specify a mapping
|
110
|
+
of Terraform variable name to environment variable name in the ``tf_vars_from_env`` option; these variables
|
111
|
+
will be automatically read from the environment and passed into Terraform with the appropriate names. The following
|
112
|
+
example sets the ``consul_address`` terraform variable to the value of the ``CONSUL_HOST`` environment variable
|
113
|
+
(defaulting it to ``consul.example.com:8500`` if it is not already set in the environment),
|
114
|
+
and likewise for the ``environment`` terraform variable from the ``ENVIRONMENT`` env var.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
require 'tfwrapper/raketasks'
|
118
|
+
ENV['CONSUL_HOST'] ||= 'consul.example.com:8500'
|
119
|
+
|
120
|
+
TFWrapper::RakeTasks.install_tasks(
|
121
|
+
'.',
|
122
|
+
tf_vars_from_env: {
|
123
|
+
'consul_address' => 'CONSUL_HOST',
|
124
|
+
'environment' => 'ENVIRONMENT',
|
125
|
+
}
|
126
|
+
)
|
127
|
+
```
|
128
|
+
|
129
|
+
### Ruby Variables to Terraform Variables
|
130
|
+
|
131
|
+
If you wish to explicitly bind values from your Ruby code to terraform variables, you can do this with
|
132
|
+
the ``tf_extra_vars`` option. Variables specified in this way will override same-named variables populated
|
133
|
+
via ``tf_vars_from_env``. In the following example, the ``foobar`` terraform variable will have a value
|
134
|
+
of ``baz``, regardless of what the ``FOOBAR`` environment variable is set to, and the ``hostname``
|
135
|
+
terraform variable will be set to the hostname (``Socket.gethostname``) of the system Rake is running on:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
require 'socket'
|
139
|
+
require 'tfwrapper/raketasks'
|
140
|
+
|
141
|
+
ENV['FOOBAR'] ||= 'not_baz'
|
142
|
+
|
143
|
+
TFWrapper::RakeTasks.install_tasks(
|
144
|
+
'.',
|
145
|
+
tf_vars_from_env: {
|
146
|
+
'foobar' => 'FOOBAR'
|
147
|
+
},
|
148
|
+
tf_extra_vars: {
|
149
|
+
'foobar' => 'baz',
|
150
|
+
'hostname' => Socket.gethostname
|
151
|
+
}
|
152
|
+
)
|
153
|
+
```
|
154
|
+
|
155
|
+
### Namespace Prefixes for Multiple Configurations
|
156
|
+
|
157
|
+
If you need to work with multiple different Terraform configurations, this is possible
|
158
|
+
by adding a namespace prefix and calling ``install_tasks`` multiple times. The following example
|
159
|
+
will produce two sets of terraform Rake tasks; one with the default ``tf:`` namespace
|
160
|
+
that acts on the configurations under ``tf/foo``, and one with a ``bar_tf:`` namespace
|
161
|
+
that acts on the configurations under ``tf/bar``. You can use as many namespaces as
|
162
|
+
you want.
|
163
|
+
|
164
|
+
Directory tree:
|
165
|
+
|
166
|
+
```
|
167
|
+
.
|
168
|
+
├── Rakefile
|
169
|
+
└── tf
|
170
|
+
├── bar
|
171
|
+
│ └── bar.tf
|
172
|
+
└── foo
|
173
|
+
└── foo.tf
|
174
|
+
```
|
175
|
+
|
176
|
+
Rakefile:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
require 'tfwrapper/raketasks'
|
180
|
+
|
181
|
+
# foo/ (default) terraform tasks
|
182
|
+
TFWrapper::RakeTasks.install_tasks('tf/foo')
|
183
|
+
|
184
|
+
# bar/ terraform tasks
|
185
|
+
TFWrapper::RakeTasks.install_tasks('tf/bar', namespace_prefix: 'bar')
|
186
|
+
```
|
187
|
+
|
188
|
+
``rake -T`` output:
|
189
|
+
|
190
|
+
```
|
191
|
+
rake bar_tf:apply[target] # Apply a terraform plan that will provision your resources; specify optional CSV targets
|
192
|
+
rake bar_tf:destroy[target] # Destroy any live resources that are tracked by your state files; specify optional CSV targets
|
193
|
+
rake bar_tf:init # Run terraform init with appropriate arguments
|
194
|
+
rake bar_tf:plan[target] # Output the set plan to be executed by apply; specify optional CSV targets
|
195
|
+
rake bar_tf:write_tf_vars # Write PWD/bar_build.tfvars.json
|
196
|
+
rake tf:apply[target] # Apply a terraform plan that will provision your resources; specify optional CSV targets
|
197
|
+
rake tf:destroy[target] # Destroy any live resources that are tracked by your state files; specify optional CSV targets
|
198
|
+
rake tf:init # Run terraform init with appropriate arguments
|
199
|
+
rake tf:plan[target] # Output the set plan to be executed by apply; specify optional CSV targets
|
200
|
+
rake tf:write_tf_vars # Write PWD/build.tfvars.json
|
201
|
+
```
|
202
|
+
|
203
|
+
### Backend Configuration Options
|
204
|
+
|
205
|
+
``install_tasks`` accepts a ``backend_config`` hash of options to pass as backend configuration
|
206
|
+
to ``terraform init`` via the ``-backend-config='key=value'`` command line argument. This can
|
207
|
+
be used when you need to pass some backend configuration in from the environment, such as a
|
208
|
+
specific remote state storage path, credentials, etc.
|
209
|
+
|
210
|
+
For a simple example, assume we aren't using [state environments](https://www.terraform.io/docs/state/environments.html)
|
211
|
+
but instead opt to use specific paths based on a ``ENVIRONMENT`` environment variable.
|
212
|
+
|
213
|
+
Our terraform configuration might include something like:
|
214
|
+
|
215
|
+
```
|
216
|
+
terraform {
|
217
|
+
required_version = "> 0.9.0"
|
218
|
+
backend "consul" {
|
219
|
+
address = "consul.example.com:8500"
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
variable "environment" {}
|
224
|
+
```
|
225
|
+
|
226
|
+
And the Rakefile would pass in the path to store state in Consul, as well as
|
227
|
+
passing the ``ENVIRONMENT`` env var into Terraform for use:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
require 'tfwrapper/raketasks'
|
231
|
+
|
232
|
+
TFWrapper::RakeTasks.install_tasks(
|
233
|
+
'.',
|
234
|
+
tf_vars_from_env: {'environment' => 'ENVIRONMENT'},
|
235
|
+
backend_config: {'path' => "terraform/foo/#{ENVIRONMENT}"}
|
236
|
+
)
|
237
|
+
```
|
238
|
+
|
239
|
+
### Environment Variables to Consul
|
240
|
+
|
241
|
+
tfwrapper also includes functionality to push environment variables to Consul
|
242
|
+
(as a JSON object) after a successful apply. This is mainly useful when running
|
243
|
+
tfwrapper from within Jenkins or another job runner, where they can be used to
|
244
|
+
pre-populate user input fields on subsequent runs. This is configured via the
|
245
|
+
``consul_url`` and ``consul_env_vars_prefix`` options:
|
246
|
+
|
247
|
+
Example Terraform snippet:
|
248
|
+
|
249
|
+
```
|
250
|
+
variable "foo" {}
|
251
|
+
variable "bar" {}
|
252
|
+
```
|
253
|
+
|
254
|
+
Rakefile:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
require 'tfwrapper/raketasks'
|
258
|
+
|
259
|
+
TFWrapper::RakeTasks.install_tasks(
|
260
|
+
'.',
|
261
|
+
tf_vars_from_env: {'foo' => 'FOO', 'bar' => 'BAR'},
|
262
|
+
consul_url: 'http://consul.example.com:8500',
|
263
|
+
consul_env_vars_prefix: 'terraform/inputs/foo'
|
264
|
+
)
|
265
|
+
```
|
266
|
+
|
267
|
+
After a successful terraform apply, e.g.:
|
268
|
+
|
269
|
+
```
|
270
|
+
FOO=one BAR=two bundle exec rake tf:apply
|
271
|
+
```
|
272
|
+
|
273
|
+
The key in Consul at ``terraform/inputs/foo`` will be set to a JSON hash of the
|
274
|
+
environment variables used via ``tf_vars_from_env`` and their values:
|
275
|
+
|
276
|
+
```shell
|
277
|
+
$ consul kv get terraform/inputs/foo
|
278
|
+
{"FOO":"one", "BAR":"two"}
|
279
|
+
```
|
280
|
+
|
281
|
+
## Development
|
282
|
+
|
283
|
+
1. ``bundle install --path vendor``
|
284
|
+
2. ``bundle exec rake pre_commit`` to ensure unit tests are passing and style is valid before making your changes.
|
285
|
+
3. ``bundle exec rake spec:acceptance`` to ensure acceptance tests are passing before making your changes.
|
286
|
+
4. make your changes, and write unit tests for them. If you introduced user-visible (public API) changes, write acceptance tests for them. You can run ``bundle exec guard`` to continually run unit tests and rubocop when files change.
|
287
|
+
5. ``bundle exec rake pre_commit`` to confirm your unit tests pass and your style is valid. You should confirm 100% coverage. If you wish, you can run ``bundle exec guard`` to dynamically run rspec, rubocop and YARD when relevant files change.
|
288
|
+
6. ``bundle exec rake spec:acceptance`` to ensure acceptance tests are passing.
|
289
|
+
7. Update ``ChangeLog.md`` for your changes.
|
290
|
+
8. Run ``bundle exec rake yard:serve`` to generate documentation for your Gem and serve it live at [http://localhost:8808](http://localhost:8808), and ensure it looks correct.
|
291
|
+
9. Open a pull request for your changes.
|
292
|
+
10. When shipped, wait for CircleCI to test. Once shipped and tests pass, merge the PR.
|
293
|
+
|
294
|
+
When running inside CircleCI, rspec will place reports and artifacts under the right locations for CircleCI to archive them. When running outside of CircleCI, coverage reports will be written to ``coverage/`` and test reports (HTML and JUnit XML) will be written to ``results/``.
|
295
|
+
|
296
|
+
### Acceptance Tests
|
297
|
+
|
298
|
+
This gem includes some rspec-based acceptance tests, runnable via ``bundle exec rake spec:acceptance``. These tests download
|
299
|
+
a specific version of Terraform and Consul, run a local Consul server (in ``-dev`` mode), and actually run ``terraform`` via
|
300
|
+
``rake`` and confirm that Terraform both runs correctly and correctly updates state in Consul. The terraform configurations
|
301
|
+
and rakefiles used can be found in ``spec/acceptance``. The terraform configurations use only the
|
302
|
+
[consul](https://www.terraform.io/docs/providers/consul/index.html) provider, to remove any external dependencies other than
|
303
|
+
Consul (which is already used to test remote state).
|
304
|
+
|
305
|
+
Note that the acceptance tests depend on the GNU coreutils ``timeout`` command.
|
306
|
+
|
307
|
+
## Release Checklist
|
308
|
+
|
309
|
+
1. Ensure Circle tests are passing.
|
310
|
+
2. Build docs locally (``bundle exec rake yard:serve``) and ensure they look correct.
|
311
|
+
3. Ensure changelog entries exist for all changes since the last release.
|
312
|
+
4. Bump the version in ``lib/tfwrapper/version.rb``
|
313
|
+
5. Change the version specifier in the "Installation" section of this README, above, as appropriate.
|
314
|
+
6. Commit those changes, open a PR for the release. Once shipped and Circle passes, merge and pull down locally.
|
315
|
+
7. Deployment is done locally, with ``bundle exec rake release``.
|
316
|
+
|
317
|
+
## License
|
318
|
+
|
319
|
+
The gem is available as open source under the terms of the
|
320
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'bundler/gem_tasks'
|
7
|
+
require 'rake/clean'
|
8
|
+
require 'yard'
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
require 'rubocop/rake_task'
|
11
|
+
|
12
|
+
CLOBBER.include 'pkg'
|
13
|
+
|
14
|
+
desc 'Run RuboCop on the lib directory'
|
15
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
16
|
+
task.fail_on_error = true
|
17
|
+
end
|
18
|
+
|
19
|
+
namespace :spec do
|
20
|
+
desc 'run ALL spec tests'
|
21
|
+
task all: %i[unit acceptance]
|
22
|
+
|
23
|
+
desc 'run unit tests'
|
24
|
+
RSpec::Core::RakeTask.new(:unit) do |t|
|
25
|
+
t.rspec_opts = ['--require', 'spec_helper']
|
26
|
+
t.pattern = 'spec/unit/*_spec.rb'
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'run acceptance tests'
|
30
|
+
RSpec::Core::RakeTask.new(:acceptance) do |t|
|
31
|
+
t.rspec_opts = ['--require', 'spec_helper']
|
32
|
+
t.pattern = 'spec/acceptance/*_spec.rb'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
namespace :yard do
|
37
|
+
YARD::Rake::YardocTask.new do |t|
|
38
|
+
t.name = 'generate'
|
39
|
+
t.files = ['lib/**/*.rb'] # optional
|
40
|
+
t.options = ['--private', '--protected'] # optional
|
41
|
+
t.stats_options = ['--list-undoc'] # optional
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'serve YARD documentation on port 8808 (restart to regenerate)'
|
45
|
+
task serve: [:generate] do
|
46
|
+
puts 'Running YARD server on port 8808'
|
47
|
+
puts 'Use Ctrl+C to exit server.'
|
48
|
+
YARD::CLI::Server.run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'Run specs and rubocop before pushing'
|
53
|
+
task pre_commit: %i[spec:unit rubocop]
|
54
|
+
|
55
|
+
desc 'Display the list of available rake tasks'
|
56
|
+
task :help do
|
57
|
+
system('rake -T')
|
58
|
+
end
|
59
|
+
|
60
|
+
task default: [:help]
|
data/circle.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
dependencies:
|
3
|
+
cache_directories:
|
4
|
+
- "~/.rvm/gems"
|
5
|
+
pre:
|
6
|
+
- sudo add-apt-repository -y ppa:git-core/ppa && sudo apt-get update && sudo apt-get install git
|
7
|
+
override:
|
8
|
+
- 'bundle install'
|
9
|
+
- 'rvm 2.0.0-p598 exec bundle install'
|
10
|
+
- 'rvm 2.1.8 exec bundle install'
|
11
|
+
- 'rvm 2.2.5 exec bundle install'
|
12
|
+
- 'rvm 2.3.1 exec bundle install'
|
13
|
+
|
14
|
+
test:
|
15
|
+
override:
|
16
|
+
- 'bundle exec ruby --version'
|
17
|
+
- 'bundle exec rake spec:unit'
|
18
|
+
- 'bundle exec rake rubocop'
|
19
|
+
- 'rvm 2.0.0-p598 exec bundle exec rake spec:unit'
|
20
|
+
- 'rvm 2.1.8 exec bundle exec rake spec:unit'
|
21
|
+
- 'rvm 2.2.5 exec bundle exec rake spec:unit'
|
22
|
+
- 'rvm 2.3.1 exec bundle exec rake spec:unit'
|
23
|
+
- 'rvm 2.3.1 exec bundle exec rake spec:acceptance'
|
24
|
+
- 'bundle exec rake yard:generate'
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'English'
|
5
|
+
|
6
|
+
# TFWrapper
|
7
|
+
module TFWrapper
|
8
|
+
# generic helper functions for TFWrapper
|
9
|
+
module Helpers
|
10
|
+
# Run a system command, print the command before running it. If it exits
|
11
|
+
# with a non-zero status, print the exit status and output and then
|
12
|
+
# `fail`.
|
13
|
+
#
|
14
|
+
# @param cmd [String] the command to run
|
15
|
+
def self.run_cmd(cmd)
|
16
|
+
puts "Running command: #{cmd}"
|
17
|
+
out = `#{cmd}`
|
18
|
+
status = $CHILD_STATUS.exitstatus
|
19
|
+
return if status.zero?
|
20
|
+
puts "Command exited #{status}:"
|
21
|
+
puts out
|
22
|
+
raise StandardError, "ERROR: Command failed: #{cmd}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# popen2e wrapper to simultaneously stream command output and capture it.
|
26
|
+
#
|
27
|
+
# STDOUT and STDERR will be combined to the same stream, and returned as one
|
28
|
+
# string. This is because there doesn't seem to be a safe, cross-platform
|
29
|
+
# way to both capture and stream STDOUT and STDERR separately that isn't
|
30
|
+
# prone to deadlocking if large chunks of data are written to the pipes.
|
31
|
+
#
|
32
|
+
# @param cmd [String] command to run
|
33
|
+
# @param pwd [String] directory/path to run command in
|
34
|
+
# @return [Array] - out_err [String], exit code [Fixnum]
|
35
|
+
def self.run_cmd_stream_output(cmd, pwd)
|
36
|
+
old_sync = $stdout.sync
|
37
|
+
$stdout.sync = true
|
38
|
+
all_out_err = ''.dup
|
39
|
+
exit_status = nil
|
40
|
+
Open3.popen2e(cmd, chdir: pwd) do |stdin, stdout_and_err, wait_thread|
|
41
|
+
stdin.close_write
|
42
|
+
begin
|
43
|
+
while (line = stdout_and_err.gets)
|
44
|
+
puts line
|
45
|
+
all_out_err << line
|
46
|
+
end
|
47
|
+
rescue IOError => e
|
48
|
+
STDERR.puts "IOError: #{e}"
|
49
|
+
end
|
50
|
+
exit_status = wait_thread.value.exitstatus
|
51
|
+
end
|
52
|
+
# rubocop:disable Style/RedundantReturn
|
53
|
+
$stdout.sync = old_sync
|
54
|
+
return all_out_err, exit_status
|
55
|
+
# rubocop:enable Style/RedundantReturn
|
56
|
+
end
|
57
|
+
|
58
|
+
# Ensure that a given list of environment variables are present and
|
59
|
+
# non-empty. Raise StandardError if any aren't.
|
60
|
+
#
|
61
|
+
# @param required [Array] list of required environment variables
|
62
|
+
def self.check_env_vars(required)
|
63
|
+
missing = []
|
64
|
+
required.each do |name|
|
65
|
+
if !ENV.include?(name)
|
66
|
+
puts "ERROR: Environment variable '#{name}' must be set."
|
67
|
+
missing << name
|
68
|
+
elsif ENV[name].to_s.strip.empty?
|
69
|
+
puts "ERROR: Environment variable '#{name}' must not be empty."
|
70
|
+
missing << name
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# rubocop:disable Style/GuardClause
|
74
|
+
unless missing.empty?
|
75
|
+
raise StandardError, 'Missing or empty environment variables: ' \
|
76
|
+
"#{missing}"
|
77
|
+
end
|
78
|
+
# rubocop:enable Style/GuardClause
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|