Skip to content

Commit

Permalink
Merge pull request #113 from YakDriver/templatevars
Browse files Browse the repository at this point in the history
Templatevars
  • Loading branch information
YakDriver authored May 29, 2020
2 parents 29f8bd3 + 41df496 commit 923a8f2
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 34 deletions.
3 changes: 2 additions & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[bumpversion]
current_version = 0.5.14
current_version = 0.6.0
commit = False
tag = False
tag_name = {new_version}

[bumpversion:file:scratchrelaxtv/__init__.py]

[bumpversion:file:setup.cfg]

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGE LOG

## 0.6.0 - 2020.05.29

* Output Terraform 0.12.x variables (variables without `"${}"`)
* Add feature to extract template variables from templates and generate corresponding HCL

## 0.5.2 - 2019.07.19

* Add compatibility with HCL2 / Terraform 0.12.x variables (variables without `"${}"`)
Expand Down
95 changes: 79 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ Terraform module development tool.
1. Extract variables from `main.tf` and generate a `variables.tf` file
1. Find missing variables in `variables.tf` and `main.tf` based on each other
1. Generate a module use stub from a `variables.tf` file
1. Generate a .env file with variables from `main.tf`
1. Generate a .env or `.tfvars` file with variables from `main.tf`
1. Generate HCL with template variables corresponding to variables from a Terraform template
1. Delete extra *scratchrelaxtv* files

# install
Expand Down Expand Up @@ -51,14 +52,14 @@ Here are two example workflows using *scratchrelaxtv*.

## example: generate `variables.tf`

By default, *scratchrelaxtv* looks for `main.tf` and will generate a `variables.tf` file. Variables will be in the same order in `variables.tf` as they were in `main.tf`. There are options to sort variables. You can `--force` to overwrite an existing `variables.tf` file. Otherwise, *scratchrelaxtv* will generate new `variables.tf` files with each run: `variables.1.tf`, `variables.2.tf` and so on.
By default, *scratchrelaxtv* looks for `main.tf` and will generate a `variables.tf` file. (Use the `-i` to specify a different input file.) Variables will be in the same order in `variables.tf` as they were in `main.tf`. You can sort the variables using the `-a` (ascending) and `-d` (descending) options. You can also `--force` to overwrite an existing `variables.tf` file. Otherwise, *scratchrelaxtv* will generate new `variables.tf` files with each run: `variables.1.tf`, `variables.2.tf` and so on.

Assume this `main.tf`:
```hcl
resource "aws_s3_bucket" "this" {
count = "${var.create_bucket ? 1 : 0}"
bucket = "${var.bucket}"
region = "${var.region}"
count = var.create_bucket ? 1 : 0
bucket = var.bucket
region = var.region
}
```

Expand Down Expand Up @@ -100,8 +101,8 @@ Assume you already have a `main.tf` and a `variables.tf`. In this example, the `
`main.tf`:
```hcl
resource "aws_s3_bucket" "this" {
bucket = "${var.bucket}"
region = "${var.region}"
bucket = var.bucket
region = var.region
}
```

Expand Down Expand Up @@ -186,29 +187,89 @@ module "tests2" {
aws = "aws"
}
id = "${local.id}"
bucket = "${local.bucket}"
region = "${local.region}"
id = local.id
bucket = local.bucket
region = local.region
}
```

## example: generate a `.tfvars` file

By default, when generating a `.tfvars` file, *scratchrelaxtv* looks for `variables.tf`.

Assume this `variables.tf`:
```hcl
resource "aws_s3_bucket" "this" {
bucket = var.bucket
region = var.region
}
```

Run *scratchrelaxtv* with the generate `.tfvars` and sort-ascending options:
```console
$ relaxtv -ta
2019-06-21 20:01:35,362 - INFO - generating .tfvars file
2019-06-21 20:01:35,362 - INFO - input file: variables.tf
2019-06-21 20:01:35,362 - INFO - output file: terraform.tfvars
2019-06-21 20:01:35,362 - INFO - not forcing overwrite of output file
2019-06-21 20:01:35,362 - INFO - ordering output file ascending
```

The generated `terraform.tfvars` file:
```
bucket = "replace"
region = "replace"
```

## example: generate a `.tf` file from Terraform template

By default, when using template mode, *scratchrelaxtv* looks for `template.sh`. (Use the `-i` to specify a different input file.)

Assume this `template.sh`:
```bash
#!/bin/bash

build_os="${build_os}"
build_type="${build_type}"
```

