@@ -144,7 +144,13 @@ func parameterDataSource() *schema.Resource {
144
144
input = & envValue
145
145
}
146
146
147
- value , diags := parameter .ValidateInput (input )
147
+ var previous * string
148
+ envPreviousValue , ok := os .LookupEnv (ParameterEnvironmentVariablePrevious (parameter .Name ))
149
+ if ok {
150
+ previous = & envPreviousValue
151
+ }
152
+
153
+ value , diags := parameter .ValidateInput (input , previous )
148
154
if diags .HasError () {
149
155
return diags
150
156
}
@@ -393,7 +399,7 @@ func valueIsType(typ OptionType, value string) error {
393
399
return nil
394
400
}
395
401
396
- func (v * Parameter ) ValidateInput (input * string ) (string , diag.Diagnostics ) {
402
+ func (v * Parameter ) ValidateInput (input * string , previous * string ) (string , diag.Diagnostics ) {
397
403
var err error
398
404
var optionType OptionType
399
405
@@ -436,7 +442,7 @@ func (v *Parameter) ValidateInput(input *string) (string, diag.Diagnostics) {
436
442
forcedValue = * value
437
443
}
438
444
439
- d := v .validValue (forcedValue , optionType , optionValues , cty.Path {})
445
+ d := v .validValue (forcedValue , previous , optionType , optionValues , cty.Path {})
440
446
if d .HasError () {
441
447
return "" , d
442
448
}
@@ -500,7 +506,7 @@ func (v *Parameter) ValidOptions(optionType OptionType) (map[string]struct{}, di
500
506
return optionValues , nil
501
507
}
502
508
503
- func (v * Parameter ) validValue (value string , optionType OptionType , optionValues map [string ]struct {}, path cty.Path ) diag.Diagnostics {
509
+ func (v * Parameter ) validValue (value string , previous * string , optionType OptionType , optionValues map [string ]struct {}, path cty.Path ) diag.Diagnostics {
504
510
// name is used for constructing more precise error messages.
505
511
name := "Value"
506
512
if path .Equals (defaultValuePath ) {
@@ -567,7 +573,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
567
573
568
574
if len (v .Validation ) == 1 {
569
575
validCheck := & v .Validation [0 ]
570
- err := validCheck .Valid (v .Type , value )
576
+ err := validCheck .Valid (v .Type , value , previous )
571
577
if err != nil {
572
578
return diag.Diagnostics {
573
579
{
@@ -583,7 +589,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
583
589
return nil
584
590
}
585
591
586
- func (v * Validation ) Valid (typ OptionType , value string ) error {
592
+ func (v * Validation ) Valid (typ OptionType , value string , previous * string ) error {
587
593
if typ != OptionTypeNumber {
588
594
if ! v .MinDisabled {
589
595
return fmt .Errorf ("a min cannot be specified for a %s type" , typ )
@@ -633,6 +639,28 @@ func (v *Validation) Valid(typ OptionType, value string) error {
633
639
if v .Monotonic != "" && v .Monotonic != ValidationMonotonicIncreasing && v .Monotonic != ValidationMonotonicDecreasing {
634
640
return fmt .Errorf ("number monotonicity can be either %q or %q" , ValidationMonotonicIncreasing , ValidationMonotonicDecreasing )
635
641
}
642
+
643
+ switch v .Monotonic {
644
+ case "" :
645
+ // No monotonicity check
646
+ case ValidationMonotonicIncreasing , ValidationMonotonicDecreasing :
647
+ if previous != nil { // Only check if previous value exists
648
+ previousNum , err := strconv .Atoi (* previous )
649
+ if err != nil {
650
+ return fmt .Errorf ("previous value %q is not a number" , * previous )
651
+ }
652
+
653
+ if v .Monotonic == ValidationMonotonicIncreasing && ! (num >= previousNum ) {
654
+ return fmt .Errorf ("parameter value '%d' must be equal or greater than previous value: %d" , num , previousNum )
655
+ }
656
+
657
+ if v .Monotonic == ValidationMonotonicDecreasing && ! (num <= previousNum ) {
658
+ return fmt .Errorf ("parameter value '%d' must be equal or lower than previous value: %d" , num , previousNum )
659
+ }
660
+ }
661
+ default :
662
+ return fmt .Errorf ("number monotonicity can be either %q or %q" , ValidationMonotonicIncreasing , ValidationMonotonicDecreasing )
663
+ }
636
664
case OptionTypeListString :
637
665
var listOfStrings []string
638
666
err := json .Unmarshal ([]byte (value ), & listOfStrings )
@@ -660,6 +688,15 @@ func ParameterEnvironmentVariable(name string) string {
660
688
return "CODER_PARAMETER_" + hex .EncodeToString (sum [:])
661
689
}
662
690
691
+ // ParameterEnvironmentVariablePrevious returns the environment variable to
692
+ // specify for a parameter's previous value. This is used for workspace
693
+ // subsequent builds after the first. Primarily to validate monotonicity in the
694
+ // `validation` block.
695
+ func ParameterEnvironmentVariablePrevious (name string ) string {
696
+ sum := sha256 .Sum256 ([]byte (name ))
697
+ return "CODER_PARAMETER_PREVIOUS_" + hex .EncodeToString (sum [:])
698
+ }
699
+
663
700
func takeFirstError (errs ... error ) error {
664
701
for _ , err := range errs {
665
702
if err != nil {
0 commit comments