Skip to content

Commit e86224c

Browse files
committed
feat: include template variables in dynamic parameter rendering
1 parent 4980f18 commit e86224c

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.33.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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy