Skip to content

Commit 3866d73

Browse files
committed
chore: implement form_type and use custom terraform provider
1 parent 48003cd commit 3866d73

File tree

12 files changed

+166
-66
lines changed

12 files changed

+166
-66
lines changed

.github/workflows/gotest.yml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,37 @@ jobs:
1515
runs-on: ubuntu-latest
1616
timeout-minutes: 5
1717
steps:
18-
- uses: actions/[email protected]
1918
- uses: actions/setup-go@v5
2019
with:
2120
go-version: 1.22.8
21+
- name: Install Coder Terraform Provider Locally
22+
uses: actions/[email protected]
23+
with:
24+
repository: 'coder/terraform-provider-coder'
25+
ref: 'stevenmasley/form_control'
26+
path: "terraform-provider-coder"
27+
- name: Build Local Terraform Provider
28+
run: |
29+
cd terraform-provider-coder
30+
go build -o terraform-provider-coder
31+
cd ..
32+
- name: Setup dev terraform
33+
run: |
34+
touch $HOME/.terraformrc
35+
echo provider_installation { > $HOME/.terraformrc
36+
echo dev_overrides { >> $HOME/.terraformrc
37+
echo "coder/coder" = "$GITHUB_WORKSPACE/terraform-provider-coder" >> $HOME/.terraformrc
38+
echo } >> $HOME/.terraformrc
39+
echo direct {} >> $HOME/.terraformrc
40+
echo } >> $HOME/.terraformrc
41+
42+
echo "'.terraformrc' contents"
43+
cat $HOME/.terraformrc
44+
45+
- uses: actions/[email protected]
2246
- name: Install gotestsum
2347
shell: bash
2448
run: go install gotest.tools/gotestsum@latest
49+
2550
- name: Run tests
2651
run: gotestsum ./...

extract/parameter.go

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package extract
22

33
import (
44
"fmt"
5+
"slices"
56
"strings"
67

78
"github.com/aquasecurity/trivy/pkg/iac/terraform"
@@ -11,6 +12,7 @@ import (
1112

1213
"github.com/coder/preview/hclext"
1314
"github.com/coder/preview/types"
15+
"github.com/coder/terraform-provider-coder/v2/provider"
1416
)
1517

1618
func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnostics) {
@@ -26,6 +28,16 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti
2628
diags = diags.Append(typDiag)
2729
}
2830

31+
formType, formTypeDiags := optionalStringEnum[provider.ParameterFormType](block, "form_type", provider.ParameterFormTypeDefault, func(s provider.ParameterFormType) error {
32+
if !slices.Contains(provider.ParameterFormTypes(), s) {
33+
return fmt.Errorf("invalid form type %q, expected one of [%s]", s, strings.Join(toStrings(provider.ParameterFormTypes()), ", "))
34+
}
35+
return nil
36+
})
37+
if formTypeDiags != nil {
38+
diags = diags.Append(formTypeDiags)
39+
}
40+
2941
pName, nameDiag := requiredString(block, "name")
3042
if nameDiag != nil {
3143
diags = diags.Append(nameDiag)
@@ -36,16 +48,24 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti
3648
}
3749

3850
pVal := richParameterValue(block)
51+
52+
def := types.StringLiteral("")
53+
defAttr := block.GetAttribute("default")
54+
if !defAttr.IsNil() {
55+
def = types.ToHCLString(block, defAttr)
56+
}
57+
3958
p := types.Parameter{
4059
Value: pVal,
41-
RichParameter: types.RichParameter{
60+
ParameterData: types.ParameterData{
4261
Name: pName,
4362
Description: optionalString(block, "description"),
4463
Type: pType,
64+
FormType: formType,
4565
Mutable: optionalBoolean(block, "mutable"),
4666
// Default value is always written as a string, then converted
4767
// to the correct type.
48-
DefaultValue: optionalString(block, "default"),
68+
DefaultValue: def,
4969
Icon: optionalString(block, "icon"),
5070
Options: make([]*types.ParameterOption, 0),
5171
Validations: make([]*types.ParameterValidation, 0),
@@ -58,7 +78,25 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti
5878
},
5979
}
6080

61-
for _, b := range block.GetBlocks("option") {
81+
optBlocks := block.GetBlocks("option")
82+
83+
optionType, newFormType, err := provider.ValidateFormType(string(p.Type), len(optBlocks), p.FormType)
84+
if err != nil {
85+
diags = diags.Append(&hcl.Diagnostic{
86+
Severity: hcl.DiagError,
87+
Summary: fmt.Sprintf("Invalid parameter `type=%q` and `form_type=%q`", p.Type, p.FormType),
88+
Detail: err.Error(),
89+
Context: &block.HCLBlock().DefRange,
90+
})
91+
92+
// Parameter cannot be used
93+
p.FormType = provider.ParameterFormTypeError
94+
} else {
95+
p.Type = types.ParameterType(optionType)
96+
p.FormType = newFormType
97+
}
98+
99+
for _, b := range optBlocks {
62100
opt, optDiags := ParameterOptionFromBlock(b)
63101
diags = diags.Extend(optDiags)
64102

@@ -107,6 +145,7 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti
107145
paramTypeDiag.EvalContext = block.Context().Inner()
108146
}
109147
diags = diags.Append(paramTypeDiag)
148+
p.FormType = provider.ParameterFormTypeError
110149
}
111150

112151
if ctyType != cty.NilType && pVal.Value.Type().Equals(cty.String) {
@@ -126,10 +165,9 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti
126165
}
127166
}
128167

