From 413d1c845bdca428a7b50b1a5d39ddc3f7475be4 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 27 Jan 2025 18:39:10 +0000 Subject: [PATCH 1/3] fix(provisioner/terraform/tfparse): allow empty values in coder_workspace_tag defaults --- coderd/templateversions_test.go | 41 +++++++++++++- enterprise/coderd/workspaces_test.go | 55 ++++++++++++++++--- provisioner/terraform/tfparse/tfparse.go | 7 --- provisioner/terraform/tfparse/tfparse_test.go | 2 +- 4 files changed, 85 insertions(+), 20 deletions(-) diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 3282defb968c5..9fd1bf6e2d830 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -429,9 +429,8 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { } }`, }, - reqTags: map[string]string{"a": "b"}, - // wantTags: map[string]string{"owner": "", "scope": "organization", "a": "b"}, - expectError: `provisioner tag "a" evaluated to an empty value`, + reqTags: map[string]string{"a": "b"}, + wantTags: map[string]string{"owner": "", "scope": "organization", "a": "b"}, }, { name: "main.tf with disallowed workspace tag value", @@ -568,6 +567,42 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { }, wantTags: map[string]string{"owner": "", "scope": "organization"}, }, + { + name: "main.tf with tags from parameter with default value from variable no default", + files: map[string]string{ + `main.tf`: ` + variable "provisioner" { + type = string + } + variable "default_provisioner" { + type = string + default = "" # intentionally blank, set on template creation + } + data "coder_parameter" "provisioner" { + name = "provisioner" + mutable = false + default = var.default_provisioner + dynamic "option" { + for_each = toset(split(",", var.provisioner)) + content { + name = option.value + value = option.value + } + } + } + data "coder_workspace_tags" "tags" { + tags = { + "provisioner" : data.coder_parameter.provisioner.value + } + }`, + }, + reqTags: map[string]string{ + "provisioner": "alpha", + }, + wantTags: map[string]string{ + "provisioner": "alpha", "owner": "", "scope": "organization", + }, + }, } { tt := tt t.Run(tt.name, func(t *testing.T) { diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index e2149603135c8..eedd6f1bcfa1c 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -1488,8 +1488,11 @@ func TestWorkspaceTagsTerraform(t *testing.T) { createTemplateVersionRequestTags map[string]string // the coder_workspace_tags bit of main.tf. // you can add more stuff here if you need - tfWorkspaceTags string - skipCreateWorkspace bool + tfWorkspaceTags string + templateImportUserVariableValues []codersdk.VariableValue + // if we need to set parameters on workspace build + workspaceBuildParameters []codersdk.WorkspaceBuildParameter + skipCreateWorkspace bool }{ { name: "no tags", @@ -1589,6 +1592,38 @@ func TestWorkspaceTagsTerraform(t *testing.T) { // matching tag foo=bar. skipCreateWorkspace: true, }, + { + name: "overrides with dynamic option from var", + provisionerTags: map[string]string{"foo": "bar"}, + createTemplateVersionRequestTags: map[string]string{"foo": "bar"}, + templateImportUserVariableValues: []codersdk.VariableValue{{Name: "default_foo", Value: "baz"}, {Name: "foo", Value: "bar,baz"}}, + workspaceBuildParameters: []codersdk.WorkspaceBuildParameter{{Name: "foo", Value: "bar"}}, + tfWorkspaceTags: ` + variable "default_foo" { + type = string + } + variable "foo" { + type = string + } + data "coder_parameter" "foo" { + name = "foo" + type = "string" + default = var.default_foo + mutable = false + dynamic "option" { + for_each = toset(split(",", var.foo)) + content { + name = option.value + value = option.value + } + } + } + data "coder_workspace_tags" "tags" { + tags = { + "foo" = data.coder_parameter.foo.value + } + }`, + }, } { tc := tc t.Run(tc.name, func(t *testing.T) { @@ -1617,11 +1652,12 @@ func TestWorkspaceTagsTerraform(t *testing.T) { fi, err := templateAdmin.Upload(ctx, "application/x-tar", bytes.NewReader(tarBytes)) require.NoError(t, err, "failed to upload file") tv, err := templateAdmin.CreateTemplateVersion(ctx, owner.OrganizationID, codersdk.CreateTemplateVersionRequest{ - Name: testutil.GetRandomName(t), - FileID: fi.ID, - StorageMethod: codersdk.ProvisionerStorageMethodFile, - Provisioner: codersdk.ProvisionerTypeTerraform, - ProvisionerTags: tc.createTemplateVersionRequestTags, + Name: testutil.GetRandomName(t), + FileID: fi.ID, + StorageMethod: codersdk.ProvisionerStorageMethodFile, + Provisioner: codersdk.ProvisionerTypeTerraform, + ProvisionerTags: tc.createTemplateVersionRequestTags, + UserVariableValues: tc.templateImportUserVariableValues, }) require.NoError(t, err, "failed to create template version") coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdmin, tv.ID) @@ -1630,8 +1666,9 @@ func TestWorkspaceTagsTerraform(t *testing.T) { if !tc.skipCreateWorkspace { // Creating a workspace as a non-privileged user must succeed ws, err := member.CreateUserWorkspace(ctx, memberUser.Username, codersdk.CreateWorkspaceRequest{ - TemplateID: tpl.ID, - Name: coderdtest.RandomUsername(t), + TemplateID: tpl.ID, + Name: coderdtest.RandomUsername(t), + RichParameterValues: tc.workspaceBuildParameters, }) require.NoError(t, err, "failed to create workspace") coderdtest.AwaitWorkspaceBuildJobCompleted(t, member, ws.LatestBuild.ID) diff --git a/provisioner/terraform/tfparse/tfparse.go b/provisioner/terraform/tfparse/tfparse.go index 901f52babbdf1..281ce55f99146 100644 --- a/provisioner/terraform/tfparse/tfparse.go +++ b/provisioner/terraform/tfparse/tfparse.go @@ -239,13 +239,6 @@ func (p *Parser) WorkspaceTagDefaults(ctx context.Context) (map[string]string, e return nil, xerrors.Errorf("eval provisioner tags: %w", err) } - // Ensure that none of the tag values are empty after evaluation. - for k, v := range evalTags { - if len(strings.TrimSpace(v)) > 0 { - continue - } - return nil, xerrors.Errorf("provisioner tag %q evaluated to an empty value, please set a default value", k) - } return evalTags, nil } diff --git a/provisioner/terraform/tfparse/tfparse_test.go b/provisioner/terraform/tfparse/tfparse_test.go index 6f36d653c697c..ceefc484b2169 100644 --- a/provisioner/terraform/tfparse/tfparse_test.go +++ b/provisioner/terraform/tfparse/tfparse_test.go @@ -268,7 +268,7 @@ func Test_WorkspaceTagDefaultsFromFile(t *testing.T) { } }`, }, - expectError: `provisioner tag "az" evaluated to an empty value, please set a default value`, + expectTags: map[string]string{"cluster": "developers", "az": "", "platform": "kubernetes", "region": "us"}, }, { name: "main.tf with missing parameter default value outside workspace tags", From 85a168e23eb1ec2ebe150e0be0bb7783acc60d1e Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 27 Jan 2025 18:39:45 +0000 Subject: [PATCH 2/3] chore(coderdenttest): use random name instead of t.Name() in newExternalProvisionerDaemon --- enterprise/coderd/coderdenttest/coderdenttest.go | 2 +- enterprise/coderd/provisionerdaemons_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/enterprise/coderd/coderdenttest/coderdenttest.go b/enterprise/coderd/coderdenttest/coderdenttest.go index 0d44937e4a82d..d76722b5bac1a 100644 --- a/enterprise/coderd/coderdenttest/coderdenttest.go +++ b/enterprise/coderd/coderdenttest/coderdenttest.go @@ -389,7 +389,7 @@ func newExternalProvisionerDaemon(t testing.TB, client *codersdk.Client, org uui daemon := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) { return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{ ID: uuid.New(), - Name: t.Name(), + Name: testutil.GetRandomName(t), Organization: org, Provisioners: []codersdk.ProvisionerType{provisionerType}, Tags: tags, diff --git a/enterprise/coderd/provisionerdaemons_test.go b/enterprise/coderd/provisionerdaemons_test.go index 9efb002a8e910..ff3ce625707ee 100644 --- a/enterprise/coderd/provisionerdaemons_test.go +++ b/enterprise/coderd/provisionerdaemons_test.go @@ -285,7 +285,7 @@ func TestProvisionerDaemonServe(t *testing.T) { daemons, err := client.ProvisionerDaemons(context.Background()) assert.NoError(t, err, "failed to get provisioner daemons") return len(daemons) > 0 && - assert.Equal(t, t.Name(), daemons[0].Name) && + assert.NotEmpty(t, daemons[0].Name) && assert.Equal(t, provisionersdk.ScopeUser, daemons[0].Tags[provisionersdk.TagScope]) && assert.Equal(t, user.UserID.String(), daemons[0].Tags[provisionersdk.TagOwner]) }, testutil.WaitShort, testutil.IntervalMedium) From ecb2ffd963b5f2f93b5d993e75fa6dae5be4f6b5 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 27 Jan 2025 20:32:02 +0000 Subject: [PATCH 3/3] chore(docs): update docs re workspace tag default values --- docs/admin/templates/extending-templates/workspace-tags.md | 7 +------ examples/workspace-tags/README.md | 6 ++---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/admin/templates/extending-templates/workspace-tags.md b/docs/admin/templates/extending-templates/workspace-tags.md index e49957d9ba515..96691fe162540 100644 --- a/docs/admin/templates/extending-templates/workspace-tags.md +++ b/docs/admin/templates/extending-templates/workspace-tags.md @@ -62,11 +62,6 @@ variables and parameters. This is illustrated in the table below: ## Constraints -### Default Values - -All template variables and `coder_parameter` data sources **must** provide a -default value. Failure to do so will result in an error. - ### Tagged provisioners It is possible to choose tag combinations that no provisioner can handle. This @@ -127,6 +122,6 @@ variables, and references to other resources. #### Not supported -- Function calls: `try(var.foo, "default")` +- Function calls that reference files on disk: `abspath`, `file*`, `pathexpand` - Resources: `compute_instance.dev.name` - Data sources other than `coder_parameter`: `data.local_file.hostname.content` diff --git a/examples/workspace-tags/README.md b/examples/workspace-tags/README.md index 386aab6262e47..4e9ac06643cee 100644 --- a/examples/workspace-tags/README.md +++ b/examples/workspace-tags/README.md @@ -7,7 +7,7 @@ icon: /icon/docker.png ## Overview -This Coder template presents use of [Workspace Tags](https://coder.com/docs/templates/workspace-tags) [Coder Parameters](https://coder.com/docs/templates/parameters). +This Coder template presents use of [Workspace Tags](https://coder.com/docs/admin/templates/extending-templates/workspace-tags) and [Coder Parameters](https://coder.com/docs/templates/parameters). ## Use case @@ -18,10 +18,8 @@ By using `coder_workspace_tags` and `coder_parameter`s, template administrators ## Notes - You will need to have an [external provisioner](https://coder.com/docs/admin/provisioners#external-provisioners) with the correct tagset running in order to import this template. -- When specifying values for the `coder_workspace_tags` data source, you are restricted to using a subset of Terraform's capabilities. -- You must specify default values for all data sources and variables referenced by the `coder_workspace_tags` data source. +- When specifying values for the `coder_workspace_tags` data source, you are restricted to using a subset of Terraform's capabilities. See [here](https://coder.com/docs/admin/templates/extending-templates/workspace-tags) for more details. -See [Workspace Tags](https://coder.com/docs/templates/workspace-tags) for more information. ## Development 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