Skip to content

Commit 50bda99

Browse files
committed
Merge remote-tracking branch 'origin/main' into jjs/364
2 parents 0a50b31 + e51ae3a commit 50bda99

15 files changed

+839
-105
lines changed

docs/data-sources/parameter.md

+2
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,12 @@ data "coder_parameter" "home_volume_size" {
145145
- `description` (String) Describe what this parameter does.
146146
- `display_name` (String) The displayed name of the parameter as it will appear in the interface.
147147
- `ephemeral` (Boolean) The value of an ephemeral parameter will not be preserved between consecutive workspace builds.
148+
- `form_type` (String) The type of this parameter. Must be one of: [radio, slider, input, dropdown, checkbox, switch, multi-select, tag-select, textarea, error].
148149
- `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>"`.
149150
- `mutable` (Boolean) Whether this value can be changed after workspace creation. This can be destructive for values like region, so use with caution!
150151
- `option` (Block List) Each `option` block defines a value for a user to select from. (see [below for nested schema](#nestedblock--option))
151152
- `order` (Number) The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order).
153+
- `styling` (String) JSON encoded string containing the metadata for controlling the appearance of this parameter in the UI. This option is purely cosmetic and does not affect the function of the parameter in terraform.
152154
- `type` (String) The type of this parameter. Must be one of: `"number"`, `"string"`, `"bool"`, or `"list(string)"`.
153155
- `validation` (Block List, Max: 1) Validate the input of a parameter. (see [below for nested schema](#nestedblock--validation))
154156

docs/data-sources/workspace.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ resource "docker_container" "workspace" {
6969
- `access_port` (Number) The access port of the Coder deployment provisioning this workspace.
7070
- `access_url` (String) The access URL of the Coder deployment provisioning this workspace.
7171
- `id` (String) UUID of the workspace.
72-
- `is_prebuild` (Boolean) Whether the workspace is a prebuild.
72+
- `is_prebuild` (Boolean) Similar to `prebuild_count`, but a boolean value instead of a count. This is set to true if the workspace is a currently unassigned prebuild. Once the workspace is assigned, this value will be false.
7373
- `name` (String) Name of the workspace.
74-
- `prebuild_count` (Number) A computed count, equal to 1 if the workspace was prebuilt.
74+
- `prebuild_count` (Number) A computed count, equal to 1 if the workspace is a currently unassigned prebuild. Use this to conditionally act on the status of a prebuild. Actions that do not require user identity can be taken when this value is set to 1. Actions that should only be taken once the workspace has been assigned to a user may be taken when this value is set to 0.
7575
- `start_count` (Number) A computed count based on `transition` state. If `start`, count will equal 1.
7676
- `template_id` (String) ID of the workspace's template.
7777
- `template_name` (String) Name of the workspace's template.

docs/data-sources/workspace_preset.md

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title: "coder_workspace_preset Data Source - terraform-provider-coder"
44
subcategory: ""
55
description: |-
6-
Use this data source to predefine common configurations for workspaces.
6+
Use this data source to predefine common configurations for coder workspaces. Users will have the option to select a defined preset, which will automatically apply the selected configuration. Any parameters defined in the preset will be applied to the workspace. Parameters that are not defined by the preset will still be configurable when creating a workspace.
77
---
88

99
# coder_workspace_preset (Data Source)
1010

11-
Use this data source to predefine common configurations for workspaces.
11+
Use this data source to predefine common configurations for coder workspaces. Users will have the option to select a defined preset, which will automatically apply the selected configuration. Any parameters defined in the preset will be applied to the workspace. Parameters that are not defined by the preset will still be configurable when creating a workspace.
1212

1313
## Example Usage
1414

@@ -34,20 +34,24 @@ data "coder_workspace_preset" "example" {
3434

3535
### Required
3636

37-
- `name` (String) Name of the workspace preset.
38-
- `parameters` (Map of String) Parameters of the workspace preset.
37+
- `name` (String) The name of the workspace preset.
38+
39+
### Optional
40+
41+
- `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.
42+
- `prebuilds` (Block Set, Max: 1) Prebuilt workspace configuration related to this workspace preset. Coder will build and maintain workspaces in reserve based on this configuration. When a user creates a new workspace using a preset, they will be assigned a prebuilt workspace, instead of waiting for a new workspace to build. (see [below for nested schema](#nestedblock--prebuilds))
3943

4044
### Optional
4145

4246
- `prebuilds` (Block Set, Max: 1) Prebuilds of the workspace preset. (see [below for nested schema](#nestedblock--prebuilds))
4347

4448
### Read-Only
4549

46-
- `id` (String) ID of the workspace preset.
50+
- `id` (String) The preset ID is automatically generated and may change between runs. It is recommended to use the `name` attribute to identify the preset.
4751

4852
<a id="nestedblock--prebuilds"></a>
4953
### Nested Schema for `prebuilds`
5054

5155
Required:
5256

53-
- `instances` (Number)
57+
- `instances` (Number) The number of workspaces to keep in reserve for this preset.

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/coder/terraform-provider-coder/v2
22

3-
go 1.22.9
3+
go 1.24.2
44

55
require (
66
github.com/docker/docker v26.1.5+incompatible
@@ -79,7 +79,7 @@ require (
7979
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect
8080
go.opentelemetry.io/otel/metric v1.31.0 // indirect
8181
go.opentelemetry.io/otel/trace v1.31.0 // indirect
82-
golang.org/x/crypto v0.33.0 // indirect
82+
golang.org/x/crypto v0.35.0 // indirect
8383
golang.org/x/net v0.34.0 // indirect
8484
golang.org/x/sync v0.11.0 // indirect
8585
golang.org/x/sys v0.30.0 // indirect

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
225225
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
226226
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
227227
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
228-
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
229-
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
228+
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
229+
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
230230
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
231231
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
232232
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=

integration/integration_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,11 @@ func TestIntegration(t *testing.T) {
9090
// TODO (sasswart): the cli doesn't support presets yet.
9191
// once it does, the value for workspace_parameter.value
9292
// will be the preset value.
93-
"workspace_parameter.value": `param value`,
94-
"workspace_parameter.icon": `param icon`,
95-
"workspace_preset.name": `preset`,
96-
"workspace_preset.parameters.param": `preset param value`,
93+
"workspace_parameter.value": `param value`,
94+
"workspace_parameter.icon": `param icon`,
95+
"workspace_preset.name": `preset`,
96+
"workspace_preset.parameters.param": `preset param value`,
97+
"workspace_preset.prebuilds.instances": `1`,
9798
},
9899
},
99100
{

integration/test-data-source/main.tf

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ data "coder_workspace_preset" "preset" {
2424
parameters = {
2525
(data.coder_parameter.param.name) = "preset param value"
2626
}
27+
28+
prebuilds {
29+
instances = 1
30+
}
2731
}
2832

2933
locals {
@@ -47,6 +51,7 @@ locals {
4751
"workspace_parameter.icon" : data.coder_parameter.param.icon,
4852
"workspace_preset.name" : data.coder_workspace_preset.preset.name,
4953
"workspace_preset.parameters.param" : data.coder_workspace_preset.preset.parameters.param,
54+
"workspace_preset.prebuilds.instances" : tostring(one(data.coder_workspace_preset.preset.prebuilds).instances),
5055
}
5156
}
5257

main.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
"flag"
5+
46
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
57

68
"github.com/coder/terraform-provider-coder/v2/provider"
@@ -11,8 +13,15 @@ import (
1113
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
1214

1315
func main() {
14-
servePprof()
15-
plugin.Serve(&plugin.ServeOpts{
16+
debug := flag.Bool("debug", false, "Enable debug mode for the provider")
17+
flag.Parse()
18+
19+
opts := &plugin.ServeOpts{
20+
Debug: *debug,
21+
ProviderAddr: "registry.terraform.io/coder/coder",
1622
ProviderFunc: provider.New,
17-
})
23+
}
24+
25+
servePprof()
26+
plugin.Serve(opts)
1827
}

provider/formtype.go

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package provider
2+
3+
import (
4+
"slices"
5+
6+
"golang.org/x/xerrors"
7+
)
8+
9+
// OptionType is a type of option that can be used in the 'type' argument of
10+
// a parameter. These should match types as defined in terraform:
11+
//
12+
// https://developer.hashicorp.com/terraform/language/expressions/types
13+
//
14+
// The value have to be string literals, as type constraint keywords are not
15+
// supported in providers.
16+
type OptionType = string
17+
18+
const (
19+
OptionTypeString OptionType = "string"
20+
OptionTypeNumber OptionType = "number"
21+
OptionTypeBoolean OptionType = "bool"
22+
OptionTypeListString OptionType = "list(string)"
23+
)
24+
25+
func OptionTypes() []OptionType {
26+
return []OptionType{
27+
OptionTypeString,
28+
OptionTypeNumber,
29+
OptionTypeBoolean,
30+
OptionTypeListString,
31+
}
32+
}
33+
34+
// ParameterFormType is the list of supported form types for display in
35+
// the Coder "create workspace" form. These form types are functional as well
36+
// as cosmetic. Refer to `formTypeTruthTable` for the allowed pairings.
37+
// For example, "multi-select" has the type "list(string)" but the option
38+
// values are "string".
39+
type ParameterFormType string
40+
41+
const (
42+
ParameterFormTypeDefault ParameterFormType = ""
43+
ParameterFormTypeRadio ParameterFormType = "radio"
44+
ParameterFormTypeSlider ParameterFormType = "slider"
45+
ParameterFormTypeInput ParameterFormType = "input"
46+
ParameterFormTypeDropdown ParameterFormType = "dropdown"
47+
ParameterFormTypeCheckbox ParameterFormType = "checkbox"
48+
ParameterFormTypeSwitch ParameterFormType = "switch"
49+
ParameterFormTypeMultiSelect ParameterFormType = "multi-select"
50+
ParameterFormTypeTagSelect ParameterFormType = "tag-select"
51+
ParameterFormTypeTextArea ParameterFormType = "textarea"
52+
ParameterFormTypeError ParameterFormType = "error"
53+
)
54+
55+
// ParameterFormTypes should be kept in sync with the enum list above.
56+
func ParameterFormTypes() []ParameterFormType {
57+
return []ParameterFormType{
58+
// Intentionally omit "ParameterFormTypeDefault" from this set.
59+
// It is a valid enum, but will always be mapped to a real value when
60+
// being used.
61+
ParameterFormTypeRadio,
62+
ParameterFormTypeSlider,
63+
ParameterFormTypeInput,
64+
ParameterFormTypeDropdown,
65+
ParameterFormTypeCheckbox,
66+
ParameterFormTypeSwitch,
67+
ParameterFormTypeMultiSelect,
68+
ParameterFormTypeTagSelect,
69+
ParameterFormTypeTextArea,
70+
ParameterFormTypeError,
71+
}
72+
}
73+
74+
// formTypeTruthTable is a map of [`type`][`optionCount` > 0] to `form_type`.
75+
// The first value in the slice is the default value assuming `form_type` is
76+
// not specified.
77+
//
78+
// The boolean key indicates whether the `options` field is specified.
79+
// | Type | Options | Specified Form Type | form_type | Notes |
80+
// |-------------------|---------|---------------------|----------------|--------------------------------|
81+
// | `string` `number` | Y | | `radio` | |
82+
// | `string` `number` | Y | `dropdown` | `dropdown` | |
83+
// | `string` `number` | N | | `input` | |
84+
// | `string` | N | 'textarea' | `textarea` | |
85+
// | `number` | N | 'slider' | `slider` | min/max validation |
86+
// | `bool` | Y | | `radio` | |
87+
// | `bool` | N | | `checkbox` | |
88+
// | `bool` | N | `switch` | `switch` | |
89+
// | `list(string)` | Y | | `radio` | |
90+
// | `list(string)` | N | | `tag-select` | |
91+
// | `list(string)` | Y | `multi-select` | `multi-select` | Option values will be `string` |
92+
var formTypeTruthTable = map[OptionType]map[bool][]ParameterFormType{
93+
OptionTypeString: {
94+
true: {ParameterFormTypeRadio, ParameterFormTypeDropdown},
95+
false: {ParameterFormTypeInput, ParameterFormTypeTextArea},
96+
},
97+
OptionTypeNumber: {
98+
true: {ParameterFormTypeRadio, ParameterFormTypeDropdown},
99+
false: {ParameterFormTypeInput, ParameterFormTypeSlider},
100+
},
101+
OptionTypeBoolean: {
102+
true: {ParameterFormTypeRadio},
103+
false: {ParameterFormTypeCheckbox, ParameterFormTypeSwitch},
104+
},
105+
OptionTypeListString: {
106+
true: {ParameterFormTypeRadio, ParameterFormTypeMultiSelect},
107+
false: {ParameterFormTypeTagSelect},
108+
},
109+
}
110+
111+
// ValidateFormType handles the truth table for the valid set of `type` and
112+
// `form_type` options.
113+
// The OptionType is also returned because it is possible the 'type' of the
114+
// 'value' & 'default' fields is different from the 'type' of the options.
115+
// The use case is when using multi-select. The options are 'string' and the
116+
// value is 'list(string)'.
117+
func ValidateFormType(paramType OptionType, optionCount int, specifiedFormType ParameterFormType) (OptionType, ParameterFormType, error) {
118+
optionsExist := optionCount > 0
119+
allowed, ok := formTypeTruthTable[paramType][optionsExist]
120+
if !ok || len(allowed) == 0 {
121+
// This error should really never be hit, as the provider sdk does an enum validation.
122+
return paramType, specifiedFormType, xerrors.Errorf("\"type\" attribute=%q is not supported, choose one of %v", paramType, OptionTypes())
123+
}
124+
125+
if specifiedFormType == ParameterFormTypeDefault {
126+
// handle the default case
127+
specifiedFormType = allowed[0]
128+
}
129+
130+
if !slices.Contains(allowed, specifiedFormType) {
131+
optionMsg := ""
132+
opposite := formTypeTruthTable[paramType][!optionsExist]
133+
134+
// This extra message tells a user if they are using a valid form_type
135+
// for a 'type', but it is invalid because options do/do-not exist.
136+
// It serves as a more helpful error message.
137+
//
138+
// Eg: form_type=slider is valid for type=number, but invalid if options exist.
139+
// And this error message is more accurate than just saying "form_type=slider is
140+
// not valid for type=number".
141+
if slices.Contains(opposite, specifiedFormType) {
142+
if optionsExist {
143+
optionMsg = " when options exist"
144+
} else {
145+
optionMsg = " when options do not exist"
146+
}
147+
}
148+
return paramType, specifiedFormType,
149+
xerrors.Errorf("\"form_type\" attribute=%q is not supported for \"type\"=%q%s, choose one of %v",
150+
specifiedFormType, paramType,
151+
optionMsg, toStrings(allowed))
152+
}
153+
154+
// This is the only current special case. If 'multi-select' is selected, the type
155+
// of 'value' and an options 'value' are different. The type of the parameter is
156+
// `list(string)` but the type of the individual options is `string`.
157+
if paramType == OptionTypeListString && specifiedFormType == ParameterFormTypeMultiSelect {
158+
return OptionTypeString, ParameterFormTypeMultiSelect, nil
159+
}
160+
161+
return paramType, specifiedFormType, nil
162+
}
163+
164+
func toStrings[A ~string](l []A) []string {
165+
var r []string
166+
for _, v := range l {
167+
r = append(r, string(v))
168+
}
169+
return r
170+
}

0 commit comments

Comments
 (0)