Skip to content

Commit e6d58d0

Browse files
authored
feat: add icon and description fields to workspace preset (#422)
* feat: add icon and description fields to workspace preset * chore: update preset.icon to use ValidateURL * docs: add description and icon to preset examples * chore: run make fmt * chore: run make gen * chore: add size limit to preset icon and description
1 parent c85b5f7 commit e6d58d0

File tree

6 files changed

+124
-11
lines changed

6 files changed

+124
-11
lines changed

docs/data-sources/workspace_preset.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ provider "coder" {}
2121
# See the coder_parameter data source's documentation for examples of how to define
2222
# parameters like the ones used below.
2323
data "coder_workspace_preset" "example" {
24-
name = "example"
24+
name = "example"
25+
description = "Example description of what this preset does."
26+
icon = "/icon/example.svg"
2527
parameters = {
2628
(data.coder_parameter.example.name) = "us-central1-a"
2729
(data.coder_parameter.ami.name) = "ami-xxxxxxxx"
@@ -30,8 +32,10 @@ data "coder_workspace_preset" "example" {
3032
3133
# Example of a default preset that will be pre-selected for users
3234
data "coder_workspace_preset" "standard" {
33-
name = "Standard"
34-
default = true
35+
name = "Standard"
36+
description = "A workspace preset with medium compute in the US West region."
37+
icon = "/icon/standard.svg"
38+
default = true
3539
parameters = {
3640
(data.coder_parameter.instance_type.name) = "t3.medium"
3741
(data.coder_parameter.region.name) = "us-west-2"
@@ -49,6 +53,8 @@ data "coder_workspace_preset" "standard" {
4953
### Optional
5054

5155
- `default` (Boolean) Whether this preset should be selected by default when creating a workspace. Only one preset per template can be marked as default.
56+
- `description` (String) Describe what this preset does.
57+
- `icon` (String) A URL to an icon that will display in the dashboard. View built-in icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a built-in icon with `"${data.coder_workspace.me.access_url}/icon/<path>"`.
5258
- `parameters` (Map of String) Workspace parameters that will be set by the workspace preset. For simple templates that only need prebuilds, you may define a preset with zero parameters. Because workspace parameters may change between Coder template versions, preset parameters are allowed to define values for parameters that do not exist in the current template version.
5359
- `prebuilds` (Block Set, Max: 1) Configuration for prebuilt workspaces associated with this preset. Coder will maintain a pool of standby workspaces based on this configuration. When a user creates a workspace using this preset, they are assigned a prebuilt workspace instead of waiting for a new one to build. See prebuilt workspace documentation [here](https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces.md) (see [below for nested schema](#nestedblock--prebuilds))
5460

examples/data-sources/coder_workspace_preset/data-source.tf

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ provider "coder" {}
66
# See the coder_parameter data source's documentation for examples of how to define
77
# parameters like the ones used below.
88
data "coder_workspace_preset" "example" {
9-
name = "example"
9+
name = "example"
10+
description = "Example description of what this preset does."
11+
icon = "/icon/example.svg"
1012
parameters = {
1113
(data.coder_parameter.example.name) = "us-central1-a"
1214
(data.coder_parameter.ami.name) = "ami-xxxxxxxx"
@@ -15,8 +17,10 @@ data "coder_workspace_preset" "example" {
1517

1618
# Example of a default preset that will be pre-selected for users
1719
data "coder_workspace_preset" "standard" {
18-
name = "Standard"
19-
default = true
20+
name = "Standard"
21+
description = "A workspace preset with medium compute in the US West region."
22+
icon = "/icon/standard.svg"
23+
default = true
2024
parameters = {
2125
(data.coder_parameter.instance_type.name) = "t3.medium"
2226
(data.coder_parameter.region.name) = "us-west-2"

integration/integration_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ func TestIntegration(t *testing.T) {
9393
"workspace_parameter.value": `param value`,
9494
"workspace_parameter.icon": `param icon`,
9595
"workspace_preset.name": `preset`,
96+
"workspace_preset.description": `preset description`,
97+
"workspace_preset.icon": `preset icon`,
9698
"workspace_preset.default": `true`,
9799
"workspace_preset.parameters.param": `preset param value`,
98100
"workspace_preset.prebuilds.instances": `1`,

integration/test-data-source/main.tf

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ data "coder_parameter" "param" {
2020
icon = "param icon"
2121
}
2222
data "coder_workspace_preset" "preset" {
23-
name = "preset"
24-
default = true
23+
name = "preset"
24+
description = "preset description"
25+
icon = "preset icon"
26+
default = true
2527
parameters = {
2628
(data.coder_parameter.param.name) = "preset param value"
2729
}
@@ -65,6 +67,8 @@ locals {
6567
"workspace_parameter.value" : data.coder_parameter.param.value,
6668
"workspace_parameter.icon" : data.coder_parameter.param.icon,
6769
"workspace_preset.name" : data.coder_workspace_preset.preset.name,
70+
"workspace_preset.description" : data.coder_workspace_preset.preset.description,
71+
"workspace_preset.icon" : data.coder_workspace_preset.preset.icon,
6872
"workspace_preset.default" : tostring(data.coder_workspace_preset.preset.default),
6973
"workspace_preset.parameters.param" : data.coder_workspace_preset.preset.parameters.param,
7074
"workspace_preset.prebuilds.instances" : tostring(one(data.coder_workspace_preset.preset.prebuilds).instances),

provider/workspace_preset.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import (
1818
var PrebuildsCRONParser = rbcron.NewParser(rbcron.Minute | rbcron.Hour | rbcron.Dom | rbcron.Month | rbcron.Dow)
1919

2020
type WorkspacePreset struct {
21-
Name string `mapstructure:"name"`
22-
Default bool `mapstructure:"default"`
23-
Parameters map[string]string `mapstructure:"parameters"`
21+
Name string `mapstructure:"name"`
22+
Description string `mapstructure:"description"`
23+
Icon string `mapstructure:"icon"`
24+
Default bool `mapstructure:"default"`
25+
Parameters map[string]string `mapstructure:"parameters"`
2426
// There should always be only one prebuild block, but Terraform's type system
2527
// still parses them as a slice, so we need to handle it as such. We could use
2628
// an anonymous type and rd.Get to avoid a slice here, but that would not be possible
@@ -93,6 +95,24 @@ func workspacePresetDataSource() *schema.Resource {
9395
Required: true,
9496
ValidateFunc: validation.StringIsNotEmpty,
9597
},
98+
"description": {
99+
Type: schema.TypeString,
100+
Optional: true,
101+
Description: "Describe what this preset does.",
102+
ValidateFunc: validation.StringLenBetween(0, 128),
103+
},
104+
"icon": {
105+
Type: schema.TypeString,
106+
Description: "A URL to an icon that will display in the dashboard. View built-in " +
107+
"icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a " +
108+
"built-in icon with `\"${data.coder_workspace.me.access_url}/icon/<path>\"`.",
109+
ForceNew: true,
110+
Optional: true,
111+
ValidateFunc: validation.All(
112+
helpers.ValidateURL,
113+
validation.StringLenBetween(0, 256),
114+
),
115+
},
96116
"default": {
97117
Type: schema.TypeBool,
98118
Description: "Whether this preset should be selected by default when creating a workspace. Only one preset per template can be marked as default.",

provider/workspace_preset_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ func TestWorkspacePreset(t *testing.T) {
2323
Config: `
2424
data "coder_workspace_preset" "preset_1" {
2525
name = "preset_1"
26+
description = <<-EOT
27+
# Select the machine image
28+
See the [registry](https://container.registry.blah/namespace) for options.
29+
EOT
30+
icon = "/icon/region.svg"
2631
parameters = {
2732
"region" = "us-east1-a"
2833
}
@@ -34,6 +39,8 @@ func TestWorkspacePreset(t *testing.T) {
3439
require.NotNil(t, resource)
3540
attrs := resource.Primary.Attributes
3641
require.Equal(t, attrs["name"], "preset_1")
42+
require.Equal(t, attrs["description"], "# Select the machine image\nSee the [registry](https://container.registry.blah/namespace) for options.\n")
43+
require.Equal(t, attrs["icon"], "/icon/region.svg")
3744
require.Equal(t, attrs["parameters.region"], "us-east1-a")
3845
return nil
3946
},
@@ -76,6 +83,76 @@ func TestWorkspacePreset(t *testing.T) {
7683
// So we test it here to make sure we don't regress.
7784
ExpectError: regexp.MustCompile("Incorrect attribute value type"),
7885
},
86+
{
87+
Name: "Description field is empty",
88+
Config: `
89+
data "coder_workspace_preset" "preset_1" {
90+
name = "preset_1"
91+
description = ""
92+
parameters = {
93+
"region" = "us-east1-a"
94+
}
95+
}`,
96+
// This validation is done by Terraform, but it could still break if we misconfigure the schema.
97+
// So we test it here to make sure we don't regress.
98+
ExpectError: nil,
99+
},
100+
{
101+
Name: "Description field exceeds maximum supported length (128 characters)",
102+
Config: `
103+
data "coder_workspace_preset" "preset_1" {
104+
name = "preset_1"
105+
description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vehicula leo sit amet mi laoreet, sed ornare velit tincidunt. Proin gravida lacinia blandit."
106+
parameters = {
107+
"region" = "us-east1-a"
108+
}
109+
}`,
110+
// This validation is done by Terraform, but it could still break if we misconfigure the schema.
111+
// So we test it here to make sure we don't regress.
112+
ExpectError: regexp.MustCompile(`expected length of description to be in the range \(0 - 128\)`),
113+
},
114+
{
115+
Name: "Icon field is empty",
116+
Config: `
117+
data "coder_workspace_preset" "preset_1" {
118+
name = "preset_1"
119+
icon = ""
120+
parameters = {
121+
"region" = "us-east1-a"
122+
}
123+
}`,
124+
// This validation is done by Terraform, but it could still break if we misconfigure the schema.
125+
// So we test it here to make sure we don't regress.
126+
ExpectError: nil,
127+
},
128+
{
129+
Name: "Icon field is an invalid URL",
130+
Config: `
131+
data "coder_workspace_preset" "preset_1" {
132+
name = "preset_1"
133+
icon = "/icon%.svg"
134+
parameters = {
135+
"region" = "us-east1-a"
136+
}
137+
}`,
138+
// This validation is done by Terraform, but it could still break if we misconfigure the schema.
139+
// So we test it here to make sure we don't regress.
140+
ExpectError: regexp.MustCompile("invalid URL escape"),
141+
},
142+
{
143+
Name: "Icon field exceeds maximum supported length (256 characters)",
144+
Config: `
145+
data "coder_workspace_preset" "preset_1" {
146+
name = "preset_1"
147+
icon = "https://example.com/path/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.svg"
148+
parameters = {
149+
"region" = "us-east1-a"
150+
}
151+
}`,
152+
// This validation is done by Terraform, but it could still break if we misconfigure the schema.
153+
// So we test it here to make sure we don't regress.
154+
ExpectError: regexp.MustCompile(`expected length of icon to be in the range \(0 - 256\)`),
155+
},
79156
{
80157
Name: "Parameters field is not provided",
81158
Config: `

0 commit comments

Comments
 (0)