@@ -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
}
@@ -395,7 +401,7 @@ func valueIsType(typ OptionType, value string) error {
395
401
return nil
396
402
}
397
403
398
- func (v * Parameter )ValidateInput (input * string ) (string , diag.Diagnostics ) {
404
+ func (v * Parameter )ValidateInput (input * string , previous * string ) (string , diag.Diagnostics ) {
399
405
var err error
400
406
var optionType OptionType
401
407
@@ -442,7 +448,7 @@ func (v *Parameter) ValidateInput(input *string) (string, diag.Diagnostics) {
442
448
forcedValue = * value
443
449
}
444
450
445
- d := v .validValue (forcedValue ,optionType ,optionValues ,valuePath )
451
+ d := v .validValue (forcedValue ,previous , optionType ,optionValues ,valuePath )
446
452
if d .HasError () {
447
453
return "" ,d
448
454
}
@@ -506,7 +512,7 @@ func (v *Parameter) ValidOptions(optionType OptionType) (map[string]struct{}, di
506
512
return optionValues ,nil
507
513
}
508
514
509
- func (v * Parameter )validValue (value string ,optionType OptionType ,optionValues map [string ]struct {},path cty.Path ) diag.Diagnostics {
515
+ func (v * Parameter )validValue (value string ,previous * string , optionType OptionType ,optionValues map [string ]struct {},path cty.Path ) diag.Diagnostics {
510
516
// name is used for constructing more precise error messages.
511
517
name := "Value"
512
518
if path .Equals (defaultValuePath ) {
@@ -573,7 +579,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
573
579
574
580
if len (v .Validation )== 1 {
575
581
validCheck := & v .Validation [0 ]
576
- err := validCheck .Valid (v .Type ,value )
582
+ err := validCheck .Valid (v .Type ,value , previous )
577
583
if err != nil {
578
584
return diag.Diagnostics {
579
585
{
@@ -589,7 +595,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
589
595
return nil
590
596
}
591
597
592
- func (v * Validation )Valid (typ OptionType ,value string )error {
598
+ func (v * Validation )Valid (typ OptionType ,value string , previous * string )error {
593
599
if typ != OptionTypeNumber {
594
600
if ! v .MinDisabled {
595
601
return fmt .Errorf ("a min cannot be specified for a %s type" ,typ )
@@ -639,6 +645,28 @@ func (v *Validation) Valid(typ OptionType, value string) error {
639
645
if v .Monotonic != "" && v .Monotonic != ValidationMonotonicIncreasing && v .Monotonic != ValidationMonotonicDecreasing {
640
646
return fmt .Errorf ("number monotonicity can be either %q or %q" ,ValidationMonotonicIncreasing ,ValidationMonotonicDecreasing )
641
647
}
648
+
649
+ switch v .Monotonic {
650
+ case "" :
651
+ // No monotonicity check
652
+ case ValidationMonotonicIncreasing ,ValidationMonotonicDecreasing :
653
+ if previous != nil {// Only check if previous value exists
654
+ previousNum ,err := strconv .Atoi (* previous )
655
+ if err != nil {
656
+ return fmt .Errorf ("previous value %q is not a number" ,* previous )
657
+ }
658
+
659
+ if v .Monotonic == ValidationMonotonicIncreasing && ! (num >= previousNum ) {
660
+ return fmt .Errorf ("parameter value '%d' must be equal or greater than previous value: %d" ,num ,previousNum )
661
+ }
662
+
663
+ if v .Monotonic == ValidationMonotonicDecreasing && ! (num <= previousNum ) {
664
+ return fmt .Errorf ("parameter value '%d' must be equal or lower than previous value: %d" ,num ,previousNum )
665
+ }
666
+ }
667
+ default :
668
+ return fmt .Errorf ("number monotonicity can be either %q or %q" ,ValidationMonotonicIncreasing ,ValidationMonotonicDecreasing )
669
+ }
642
670
case OptionTypeListString :
643
671
var listOfStrings []string
644
672
err := json .Unmarshal ([]byte (value ),& listOfStrings )
@@ -666,6 +694,15 @@ func ParameterEnvironmentVariable(name string) string {
666
694
return "CODER_PARAMETER_" + hex .EncodeToString (sum [:])
667
695
}
668
696
697
+ // ParameterEnvironmentVariablePrevious returns the environment variable to
698
+ // specify for a parameter's previous value. This is used for workspace
699
+ // subsequent builds after the first. Primarily to validate monotonicity in the
700
+ // `validation` block.
701
+ func ParameterEnvironmentVariablePrevious (name string )string {
702
+ sum := sha256 .Sum256 ([]byte (name ))
703
+ return "CODER_PARAMETER_PREVIOUS_" + hex .EncodeToString (sum [:])
704
+ }
705
+
669
706
func takeFirstError (errs ... error )error {
670
707
for _ ,err := range errs {
671
708
if err != nil {