-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
Feature request: raise(error_msg)
function
#24269
Comments
raise({error_msg})
raise({error_msg})
function
raise({error_msg})
functionraise(error_msg)
function
The question came up on #24223 if this would allow multiple distinct error messages. Below is my response based on the spec - at least in theory. Someone from TF team would need to confirm if this is correct, but I believe this proposal does give the option of providing multiple error messages for the same input variable, with multiple tests all being able to be "reached" as long as they are not downstream from one another.
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. |
If it were desirable in the above case to only ever receive a single error message, you could accomplish that in this way: locals {
my_int = "asdf"
validated_int = (
can(local.my_int *5) ?
local.my_int :
raise("Can't perform any calculations on my_int. Is the int really an int?")
)
my_calc_a = try(local.validated_int * 5, raise("Can't perform calc A. Is the int really an int?")
my_calc_b = try(local.validated_int / 5, raise("Can't perform calc B. Is the int really an int?")
} Although the above is error-handling overkill, there are cases where it would make sense to take this approach, and the fact that calculations A and B reference |
Hi @aaronsteers, We are planning to do some deeper thinking about this overall proposal, but for now just to address your direct question in your second comment: Because functions are executed only during expression validation, and expression validation is part of Terraform's graph walk, it's unfortunately undefined whether something like the example above would produce one error or two, because it depends on how the concurrent graph walk plays out:
I expect this is also true for the existing variable-specific validation experiment, because it also happens during the validation graph walk. Anything that explicitly depends on the failing variable would not get a chance to generate any additional errors, and a pair of variables that do not depend on one another at all could potentially evaluate concurrently, but probably would not. It may be possible to mark these errors (in both cases) as a sort of "soft error" that doesn't prevent the graph walk from continuing. I expect that's marginally harder to do for the |
@apparentlymart - Great! Thanks so very much for the update, and I'll look forward to updates after team discussions. Regarding the answer and clarification of how many errors are generated, this is likewise very helpful. The response basically confirms my expectation that:
No surprises here, per se, and I still think this meets most/all the my own desired validation requirements - and most of the main use cases I've been able to understand from the related threads. |
FWIW I (and I suspect many others) would be very happy to use this in practice even if the two-reachable-errors case remains nondeterministic. In the relatively infrequent case where I'm simultaneously doing two separate things wrong, I don't mind having to correct them one at a time. |
Hello, @apparentlymart - just wanted to check in on this. I find myself wishing for / dreaming of a raise() function on almost a weekly basis now - and how much they could improve the usability of our terraform modules. Is there any chance this may be added in the next terraform release? |
commenting here to help others until this is implemented: locals {
error = regex((<boolean_conditional>) ? "(INTENTIONALLY RAISE ERROR":""),"<Error message text>")
} It produces the still awkward but definitely clear error message that looks like this:
I felt like creating a scenario where the regex parser fails with the characters |
Many have posted workarounds that get Terraform to "appear" as if it has this functionality without actually having it. Any chance we can include this with the 0.13.0 release ( #25016 ) ? 🙏 |
@apparentlymart - I finally had some time to work on this and I understand golang well enough now to start contributing. Do you mind taking a look at these PRs:
Ditto - to anyone else who knows golang and/or the Terraform/HCL platform, I would welcome feedback and/or contributions on the above. |
I don't really know the stuff good enough yet so can't really contribute on this, other than to say, nice @aaronsteers! This would be a good feature I hope this or something very similar gets added soon! |
@apparentlymart, @jbardin - Could I please request a review on the HCL PR here: hashicorp/hcl#384 The code is feature complete and tests are passing. Would kindly appreciate your review and feedback. Thanks very much! |
This feature would lead to vastly improved Terraform code. Currently we're just hacking around this issue by trying to read a non-existent file. It's really a bummer because Terraform is so clean and and elegant except for a few cases like this |
+1 what @brandon-fryslie said. I would like to be able to assert in a way that doesn't fail |
Hi all! It looks like we missed this one when we were looking for issues related to the use-cases that motivated the Custom Condition Checks features in Terraform v1.2. The design effort which led there caused us to conclude that explicitly monitoring conditions as side-effects met the apparent requirements better than what was achievable with just a function, and so the final design uses conditions associated with resources and output values (both of which already have explicit side-effects for the conditions to guard) rather than the function design proposed here. Because of that, I'm going to close this issue. If you are using Terraform v1.2 or later, you can use Preconditions and Postconditions to cause Terraform to return an error if some arbitrary condition doesn't hold. Thanks for proposing this, and sorry we missed closing it after the v1.2 release! |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. |
UPDATE (2): Terraform have opened a 'discuss' topic for this capability: https://discuss.hashicorp.com/t/terraform-core-research-ability-to-raise-an-error/35818
UPDATE: This item is not yet prioritized/approved from Hashicorp, but we now have PRs open: hashicorp/hcl#384 for the HCL components and #25088 for Terraform. Please watch and vote those as well. Thank you!
Current Terraform Version
Use-cases
locals
working variablesAttempted Solutions
variable
level is too early - they do not have access to other input variables or to calculated interim variables (locals
in terraform-speak)user most provide 'var_a' or 'var_b', but not both
.Proposal
TL;DR:
raise()
which accepts a singleerror_msg
argument and which alerts the user of the failure, printing the provided error message (and the same normal call stack info for other errors, per usual)Details:
try()
andcan()
functions.{the-world-makes-sense} ? {something-sane} : raise("something is definitely wrong")
try()
function:try({this-often-fails}, raise("Here's is why it failed: 'reasons'."))
coalesce()
statement to handle non-nullables:coalesce({first-attempt}, {second-attempt}, ..., raise("Oh geez, I have no value. Danger, Will Robinson!"))
can()
in an if-then-else statement to catch otherwise hard-to-define errors:can(100/{denominator}) ? {denominator} : raise("Gah - something is wrong! Did you pass a denominator of zero???")
contains(my_map, my_key) ? my_map[my_key] : raise("Error. The key '${my_key}' is suspiciously absent")
Benefits:
try()
andcan()
functions. It encourages easy-to-read code, and it does not require any new complex structures or new syntax to be memorized.variable
level can also be validated at thelocals
level. (Not saying it should replace it, just that it does not appear there are any functional validation requirements that the variable-level solution meets which this could not also meet.)References
The text was updated successfully, but these errors were encountered: