feat: use preview to compute workspace tags from terraform (#18720)

If using dynamic parameters, workspace tags are extracted using
`coder/preview`.
This commit is contained in:
Steven Masley
2025-07-03 14:35:44 -05:00
committed by GitHub
parent 4607e5113b
commit a099a8a25c
14 changed files with 1185 additions and 78 deletions

View File

@ -25,7 +25,9 @@ func TestDynamicParameterBuild(t *testing.T) {
t.Parallel()
owner, _, _, first := coderdenttest.NewWithAPI(t, &coderdenttest.Options{
Options: &coderdtest.Options{IncludeProvisionerDaemon: true},
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
@ -355,6 +357,92 @@ func TestDynamicParameterBuild(t *testing.T) {
})
}
func TestDynamicWorkspaceTags(t *testing.T) {
t.Parallel()
owner, _, _, first := coderdenttest.NewWithAPI(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
codersdk.FeatureExternalProvisionerDaemons: 1,
},
},
})
orgID := first.OrganizationID
templateAdmin, _ := coderdtest.CreateAnotherUser(t, owner, orgID, rbac.ScopedRoleOrgTemplateAdmin(orgID))
// create the template first, mark it as dynamic, then create the second version with the workspace tags.
// This ensures the template import uses the dynamic tags flow. The second step will happen in a test below.
workspaceTags, _ := coderdtest.DynamicParameterTemplate(t, templateAdmin, orgID, coderdtest.DynamicParameterTemplateParams{
MainTF: ``,
})
expectedTags := map[string]string{
"function": "param is foo",
"stringvar": "bar",
"numvar": "42",
"boolvar": "true",
"stringparam": "foo",
"numparam": "7",
"boolparam": "true",
"listparam": `["a","b"]`,
"static": "static value",
}
// A new provisioner daemon is required to make the template version.
importProvisioner := coderdenttest.NewExternalProvisionerDaemon(t, owner, first.OrganizationID, expectedTags)
defer importProvisioner.Close()
// This tests the template import's workspace tags extraction.
workspaceTags, workspaceTagsVersion := coderdtest.DynamicParameterTemplate(t, templateAdmin, orgID, coderdtest.DynamicParameterTemplateParams{
MainTF: string(must(os.ReadFile("testdata/parameters/workspacetags/main.tf"))),
TemplateID: workspaceTags.ID,
Version: func(request *codersdk.CreateTemplateVersionRequest) {
request.ProvisionerTags = map[string]string{
"static": "static value",
}
},
})
importProvisioner.Close() // No longer need this provisioner daemon, as the template import is done.
// Test the workspace create tag extraction.
expectedTags["function"] = "param is baz"
expectedTags["stringparam"] = "baz"
expectedTags["numparam"] = "8"
expectedTags["boolparam"] = "false"
workspaceProvisioner := coderdenttest.NewExternalProvisionerDaemon(t, owner, first.OrganizationID, expectedTags)
defer workspaceProvisioner.Close()
ctx := testutil.Context(t, testutil.WaitShort)
wrk, err := templateAdmin.CreateUserWorkspace(ctx, codersdk.Me, codersdk.CreateWorkspaceRequest{
TemplateVersionID: workspaceTagsVersion.ID,
Name: coderdtest.RandomUsername(t),
RichParameterValues: []codersdk.WorkspaceBuildParameter{
{Name: "stringparam", Value: "baz"},
{Name: "numparam", Value: "8"},
{Name: "boolparam", Value: "false"},
},
})
require.NoError(t, err)
build, err := templateAdmin.WorkspaceBuild(ctx, wrk.LatestBuild.ID)
require.NoError(t, err)
job, err := templateAdmin.OrganizationProvisionerJob(ctx, first.OrganizationID, build.Job.ID)
require.NoError(t, err)
// If the tags do no match, the await will fail.
// 'scope' and 'owner' tags are always included.
expectedTags["scope"] = "organization"
expectedTags["owner"] = ""
require.Equal(t, expectedTags, job.Tags)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, templateAdmin, wrk.LatestBuild.ID)
}
// TestDynamicParameterTemplate uses a template with some dynamic elements, and
// tests the parameters, values, etc are all as expected.
func TestDynamicParameterTemplate(t *testing.T) {

View File

@ -0,0 +1,66 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
}
}
variable "stringvar" {
type = string
default = "bar"
}
variable "numvar" {
type = number
default = 42
}
variable "boolvar" {
type = bool
default = true
}
data "coder_parameter" "stringparam" {
name = "stringparam"
type = "string"
default = "foo"
}
data "coder_parameter" "stringparamref" {
name = "stringparamref"
type = "string"
default = data.coder_parameter.stringparam.value
}
data "coder_parameter" "numparam" {
name = "numparam"
type = "number"
default = 7
}
data "coder_parameter" "boolparam" {
name = "boolparam"
type = "bool"
default = true
}
data "coder_parameter" "listparam" {
name = "listparam"
type = "list(string)"
default = jsonencode(["a", "b"])
}
data "coder_workspace_tags" "tags" {
tags = {
"function" = format("param is %s", data.coder_parameter.stringparamref.value)
"stringvar" = var.stringvar
"numvar" = var.numvar
"boolvar" = var.boolvar
"stringparam" = data.coder_parameter.stringparam.value
"numparam" = data.coder_parameter.numparam.value
"boolparam" = data.coder_parameter.boolparam.value
"listparam" = data.coder_parameter.listparam.value
}
}