@@ -6,14 +6,14 @@ import (
66 "fmt"
77 "io/fs"
88 "log/slog"
9- "path/filepath"
109
1110 "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser"
1211 "github.com/hashicorp/hcl/v2"
1312 "github.com/zclconf/go-cty/cty"
1413 ctyjson "github.com/zclconf/go-cty/cty/json"
1514
1615 "github.com/coder/preview/hclext"
16+ "github.com/coder/preview/tfvars"
1717 "github.com/coder/preview/types"
1818)
1919
@@ -26,6 +26,9 @@ type Input struct {
2626 ParameterValues map [string ]string
2727 Owner types.WorkspaceOwner
2828 Logger * slog.Logger
29+ // TFVars will override any variables set in '.tfvars' files.
30+ // The value set must be a cty.Value, as the type can be anything.
31+ TFVars map [string ]cty.Value
2932}
3033
3134type Output struct {
@@ -80,12 +83,7 @@ func Preview(ctx context.Context, input Input, dir fs.FS) (output *Output, diagn
8083 }
8184 }()
8285
83- // TODO: Fix logging. There is no way to pass in an instanced logger to
84- // the parser.
85- // slog.SetLogLoggerLevel(slog.LevelDebug)
86- // slog.SetDefault(slog.New(log.NewHandler(os.Stderr, nil)))
87-
88- varFiles , err := tfVarFiles ("" , dir )
86+ varFiles , err := tfvars .TFVarFiles ("" , dir )
8987 if err != nil {
9088 return nil , hcl.Diagnostics {
9189 {
@@ -96,6 +94,17 @@ func Preview(ctx context.Context, input Input, dir fs.FS) (output *Output, diagn
9694 }
9795 }
9896
97+ variableValues , err := tfvars .LoadTFVars (dir , varFiles )
98+ if err != nil {
99+ return nil , hcl.Diagnostics {
100+ {
101+ Severity : hcl .DiagError ,
102+ Summary : "Failed to load tfvars from files" ,
103+ Detail : err .Error (),
104+ },
105+ }
106+ }
107+
99108 planHook , err := planJSONHook (dir , input )
100109 if err != nil {
101110 return nil , hcl.Diagnostics {
@@ -123,17 +132,24 @@ func Preview(ctx context.Context, input Input, dir fs.FS) (output *Output, diagn
123132 logger = slog .New (slog .DiscardHandler )
124133 }
125134
135+ // Override with user-supplied variables
136+ for k , v := range input .TFVars {
137+ variableValues [k ] = v
138+ }
139+
126140 // moduleSource is "" for a local module
127141 p := parser .New (dir , "" ,
128142 parser .OptionWithLogger (logger ),
129143 parser .OptionStopOnHCLError (false ),
130144 parser .OptionWithDownloads (false ),
131145 parser .OptionWithSkipCachedModules (true ),
132- parser .OptionWithTFVarsPaths (varFiles ... ),
133146 parser .OptionWithEvalHook (planHook ),
134147 parser .OptionWithEvalHook (ownerHook ),
135148 parser .OptionWithWorkingDirectoryPath ("/" ),
136149 parser .OptionWithEvalHook (parameterContextsEvalHook (input )),
150+ // 'OptionsWithTfVars' cannot be set with 'OptionWithTFVarsPaths'. So load the
151+ // tfvars from the files ourselves and merge with the user-supplied tf vars.
152+ parser .OptionsWithTfVars (variableValues ),
137153 )
138154
139155 err = p .ParseFS (ctx , "." )
@@ -179,33 +195,3 @@ func (i Input) RichParameterValue(key string) (string, bool) {
179195 p , ok := i .ParameterValues [key ]
180196 return p , ok
181197}
182-
183- // tfVarFiles extracts any .tfvars files from the given directory.
184- // TODO: Test nested directories and how that should behave.
185- func tfVarFiles (path string , dir fs.FS ) ([]string , error ) {
186- dp := "."
187- entries , err := fs .ReadDir (dir , dp )
188- if err != nil {
189- return nil , fmt .Errorf ("read dir %q: %w" , dp , err )
190- }
191-
192- files := make ([]string , 0 )
193- for _ , entry := range entries {
194- if entry .IsDir () {
195- subD , err := fs .Sub (dir , entry .Name ())
196- if err != nil {
197- return nil , fmt .Errorf ("sub dir %q: %w" , entry .Name (), err )
198- }
199- newFiles , err := tfVarFiles (filepath .Join (path , entry .Name ()), subD )
200- if err != nil {
201- return nil , err
202- }
203- files = append (files , newFiles ... )
204- }
205-
206- if filepath .Ext (entry .Name ()) == ".tfvars" {
207- files = append (files , filepath .Join (path , entry .Name ()))
208- }
209- }
210- return files , nil
211- }
0 commit comments