diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml new file mode 100644 index 0000000..0053d5a --- /dev/null +++ b/.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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0f987d2 --- /dev/null +++ b/.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 }} diff --git a/README.md b/README.md index 867d43e..beb9ac2 100644 --- a/README.md +++ b/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 @@ -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" @@ -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 diff --git a/cmd/cmd.go b/cmd/cmd.go index cfc2a58..0578221 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -19,8 +19,9 @@ const ( var ( generatorVersion string - vars bool - varsFile string + vars bool + varsFile string + localsFile string ) // Execute will run main logic @@ -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) @@ -51,6 +53,10 @@ 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 { @@ -58,6 +64,6 @@ func runGenerator(cmd *cobra.Command, args []string) { return } - generator.GenerateVars(tfFiles, varsFile) + generator.Generate(tfFiles, varsFile, localsFile) } } diff --git a/pkg/generator/configuration.mock b/pkg/generator/configuration.mock index 910ac97..f961af5 100644 --- a/pkg/generator/configuration.mock +++ b/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}" } } diff --git a/pkg/generator/terraform.go b/pkg/generator/terraform.go index d893ac6..1bfb1e1 100644 --- a/pkg/generator/terraform.go +++ b/pkg/generator/terraform.go @@ -10,6 +10,7 @@ import ( type terraformVars struct { Variables []string + Locals []string } func (t *terraformVars) matchVarPref(row, varPrefix string) { @@ -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) } diff --git a/pkg/generator/vars.go b/pkg/generator/vars.go index d222d1f..1c75063 100644 --- a/pkg/generator/vars.go +++ b/pkg/generator/vars.go @@ -14,6 +14,7 @@ import ( var replacer *strings.Replacer var varPrefix = "var." +var localPrefix = "local." var varTemplate = template.Must(template.New("var_file").Parse(`{{range .}} variable "{{ . }}" { @@ -21,6 +22,11 @@ variable "{{ . }}" { } {{end}}`)) +var localsTemplate = template.Must(template.New("locals_file").Parse(`locals { {{ range . }} + {{ . }} ={{ end }} +} +`)) + func init() { replacer = strings.NewReplacer(":", ".", "]", "", @@ -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)) @@ -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) + } } diff --git a/pkg/generator/vars_test.go b/pkg/generator/vars_test.go index 9b341d2..26d0e97 100644 --- a/pkg/generator/vars_test.go +++ b/pkg/generator/vars_test.go @@ -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) + } }