Skip to content

Instantly share code, notes, and snippets.

@owulveryck
Last active April 16, 2019 14:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save owulveryck/fb0a64f92ccc18cf1b250e824f7c5d9e to your computer and use it in GitHub Desktop.
Save owulveryck/fb0a64f92ccc18cf1b250e824f7c5d9e to your computer and use it in GitHub Desktop.
terraform tfvars diff: do a diff against two tfvars and returns an error if they do not contain the same variables.
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/hashicorp/hcl"
)
var (
file1 string
file2 string
)
func main() {
silent := flag.Bool("s", false, "silent mode")
flag.Parse()
args := flag.Args()
if len(args) != 2 {
usage()
return
}
file1 = args[0]
file2 = args[1]
content1, err := ioutil.ReadFile(file1)
if err != nil {
log.Fatal(err)
}
content2, err := ioutil.ReadFile(file2)
if err != nil {
log.Fatal(err)
}
receiver1, err := parseContent(content1)
if err != nil {
log.Fatal(err)
}
receiver2, err := parseContent(content2)
if err != nil {
log.Fatal(err)
}
err = diffKeys(receiver1, receiver2, false)
if err != nil {
if *silent {
os.Exit(1)
}
log.Fatal(err)
}
}
func usage() {
fmt.Printf("usage: %v [-s] file1 file2", os.Args[0])
}
func parseContent(b []byte) (map[string]interface{}, error) {
var output map[string]interface{}
err := hcl.Unmarshal(b, &output)
if err != nil {
return nil, fmt.Errorf("cannot parse content: %v", err)
}
return output, nil
}
func diffKeys(receiver1, receiver2 map[string]interface{}, stop bool) error {
for k, v1 := range receiver1 {
var v2 interface{}
var ok bool
if v2, ok = receiver2[k]; !ok {
return fmt.Errorf("variable %v is defined in one file but not the other", k)
}
if reflect.ValueOf(v1).Kind() != reflect.ValueOf(v2).Kind() {
return fmt.Errorf("%v is of type %v in one file and %v in the other", k, reflect.ValueOf(v1).Kind(), reflect.ValueOf(v2).Kind())
}
val1, ok1 := v1.([]map[string]interface{})
val2, ok2 := v2.([]map[string]interface{})
switch {
case ok1 && ok2:
if len(val1) != len(val2) {
return fmt.Errorf("array's size differs")
}
for i := 0; i < len(val1); i++ {
return diffKeys(val1[i], val2[i], false)
}
case ok1 != ok2:
return fmt.Errorf("different types")
}
}
if stop {
return nil
}
return diffKeys(receiver2, receiver1, true)
}
package main
import "testing"
func TestDivKeys_ok(t *testing.T) {
valid1 := []byte(`
b = 1
c = {
bla = "truc"
d = {
aha = "b"
}
}
`)
valid2 := []byte(`
b = 4
c = {
bla = "blo"
d = {
aha = "b"
}
}
`)
receiver1, err := parseContent(valid1)
if err != nil {
t.Fatal(err)
}
receiver2, err := parseContent(valid2)
if err != nil {
t.Fatal(err)
}
err = diffKeys(receiver1, receiver2, false)
if err != nil {
t.Fatal(err)
}
}
func TestDivKeys_koType(t *testing.T) {
valid1 := []byte(`
b = 1
c = {
bla = "truc"
d = {
aha = "b"
}
}
`)
valid2 := []byte(`
b = 4
c = {
bla = true
d = {
aha = "b"
}
}
`)
receiver1, err := parseContent(valid1)
if err != nil {
t.Fatal(err)
}
receiver2, err := parseContent(valid2)
if err != nil {
t.Fatal(err)
}
err = diffKeys(receiver1, receiver2, false)
if err == nil {
t.Fatal("should have failed because types defers")
}
}
func TestDivKeys_ko1(t *testing.T) {
valid1 := []byte(`
b = 1
c = {
bla = "truc"
d = "aha"
}
`)
valid2 := []byte(`
b = 4
c = {
bla = true
d = {
aha = "b"
}
}
`)
receiver1, err := parseContent(valid1)
if err != nil {
t.Fatal(err)
}
receiver2, err := parseContent(valid2)
if err != nil {
t.Fatal(err)
}
err = diffKeys(receiver2, receiver1, false)
if err == nil {
t.Fatalf("%v should be different from %v and return an error", receiver1, receiver2)
}
}
func TestDivKeys_ko2(t *testing.T) {
valid1 := []byte(`
c = {
bla = "truc"
d = "aha"
}
f = ["a","b"]
`)
valid2 := []byte(`
c = "bla"
`)
receiver1, err := parseContent(valid1)
if err != nil {
t.Fatal(err)
}
receiver2, err := parseContent(valid2)
if err != nil {
t.Fatal(err)
}
err = diffKeys(receiver1, receiver2, false)
if err == nil {
t.Fatalf("%v should be different from %v and return an error", receiver1, receiver2)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment