Giter Club home page Giter Club logo

ukhomeoffice.tf-testrunner's Introduction

Tf Testrunner

Docker Repository on Quay

tf-testrunner parses Terraform configuration to Python and then runs your tests.

Current terraform upgade tag is 32.

How it works:

Testrunner automates the output of the command terraform plan, saves its output to a temp directory. Parses the temp file to a Python dict object and then runs your test folder against it.

Refer to the examples directory for example Terraform projects that use tf-testrunner.

Usage

CI (Drone ~> 0.5) execution

Add a build step

  test:
    image: quay.io/ukhomeofficedigital/tf-testrunner:32
    commands: python -m unittest tests/*_test.py
drone exec

Docker (~> 1.13) in-situ execution

docker run --rm -v `pwd`:/mytests -w /mytests quay.io/ukhomeofficedigital/tf-testrunner:32

Python (~> 3.6.3) & Go (~> 1.9.2) execution

pip install git+git://github.com/UKHomeOffice/tf-testrunner.git#egg=tf-testrunner
go get github.com/wybczu/tfjson

Test authoring

mkdir tests
touch tests/__init__.py

tests/my_test.py

# pylint: disable=missing-docstring, line-too-long, protected-access
import unittest
from runner import Runner

class TestMyModule(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        self.snippet = """
            provider "aws" {
              region = "eu-west-2"
              access_key = "foo"
              secret_key = "bar"
              profile = "foo"
              skip_credentials_validation = true
              skip_get_ec2_platforms = true
              skip_requesting_account_id = true
            }
            module "my_module" {
              source = "./mymodule"
            }
        """
        self.runner = Runner(self.snippet)
        self.result = self.runner.result

    def test_terraform_version(self):
        print(self.result)
        self.assertEqual(self.result["terraform_version"], "0.12.25")

    def test_root_module(self):
        self.assertEqual(self.result["configuration"]["root_module"]["module_calls"]["my_module"]["source"], "./mymodule")

    def test_instance_type(self):
        self.assertEqual(self.runner.get_value("module.my_module.aws_instance.foo", "instance_type"), "t2.micro")

    def test_ami(self):
        self.assertEqual(self.runner.get_value("module.my_module.aws_instance.foo", "ami"), "foo")


if __name__ == '__main__':
    unittest.main()

my_module.tf

resource "aws_instance" "foo" {
  ami           = "foo"
  instance_type = "t2.micro"
}

More examples

Additional Usage Mothod get_vaule

To handle the terraform output plan of json structure, we are only interested in resource_changes sections with arrays of resources to be changed. Helper method get_vaule will get first parmater of module resource name and its change value in second parameter. See example snippet.

tests/tf_assertion_helper_test.py

import unittest
from tf_assertion_helper import get_value

class TestGetValue(unittest.TestCase):
    def setUp(self):
        self.snippet = {
            "format_version": "0.1",
            "terraform_version": "0.12.25",
            "planned_values": {},
            "resource_changes": [{
                "address": "module.rds_alarms.aws_cloudwatch_log_group.lambda_log_group_slack",
                "module_address": "module.rds_alarms",
                "mode": "managed",
                "type": "aws_cloudwatch_log_group",
                "name": "lambda_log_group_slack",
                "provider_name": "aws",
                "change": {
                    "actions": [
                        "create"
                    ],
                    "before": "None",
                    "after": {
                        "kms_key_id": "None",
                        "name": "/aws/lambda/foo-lambda-slack-notprod",
                        "name_prefix": "None",
                        "retention_in_days": 14,
                        "tags": {
                            "Name": "lambda-log-group-slack-1234-apps"
                        }
                    },
                    "after_unknown": {
                        "arn": "blah",
                        "id": "blah",
                        "tags": {}
                    }
                }
            }]
        }

    def test_happy_path(self):
        self.assertEqual(get_value(self.snippet, "module.rds_alarms.aws_cloudwatch_log_group.lambda_log_group_slack", "retention_in_days"), 14)

    def test_unhappy_path(self):
        self.assertNotEqual(get_value(self.snippet, "module.rds_alarms.aws_cloudwatch_log_group.lambda_log_group_slack", "kms_key_id"), "something_not_there")


if __name__ == '__main__':
    unittest.main()

Additional Usage Methos finder

To handle the occurrence of unique numbers in keys after parsing, use the assertion helper method finder.

tests/tf_assertion_helper_test.py

import unittest
from runner import Runner

parent = {
    'egress.482069346.cidr_blocks.#': '1',
    'egress.482069346.cidr_blocks.0': '0.0.0.0/0',
    'egress.482069346.description': '',
    'egress.482069346.from_port': '0',
    'egress.482069346.ipv6_cidr_blocks.#': '0',
    'egress.482069346.prefix_list_ids.#': '0',
    'egress.482069346.protocol': '-1',
    'egress.482069346.security_groups.#': '0',
    'egress.482069346.self': 'false',
    'egress.482069346.to_port': '0',
    'id': '',
    'ingress.#': '2',
    'ingress.244708223.cidr_blocks.#': '1',
    'ingress.244708223.cidr_blocks.0': '0.0.0.0/0',
    'ingress.244708223.description': '',
    'ingress.244708223.from_port': '3389',
    'ingress.244708223.ipv6_cidr_blocks.#': '0'
}

class TestFinder(unittest.TestCase):

    def test_happy_path(self):
        self.assertTrue(Runner.finder(parent, 'ingress', {'cidr_blocks.0': '0.0.0.0/0', 'from_port': '3389'}))

    def test_unhappy_path(self):
        self.assertFalse(Runner.finder(parent, 'ingress', {'cidr_blocks.0': '0.0.0.0/0', 'from_port': '0', 'self': 'true'}))


if __name__ == '__main__':
    unittest.main()

Acknowledgements

UPDATE TF12

Following tfjson is no longer support for terraform 12. This is the reason terraform 12 can only use default output of terraform plan in json format. Use terraform show -json planned_file

OLD TF11

We leverage tfjson to get a machine readable output of the terraform plan which we can then evaluate against. When terraform has an inbuilt machine readable output, expect a refactor of this tool to use that instead of tfjson.

When researching testing strategies for Terraform, we found Carlos Nunez's article Top 3 Terraform Testing Strategies... to be great inspiration and very informative.

ukhomeoffice.tf-testrunner's People

Contributors

chrisns avatar david-wirelab avatar duncan9562 avatar robinknipe avatar vaijab avatar

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.