From cfc201786f178771879c55fba710f933afab3c23 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 14 Apr 2025 11:37:19 -0500 Subject: [PATCH 01/14] chore: bad default values should throw errors --- cli/clidisplay/resources.go | 6 ++- extract/parameter.go | 7 +++- go.mod | 2 +- go.sum | 4 +- types/convert.go | 73 +++++++++++++++++++++++++++++++++++++ types/enum.go | 5 ++- types/parameter.go | 40 ++++++++++++++------ 7 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 types/convert.go diff --git a/cli/clidisplay/resources.go b/cli/clidisplay/resources.go index ac93025..aad72ba 100644 --- a/cli/clidisplay/resources.go +++ b/cli/clidisplay/resources.go @@ -77,9 +77,13 @@ func Parameters(writer io.Writer, params []types.Parameter, files map[string]*hc //} else { // strVal = value.GoString() //} + dp := p.DisplayName + if p.DisplayName == "" { + dp = p.Name + } tableWriter.AppendRow(table.Row{ - fmt.Sprintf("(%s) %s: %s\n%s", p.DisplayName, p.Name, p.Description, formatOptions(selections, p.Options)), + fmt.Sprintf("(%s) %s: %s\n%s", dp, p.Name, p.Description, formatOptions(selections, p.Options)), }) if hcl.Diagnostics(p.Diagnostics).HasErrors() { diff --git a/extract/parameter.go b/extract/parameter.go index 3851635..faae374 100644 --- a/extract/parameter.go +++ b/extract/parameter.go @@ -173,6 +173,12 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti } } + if !diags.HasErrors() { + // Only do this validation if the parameter is valid, as if some errors + // exist, then this is likely to fail be excess information. + diags = diags.Extend(p.Valid()) + } + usageDiags := ParameterUsageDiagnostics(p) if usageDiags.HasErrors() { p.FormType = provider.ParameterFormTypeError @@ -243,7 +249,6 @@ func ParameterValidationFromBlock(block *terraform.Block) (types.ParameterValida Min: nullableInteger(block, "min"), Max: nullableInteger(block, "max"), Monotonic: nullableString(block, "monotonic"), - Invalid: nullableBoolean(block, "invalid"), } return p, diags diff --git a/go.mod b/go.mod index 84a2d03..8a0ca87 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/aquasecurity/trivy v0.58.2 github.com/coder/guts v1.0.2-0.20250227211802-139809366a22 github.com/coder/serpent v0.10.0 - github.com/coder/terraform-provider-coder/v2 v2.4.0-pre0 + github.com/coder/terraform-provider-coder/v2 v2.4.0-pre0.0.20250414140516-f66adaca2adf github.com/coder/websocket v1.8.13 github.com/go-chi/chi v4.1.2+incompatible github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 68ca4f3..72803d2 100644 --- a/go.sum +++ b/go.sum @@ -718,8 +718,8 @@ github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= github.com/coder/serpent v0.10.0 h1:ofVk9FJXSek+SmL3yVE3GoArP83M+1tX+H7S4t8BSuM= github.com/coder/serpent v0.10.0/go.mod h1:cZFW6/fP+kE9nd/oRkEHJpG6sXCtQ+AX7WMMEHv0Y3Q= -github.com/coder/terraform-provider-coder/v2 v2.4.0-pre0 h1:NPt2+FVr+2QJoxrta5ZwyTaxocWMEKdh2WpIumffxfM= -github.com/coder/terraform-provider-coder/v2 v2.4.0-pre0/go.mod h1:X28s3rz+aEM5PkBKvk3xcUrQFO2eNPjzRChUg9wb70U= +github.com/coder/terraform-provider-coder/v2 v2.4.0-pre0.0.20250414140516-f66adaca2adf h1:h0ZMBLv/NiHeMWANSiUKHZkuxti4zIWCGXAXYF0tuJQ= +github.com/coder/terraform-provider-coder/v2 v2.4.0-pre0.0.20250414140516-f66adaca2adf/go.mod h1:X28s3rz+aEM5PkBKvk3xcUrQFO2eNPjzRChUg9wb70U= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= diff --git a/types/convert.go b/types/convert.go new file mode 100644 index 0000000..f8ee948 --- /dev/null +++ b/types/convert.go @@ -0,0 +1,73 @@ +package types + +import ( + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + + "github.com/coder/terraform-provider-coder/v2/provider" +) + +func providerValidations(vals []*ParameterValidation) []provider.Validation { + cpy := make([]provider.Validation, 0, len(vals)) + for _, val := range vals { + cpy = append(cpy, providerValidation(val)) + } + return cpy +} + +func providerValidation(v *ParameterValidation) provider.Validation { + return provider.Validation{ + Min: int(orZero(v.Min)), + MinDisabled: v.Min == nil, + Max: int(orZero(v.Max)), + MaxDisabled: v.Max == nil, + Monotonic: orZero(v.Monotonic), + Regex: orZero(v.Regex), + Error: v.Error, + } +} + +func providerOptions(opts []*ParameterOption) []provider.Option { + cpy := make([]provider.Option, 0, len(opts)) + for _, opt := range opts { + cpy = append(cpy, providerOption(opt)) + } + return cpy +} + +func providerOption(opt *ParameterOption) provider.Option { + return provider.Option{ + Name: opt.Name, + Description: opt.Description, + Value: opt.Value.AsString(), + Icon: opt.Icon, + } +} + +func hclDiagnostics(diagnostics diag.Diagnostics) hcl.Diagnostics { + cpy := make(hcl.Diagnostics, 0, len(diagnostics)) + for _, d := range diagnostics { + cpy = append(cpy, hclDiagnostic(d)) + } + return cpy +} + +func hclDiagnostic(d diag.Diagnostic) *hcl.Diagnostic { + sev := hcl.DiagInvalid + switch d.Severity { + case diag.Error: + sev = hcl.DiagError + case diag.Warning: + sev = hcl.DiagWarning + } + return &hcl.Diagnostic{ + Severity: sev, + Summary: d.Summary, + Detail: d.Detail, + Subject: nil, + Context: nil, + Expression: nil, + EvalContext: nil, + Extra: nil, + } +} diff --git a/types/enum.go b/types/enum.go index ab29945..190668b 100644 --- a/types/enum.go +++ b/types/enum.go @@ -3,9 +3,12 @@ package types import ( "fmt" "strings" + + "github.com/coder/terraform-provider-coder/v2/provider" ) -type ParameterType string +// TODO: Just use the provider type directly. +type ParameterType provider.OptionType const ( ParameterTypeString ParameterType = "string" diff --git a/types/parameter.go b/types/parameter.go index 6f58ff2..90f8c79 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/aquasecurity/trivy/pkg/iac/terraform" + "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" "github.com/coder/terraform-provider-coder/v2/provider" @@ -73,24 +74,16 @@ type ParameterValidation struct { Min *int64 `json:"validation_min"` Max *int64 `json:"validation_max"` Monotonic *string `json:"validation_monotonic"` - Invalid *bool `json:"validation_invalid"` } // Valid takes the type of the value and the value itself and returns an error // if the value is invalid. -func (v ParameterValidation) Valid(typ string, value string) error { +func (v *ParameterValidation) Valid(typ string, value string) error { // TODO: Validate typ is the enum? // Use the provider.Validation struct to validate the value to be // consistent with the provider. - return (&provider.Validation{ - Min: int(orZero(v.Min)), - MinDisabled: v.Min == nil, - Max: int(orZero(v.Max)), - MaxDisabled: v.Max == nil, - Monotonic: orZero(v.Monotonic), - Regex: orZero(v.Regex), - Error: v.Error, - }).Valid(provider.OptionType(typ), value) + pv := providerValidation(v) + return (&pv).Valid(provider.OptionType(typ), value) } type ParameterOption struct { @@ -100,6 +93,31 @@ type ParameterOption struct { Icon string `json:"icon"` } +func (r *ParameterData) Valid() hcl.Diagnostics { + diag := (&provider.Parameter{ + Name: r.Name, + DisplayName: r.DisplayName, + Description: r.Description, + Type: provider.OptionType(r.Type), + FormType: r.FormType, + Mutable: r.Mutable, + Default: r.DefaultValue.AsString(), + Icon: r.Icon, + Option: providerOptions(r.Options), + Validation: providerValidations(r.Validations), + Optional: false, + Order: int(r.Order), + Ephemeral: r.Ephemeral, + }).Valid() + + if diag.HasError() { + // TODO: We can take the attr path and decorate the error with + // source information. + return hclDiagnostics(diag) + } + return nil +} + // CtyType returns the cty.Type for the ParameterData. // A fixed set of types are supported. func (r *ParameterData) CtyType() (cty.Type, error) { From db2467948150ac3882b685334c962b0434be1b0c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 16 Apr 2025 09:55:12 -0500 Subject: [PATCH 02/14] add more error context --- types/convert.go | 30 ++++++++++++++++++++++++++---- types/parameter.go | 2 +- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/types/convert.go b/types/convert.go index f8ee948..354db93 100644 --- a/types/convert.go +++ b/types/convert.go @@ -1,8 +1,15 @@ package types import ( + "fmt" + + "github.com/aquasecurity/trivy/pkg/iac/terraform" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/zclconf/go-cty/cty" + hcty "github.com/hashicorp/go-cty/cty" + hctyjson "github.com/hashicorp/go-cty/cty/json" + ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/coder/terraform-provider-coder/v2/provider" ) @@ -44,15 +51,15 @@ func providerOption(opt *ParameterOption) provider.Option { } } -func hclDiagnostics(diagnostics diag.Diagnostics) hcl.Diagnostics { +func hclDiagnostics(diagnostics diag.Diagnostics, source *terraform.Block) hcl.Diagnostics { cpy := make(hcl.Diagnostics, 0, len(diagnostics)) for _, d := range diagnostics { - cpy = append(cpy, hclDiagnostic(d)) + cpy = append(cpy, hclDiagnostic(d, source)) } return cpy } -func hclDiagnostic(d diag.Diagnostic) *hcl.Diagnostic { +func hclDiagnostic(d diag.Diagnostic, source *terraform.Block) *hcl.Diagnostic { sev := hcl.DiagInvalid switch d.Severity { case diag.Error: @@ -60,11 +67,26 @@ func hclDiagnostic(d diag.Diagnostic) *hcl.Diagnostic { case diag.Warning: sev = hcl.DiagWarning } + + // This is an imperfect way to finding the source code of the error. There is 2 + // different `cty` types at place here, the hashicorp fork and the original. So a + // more general solution is difficult. This is good enough for now to add more + // context to an error. + var subject *hcl.Range + if len(d.AttributePath) == 1 && source != nil { + if attr, ok := d.AttributePath[0].(hcty.GetAttrStep); ok { + src := source.GetAttribute(attr.Name) + if src != nil { + subject = &(src.HCLAttribute().Range) + } + } + } + return &hcl.Diagnostic{ Severity: sev, Summary: d.Summary, Detail: d.Detail, - Subject: nil, + Subject: subject, Context: nil, Expression: nil, EvalContext: nil, diff --git a/types/parameter.go b/types/parameter.go index 90f8c79..ffa0504 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -113,7 +113,7 @@ func (r *ParameterData) Valid() hcl.Diagnostics { if diag.HasError() { // TODO: We can take the attr path and decorate the error with // source information. - return hclDiagnostics(diag) + return hclDiagnostics(diag, source) } return nil } From ed766d08f9cd3056fda489494df8051b91f0a3fa Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 16 Apr 2025 09:55:33 -0500 Subject: [PATCH 03/14] add more error context --- types/convert.go | 7 +------ types/parameter.go | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/types/convert.go b/types/convert.go index 354db93..3d395e5 100644 --- a/types/convert.go +++ b/types/convert.go @@ -1,15 +1,10 @@ package types import ( - "fmt" - "github.com/aquasecurity/trivy/pkg/iac/terraform" + hcty "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/zclconf/go-cty/cty" - hcty "github.com/hashicorp/go-cty/cty" - hctyjson "github.com/hashicorp/go-cty/cty/json" - ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/coder/terraform-provider-coder/v2/provider" ) diff --git a/types/parameter.go b/types/parameter.go index ffa0504..cf2565b 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -113,7 +113,7 @@ func (r *ParameterData) Valid() hcl.Diagnostics { if diag.HasError() { // TODO: We can take the attr path and decorate the error with // source information. - return hclDiagnostics(diag, source) + return hclDiagnostics(diag, r.Source) } return nil } From 3fcc847522f0f7c402fc84bb52bf1f0dd545caf7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 10:40:53 -0500 Subject: [PATCH 04/14] terraform provider dep update --- go.mod | 6 +++--- go.sum | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 7ea2251..ea18140 100644 --- a/go.mod +++ b/go.mod @@ -7,14 +7,16 @@ require ( github.com/aquasecurity/trivy v0.58.2 github.com/coder/guts v1.0.2-0.20250227211802-139809366a22 github.com/coder/serpent v0.10.0 - github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250417100258-c86bb5c3ddcd + github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250502142605-22f12cb73817 github.com/coder/websocket v1.8.13 github.com/go-chi/chi v4.1.2+incompatible + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/hc-install v0.9.2 github.com/hashicorp/hcl/v2 v2.23.0 github.com/hashicorp/terraform-exec v0.23.0 github.com/hashicorp/terraform-json v0.24.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 github.com/jedib0t/go-pretty/v6 v6.6.7 github.com/stretchr/testify v1.10.0 github.com/zclconf/go-cty v1.16.2 @@ -69,7 +71,6 @@ require ( github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-getter v1.7.8 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -79,7 +80,6 @@ require ( github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect - github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect diff --git a/go.sum b/go.sum index c22d751..81daf7e 100644 --- a/go.sum +++ b/go.sum @@ -718,8 +718,8 @@ github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= github.com/coder/serpent v0.10.0 h1:ofVk9FJXSek+SmL3yVE3GoArP83M+1tX+H7S4t8BSuM= github.com/coder/serpent v0.10.0/go.mod h1:cZFW6/fP+kE9nd/oRkEHJpG6sXCtQ+AX7WMMEHv0Y3Q= -github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250417100258-c86bb5c3ddcd h1:FsIG6Fd0YOEK7D0Hl/CJywRA+Y6Gd5RQbSIa2L+/BmE= -github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250417100258-c86bb5c3ddcd/go.mod h1:56/KdGYaA+VbwXJbTI8CA57XPfnuTxN8rjxbR34PbZw= +github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250502142605-22f12cb73817 h1:4ryzTbgCe+AHk/G03y48Rtg3Y+pfr5FfhGt/eEckJ6g= +github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250502142605-22f12cb73817/go.mod h1:56/KdGYaA+VbwXJbTI8CA57XPfnuTxN8rjxbR34PbZw= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= From 1a0b055a14938448e34705a5625e454ecf90af7b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 15:36:58 -0500 Subject: [PATCH 05/14] accept empty default values --- extract/parameter.go | 51 +++++++++-------------------------- go.mod | 2 +- go.sum | 4 +-- preview_test.go | 10 +++++++ testdata/emptydefault/main.tf | 26 ++++++++++++++++++ types/parameter.go | 21 ++++++++++++--- types/value.go | 8 ++++++ 7 files changed, 77 insertions(+), 45 deletions(-) create mode 100644 testdata/emptydefault/main.tf diff --git a/extract/parameter.go b/extract/parameter.go index faae374..b5b3b05 100644 --- a/extract/parameter.go +++ b/extract/parameter.go @@ -50,7 +50,7 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti pVal := richParameterValue(block) - def := types.StringLiteral("") + def := types.NullString() defAttr := block.GetAttribute("default") if !defAttr.IsNil() { def = types.ToHCLString(block, defAttr) @@ -138,45 +138,10 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti p.Validations = append(p.Validations, &valid) } - ctyType, err := p.CtyType() - if err != nil { - paramTypeDiag := &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Invalid parameter type %q", p.Type), - Detail: err.Error(), - Context: &block.HCLBlock().DefRange, - } - - if attr := block.GetAttribute("type"); attr != nil && !attr.IsNil() { - paramTypeDiag.Subject = &attr.HCLAttribute().Range - paramTypeDiag.Expression = attr.HCLAttribute().Expr - paramTypeDiag.EvalContext = block.Context().Inner() - } - diags = diags.Append(paramTypeDiag) - p.FormType = provider.ParameterFormTypeError - } - - if ctyType != cty.NilType && pVal.Value.Type().Equals(cty.String) { - // TODO: Wish we could support more types, but only string types are - // allowed. - valStr := pVal.Value.AsString() - // Apply validations to the parameter value - for _, v := range p.Validations { - if err := v.Valid(string(pType), valStr); err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Paramater validation failed for value %q", valStr), - Detail: err.Error(), - Expression: pVal.ValueExpr, - }) - } - } - } - if !diags.HasErrors() { // Only do this validation if the parameter is valid, as if some errors // exist, then this is likely to fail be excess information. - diags = diags.Extend(p.Valid()) + diags = diags.Extend(p.Valid(p.Value)) } usageDiags := ParameterUsageDiagnostics(p) @@ -194,7 +159,9 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti func ParameterUsageDiagnostics(p types.Parameter) hcl.Diagnostics { valErr := "The value of a parameter is required to be sourced (default or input) for the parameter to function." var diags hcl.Diagnostics - if !p.Value.Valid() { + if p.Value.Value.IsNull() { + // Allow null values + } else if !p.Value.Valid() { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Parameter value is not valid", @@ -478,6 +445,14 @@ func richParameterValue(block *terraform.Block) types.HCLString { val, diags := valRef.Value(block.Context().Inner()) source := hclext.CreateDotReferenceFromTraversal(valRef.Traversal) + + // If no value attribute exists, then the value is `null`. + if diags.HasErrors() && diags[0].Summary == "Unsupported attribute" { + s := types.NullString() + s.Source = &source + return s + } + return types.HCLString{ Value: val, ValueDiags: diags, diff --git a/go.mod b/go.mod index ea18140..f332927 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/aquasecurity/trivy v0.58.2 github.com/coder/guts v1.0.2-0.20250227211802-139809366a22 github.com/coder/serpent v0.10.0 - github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250502142605-22f12cb73817 + github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250505161541-0fd96eeace73 github.com/coder/websocket v1.8.13 github.com/go-chi/chi v4.1.2+incompatible github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 diff --git a/go.sum b/go.sum index 81daf7e..e797eda 100644 --- a/go.sum +++ b/go.sum @@ -718,8 +718,8 @@ github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= github.com/coder/serpent v0.10.0 h1:ofVk9FJXSek+SmL3yVE3GoArP83M+1tX+H7S4t8BSuM= github.com/coder/serpent v0.10.0/go.mod h1:cZFW6/fP+kE9nd/oRkEHJpG6sXCtQ+AX7WMMEHv0Y3Q= -github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250502142605-22f12cb73817 h1:4ryzTbgCe+AHk/G03y48Rtg3Y+pfr5FfhGt/eEckJ6g= -github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250502142605-22f12cb73817/go.mod h1:56/KdGYaA+VbwXJbTI8CA57XPfnuTxN8rjxbR34PbZw= +github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250505161541-0fd96eeace73 h1:Gax/pSsln9cSTueP5teoWM4EPqEux4BUp7VlECiuW2M= +github.com/coder/terraform-provider-coder/v2 v2.4.0-pre1.0.20250505161541-0fd96eeace73/go.mod h1:2kaBpn5k9ZWtgKq5k4JbkVZG9DzEqR4mJSmpdshcO+s= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= diff --git a/preview_test.go b/preview_test.go index 0c91d36..f0061e3 100644 --- a/preview_test.go +++ b/preview_test.go @@ -211,6 +211,16 @@ func Test_Extract(t *testing.T) { unknownTags: []string{}, params: map[string]assertParam{}, }, + { + name: "empty default", + dir: "emptydefault", + expTags: map[string]string{}, + input: preview.Input{}, + unknownTags: []string{}, + params: map[string]assertParam{ + "word": ap(), + }, + }, { name: "many modules", dir: "manymodules", diff --git a/testdata/emptydefault/main.tf b/testdata/emptydefault/main.tf new file mode 100644 index 0000000..baa398e --- /dev/null +++ b/testdata/emptydefault/main.tf @@ -0,0 +1,26 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + version = "2.4.0-pre0" + } + } +} + +data "coder_parameter" "word" { + name = "word" + description = "Select something" + type = "string" + order = 1 + # No default selected + + option { + name = "Bird" + value = "bird" + description = "An animal that can fly." + } + option { + name = "Boat" + value = "boat" + } +} diff --git a/types/parameter.go b/types/parameter.go index cf2565b..ebb8988 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -93,22 +93,35 @@ type ParameterOption struct { Icon string `json:"icon"` } -func (r *ParameterData) Valid() hcl.Diagnostics { - diag := (&provider.Parameter{ +func (r *ParameterData) Valid(value HCLString) hcl.Diagnostics { + var defPtr *string + if !r.DefaultValue.Value.IsNull() { + def := r.DefaultValue.Value.AsString() + defPtr = &def + } + + var valuePtr *string + // TODO: What to do if it is not valid? + if value.Valid() { + val := value.Value.AsString() + valuePtr = &val + } + + _, diag := (&provider.Parameter{ Name: r.Name, DisplayName: r.DisplayName, Description: r.Description, Type: provider.OptionType(r.Type), FormType: r.FormType, Mutable: r.Mutable, - Default: r.DefaultValue.AsString(), + Default: defPtr, Icon: r.Icon, Option: providerOptions(r.Options), Validation: providerValidations(r.Validations), Optional: false, Order: int(r.Order), Ephemeral: r.Ephemeral, - }).Valid() + }).ValidateInput(valuePtr) if diag.HasError() { // TODO: We can take the attr path and decorate the error with diff --git a/types/value.go b/types/value.go index 4cc269b..4e22b91 100644 --- a/types/value.go +++ b/types/value.go @@ -73,6 +73,14 @@ func StringLiteral(s string) HCLString { } } +func NullString() HCLString { + v := cty.NullVal(cty.String) + return HCLString{ + Value: v, + ValueExpr: &hclsyntax.LiteralValueExpr{Val: v}, + } +} + // AsString is a safe function. It will always return a string. // The caller should check if this value is Valid and known before // calling this function. From 4f4463b504ea8be125d3e70ed3246ed4d404aa9e Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 15:40:27 -0500 Subject: [PATCH 06/14] populate required field --- extract/parameter.go | 4 +++- extract/state.go | 2 +- types/parameter.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/extract/parameter.go b/extract/parameter.go index b5b3b05..86b09f6 100644 --- a/extract/parameter.go +++ b/extract/parameter.go @@ -50,10 +50,12 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti pVal := richParameterValue(block) + requiredValue := true def := types.NullString() defAttr := block.GetAttribute("default") if !defAttr.IsNil() { def = types.ToHCLString(block, defAttr) + requiredValue = false } ftmeta := optionalString(block, "styling") @@ -77,7 +79,7 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti Icon: optionalString(block, "icon"), Options: make([]*types.ParameterOption, 0), Validations: make([]*types.ParameterValidation, 0), - Required: optionalBoolean(block, "required"), + Required: requiredValue, DisplayName: optionalString(block, "display_name"), Order: optionalInteger(block, "order"), Ephemeral: optionalBoolean(block, "ephemeral"), diff --git a/extract/state.go b/extract/state.go index bb1a942..905cf6a 100644 --- a/extract/state.go +++ b/extract/state.go @@ -74,7 +74,7 @@ func ParameterFromState(block *tfjson.StateResource) (types.Parameter, error) { Icon: st.optionalString("icon"), Options: options, Validations: validations, - Required: st.optionalBool("required"), + Required: !st.optionalBool("optional"), DisplayName: st.optionalString("display_name"), Order: st.optionalInteger("order"), Ephemeral: st.optionalBool("ephemeral"), diff --git a/types/parameter.go b/types/parameter.go index ebb8988..a155b03 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -118,7 +118,7 @@ func (r *ParameterData) Valid(value HCLString) hcl.Diagnostics { Icon: r.Icon, Option: providerOptions(r.Options), Validation: providerValidations(r.Validations), - Optional: false, + Optional: !r.Required, Order: int(r.Order), Ephemeral: r.Ephemeral, }).ValidateInput(valuePtr) From dd15cc786002df7c92b23718d50b1179441073cb Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 15:44:00 -0500 Subject: [PATCH 07/14] make gen --- site/src/types/preview.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/types/preview.ts b/site/src/types/preview.ts index 945c7e1..1ab72e5 100644 --- a/site/src/types/preview.ts +++ b/site/src/types/preview.ts @@ -67,7 +67,6 @@ export interface ParameterValidation { readonly validation_min: number | null; readonly validation_max: number | null; readonly validation_monotonic: string | null; - readonly validation_invalid: boolean | null; } // From web/session.go From 5104c529d6309b55a724dbc780097420ea8ba5ed Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 15:48:10 -0500 Subject: [PATCH 08/14] fixup default usage --- types/parameter.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/parameter.go b/types/parameter.go index a155b03..700b42e 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -95,7 +95,8 @@ type ParameterOption struct { func (r *ParameterData) Valid(value HCLString) hcl.Diagnostics { var defPtr *string - if !r.DefaultValue.Value.IsNull() { + + if r.DefaultValue.Valid() && r.DefaultValue.IsKnown() { def := r.DefaultValue.Value.AsString() defPtr = &def } From 3ba59d2fb72acdc5f5b330489b21c800f445ddc7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 16:05:08 -0500 Subject: [PATCH 09/14] fix AsString reference --- cli/clidisplay/resources.go | 2 +- types/parameter.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/clidisplay/resources.go b/cli/clidisplay/resources.go index aad72ba..dea4ba9 100644 --- a/cli/clidisplay/resources.go +++ b/cli/clidisplay/resources.go @@ -30,7 +30,7 @@ func WorkspaceTags(writer io.Writer, tags types.TagBlocks) hcl.Diagnostics { k, v := tag.AsStrings() tableWriter.AppendRow(table.Row{k, v, ""}) continue - //diags = diags.Extend(tDiags) + // diags = diags.Extend(tDiags) //if !diags.HasErrors() { // tableWriter.AppendRow(table.Row{k, v, ""}) // continue diff --git a/types/parameter.go b/types/parameter.go index 700b42e..6eeee34 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -97,7 +97,7 @@ func (r *ParameterData) Valid(value HCLString) hcl.Diagnostics { var defPtr *string if r.DefaultValue.Valid() && r.DefaultValue.IsKnown() { - def := r.DefaultValue.Value.AsString() + def := r.DefaultValue.AsString() defPtr = &def } From 0876a7db87f0735012c1122215fe4dace7b3d08b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 17:11:06 -0500 Subject: [PATCH 10/14] fix AsString to account for other types --- types/parameter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/parameter.go b/types/parameter.go index 6eeee34..e390bfd 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -104,7 +104,7 @@ func (r *ParameterData) Valid(value HCLString) hcl.Diagnostics { var valuePtr *string // TODO: What to do if it is not valid? if value.Valid() { - val := value.Value.AsString() + val := value.AsString() valuePtr = &val } From b20a5bcc182f543dcf5feb2be1f2140eaf0de18e Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 17:13:52 -0500 Subject: [PATCH 11/14] add skip until provider validation is merged and released --- testdata/emptydefault/skipe2e | 1 + 1 file changed, 1 insertion(+) create mode 100644 testdata/emptydefault/skipe2e diff --git a/testdata/emptydefault/skipe2e b/testdata/emptydefault/skipe2e new file mode 100644 index 0000000..63ecd04 --- /dev/null +++ b/testdata/emptydefault/skipe2e @@ -0,0 +1 @@ +Skipping until https://github.com/coder/terraform-provider-coder/pull/381 is merged and released \ No newline at end of file From 6cf3c102c2ac8f19d5d78eb88166a4c1402acf85 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 5 May 2025 17:24:53 -0500 Subject: [PATCH 12/14] remove unused code --- types/parameter.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/types/parameter.go b/types/parameter.go index e390bfd..91cd8ee 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -76,16 +76,6 @@ type ParameterValidation struct { Monotonic *string `json:"validation_monotonic"` } -// Valid takes the type of the value and the value itself and returns an error -// if the value is invalid. -func (v *ParameterValidation) Valid(typ string, value string) error { - // TODO: Validate typ is the enum? - // Use the provider.Validation struct to validate the value to be - // consistent with the provider. - pv := providerValidation(v) - return (&pv).Valid(provider.OptionType(typ), value) -} - type ParameterOption struct { Name string `json:"name"` Description string `json:"description"` From 154d86b5a92a201f86a68de03816c507ef1ab292 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 6 May 2025 14:53:23 -0500 Subject: [PATCH 13/14] update provider --- types/parameter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/parameter.go b/types/parameter.go index 91cd8ee..5195bcf 100644 --- a/types/parameter.go +++ b/types/parameter.go @@ -112,7 +112,7 @@ func (r *ParameterData) Valid(value HCLString) hcl.Diagnostics { Optional: !r.Required, Order: int(r.Order), Ephemeral: r.Ephemeral, - }).ValidateInput(valuePtr) + }).ValidateInput(valuePtr, nil) // TODO: Pass in previous value if diag.HasError() { // TODO: We can take the attr path and decorate the error with From 753772a63f1520712e5b75ecbf05e777552dd465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=B1=E3=82=A4=E3=83=A9?= Date: Tue, 6 May 2025 16:37:43 -0600 Subject: [PATCH 14/14] Update testdata/emptydefault/skipe2e --- testdata/emptydefault/skipe2e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/emptydefault/skipe2e b/testdata/emptydefault/skipe2e index 63ecd04..bc530d8 100644 --- a/testdata/emptydefault/skipe2e +++ b/testdata/emptydefault/skipe2e @@ -1 +1 @@ -Skipping until https://github.com/coder/terraform-provider-coder/pull/381 is merged and released \ No newline at end of file +Skipping until https://github.com/coder/terraform-provider-coder/pull/381 is merged and released