Skip to content

Commit

Permalink
feat: First working version
Browse files Browse the repository at this point in the history
  • Loading branch information
sylwit authored and sylvain committed Mar 4, 2023
1 parent 7e9e6b1 commit 93ccc41
Show file tree
Hide file tree
Showing 13 changed files with 1,159 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Binaries for programs and plugins
bin
*.exe
*.exe~
*.dll
Expand Down
38 changes: 38 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
XC_OS="linux darwin"
XC_ARCH="amd64 arm64"
XC_PARALLEL="2"
BIN="bin"
SRC=$(shell find . -name "*.go")

ifeq (, $(shell which gox))
$(warning "could not find gox in $(PATH), run: go get github.com/mitchellh/gox")
endif

.PHONY: all build fmt test install_deps clean

default: all

all: fmt test build

build: install_deps
gox \
-os=$(XC_OS) \
-arch=$(XC_ARCH) \
-parallel=$(XC_PARALLEL) \
-output=$(BIN)/{{.Dir}}_{{.OS}}_{{.Arch}} \
;

fmt:
$(info ******************** checking formatting ********************)
@test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1)

test: install_deps
$(info ******************** running tests ********************)
go test -v ./...

install_deps:
$(info ******************** downloading dependencies ********************)
go get -v ./...

clean:
rm -rf $(BIN)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# terraform-cleaner

Tiny utility which detects unused variables in your terraform modules
138 changes: 138 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package cmd

import (
"fmt"
"github.com/hashicorp/terraform-config-inspect/tfconfig"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"io/fs"
"os"
"path/filepath"
"regexp"
)

var rootCmd = &cobra.Command{
Use: "terraform-cleaner",
Short: "Remove unused variables",
RunE: rootCmdExec,
}

func rootCmdExec(cmd *cobra.Command, args []string) error {
dir := "."
if len(args) > 0 {
dir = args[0]
}
cmd.SilenceUsage = true

fUnusedOnly, _ := cmd.Flags().GetBool("unused-only")

modules, err := findTfModules(dir)
if err != nil {
return err
}

for path, _ := range modules {
stats, err := findVariablesUsage(path)
if err != nil {
return err
}
fmt.Printf("Module: %s (%d variables found)\n", path, len(stats.variables))

for name, count := range stats.variables {
if fUnusedOnly && count > 0 {
continue
}
fmt.Printf("%s : %d\n", name, count)
}
fmt.Println("")
}

fmt.Printf("%d modules processed", len(modules))

return nil
}

type VariablesUsage struct {
variables map[string]int
}

func findVariablesUsage(path string) (VariablesUsage, error) {
out := VariablesUsage{variables: map[string]int{}}

module, diagnostics := tfconfig.LoadModule(path)
if diagnostics.HasErrors() {
return out, diagnostics.Err()
}

result, err := countVariables(path, module)
if err != nil {
return out, err
}

out.variables = result

return out, nil
}

func countVariables(path string, tfconfig *tfconfig.Module) (map[string]int, error) {
out := map[string]int{}

files, err := os.ReadDir(path)
if err != nil {
return out, err
}

for _, file := range files {
if filepath.Ext(file.Name()) == ".tf" {
data, err := os.ReadFile(filepath.Join(path, file.Name()))
if err != nil {
return out, err
}

content := string(data)

for variable, _ := range tfconfig.Variables {
regex := regexp.MustCompile(fmt.Sprintf(`var\.%s\W`, variable))
matches := regex.FindAllStringIndex(content, -1)

out[variable] += len(matches)
}

}
}

return out, err
}

func findTfModules(path string) (map[string]bool, error) {
var directories = make(map[string]bool)

err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if filepath.Ext(path) == ".tf" {
module := filepath.Dir(path)
log.Debugf("Visited: %s\n", module)
if _, ok := directories[module]; !ok {
directories[module] = true
}
}
return nil
})

if err != nil {
return nil, err
}

return directories, nil
}

func Execute() error {
return rootCmd.Execute()
}

func init() {
rootCmd.Flags().Bool("unused-only", false, "Display only unused variables")
}
33 changes: 33 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cmd

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestFindTfModules(t *testing.T) {
t.Run("must found 3 modules", func(t *testing.T) {
path := "./testdata"

result, err := findTfModules(path)
assert.Equal(t, err, nil)
assert.Equal(t, len(result), 3)

assert.Contains(t, result, "testdata")
assert.Contains(t, result, "testdata/tf")
assert.Contains(t, result, "testdata/tf/1")
})
}

func TestFindVariablesUsage(t *testing.T) {
t.Run("should find all variables", func(t *testing.T) {
path := "./testdata/tf"

out, err := findVariablesUsage(path)
assert.Equal(t, err, nil)
assert.Equal(t, 1, out.variables["name"])
assert.Equal(t, 1, out.variables["region"])
assert.Equal(t, 1, out.variables["instance_ids"])
assert.Equal(t, 0, out.variables["legacy"])
})
}
Empty file added cmd/testdata/main.tf
Empty file.
Empty file added cmd/testdata/tf/1/main.tf
Empty file.
23 changes: 23 additions & 0 deletions cmd/testdata/tf/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
terraform {
required_providers {
null = {
source = "hashicorp/null"
version = "3.2.1"
}
}
}

provider "null" {
region = var.region
}

data "null_data_source" "values" {
inputs = var.name
}

resource "null_resource" "cluster" {
# Changes to any instance of the cluster requires re-provisioning
triggers = {
instance_ids = var.instance_ids
}
}
3 changes: 3 additions & 0 deletions cmd/testdata/tf/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "out" {
value = data.null_data_source.values.inputs
}
16 changes: 16 additions & 0 deletions cmd/testdata/tf/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
variable "name" {
type = string
default = "terraform-cleaner"
}

variable "region" {
type = string
default = "ca-central-1"
}

variable "instance_ids" {
type = list(string)
default = ["i-123", "i-456"]
}

variable "legacy" {}
27 changes: 27 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module github.com/sylwit/terraform-cleaner

go 1.19

require (
github.com/hashicorp/terraform-config-inspect v0.0.0-20230223165911-2d94e3d51111
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
)

require (
github.com/agext/levenshtein v1.2.2 // indirect
github.com/apparentlymart/go-textseg v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.3.0 // indirect
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f // indirect
github.com/hashicorp/hcl/v2 v2.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect˚
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/zclconf/go-cty v1.1.0 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 93ccc41

Please # to comment.