Skip to content

Commit 8396c67

Browse files
committed
feat: include template variables in dynamic parameter rendering
1 parent 00ba027 commit 8396c67

File tree

5 files changed

+94
-7
lines changed

5 files changed

+94
-7
lines changed

coderd/dynamicparameters/render.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/google/uuid"
12+
"github.com/zclconf/go-cty/cty"
1213
"golang.org/x/xerrors"
1314

1415
"github.com/coder/coder/v2/apiversion"
@@ -41,9 +42,10 @@ type loader struct {
4142
templateVersionID uuid.UUID
4243

4344
// cache of objects
44-
templateVersion *database.TemplateVersion
45-
job *database.ProvisionerJob
46-
terraformValues *database.TemplateVersionTerraformValue
45+
templateVersion *database.TemplateVersion
46+
job *database.ProvisionerJob
47+
terraformValues *database.TemplateVersionTerraformValue
48+
templateVariableValues *[]database.TemplateVersionVariable
4749
}
4850

4951
// Prepare is the entrypoint for this package. It loads the necessary objects &
@@ -61,6 +63,12 @@ func Prepare(ctx context.Context, db database.Store, cache files.FileAcquirer, v
6163
return l.Renderer(ctx, db, cache)
6264
}
6365

66+
func WithTemplateVariableValues(vals []database.TemplateVersionVariable) func(r *loader) {
67+
return func(r *loader) {
68+
r.templateVariableValues = &vals
69+
}
70+
}
71+
6472
func WithTemplateVersion(tv database.TemplateVersion) func(r *loader) {
6573
return func(r *loader) {
6674
if tv.ID == r.templateVersionID {
@@ -127,6 +135,14 @@ func (r *loader) loadData(ctx context.Context, db database.Store) error {
127135
r.terraformValues = &values
128136
}
129137

138+
if r.templateVariableValues == nil {
139+
vals, err := db.GetTemplateVersionVariables(ctx, r.templateVersion.ID)
140+
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
141+
return xerrors.Errorf("template version variables: %w", err)
142+
}
143+
r.templateVariableValues = &vals
144+
}
145+
130146
return nil
131147
}
132148

@@ -160,13 +176,17 @@ func (r *loader) dynamicRenderer(ctx context.Context, db database.Store, cache *
160176
}
161177
}()
162178

179+
tfVarValues, err := VariableValues(*r.templateVariableValues)
180+
if err != nil {
181+
return nil, xerrors.Errorf("parse variable values: %w", err)
182+
}
183+
163184
// If they can read the template version, then they can read the file for
164185
// parameter loading purposes.
165186
//nolint:gocritic
166187
fileCtx := dbauthz.AsFileReader(ctx)
167188

168189
var templateFS fs.FS
169-
var err error
170190

171191
templateFS, err = cache.Acquire(fileCtx, db, r.job.FileID)
172192
if err != nil {
@@ -189,6 +209,7 @@ func (r *loader) dynamicRenderer(ctx context.Context, db database.Store, cache *
189209
db: db,
190210
ownerErrors: make(map[uuid.UUID]error),
191211
close: cache.Close,
212+
tfvarValues: tfVarValues,
192213
}, nil
193214
}
194215

