Skip to content

Commit 00533fa

Browse files
committed
Works
1 parent a77a2dc commit 00533fa

File tree

2 files changed

+93
-9
lines changed

2 files changed

+93
-9
lines changed

coderd/wsbuilder/wsbuilder.go

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import (
77
"database/sql"
88
"encoding/json"
99
"fmt"
10-
"log"
1110
"net/http"
1211
"time"
1312

13+
"github.com/hashicorp/hcl/v2"
14+
"github.com/hashicorp/hcl/v2/hclsyntax"
15+
"github.com/zclconf/go-cty/cty"
16+
1417
"github.com/coder/coder/v2/coderd/rbac/policy"
1518
"github.com/coder/coder/v2/provisionersdk"
1619

@@ -670,12 +673,61 @@ func (b *Builder) getProvisionerTags() (map[string]string, error) {
670673
tags[name] = value
671674
}
672675

673-
// TODO: Take "workspaceTags", evaluate expressions using parameters, update "tags" map
674-
log.Println(workspaceTags, parameterNames, parameterValues)
676+
evalCtx := buildParametersEvalContext(parameterNames, parameterValues)
677+
for _, workspaceTag := range workspaceTags {
678+
expr, diags := hclsyntax.ParseExpression([]byte(workspaceTag.Value), "partial.hcl", hcl.InitialPos)
679+
if diags.HasErrors() {
680+
return nil, BuildError{http.StatusBadRequest, "failed to parse workspace tag value", xerrors.Errorf(diags.Error())}
681+
}
675682

683+
val, diags := expr.Value(evalCtx)
684+
if diags.HasErrors() {
685+
return nil, BuildError{http.StatusBadRequest, "failed to evaluate workspace tag value", xerrors.Errorf(diags.Error())}
686+
}
687+
688+
// Do not use "val.AsString()" as it can panic
689+
str, err := ctyValueString(val)
690+
if err != nil {
691+
return nil, BuildError{http.StatusBadRequest, "failed to marshal cty.Value as string", err}
692+
}
693+
tags[workspaceTag.Key] = str
694+
}
676695
return tags, nil
677696
}
678697

