Skip to content

Commit b1e7015

Browse files
committed
feat: template variables added to input
Input can override template vars, implementing the equivalent of '-var' in terraform.
1 parent 9e7d207 commit b1e7015

File tree

6 files changed

+206
-5
lines changed

6 files changed

+206
-5
lines changed

preview.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"io/fs"
88
"log/slog"
99
"path/filepath"
10+
"strings"
1011

1112
"github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser"
1213
"github.com/hashicorp/hcl/v2"
1314
"github.com/zclconf/go-cty/cty"
1415
ctyjson "github.com/zclconf/go-cty/cty/json"
1516

1617
"github.com/coder/preview/hclext"
18+
"github.com/coder/preview/tfvars"
1719
"github.com/coder/preview/types"
1820
)
1921

@@ -26,6 +28,7 @@ type Input struct {
2628
ParameterValues map[string]string
2729
Owner types.WorkspaceOwner
2830
Logger *slog.Logger
31+
TFVars map[string]cty.Value
2932
}
3033

3134
type Output struct {
@@ -96,7 +99,18 @@ func Preview(ctx context.Context, input Input, dir fs.FS) (output *Output, diagn
9699
}
97100
}
98101

99-
planHook, err := planJSONHook(dir, input)
102+
variableValues, err := tfvars.LoadTFVars(dir, varFiles)
103+
if err != nil {
104+
return nil, hcl.Diagnostics{
105+
{
106+
Severity: hcl.DiagError,
107+
Summary: "Failed to load tfvars from files",
108+
Detail: err.Error(),
109+
},
110+
}
111+
}
112+
113+
planHook, err := PlanJSONHook(dir, input)
100114
if err != nil {
101115
return nil, hcl.Diagnostics{
102116
{
@@ -121,6 +135,11 @@ func Preview(ctx context.Context, input Input, dir fs.FS) (output *Output, diagn
121135
logger := input.Logger
122136
if logger == nil { // Default to discarding logs
123137
logger = slog.New(slog.DiscardHandler)
138+
}
139+
140+
// Override with user supplied variables
141+
for k, v := range input.TFVars {
142+
variableValues[k] = v
124143
}
125144

126145
// moduleSource is "" for a local module
@@ -129,11 +148,12 @@ func Preview(ctx context.Context, input Input, dir fs.FS) (output *Output, diagn
129148
parser.OptionStopOnHCLError(false),
130149
parser.OptionWithDownloads(false),
131150
parser.OptionWithSkipCachedModules(true),
132-
parser.OptionWithTFVarsPaths(varFiles...),
133151
parser.OptionWithEvalHook(planHook),
134152
parser.OptionWithEvalHook(ownerHook),
135-
parser.OptionWithWorkingDirectoryPath("/"),
136-
parser.OptionWithEvalHook(parameterContextsEvalHook(input)),
153+
parser.OptionWithEvalHook(ParameterContextsEvalHook(input)),
154+
// 'OptionsWithTfVars' cannot be set with 'OptionWithTFVarsPaths'. So load the
155+
// tfvars from the files ourselves and merge with the user-supplied tf vars.
156+
parser.OptionsWithTfVars(variableValues),
137157
)
138158

139159
err = p.ParseFS(ctx, ".")
@@ -203,7 +223,7 @@ func tfVarFiles(path string, dir fs.FS) ([]string, error) {
203223
files = append(files, newFiles...)
204224
}
205225

206-
if filepath.Ext(entry.Name()) == ".tfvars" {
226+
if filepath.Ext(entry.Name()) == ".tfvars" || strings.HasSuffix(entry.Name(), ".tfvars.json") {
207227
files = append(files, filepath.Join(path, entry.Name()))
208228
}
209229
}

preview_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/hashicorp/hcl/v2"
1414
"github.com/stretchr/testify/assert"
1515
"github.com/stretchr/testify/require"
16+
"github.com/zclconf/go-cty/cty"
1617

1718
"github.com/coder/preview"
1819
"github.com/coder/preview/types"
@@ -471,6 +472,37 @@ func Test_Extract(t *testing.T) {
471472
optNames("GoLand 2024.3", "IntelliJ IDEA Ultimate 2024.3", "PyCharm Professional 2024.3"),
472473
},
473474
},
475+
{
476+
name: "tfvars_from_file",
477+
dir: "tfvars",
478+
expTags: map[string]string{},
479+
input: preview.Input{
480+
ParameterValues: map[string]string{},
481+
},
482+
unknownTags: []string{},
483+
params: map[string]assertParam{
484+
"variable_values": ap().
485+
def("alex").optVals("alex", "bob", "claire", "jason"),
486+
},
487+
},
488+
{
489+
name: "tfvars_from_input",
490+
dir: "tfvars",
491+
expTags: map[string]string{},
492+
input: preview.Input{
493+
ParameterValues: map[string]string{},
494+
TFVars: map[string]cty.Value{
495+
"one": cty.StringVal("andrew"),
496+
"two": cty.StringVal("bill"),
497+
"three": cty.StringVal("carter"),
498+
},
499+
},
500+
unknownTags: []string{},
501+
params: map[string]assertParam{
502+
"variable_values": ap().
503+
def("andrew").optVals("andrew", "bill", "carter", "jason"),
504+
},
505+
},
474506
{
475507
name: "unknownoption",
476508
dir: "unknownoption",

testdata/tfvars/.auto.tfvars.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"four":"jason"}

testdata/tfvars/main.tf

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Base case for workspace tags + parameters.
2+
terraform {
3+
required_providers {
4+
coder = {
5+
source = "coder/coder"
6+
}
7+
docker = {
8+
source = "kreuzwerker/docker"
9+
version = "3.0.2"
10+
}
11+
}
12+
}
13+
14+
variable "one" {
15+
default = "alice"
16+
type = string
17+
}
18+
19+
variable "two" {
20+
default = "bob"
21+
type = string
22+
}
23+
24+
variable "three" {
25+
default = "charlie"
26+
type = string
27+
}
28+
29+
variable "four" {
30+
default = "jack"
31+
type = string
32+
}
33+
34+
35+
data "coder_parameter" "variable_values" {
36+
name = "variable_values"
37+
description = "Just to show the variable values"
38+
type = "string"
39+
default = var.one
40+
41+
42+
option {
43+
name = "one"
44+
value = var.one
45+
}
46+
47+
option {
48+
name = "two"
49+
value = var.two
50+
}
51+
52+
option {
53+
name = "three"
54+
value = var.three
55+
}
56+
57+
option {
58+
name = "four"
59+
value = var.four
60+
}
61+
}

testdata/tfvars/values.tfvars

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
one="alex"
2+
three="claire"

tfvars/load.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Code taken from https://github.com/aquasecurity/trivy/blob/main/pkg/iac/scanners/terraform/parser/load_vars.go
2+
package tfvars
3+
4+
import (
5+
"fmt"
6+
"io/fs"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/hashicorp/hcl/v2"
11+
"github.com/hashicorp/hcl/v2/hclsyntax"
12+
hcljson "github.com/hashicorp/hcl/v2/json"
13+
"github.com/zclconf/go-cty/cty"
14+
)
15+
16+
func LoadTFVars(srcFS fs.FS, filenames []string) (map[string]cty.Value, error) {
17+
combinedVars := make(map[string]cty.Value)
18+
19+
// Intentionally commented out to avoid loading from host environment
20+
//
21+
//for _, env := range os.Environ() {
22+
// split := strings.Split(env, "=")
23+
// key := split[0]
24+
// if !strings.HasPrefix(key, "TF_VAR_") {
25+
// continue
26+
// }
27+
// key = strings.TrimPrefix(key, "TF_VAR_")
28+
// var val string
29+
// if len(split) > 1 {
30+
// val = split[1]
31+
// }
32+
// combinedVars[key] = cty.StringVal(val)
33+
//}
34+
35+
for _, filename := range filenames {
36+
vars, err := LoadTFVarsFile(srcFS, filename)
37+
if err != nil {
38+
return nil, fmt.Errorf("failed to load tfvars from %s: %w", filename, err)
39+
}
40+
for k, v := range vars {
41+
combinedVars[k] = v
42+
}
43+
}
44+
45+
return combinedVars, nil
46+
}
47+
48+
func LoadTFVarsFile(srcFS fs.FS, filename string) (map[string]cty.Value, error) {
49+
inputVars := make(map[string]cty.Value)
50+
if filename == "" {
51+
return inputVars, nil
52+
}
53+
54+
src, err := fs.ReadFile(srcFS, filepath.ToSlash(filename))
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
var attrs hcl.Attributes
60+
if strings.HasSuffix(filename, ".json") {
61+
variableFile, err := hcljson.Parse(src, filename)
62+
if err != nil {
63+
return nil, err
64+
}
65+
attrs, err = variableFile.Body.JustAttributes()
66+
if err != nil {
67+
return nil, err
68+
}
69+
} else {
70+
variableFile, err := hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
71+
if err != nil {
72+
return nil, err
73+
}
74+
attrs, err = variableFile.Body.JustAttributes()
75+
if err != nil {
76+
return nil, err
77+
}
78+
}
79+
80+
for _, attr := range attrs {
81+
inputVars[attr.Name], _ = attr.Expr.Value(&hcl.EvalContext{})
82+
}
83+
84+
return inputVars, nil
85+
}

0 commit comments

Comments
 (0)