Skip to content

Commit

Permalink
feat: Added inputs vars deref support
Browse files Browse the repository at this point in the history
  • Loading branch information
ofekatr authored and YairZ101 committed Feb 10, 2022
1 parent f4d1ecf commit 2914910
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 28 deletions.
5 changes: 5 additions & 0 deletions terraform/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package terraform

const (
TF = ".tf"
)
3 changes: 2 additions & 1 deletion terraform/hcl2json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package terraform

import (
"encoding/json"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
)
Expand Down Expand Up @@ -61,7 +62,7 @@ var extractVariables = func(fileName string, fileContent string) (VariableMap, e
return VariableMap{}, createInvalidHCLError(diagnostics.Errs())
}

variables, diagnostics := extractFromFile(file)
variables, diagnostics := extractFromFile(fileName, file)
if diagnostics.HasErrors() {
return VariableMap{}, createInvalidHCLError(diagnostics.Errs())
}
Expand Down
115 changes: 97 additions & 18 deletions terraform/hcl2json_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package terraform

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zclconf/go-cty/cty"
"testing"
)

func TestParseHCL2JSONSuccess(t *testing.T) {
Expand Down Expand Up @@ -531,48 +532,115 @@ block "label_one" {
}

func TestExtractVariablesSuccess(t *testing.T) {
type TestInput struct {
fileName string
fileContent string
}

type test struct {
name string
input string
input TestInput
expected VariableMap
}
tests := []test{
{
name: "Simple variable block with no default",
input: `
variable "test" {
type = "string"
}`,
input: TestInput{
fileName: "test.tf",
fileContent: `
variable "test" {
type = "string"
}`,
},
expected: VariableMap{
"var": cty.ObjectVal(VariableMap{}),
"local": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy"),
}),
},
},
{
name: "Simple variable block with default",
input: TestInput{
fileName: "test.tf",
fileContent: `
variable "test" {
type = "string"
default = "test"
}`,
},
expected: VariableMap{
"var": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy_value"),
"test": cty.StringVal("test"),
}),
"local": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy_local"),
"dummy": cty.StringVal("dummy"),
}),
},
},
{
name: "Simple variable block with default",
input: `
variable "test" {
type = "string"
default = "test"
}`,
name: "Variable with null value",
input: TestInput{
fileName: "test.tf",
fileContent: `
variable "test" {
type = "string"
default = null
}`,
},
expected: VariableMap{
"var": cty.ObjectVal(VariableMap{}),
"local": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy"),
}),
},
},
{
name: "Two variable one with null value and the other with valid value",
input: TestInput{
fileName: "test.tf",
fileContent: `
variable "nullTest" {
type = "string"
default = null
}
variable "test" {
type = "string"
default = "test"
}`,
},
expected: VariableMap{
"var": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy_value"),
"test": cty.StringVal("test"),
}),
"local": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy_local"),
"dummy": cty.StringVal("dummy"),
}),
},
},
{
name: "Non-variable block",
input: TestInput{
fileName: "test.tf",
fileContent: `
provider "google" {
project = "acme-app"
default = "us-central1"
}`,
},
expected: VariableMap{
"var": cty.ObjectVal(VariableMap{}),
"local": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy"),
}),
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
actual, err := extractVariables("test", tc.input)
actual, err := extractVariables(tc.input.fileName, tc.input.fileContent)
require.Nil(t, err)
assert.Equal(t, tc.expected, actual)
})
Expand All @@ -585,16 +653,27 @@ resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH inbound from anywhere"
cidr_blocks = var.dummy
}
variable "dummy" {
type = "string"
default = "dummy"
}`
jsonOutput := `{
"resource": {
"aws_security_group": {
"allow_ssh": {
"cidr_blocks": "dummy_value",
"cidr_blocks": "dummy",
"description": "Allow SSH inbound from anywhere",
"name": "allow_ssh"
}
}
},
"variable": {
"dummy": {
"default": "dummy",
"type": "string"
}
}
}`
type test struct {
Expand Down
16 changes: 16 additions & 0 deletions terraform/schemas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package terraform

import "github.com/hashicorp/hcl/v2"

//Taken from https://github.com/hashicorp/terraform/blob/f266d1ee82d1fa4d882c146cc131fec4bef753cf/internal/configs/parser_config.go#L214
// tfFileSchema is the schema for the top-level of a config file. We use
// the low-level HCL API for this level so we can easily deal with each
// block type separately with its own decoding logic.
var tfFileVariableSchema = &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "variable",
LabelNames: []string{"name"},
},
},
}
48 changes: 39 additions & 9 deletions terraform/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,51 @@ import (
"github.com/zclconf/go-cty/cty"
)

func extractFromFile(file *hcl.File) (VariableMap, hcl.Diagnostics) {
// TODO: remove temporary dummy variables
// implement "var" when we do input variables and default values
type VariableMap map[string]cty.Value

func extractFromFile(filename string, file *hcl.File) (VariableMap, hcl.Diagnostics) {
// TODO: remove temporary dummy locals
// implement "local" when we do local values

varMap, hclDiags := extractFromTfFile(filename, file)
if hclDiags.HasErrors() {
return VariableMap{}, hclDiags
}

return VariableMap{
"var": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy_value"),
}),
"var": cty.ObjectVal(varMap),
"local": cty.ObjectVal(VariableMap{
"dummy": cty.StringVal("dummy_local"),
"dummy": cty.StringVal("dummy"),
}),
}, nil
}, hclDiags
}

type VariableMap map[string]cty.Value
func extractFromTfFile(filename string, file *hcl.File) (VariableMap, hcl.Diagnostics) {
varMap := VariableMap{}
bodyContent, _, hclDiags := file.Body.PartialContent(tfFileVariableSchema)

if hclDiags.HasErrors() {
return VariableMap{}, hclDiags
}

for _, block := range bodyContent.Blocks {
name := block.Labels[0]

attrs, _ := block.Body.JustAttributes()

defaultValue := attrs["default"]
if defaultValue != nil {
value, diags := defaultValue.Expr.Value(&hcl.EvalContext{Functions: terraformFunctions})
if diags.HasErrors() || value.IsNull() {
continue
}

varMap[name] = value
}
}

return varMap, hclDiags
}

func mergeVariables(variableMaps []VariableMap) VariableMap {
combinedVariableMaps := make(VariableMap)
Expand Down

0 comments on commit 2914910

Please sign in to comment.