@@ -21,6 +21,15 @@ import (
21
21
"golang.org/x/xerrors"
22
22
)
23
23
24
+ type ValidationMode string
25
+
26
+ const (
27
+ // ValidationModeDefault is used for creating a workspace. It validates the
28
+ // final value used for a parameter.
29
+ ValidationModeDefault ValidationMode = ""
30
+ ValidationModeTemplateImport ValidationMode = "template-import"
31
+ )
32
+
24
33
var (
25
34
defaultValuePath = cty.Path {cty.GetAttrStep {Name : "default" }}
26
35
)
@@ -56,7 +65,7 @@ type Parameter struct {
56
65
Type OptionType
57
66
FormType ParameterFormType
58
67
Mutable bool
59
- Default string
68
+ Default * string
60
69
Icon string
61
70
Option []Option
62
71
Validation []Validation
@@ -105,10 +114,16 @@ func parameterDataSource() *schema.Resource {
105
114
Type : rd .Get ("type" ),
106
115
FormType : rd .Get ("form_type" ),
107
116
Mutable : rd .Get ("mutable" ),
108
- Default : rd .Get ("default" ),
109
- Icon : rd .Get ("icon" ),
110
- Option : rd .Get ("option" ),
111
- Validation : fixedValidation ,
117
+ Default : func () * string {
118
+ if rd .GetRawConfig ().AsValueMap ()["default" ].IsNull () {
119
+ return nil
120
+ }
121
+ val , _ := rd .Get ("default" ).(string )
122
+ return & val
123
+ }(),
124
+ Icon : rd .Get ("icon" ),
125
+ Option : rd .Get ("option" ),
126
+ Validation : fixedValidation ,
112
127
Optional : func () bool {
113
128
// This hack allows for checking if the "default" field is present in the .tf file.
114
129
// If "default" is missing or is "null", then it means that this field is required,
@@ -124,25 +139,6 @@ func parameterDataSource() *schema.Resource {
124
139
return diag .Errorf ("decode parameter: %s" , err )
125
140
}
126
141
127
- var value * string
128
- if ! rd .GetRawConfig ().AsValueMap ()["default" ].IsNull () {
129
- value = & parameter .Default
130
- }
131
-
132
- envValue , ok := os .LookupEnv (ParameterEnvironmentVariable (parameter .Name ))
133
- if ok {
134
- value = & envValue
135
- }
136
-
137
- if value != nil {
138
- rd .Set ("value" , value )
139
- } else {
140
- // Maintaining backwards compatibility. The previous behavior was
141
- // to write back an empty string.
142
- // TODO: Should an empty string exist if no value is set?
143
- rd .Set ("value" , "" )
144
- }
145
-
146
142
if ! parameter .Mutable && parameter .Ephemeral {
147
143
return diag .Errorf ("parameter can't be immutable and ephemeral" )
148
144
}
@@ -151,10 +147,17 @@ func parameterDataSource() *schema.Resource {
151
147
return diag .Errorf ("ephemeral parameter requires the default property" )
152
148
}
153
149
154
- diags := parameter .Valid (value )
150
+ var input * string
151
+ envValue , ok := os .LookupEnv (ParameterEnvironmentVariable (parameter .Name ))
152
+ if ok {
153
+ input = & envValue
154
+ }
155
+
156
+ value , diags := parameter .Valid (input , ValidationModeDefault )
155
157
if diags .HasError () {
156
158
return diags
157
159
}
160
+ rd .Set ("value" , value )
158
161
159
162
// Set the form_type as it could have changed in the validation.
160
163
rd .Set ("form_type" , parameter .FormType )
@@ -397,15 +400,20 @@ func valueIsType(typ OptionType, value string) error {
397
400
return nil
398
401
}
399
402
400
- func (v * Parameter ) Valid (value * string ) diag.Diagnostics {
403
+ func (v * Parameter ) Valid (input * string , mode ValidationMode ) ( string , diag.Diagnostics ) {
401
404
var err error
402
405
var optionType OptionType
403
406
407
+ value := input
408
+ if input == nil {
409
+ value = v .Default
410
+ }
411
+
404
412
// optionType might differ from parameter.Type. This is ok, and parameter.Type
405
413
// should be used for the value type, and optionType for options.
406
414
optionType , v .FormType , err = ValidateFormType (v .Type , len (v .Option ), v .FormType )
407
415
if err != nil {
408
- return diag.Diagnostics {
416
+ return "" , diag.Diagnostics {
409
417
{
410
418
Severity : diag .Error ,
411
419
Summary : "Invalid form_type for parameter" ,
@@ -417,28 +425,28 @@ func (v *Parameter) Valid(value *string) diag.Diagnostics {
417
425
418
426
optionValues , diags := v .ValidOptions (optionType )
419
427
if diags .HasError () {
420
- return diags
428
+ return "" , diags
421
429
}
422
430
423
- // TODO: The default value should also be validated
424
- //if v.Default != "" {
425
- // err := valueIsType(v.Type, v.Default)
426
- // if err != nil {
427
- // return diag.Diagnostics{
428
- // {
429
- // Severity: diag.Error,
430
- // Summary: fmt.Sprintf("Default value is not of type %q", v.Type),
431
- // Detail: err.Error(),
432
- // AttributePath: defaultValuePath,
433
- // },
434
- // }
435
- // }
436
- //
437
- // d := v.validValue(v.Default, optionType, optionValues, defaultValuePath)
438
- // if d.HasError() {
439
- // return d
440
- // }
441
- // }
431
+ if mode == ValidationModeTemplateImport && v . Default != nil {
432
+ // Template import should validate the default value.
433
+ err := valueIsType (v .Type , * v .Default )
434
+ if err != nil {
435
+ return "" , diag.Diagnostics {
436
+ {
437
+ Severity : diag .Error ,
438
+ Summary : fmt .Sprintf ("Default value is not of type %q" , v .Type ),
439
+ Detail : err .Error (),
440
+ AttributePath : defaultValuePath ,
441
+ },
442
+ }
443
+ }
444
+
445
+ d := v .validValue (* v .Default , optionType , optionValues , defaultValuePath )
446
+ if d .HasError () {
447
+ return "" , d
448
+ }
449
+ }
442
450
443
451
// TODO: Move this into 'Parameter.validValue'. It exists as another check outside because
444
452
// the current behavior is to always apply this validation, regardless if the param is set or not.
@@ -453,7 +461,7 @@ func (v *Parameter) Valid(value *string) diag.Diagnostics {
453
461
validCheck := & v .Validation [0 ]
454
462
err := validCheck .Valid (v .Type , * validVal )
455
463
if err != nil {
456
- return diag.Diagnostics {
464
+ return "" , diag.Diagnostics {
457
465
{
458
466
Severity : diag .Error ,
459
467
Summary : fmt .Sprintf ("Invalid parameter %s according to 'validation' block" , strings .ToLower (v .Name )),
@@ -464,17 +472,26 @@ func (v *Parameter) Valid(value *string) diag.Diagnostics {
464
472
}
465
473
}
466
474
475
+ // TODO: This is a bit of a hack. The current behavior states if validation
476
+ // is given, then apply validation to unset values.
477
+ // This should be removed, and all values should be validated. Meaning
478
+ // value == nil should not be accepted in the first place.
479
+ if len (v .Validation ) > 0 && value == nil {
480
+ empty := ""
481
+ value = & empty
482
+ }
483
+
467
484
// Value is only validated if it is set. If it is unset, validation
468
485
// is skipped.
469
486
if value != nil {
470
487
d := v .validValue (* value , optionType , optionValues , cty.Path {})
471
488
if d .HasError () {
472
- return d
489
+ return "" , d
473
490
}
474
491
475
492
err = valueIsType (v .Type , * value )
476
493
if err != nil {
477
- return diag.Diagnostics {
494
+ return "" , diag.Diagnostics {
478
495
{
479
496
Severity : diag .Error ,
480
497
Summary : fmt .Sprintf ("Parameter value is not of type %q" , v .Type ),
@@ -484,7 +501,12 @@ func (v *Parameter) Valid(value *string) diag.Diagnostics {
484
501
}
485
502
}
486
503
487
- return nil
504
+ if value == nil {
505
+ // The previous behavior is to always write an empty string
506
+ return "" , nil
507
+ }
508
+
509
+ return * value , nil
488
510
}
489
511
490
512
func (v * Parameter ) ValidOptions (optionType OptionType ) (map [string ]struct {}, diag.Diagnostics ) {
@@ -598,6 +620,21 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
598
620
}
599
621
}
600
622
623
+ if len (v .Validation ) == 1 {
624
+ validCheck := & v .Validation [0 ]
625
+ err := validCheck .Valid (v .Type , value )
626
+ if err != nil {
627
+ return diag.Diagnostics {
628
+ {
629
+ Severity : diag .Error ,
630
+ Summary : fmt .Sprintf ("Invalid parameter %s according to 'validation' block" , strings .ToLower (v .Name )),
631
+ Detail : err .Error (),
632
+ AttributePath : cty.Path {},
633
+ },
634
+ }
635
+ }
636
+ }
637
+
601
638
return nil
602
639
}
603
640
0 commit comments