Run *scratchrelaxtv* with the `--template` and sort-ascending options:
```console
$ relaxtv -a --template
2019-06-21 20:01:35,362 - INFO - extracting template variables
2019-06-21 20:01:35,362 - INFO - input file: template.sh
2019-06-21 20:01:35,362 - INFO - output file: template_vars.tf
2019-06-21 20:01:35,362 - INFO - not forcing overwrite of output file
2019-06-21 20:01:35,362 - INFO - ordering output file ascending
```

The generated `template_vars.tf` file:
```hcl
locals {
templates_vars = {
build_os = "replace"
build_type = "replace"
}
}
```

## example: generate a `.env` (dotenv) file

By default, when generating a `.env` file, *scratchrelaxtv* looks for `variables.tf`.
By default, when generating a `.env` file, *scratchrelaxtv* looks for `variables.tf`. (Use the `-i` to specify a different input file.)

Assume this `variables.tf`:
```hcl
resource "aws_s3_bucket" "this" {
bucket = "${var.bucket}"
region = "${var.region}"
bucket = var.bucket
region = var.region
}
```

Run *scratchrelaxtv* with the generate `.env` and sort-ascending options:
```console
$ relaxtv -ea
2019-06-21 20:01:35,362 - INFO - generating .env file
2019-06-21 20:01:35,362 - INFO - input file: main.tf
2019-06-21 20:01:35,362 - INFO - input file: variables.tf
2019-06-21 20:01:35,362 - INFO - output file: .env
2019-06-21 20:01:35,362 - INFO - not forcing overwrite of output file
2019-06-21 20:01:35,362 - INFO - ordering output file ascending
Expand Down Expand Up @@ -248,9 +309,9 @@ $ relaxtv -r
*scratchrelaxtv* includes help:

```console
$ relaxtv --help
$ relaxtv -h
usage: scratchrelaxtv [-h] [-i INPUT] [-o OUTPUT] [-f] [-m] [-n MODNAME] [-r]
[-c] [-e] [-a | -d]
[-c] [-e] [-t] [--template] [-a | -d]

