tfwrapper 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ Version 0.1.0
2
+
3
+ - initial release (migrated from previous internal/private gem)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
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: [![CircleCI](https://circleci.com/gh/manheim/tfwrapper.svg?style=svg)](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