Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation block should allow validation of list of values #24223

Open
avarmaavarma opened this issue Feb 26, 2020 · 7 comments
Open

Validation block should allow validation of list of values #24223

avarmaavarma opened this issue Feb 26, 2020 · 7 comments
Labels
custom-conditions Feedback on variable validation, preconditions. postconditions, checks, and test assertions enhancement

Comments

@avarmaavarma
Copy link

avarmaavarma commented Feb 26, 2020

This validation block works for a single input variable.

variable "mytestname" {
     validation {
        condition = length(regexall("^test", var.mytestname)) > 0
        error_message = "Should start with 'test'"
     }
}

This does not work for a collection of values. The issue is that there is a restriction on the condition statement - the condition only accepts the input variable itself (i.e. - it cannot accept an each.value)

variable "mytestnames" {

listnames = split(",",var.mytestnames)     

for_each = var.listnames

     validation {
        condition = length(regexall("^test", each.value)) > 0
        error_message = "Should start with test"
      }
}

@jbardin jbardin added the custom-conditions Feedback on variable validation, preconditions. postconditions, checks, and test assertions label Feb 26, 2020
@aaronsteers
Copy link

aaronsteers commented Mar 4, 2020

@avarmaavarma - I believe my feature proposal in #24269 would meet these requirements, essentially adding a raise() function which would be able to operate on any combination of variables in a locals block.

Would this meet your need?

variable "listnames" {
  type = list(string)
}
locals {
    my_test_results = ([
      for v in var.listnames :
      length(regexall("^test", v.value)) > 0 ? "ok" : raise("Should start with 'test'")
    ])
}

@avarmaavarma
Copy link
Author

Yes - that should work for what I was looking for. Also, is there a limit on the number of raises...? Can I raise more than once (provided I do so in a separate block?)

@aaronsteers
Copy link

aaronsteers commented Mar 5, 2020

@avarmaavarma - Thanks for confirming. This is still just a proposal - but in terms of number of raises, I can't say for sure and it would likely depend on the implementation. That said, at least in theory, the same rules of error handling could/would apply as with the rest of terraform, specifically that multiple errors are reported when not directly dependent upon each other. As the dependency DAG is resolved, obviously anything downstream from a failed validation check would not be executed. (Samples below.)

In theory, I expect that this code would produce two separate errors since both "Calc A" and "Calc B" can be reached, and they both raise an error:

locals {
  my_int    = "asdf"
  my_calc_a = try(local.my_int * 5, raise("Can't perform calc A. Is the int really an int?")
  my_calc_b = try(local.my_int / 5, raise("Can't perform calc B. Is the int really an int?")
}

While this would produce a single error on "Calc A":

locals {
  my_int    = "asdf"
  my_calc_a = try(local.my_int * 5, raise("Can't perform calc A. Is the int really an int?")
  my_calc_c = try(local.my_calc_a * 5, raise("Can't perform calc C. Is the int really an int?")
}

Because Calc C depends on an already failed value ("Calc A"), there's no way to execute it and it would not report an error.

@kappmeier
Copy link

It is a bit clumsy, but using for expression should work:

length([for x in var.inputs : 1 if contains(["allowed1", "allowed2"], r)]) == length(var.inputs)

Of course more complex validations are possible.

@aaronsteers
Copy link

The parallel effort I am covering in the raise(error_msg) function PR ( work in progress here: #25088 ) would also make this type of validation possible under a conditional expression living in any locals block. If you want to vote for that PR and/or contribute code, this would be much appreciated.

@aaronsteers
Copy link

aaronsteers commented Jun 17, 2020

Potentially resolved by: #25088 (PR waiting feedback, CI tests passing)

(Alternate validation strategy using raise() which could trigger on any arbitrarily complex condition.)

@sticky-note
Copy link

sticky-note commented Aug 3, 2021

I've found a quite nice one liner Workaround to validate list(string) against another list(string):

variable items {
  type = list(string)
  default = []
  validation {
    condition = !contains([for item in var.items: contains( ["val1", "val2", "val3"], item)], false)

    error_message = "The name items contain non-valid value(s)."
  }
}

The condition test if each element of items is one of ["val1", "val2", "val3"]

Seems to be easily adaptable to your case as:

variable "mytestname" {
  type = "string"
  default = ""
  validation {
    condition = !contains([for item in split(",",var.mytestnames): length(regexall("^test", item)) > 0], false)
    error_message = "The mytestname items values should start with 'test'"
  }
}

Not tested that way and interested to listen if this worked for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
custom-conditions Feedback on variable validation, preconditions. postconditions, checks, and test assertions enhancement
Projects
None yet
Development

No branches or pull requests

5 participants