Skip to content

Commit

Permalink
Added support for locals.tf (#12)
Browse files Browse the repository at this point in the history
* Added locals support and GitHub Actions
  • Loading branch information
alexandrst88 committed Nov 21, 2020
1 parent 781fb35 commit 239da7d
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 23 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/pull_requests.yml
@@ -0,0 +1,31 @@
name: pr
on:
pull_request:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.15
id: go
- name: Code checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test
run: go test -v -race ./...

- name: Lint
uses: golangci/golangci-lint-action@v2
27 changes: 27 additions & 0 deletions .github/workflows/release.yml
@@ -0,0 +1,27 @@
name: release
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
23 changes: 18 additions & 5 deletions README.md
@@ -1,7 +1,16 @@
# terraform-variables-generator

Terraform versions support ![version](https://img.shields.io/badge/version-0.11.*-blue) ![version](https://img.shields.io/badge/version-0.12.*-blue) ![version](https://img.shields.io/badge/version-0.13.*-blue)

Simple Tool to Generate Variables file from Terraform Configuration. It will find all *.tf files in current directory, and generate variables.tf file. If you already have this file, it will ask to override it.

| Version | Supports |
|---------|----------|
| 0.11.* | yes |
| 0.12.* | yes |
| 0.13.* | yes |


## Build

```bash
Expand All @@ -20,17 +29,17 @@ It will find all *.tf files in current directory, and generate variables.tf file

```text
resource "aws_vpc" "vpc" {
cidr_block = "${var.cidr}"
enable_dns_hostnames = "${var.enable_dns_hostnames}"
enable_dns_support = "${var.enable_dns_support}"
cidr_block = var.cidr
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
tags {
Name = "${var.name}"
Name = var.name
}
}
resource "aws_internet_gateway" "vpc" {
vpc_id = "${aws_vpc.vpc.id}"
vpc_id = aws_vpc.vpc.id
tags {
Name = "${var.name}-igw"
Expand Down Expand Up @@ -74,3 +83,7 @@ Run tests and linter
go test -v -race ./...
golint -set_exit_status $(go list ./...)
```

## TO DO

Move Locals and Variables to Single Interface
12 changes: 9 additions & 3 deletions cmd/cmd.go
Expand Up @@ -19,8 +19,9 @@ const (
var (
generatorVersion string

vars bool
varsFile string
vars bool
varsFile string
localsFile string
)

// Execute will run main logic
Expand All @@ -37,6 +38,7 @@ func Execute(version string) {

cmd.PersistentFlags().BoolVar(&vars, "vars", true, "generate variables")
cmd.PersistentFlags().StringVar(&varsFile, "vars-file", "./variables.tf", "path to generated variables file")
cmd.PersistentFlags().StringVar(&localsFile, "locals-file", "./locals.tf", "path to generated locals file")

if err := cmd.Execute(); err != nil {
fmt.Println(err)
Expand All @@ -51,13 +53,17 @@ func runGenerator(cmd *cobra.Command, args []string) {
utils.UserPromt(varsFile)
}

if utils.FileExists(localsFile) {
utils.UserPromt(localsFile)
}

tfFiles, err := utils.GetAllFiles(tfFileExt)
utils.CheckError(err)
if len(tfFiles) == 0 {
log.Warn("No terraform files to proceed, exiting")
return
}

generator.GenerateVars(tfFiles, varsFile)
generator.Generate(tfFiles, varsFile, localsFile)
}
}
14 changes: 9 additions & 5 deletions pkg/generator/configuration.mock
@@ -1,20 +1,24 @@
//Mock Terraform Configuration File
resource "aws_eip" "nat" {
vpc = true
count = "${length(var.public_subnets)}"
count = length(var.public_subnets)
tags {
Name = local.name_internal
Env = "${local.env}-env"
}
}

resource "aws_nat_gateway" "nat" {
allocation_id = "${element(aws_eip.nat.*.id, count.index)}"
subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
allocation_id = element(aws_eip.nat.*.id, count.index)
subnet_id = element(aws_subnet.public.*.id, count.index)}"
count = "${length(var.public_subnets)}"
}

data "template_file" "template1" {
template = "${file("${path.module}/template1.tpl")}"
template = file("${path.module}/template1.tpl")
vars {
t1_var1 = "${var.t1_var1}"
t1-var2 = "${var.t1-var2}"
t1-var2 = var.t1-var2
t1-var3 = "${var.t1-Var3}-${var.t1-inline}"
}
}
18 changes: 16 additions & 2 deletions pkg/generator/terraform.go
Expand Up @@ -10,6 +10,7 @@ import (

type terraformVars struct {
Variables []string
Locals []string
}

func (t *terraformVars) matchVarPref(row, varPrefix string) {
Expand All @@ -25,6 +26,19 @@ func (t *terraformVars) matchVarPref(row, varPrefix string) {
}
}

func (t *terraformVars) sortVars() {
sort.Strings(t.Variables)
func (t *terraformVars) matchLocalPref(row, localPrefix string) {
if strings.Contains(row, localPrefix) {
pattern := regexp.MustCompile(`local.([a-z?A-Z?0-9?_][a-z?A-Z?0-9?_?-]*)`)
match := pattern.FindAllStringSubmatch(row, -1)
for _, m := range match {
res := replacer.Replace(m[0])
if !utils.ContainsElement(t.Locals, res) {
t.Locals = append(t.Locals, res)
}
}
}
}

func (t *terraformVars) sort(vars []string) {
sort.Strings(vars)
}
36 changes: 28 additions & 8 deletions pkg/generator/vars.go
Expand Up @@ -14,13 +14,19 @@ import (

var replacer *strings.Replacer
var varPrefix = "var."
var localPrefix = "local."

var varTemplate = template.Must(template.New("var_file").Parse(`{{range .}}
variable "{{ . }}" {
description = ""
}
{{end}}`))

var localsTemplate = template.Must(template.New("locals_file").Parse(`locals { {{ range . }}
{{ . }} ={{ end }}
}
`))

func init() {
replacer = strings.NewReplacer(":", ".",
"]", "",
Expand All @@ -32,12 +38,13 @@ func init() {
"[", "",
",", "",
"var.", "",
"local.", "",
" ", "",
)
}

// GenerateVars will write generated vars to file
func GenerateVars(tfFiles []string, dstFile string) {
// Generate will write inputs to file
func Generate(tfFiles []string, varsDstFile string, localsDstFile string) {
var wg sync.WaitGroup
messages := make(chan string)
wg.Add(len(tfFiles))
Expand All @@ -57,14 +64,27 @@ func GenerateVars(tfFiles []string, dstFile string) {
go func() {
for text := range messages {
t.matchVarPref(text, varPrefix)
t.matchLocalPref(text, localPrefix)
}
}()
wg.Wait()
f, err := os.Create(dstFile)
utils.CheckError(err)

t.sortVars()
err = varTemplate.Execute(f, t.Variables)
utils.CheckError(err)
log.Infof("Variables are generated to %q file", dstFile)
if len(t.Variables) > 0 {
f, err := os.Create(varsDstFile)
utils.CheckError(err)
log.Infof("Variables are generated to %q file", varsDstFile)

t.sort(t.Variables)
err = varTemplate.Execute(f, t.Variables)
utils.CheckError(err)
}

if len(t.Locals) > 0 {
t.sort(t.Locals)
localsFile, err := os.Create(localsDstFile)
utils.CheckError(err)
err = localsTemplate.Execute(localsFile, t.Locals)
utils.CheckError(err)
log.Infof("Locals are generated to %q file", localsDstFile)
}
}
22 changes: 22 additions & 0 deletions pkg/generator/vars_test.go
Expand Up @@ -46,5 +46,27 @@ func TestMatchVariable(t *testing.T) {
t.Errorf("Should return five variable. but returned %d", len(ter.Variables))
t.Errorf("Variables found: %s", ter.Variables)
}
}

func TestMatchLocal(t *testing.T) {
ter := &terraformVars{}
var messages []string

file, err := utils.GetAllFiles(testExtFile)
utils.CheckError(err)

fileHandle, _ := os.Open(file[0])
defer fileHandle.Close()

fileScanner := bufio.NewScanner(fileHandle)
for fileScanner.Scan() {
messages = append(messages, fileScanner.Text())
}
for _, text := range messages {
ter.matchLocalPref(text, localPrefix)
}
if len(ter.Locals) != 2 {
t.Errorf("Should return two variable. but returned %d", len(ter.Locals))
t.Errorf("Variables found: %s", ter.Locals)
}
}

0 comments on commit 239da7d

Please sign in to comment.