@@ -199,6 +220,7 @@ type dynamicRenderer struct {
199220

200221
ownerErrors map[uuid.UUID]error
201222
currentOwner *previewtypes.WorkspaceOwner
223+
tfvarValues map[string]cty.Value
202224

203225
once sync.Once
204226
close func()
@@ -229,6 +251,7 @@ func (r *dynamicRenderer) Render(ctx context.Context, ownerID uuid.UUID, values
229251
PlanJSON: r.data.terraformValues.CachedPlan,
230252
ParameterValues: values,
231253
Owner: *r.currentOwner,
254+
TFVars: r.tfvarValues,
232255
// Do not emit parser logs to coderd output logs.
233256
// TODO: Returning this logs in the output would benefit the caller.
234257
// Unsure how large the logs can be, so for now we just discard them.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package dynamicparameters
2+
3+
import (
4+
"strconv"
5+
6+
"github.com/zclconf/go-cty/cty"
7+
"github.com/zclconf/go-cty/cty/json"
8+
"golang.org/x/xerrors"
9+
10+
"github.com/coder/coder/v2/coderd/database"
11+
)
12+
13+
func VariableValues(vals []database.TemplateVersionVariable) (map[string]cty.Value, error) {
14+
ctyVals := make(map[string]cty.Value, len(vals))
15+
for _, v := range vals {
16+
value := v.Value
17+
if value == "" && v.DefaultValue != "" {
18+
value = v.DefaultValue
19+
}
20+
21+
if value == "" {
22+
// Empty strings are unsupported I guess?
23+
continue // omit non-set vals
24+
}
25+
26+
var err error
27+
switch v.Type {
28+
case "string":
29+
ctyVals[v.Name] = cty.StringVal(value)
30+
case "number":
31+
ctyVals[v.Name], err = cty.ParseNumberVal(value)
32+
if err != nil {
33+
return nil, xerrors.Errorf("parse variable %q: %w", v.Name, err)
34+
}
35+
case "bool":
36+
parsed, err := strconv.ParseBool(value)
37+
if err != nil {
38+
return nil, xerrors.Errorf("parse variable %q: %w", v.Name, err)
39+
}
40+
ctyVals[v.Name] = cty.BoolVal(parsed)
41+
default:
42+
// If it is a complex type, let the cty json code give it a try.
43+
// TODO: Ideally we parse `list` & `map` and build the type ourselves.
44+
ty, err := json.ImpliedType([]byte(value))
45+
if err != nil {
46+
return nil, xerrors.Errorf("implied type for variable %q: %w", v.Name, err)
47+
}
48+
49+
jv, err := json.Unmarshal([]byte(value), ty)
50+
if err != nil {
51+
return nil, xerrors.Errorf("unmarshal variable %q: %w", v.Name, err)
52+
}
53+
ctyVals[v.Name] = jv
54+
}
55+
}
56+
57+
return ctyVals, nil
58+
}

coderd/wsbuilder/wsbuilder.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,10 +633,16 @@ func (b *Builder) getDynamicParameterRenderer() (dynamicparameters.Renderer, err
633633
return nil, xerrors.Errorf("get template version terraform values: %w", err)
634634
}
635635

636+
variableValues, err := b.getTemplateVersionVariables()
637+
if err != nil {
638+
return nil, xerrors.Errorf("get template version variables: %w", err)
639+
}
640+
636641
renderer, err := dynamicparameters.Prepare(b.ctx, b.store, b.fileCache, tv.ID,
637642
dynamicparameters.WithTemplateVersion(*tv),
638643
dynamicparameters.WithProvisionerJob(*job),
639644
dynamicparameters.WithTerraformValues(*tfVals),
645+
dynamicparameters.WithTemplateVariableValues(variableValues),
640646
)
641647
if err != nil {
642648
return nil, xerrors.Errorf("get template version renderer: %w", err)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ require (
483483
require (
484484
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225
485485
github.com/coder/aisdk-go v0.0.9
486-
github.com/coder/preview v1.0.3-0.20250701142654-c3d6e86b9393
486+
github.com/coder/preview v1.0.3-0.20250709160236-8ddde200cd69
487487
github.com/fsnotify/fsnotify v1.9.0
488488
github.com/mark3labs/mcp-go v0.32.0
489489
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -916,8 +916,8 @@ github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102 h1:ahTJlTRmTogsubgRVGO
916916
github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
917917
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=
918918
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc=
919-
github.com/coder/preview v1.0.3-0.20250701142654-c3d6e86b9393 h1:l+m2liikn8JoEv6C22QIV4qseolUfvNsyUNA6JJsD6Y=
920-
github.com/coder/preview v1.0.3-0.20250701142654-c3d6e86b9393/go.mod h1:efDWGlO/PZPrvdt5QiDhMtTUTkPxejXo9c0wmYYLLjM=
919+
github.com/coder/preview v1.0.3-0.20250709160236-8ddde200cd69 h1:bQ3r5Y22V1heD6Ah4kN/wMJ8gflyGPhzNtiFefytBVs=
920+
github.com/coder/preview v1.0.3-0.20250709160236-8ddde200cd69/go.mod h1:efDWGlO/PZPrvdt5QiDhMtTUTkPxejXo9c0wmYYLLjM=
921921
github.com/coder/quartz v0.2.1 h1:QgQ2Vc1+mvzewg2uD/nj8MJ9p9gE+QhGJm+Z+NGnrSE=
922922
github.com/coder/quartz v0.2.1/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA=
923923
github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc=

0 commit comments

Comments
 (0)