Giter Club home page Giter Club logo

rspec-terraform's Introduction

RSpec::Terraform

An RSpec extension for verifying Terraform configurations, with support for:

  • unit testing;
  • integration testing;
  • end-to-end testing; and
  • change auditing.

Installation

Add this line to your application's Gemfile:

gem 'rspec-terraform'

And then execute:

$ bundle

Or install it yourself as:

$ gem install rspec-terraform

Usage

To use RSpec::Terraform, require it in your spec_helper.rb file:

require 'rspec/terraform'

When required, RSpec::Terraform automatically configures itself against RSpec by:

  • adding helper methods to interact with Terraform;
  • adding matchers to verify Terraform plans; and
  • adding settings to control RSpec::Terraform's behaviour.

The sections below provide further details on each of these additions.

Helper methods

RSpec::Terraform adds helper methods to the RSpec DSL for planning, applying, and destroying Terraform configurations, as well as accessing variables to and outputs from Terraform configurations.

Each helper method takes a hash of parameters used to identify the configuration against which to act and to provide as options to the Terraform command being executed. Additionally, RSpec::Terraform includes a flexible approach to resolving these parameters allowing them to be sourced from a variety of locations. See the Configuration Providers section for more details.

When executing helper methods, RSpec::Terraform provides two execution modes, :in_place and :isolated. By default, RSpec::Terraform works against a Terraform configuration in place, i.e., it executes commands against the Terraform configuration directly, in the location specified. RSpec::Terraform can also operate in an isolated manner, wherein it initialises the configuration into an isolated directory before executing commands. See the Execution Mode section for more details.

plan

The plan helper produces a Terraform plan for a configuration, reads it into a Ruby representation and returns it.

plan requires a :configuration_directory parameter, representing the path to the configuration to plan and is typically invoked in a before(:context) hook, with the resulting plan stored for use in expectations:

before(:context) do
  @plan = plan(
    configuration_directory: 'path/to/configuration/directory'
  )
end

If the configuration has input variables, a :vars parameter can be provided as a hash:

before(:context) do
  @plan = plan(
    configuration_directory: 'path/to/configuration/directory',
    vars: {
      region: 'uk',
      zones: ['uk-a', 'uk-b'],
      tags: {
        name: 'important-thing',
        role: 'persistence'
      }
    }
  )
end

or within a block:

before(:context) do
  @plan = plan(
    configuration_directory: 'path/to/configuration/directory'
  ) do |vars|
    vars.region = 'uk'
    vars.zones = ['uk-a', 'uk-b']
    vars.tags = {
      name: 'important-thing',
      role: 'persistence'
    }
  end
end

plan accepts an optional :state_file parameter with the path to where the current state file for the configuration is located, useful when checking the incremental change that applying the configuration would have after a previous apply.

Internally, plan:

  • calls terraform init to initialise the configuration directory;
  • calls terraform plan to produce a plan file;
  • calls terraform show to read the contents of the plan file into a Ruby representation; and
  • deletes the plan file.

Any additional parameters passed to plan are passed on to the underlying Terraform invocations.

apply

destroy

output

var

Plan Matchers

Settings

Binary Location

Logging and Standard Streams

Execution Mode

The benefit of isolated execution is that nothing is carried over between test runs and providers and modules are fetched into a clean configuration directory every time. The downside is additional test run time.

Configuration Providers

Frequently Asked Questions

Development

To install dependencies and run the build, run the pre-commit build:

./go

This runs all unit tests and other checks including coverage and code linting / formatting.

To run only the unit tests, including coverage:

./go test:unit

To attempt to fix any code linting / formatting issues:

./go library:fix

To check for code linting / formatting issues without fixing:

./go library:check

You can also run bin/console for an interactive prompt that will allow you to experiment.

Managing CircleCI keys

To encrypt a GPG key for use by CircleCI:

openssl aes-256-cbc \
  -e \
  -md sha1 \
  -in ./config/secrets/ci/gpg.private \
  -out ./.circleci/gpg.private.enc \
  -k "<passphrase>"

To check decryption is working correctly:

openssl aes-256-cbc \
  -d \
  -md sha1 \
  -in ./.circleci/gpg.private.enc \
  -k "<passphrase>"

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/infrablocks/rspec-terraform. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

rspec-terraform's People

Contributors

dependabot[bot] avatar gryff avatar infrablocks-maintainers avatar tobyclemson avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

plukevdh

rspec-terraform's Issues

Terraform vs. Rspec logger isolation

Hi there! Thanks for this project. We found your RubyTerraform project and used it to write an rspec-terraform plugin... until we realized you all had done the same thing (and better).

Some usage feedback: One of the things it'd be nice to do is have the option to silence the terraform plan output. To do so, I can do

RSpec.configure do |config|
    config.terraform_stdout = Logger::LogDevice.new(IO::NULL)
end

However this appears to have the unintended side-effect of also silencing the rspec-terraform plugin's logging output. I assume this is because the logger gets reused between the RubyTerraform and RSpec::Terraform. It seems like it might be worthwhile to isolate the loggers so that we can toggle them/redirect them independently of one another.

Perhaps alternatively (or in addition to), it might be nice to have an rspec option flag called silence_terraform_output that would redirect the terraform output to IO::NULL.

I'm happy to submit a PR implementing either/both of these options if you all think this would be valuable. Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.