optional arguments:
-h, --help show this help message and exit
Expand All @@ -265,6 +326,8 @@ optional arguments:
-r, --remove remove all modstub.tf, variables.#.tf files
-c, --check check that all vars are listed
-e, --env generate .env with Terraform vars
-t, --tfvars generate .tfvars with Terraform vars
--template generate .tf from Terraform template vars
-a, --asc sort output variables in ascending order
-d, --desc sort output variables in descending order
```
74 changes: 59 additions & 15 deletions scratchrelaxtv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import re


__version__ = "0.5.14"
__version__ = "0.6.0"
EXIT_OKAY = 0
EXIT_NOT_OKAY = 1

Expand All @@ -36,6 +36,7 @@ def remove_prefix(text, prefix):

def remove_files():
"""Remove files from the os that look like scratchrelaxtv files."""
logger.info("attempting removal of files")
pattern = r'^(variables\.\d+|modstub(\.\d+|))\.tf$'
for root, _, files in os.walk(os.getcwd()):
for file in filter(lambda x: re.match(pattern, x), files):
Expand All @@ -61,20 +62,7 @@ def __init__(self, args):
self.log_arguments()

def log_arguments(self):
"""Log all the attributes of this run."""
if self.args.modstub:
logger.info("generating module usage stub")
elif self.args.remove:
logger.info("attempting removal of files")
elif self.args.check:
logger.info("checking for missing variables")
elif self.args.env:
logger.info("generating .env file")
elif self.args.tfvars:
logger.info("generating .tfvars file")
else:
logger.info("generating variables file")

"""Log the attributes of this run."""
logger.info("input file: %s", self.args.input)
logger.info("output file: %s", self.args.output)

Expand Down Expand Up @@ -143,6 +131,8 @@ class VarExtractor(BassExtractor):
"""
def __init__(self, args):
"""Instantiate"""
logger.info("generating variables file")

# defaults
if not args.input:
args.input = "main.tf"
Expand Down Expand Up @@ -180,6 +170,8 @@ class StubMaker(BassExtractor):
"""
def __init__(self, args):
"""Instantiate"""
logger.info("generating module usage stub")

# defaults
if not args.input:
args.input = "variables.tf"
Expand Down Expand Up @@ -226,6 +218,11 @@ class EnvGenerator(BassExtractor):
"""
def __init__(self, args):
"""Instantiate"""
if args.env:
logger.info("generating .env file")
elif args.tfvars:
logger.info("generating .tfvars file")

# defaults
if not args.input:
args.input = "main.tf"
Expand Down Expand Up @@ -278,6 +275,8 @@ class Checker(BassExtractor):
"""
def __init__(self, args):
"""Instantiate"""
logger.info("checking for missing variables")

# defaults
if not args.input:
args.input = "main.tf"
Expand Down Expand Up @@ -336,3 +335,48 @@ def extract(self):
)

return EXIT_OKAY


class TemplateExtractor(BassExtractor):
"""
A class that extracts variables from templates used with Terraform and
creates HCL showing all template variables.
"""

var_regex = r'(?<!\$)\${([a-zA-Z][a-zA-Z0-9_-]+)}'

def __init__(self, args):
"""Instantiate"""
logger.info("extracting template variables")

# defaults
if not args.input:
args.input = "template.sh"

if not args.output:
args.output = "template_vars.tf"

super().__init__(args)

def write_file(self):
"""Output vars to .tf file."""
self._find_non_existing_filename()

with open(self.args.output, "w", encoding='utf_8') as file_handle:

file_handle.write('locals {\n')
file_handle.write(' templates_vars = {\n')
for tf_var in self.tf_vars:
file_handle.write("".join([
" ",
tf_var,
" = ",
"\"replace\"\n"]))
file_handle.write(' }\n}\n')

def extract(self):
"""Extract vars from .tf file."""
self.find_vars_in_file(self.var_regex)
self.write_file()

return EXIT_OKAY
4 changes: 4 additions & 0 deletions scratchrelaxtv/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def parse_args(args):
help="generate .env with Terraform vars")
task.add_argument("-t", "--tfvars", default=False, action="store_true",
help="generate .tfvars with Terraform vars")
task.add_argument("--template", default=False, action="store_true",
help="generate .tf from Terraform template vars")

sort_order = parser.add_mutually_exclusive_group()
sort_order.add_argument("-a", "--asc", action="store_true",
Expand All @@ -65,6 +67,8 @@ def main():
extractor = scratchrelaxtv.Checker(args)
elif args.env or args.tfvars:
extractor = scratchrelaxtv.EnvGenerator(args)
elif args.template:
extractor = scratchrelaxtv.TemplateExtractor(args)
else:
extractor = scratchrelaxtv.VarExtractor(args)

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = scratchrelaxtv
description = Terraform developer tool to extract variables and generate variables.tf files.
long_description = file: README.md, CHANGELOG.md
long_description_content_type = text/markdown
version = 0.5.14
version = 0.6.0
author = YakDriver
author_email = [email protected]
url = https://github.com/YakDriver/scratchrelaxtv
Expand Down
11 changes: 11 additions & 0 deletions tests/template.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

build_os="${build_os}"
build_type="${build_type}"

exec &> "${userdata_log}"

build_slug="${build_slug}"
export AWS_REGION="${aws_region}"
debug_mode="${debug}"
debug_mode="$${not_a_var}"
10 changes: 10 additions & 0 deletions tests/template_vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
locals {
templates_vars = {
aws_region = "replace"
build_os = "replace"
build_slug = "replace"
build_type = "replace"
debug = "replace"
userdata_log = "replace"
}
}
22 changes: 21 additions & 1 deletion tests/test_scratchrelaxtv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import os
from contextlib import contextmanager
from scratchrelaxtv import cli, Checker, StubMaker, VarExtractor,\
EnvGenerator, EXIT_OKAY, remove_files
EnvGenerator, EXIT_OKAY, remove_files, TemplateExtractor


@contextmanager
Expand Down Expand Up @@ -180,3 +180,23 @@ def test_gen_tfvars():
second_list = file_handle.read().splitlines()
assert first_list == second_list
os.remove(filename)


def test_template():
"""Test extracting variables."""
with change_dir("tests"):
filename = "template_vars.1.tf"
if os.path.isfile(filename):
os.remove(filename)

args = cli.parse_args(["-fa", "--template", "-o", filename])

extractor = TemplateExtractor(args)

assert extractor.extract() == EXIT_OKAY
with open("template_vars.tf", "r", encoding='utf_8') as file_handle:
first_list = file_handle.read().splitlines()
with open(filename, "r", encoding='utf_8') as file_handle:
second_list = file_handle.read().splitlines()
assert first_list == second_list
os.remove(filename)

0 comments on commit 923a8f2

Please sign in to comment.