129-
// Parameter usage diags are useful.
130168
usageDiags := ParameterUsageDiagnostics(p)
131169
if usageDiags.HasErrors() {
132-
p.FormControl = types.FormControlError
170+
p.FormType = provider.ParameterFormTypeError
133171
}
134172
diags = diags.Extend(usageDiags)
135173

@@ -215,20 +253,14 @@ func ParameterOptionFromBlock(block *terraform.Block) (types.ParameterOption, hc
215253

216254
valAttr := block.GetAttribute("value")
217255

218-
pVal := types.HCLString{
219-
Value: hclext.Value(valAttr.HCLAttribute().Expr, block.Context().Inner()),
220-
ValueDiags: nil,
221-
ValueExpr: valAttr.HCLAttribute().Expr,
222-
}
223-
224256
if diags.HasErrors() {
225257
return types.ParameterOption{}, diags
226258
}
227259

228260
p := types.ParameterOption{
229261
Name: pName,
230262
Description: optionalString(block, "description"),
231-
Value: pVal,
263+
Value: types.ToHCLString(block, valAttr),
232264
Icon: optionalString(block, "icon"),
233265
}
234266

@@ -355,7 +387,7 @@ func optionalString(block *terraform.Block, key string) string {
355387
return ""
356388
}
357389
val := attr.Value()
358-
if val.Type() != cty.String {
390+
if !val.Type().Equals(cty.String) {
359391
return ""
360392
}
361393

@@ -440,3 +472,11 @@ func ParameterCtyType(typ string) (cty.Type, error) {
440472
return cty.Type{}, fmt.Errorf("unsupported type: %q", typ)
441473
}
442474
}
475+
476+
func toStrings[A ~string](l []A) []string {
477+
var r []string
478+
for _, v := range l {
479+
r = append(r, string(v))
480+
}
481+
return r
482+
}

extract/state.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
tfjson "github.com/hashicorp/terraform-json"
88

99
"github.com/coder/preview/types"
10+
"github.com/coder/terraform-provider-coder/v2/provider"
1011
)
1112

1213
func ParametersFromState(state *tfjson.StateModule) ([]types.Parameter, error) {
@@ -53,12 +54,13 @@ func ParameterFromState(block *tfjson.StateResource) (types.Parameter, error) {
5354

5455
param := types.Parameter{
5556
Value: types.StringLiteral(st.string("value")),
56-
RichParameter: types.RichParameter{
57+
ParameterData: types.ParameterData{
5758
Name: st.string("name"),
5859
Description: st.optionalString("description"),
5960
Type: types.ParameterType(st.optionalString("type")),
61+
FormType: provider.ParameterFormType(st.optionalString("form_type")),
6062
Mutable: st.optionalBool("mutable"),
61-
DefaultValue: st.optionalString("default"),
63+
DefaultValue: types.StringLiteral(st.optionalString("default")),
6264
Icon: st.optionalString("icon"),
6365
Options: options,
6466
Validations: validations,

go.mod

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/aquasecurity/trivy v0.58.2
88
github.com/coder/guts v1.0.2-0.20250227211802-139809366a22
99
github.com/coder/serpent v0.10.0
10-
github.com/coder/terraform-provider-coder/v2 v2.2.0
10+
github.com/coder/terraform-provider-coder/v2 v2.2.1-0.20250313232118-21ff5ae2bf31
1111
github.com/coder/websocket v1.8.12
1212
github.com/go-chi/chi v4.1.2+incompatible
1313
github.com/hashicorp/go-version v1.7.0
@@ -73,13 +73,17 @@ require (
7373
github.com/hashicorp/go-getter v1.7.8 // indirect
7474
github.com/hashicorp/go-hclog v1.6.3 // indirect
7575
github.com/hashicorp/go-multierror v1.1.1 // indirect
76+
github.com/hashicorp/go-plugin v1.6.2 // indirect
7677
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
7778
github.com/hashicorp/go-safetemp v1.0.0 // indirect
7879
github.com/hashicorp/go-uuid v1.0.3 // indirect
7980
github.com/hashicorp/logutils v1.0.0 // indirect
8081
github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect
8182
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
82-
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.0 // indirect
83+
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 // indirect
84+
github.com/hashicorp/terraform-registry-address v0.2.4 // indirect
85+
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
86+
github.com/hashicorp/yamux v0.1.1 // indirect
8387
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
8488
github.com/klauspost/compress v1.17.11 // indirect
8589
github.com/liamg/memoryfs v1.6.0 // indirect
@@ -95,6 +99,7 @@ require (
9599
github.com/mitchellh/reflectwalk v1.0.2 // indirect
96100
github.com/muesli/reflow v0.3.0 // indirect
97101
github.com/muesli/termenv v0.15.2 // indirect
102+
github.com/oklog/run v1.0.0 // indirect
98103
github.com/pion/transport/v2 v2.0.0 // indirect
99104
github.com/pion/udp v0.1.4 // indirect
100105
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect

go.sum

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,10 @@ github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx
720720
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc=
721721
github.com/coder/serpent v0.10.0 h1:ofVk9FJXSek+SmL3yVE3GoArP83M+1tX+H7S4t8BSuM=
722722
github.com/coder/serpent v0.10.0/go.mod h1:cZFW6/fP+kE9nd/oRkEHJpG6sXCtQ+AX7WMMEHv0Y3Q=
723-
github.com/coder/terraform-provider-coder/v2 v2.2.0 h1:VC29ALb/y/mgdN2+u+u6b5qmHPH/MJIO7FjjBbT3YaY=
724-
github.com/coder/terraform-provider-coder/v2 v2.2.0/go.mod h1:RHGyb+ghiy8UpDAMJM8duRFuzd+1VqA3AtkRLh2P3Ug=
723+
github.com/coder/terraform-provider-coder/v2 v2.2.1-0.20250313231042-bcfbf3924c60 h1:qOUvnd7Ea51eDXFEDNzX+/s/CGZUNPAYYTqo4Ccpyp4=
724+
github.com/coder/terraform-provider-coder/v2 v2.2.1-0.20250313231042-bcfbf3924c60/go.mod h1:X28s3rz+aEM5PkBKvk3xcUrQFO2eNPjzRChUg9wb70U=
725+
github.com/coder/terraform-provider-coder/v2 v2.2.1-0.20250313232118-21ff5ae2bf31 h1:JpDe/YonsKNbpbfruoniS/N0ozBV8Ekd67auoGOi3lY=
726+
github.com/coder/terraform-provider-coder/v2 v2.2.1-0.20250313232118-21ff5ae2bf31/go.mod h1:X28s3rz+aEM5PkBKvk3xcUrQFO2eNPjzRChUg9wb70U=
725727
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
726728
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
727729
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
@@ -980,8 +982,8 @@ github.com/hashicorp/terraform-plugin-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiy
980982
github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY=
981983
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
982984
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
983-
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.0 h1:7/iejAPyCRBhqAg3jOx+4UcAhY0A+Sg8B+0+d/GxSfM=
984-
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.0/go.mod h1:TiQwXAjFrgBf5tg5rvBRz8/ubPULpU0HjSaVi5UoJf8=
985+
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 h1:WNMsTLkZf/3ydlgsuXePa3jvZFwAJhruxTxP/c1Viuw=
986+
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1/go.mod h1:P6o64QS97plG44iFzSM6rAn6VJIC/Sy9a9IkEtl79K4=
985987
github.com/hashicorp/terraform-registry-address v0.2.4 h1:JXu/zHB2Ymg/TGVCRu10XqNa4Sh2bWcqCNyKWjnCPJA=
986988
github.com/hashicorp/terraform-registry-address v0.2.4/go.mod h1:tUNYTVyCtU4OIGXXMDp7WNcJ+0W1B4nmstVDgHMjfAU=
987989
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=

hclext/value.go

Lines changed: 0 additions & 14 deletions
This file was deleted.

internal/verify/cmp.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ func CompareParameters(t *testing.T, pr *preview.Output, values *tfjson.StateMod
3434

3535
types.SortParameters(stateParams)
3636
types.SortParameters(pr.Parameters)
37-
require.Len(t, pr.Parameters, len(stateParams), "number of parameters")
37+
passed = passed && assert.Len(t, pr.Parameters, len(stateParams), "number of parameters")
3838

3939
for i, param := range stateParams {
4040
adata, err := json.Marshal(param)
41-
require.NoError(t, err, "marshal parameter %q", param.Name)
41+
passed = passed && assert.NoError(t, err, "marshal parameter %q", param.Name)
4242

4343
bdata, err := json.Marshal(pr.Parameters[i])
44-
require.NoError(t, err, "marshal parameter %q", pr.Parameters[i].Name)
44+
passed = passed && assert.NoError(t, err, "marshal parameter %q", pr.Parameters[i].Name)
4545

46-
require.JSONEq(t, string(adata), string(bdata), "parameter %q", param.Name)
46+
passed = passed && assert.JSONEq(t, string(adata), string(bdata), "parameter %q", param.Name)
4747
}
4848

4949
return passed

preview_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/coder/preview"
1313
"github.com/coder/preview/types"
14+
"github.com/coder/terraform-provider-coder/v2/provider"
1415
)
1516

1617
func Test_Extract(t *testing.T) {
@@ -40,9 +41,10 @@ func Test_Extract(t *testing.T) {
4041
},
4142
unknownTags: []string{},
4243
params: map[string]assertParam{
43-
"Region": ap().value("us").
44+
"region": ap().value("us").
4445
def("us").
45-
optVals("us", "eu"),
46+
optVals("us", "eu").
47+
formType(provider.ParameterFormTypeRadio),
4648
"numerical": ap().value("5"),
4749
},
4850
},
@@ -369,6 +371,12 @@ func ap() assertParam {
369371
return func(t *testing.T, parameter types.Parameter) {}
370372
}
371373

374+
func (a assertParam) formType(exp provider.ParameterFormType) assertParam {
375+
return a.extend(func(t *testing.T, parameter types.Parameter) {
376+
assert.Equal(t, exp, parameter.FormType, "parameter form type equality check")
377+
})
378+
}
379+
372380
func (a assertParam) unknown() assertParam {
373381
return a.extend(func(t *testing.T, parameter types.Parameter) {
374382
assert.False(t, parameter.Value.IsKnown(), "parameter unknown check")

testdata/static/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Development
2+
3+
Currently, a branch of the coder terraform provider is required.
4+
5+
6+
1. Git clone `[email protected]:coder/terraform-provider-coder.git`
7+
- Checkout branch `stevenmasley/form_control`
8+
- Build the provider with `go build -o terraform-provider-coder`
9+
1. Create a file named `.terraformrc` in your `$HOME` directory
10+
1. Add the following content:
11+
12+
```hcl
13+
provider_installation {
14+
# Override the coder/coder provider to use your local version
15+
dev_overrides {
16+
"coder/coder" = "/path/to/terraform-provider-coder"
17+
}
18+
19+
# For all other providers, install them directly from their origin provider
20+
# registries as normal. If you omit this, Terraform will _only_ use
21+
# the dev_overrides block, and so no other providers will be available.
22+
direct {}
23+
}
24+
```
25+
26+
Now you are using the right terraform provider.

testdata/static/main.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ data "coder_workspace_tags" "custom_workspace_tags" {
1818
}
1919

2020
data "coder_parameter" "region" {
21-
name = "Region"
21+
name = "region"
2222
description = "Which region would you like to deploy to?"
2323
type = "string"
2424
default = "us"
@@ -40,4 +40,4 @@ data "coder_parameter" "numerical" {
4040
type = "number"
4141
default = 5
4242
order = 2
43-
}
43+
}

0 commit comments

Comments
 (0)