698+
func buildParametersEvalContext(names, values []string) *hcl.EvalContext {
699+
m := map[string]cty.Value{}
700+
for i, name := range names {
701+
m[name] = cty.MapVal(map[string]cty.Value{
702+
"value": cty.StringVal(values[i]),
703+
})
704+
}
705+
return &hcl.EvalContext{
706+
Variables: map[string]cty.Value{
707+
"data": cty.MapVal(map[string]cty.Value{
708+
"coder_parameter": cty.MapVal(m),
709+
}),
710+
},
711+
}
712+
}
713+
714+
func ctyValueString(val cty.Value) (string, error) {
715+
switch val.Type() {
716+
case cty.Bool:
717+
if val.True() {
718+
return "true", nil
719+
} else {
720+
return "false", nil
721+
}
722+
case cty.Number:
723+
return val.AsBigFloat().String(), nil
724+
case cty.String:
725+
return val.AsString(), nil
726+
default:
727+
return "", xerrors.Errorf("only primitive types are supported - bool, number, and string")
728+
}
729+
}
730+
679731
func (b *Builder) getTemplateVersionWorkspaceTags() ([]database.TemplateVersionWorkspaceTag, error) {
680732
if b.templateVersionWorkspaceTags != nil {
681733
return *b.templateVersionWorkspaceTags, nil

coderd/wsbuilder/wsbuilder_test.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func TestBuilder_Reason(t *testing.T) {
194194
withWorkspaceTags(inactiveVersionID, nil),
195195

196196
// Outputs
197-
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
197+
expectProvisionerJob(func(_ database.InsertProvisionerJobParams) {
198198
}),
199199
withInTx,
200200
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
@@ -254,20 +254,33 @@ func TestBuilder_ActiveVersion(t *testing.T) {
254254
func TestWorkspaceBuildWithTags(t *testing.T) {
255255
t.Parallel()
256256

257+
asrt := assert.New(t)
257258
req := require.New(t)
258259

259260
workspaceTags := []database.TemplateVersionWorkspaceTag{
261+
{
262+
Key: "fruits_tag",
263+
Value: "data.coder_parameter.number_of_apples.value + data.coder_parameter.number_of_oranges.value",
264+
},
260265
{
261266
Key: "cluster_tag",
262-
Value: "developers",
267+
Value: `"best_developers"`,
263268
},
264269
{
265270
Key: "project_tag",
266271
Value: `"${data.coder_parameter.project.value}+12345"`,
267272
},
268273
{
269274
Key: "team_tag",
270-
Value: `"data.coder_parameter.team.value`,
275+
Value: `data.coder_parameter.team.value`,
276+
},
277+
{
278+
Key: "yes_or_no",
279+
Value: `data.coder_parameter.is_debug_build.value`,
280+
},
281+
{
282+
Key: "actually_no",
283+
Value: `!data.coder_parameter.is_debug_build.value`,
271284
},
272285
{
273286
Key: "is_debug_build",
@@ -279,13 +292,15 @@ func TestWorkspaceBuildWithTags(t *testing.T) {
279292
// Parameters can be mutable although it is discouraged as the workspace can be moved between provisioner nodes.
280293
{Name: "project", Description: "This is first parameter", Mutable: true, Options: json.RawMessage("[]")},
281294
{Name: "team", Description: "This is second parameter", Mutable: true, DefaultValue: "godzilla", Options: json.RawMessage("[]")},
282-
{Name: "is_debug_build", Type: "bool", Description: "This is third parameter", Mutable: false, Options: json.RawMessage("[]")},
295+
{Name: "is_debug_build", Type: "bool", Description: "This is third parameter", Mutable: false, DefaultValue: "false", Options: json.RawMessage("[]")},
296+
{Name: "number_of_apples", Type: "number", Description: "This is fourth parameter", Mutable: false, DefaultValue: "4", Options: json.RawMessage("[]")},
297+
{Name: "number_of_oranges", Type: "number", Description: "This is fifth parameter", Mutable: false, DefaultValue: "6", Options: json.RawMessage("[]")},
283298
}
284299

285300
buildParameters := []codersdk.WorkspaceBuildParameter{
286301
{Name: "project", Value: "foobar-foobaz"},
287-
// "team" parameter is skipped, so default value is selected
288302
{Name: "is_debug_build", Value: "true"},
303+
// Parameters "team", "number_of_apples", "number_of_oranges" are skipped, so default value is selected
289304
}
290305

291306
ctx, cancel := context.WithCancel(context.Background())
@@ -301,7 +316,24 @@ func TestWorkspaceBuildWithTags(t *testing.T) {
301316
withWorkspaceTags(inactiveVersionID, workspaceTags),
302317

303318
// Outputs
304-
expectProvisionerJob(func(_ database.InsertProvisionerJobParams) {}),
319+
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
320+
asrt.Len(job.Tags, 10)
321+
322+
expected := database.StringMap{
323+
"actually_no": "false",
324+
"cluster_tag": "best_developers",
325+
"fruits_tag": "10",
326+
"is_debug_build": "in-debug-mode",
327+
"project_tag": "foobar-foobaz+12345",
328+
"team_tag": "godzilla",
329+
"yes_or_no": "true",
330+
331+
"scope": "user",
332+
"version": "inactive",
333+
"owner": userID.String(),
334+
}
335+
asrt.Equal(job.Tags, expected)
336+
}),
305337
withInTx,
306338
expectBuild(func(_ database.InsertWorkspaceBuildParams) {}),
307339
expectBuildParameters(func(_ database.InsertWorkspaceBuildParametersParams) {

0 commit comments

Comments
 (0)