@@ -144,7 +144,13 @@ func parameterDataSource() *schema.Resource {
144144 input = & envValue
145145 }
146146
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 )
148154 if diags .HasError () {
149155 return diags
150156 }
@@ -395,7 +401,7 @@ func valueIsType(typ OptionType, value string) error {
395401 return nil
396402}
397403
398- func (v * Parameter ) ValidateInput (input * string ) (string , diag.Diagnostics ) {
404+ func (v * Parameter ) ValidateInput (input * string , previous * string ) (string , diag.Diagnostics ) {
399405 var err error
400406 var optionType OptionType
401407
@@ -442,7 +448,7 @@ func (v *Parameter) ValidateInput(input *string) (string, diag.Diagnostics) {
442448 forcedValue = * value
443449 }
444450
445- d := v .validValue (forcedValue , optionType , optionValues , valuePath )
451+ d := v .validValue (forcedValue , previous , optionType , optionValues , valuePath )
446452 if d .HasError () {
447453 return "" , d
448454 }
@@ -506,7 +512,7 @@ func (v *Parameter) ValidOptions(optionType OptionType) (map[string]struct{}, di
506512 return optionValues , nil
507513}
508514
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 {
510516 // name is used for constructing more precise error messages.
511517 name := "Value"
512518 if path .Equals (defaultValuePath ) {
@@ -573,7 +579,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
573579
574580 if len (v .Validation ) == 1 {
575581 validCheck := & v .Validation [0 ]
576- err := validCheck .Valid (v .Type , value )
582+ err := validCheck .Valid (v .Type , value , previous )
577583 if err != nil {
578584 return diag.Diagnostics {
579585 {
@@ -589,7 +595,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
589595 return nil
590596}
591597
592- func (v * Validation ) Valid (typ OptionType , value string ) error {
598+ func (v * Validation ) Valid (typ OptionType , value string , previous * string ) error {
593599 if typ != OptionTypeNumber {
594600 if ! v .MinDisabled {
595601 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 {
639645 if v .Monotonic != "" && v .Monotonic != ValidationMonotonicIncreasing && v .Monotonic != ValidationMonotonicDecreasing {
640646 return fmt .Errorf ("number monotonicity can be either %q or %q" , ValidationMonotonicIncreasing , ValidationMonotonicDecreasing )
641647 }
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+ }
642670 case OptionTypeListString :
643671 var listOfStrings []string
644672 err := json .Unmarshal ([]byte (value ), & listOfStrings )
@@ -666,6 +694,15 @@ func ParameterEnvironmentVariable(name string) string {
666694 return "CODER_PARAMETER_" + hex .EncodeToString (sum [:])
667695}
668696
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+
669706func takeFirstError (errs ... error ) error {
670707 for _ , err := range errs {
671708 if err != nil {
0 commit comments