-
Notifications
You must be signed in to change notification settings - Fork 39
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
Nesting When statements in Orquesta #116
Comments
The following is an option that we have been talking about.
|
what about
will this be supported? also is and needed is fear nesting whens isn't explicit enough? |
@dbillor we could explore the To be more clear on that last point: |
Here is a snippet of our Orquesta code that is already in production: - when: <% succeeded() and (ctx().merge_upstream and ctx().mis_repo_base = openstack) %>
publish:
- shas: <% result().output.shas %>
do:
- merge_upstreams
- when: <% succeeded() and (not ctx().merge_upstream or ctx().mis_repo_base = stackstorm) %>
publish:
- shas: <% result().output.shas %>
do:
- teardown That could be simplified down to: - when: <% succeeded() %>
publish:
- shas: <% result().output.shas %>
and:
- when: <% ctx().merge_upstream and ctx().mis_repo_base = openstack %>
do:
- merge_upstreams
- when: <% not ctx().merge_upstream or ctx().mis_repo_base = stackstorm %>
do:
- teardown Instead of nesting conditions linearly within parentheses, we're nesting conditionals in YAML using, and YAML is a syntax that was built on using indentation to nest elements. We would be utilizing YAML for exactly what it was originally intended. And then when our customers ask us "why use Orquesta instead of Mistral?" we can have a specific answer for them "nested conditionals in task transitions"! And just to drive the point home further, it would be difficult for Mistral to implement a similar syntax. So not only would Orquesta be getting a feature that Mistral doesn't currently have, it would be getting a feature that Mistral could pretty much never have. However, I don't really know why |
@dzimine @bigmstone Any feedback for us on the proposed design above? |
I'm not sure how I feel about |
@bigmstone I haven't forgotten your feedback above, i'm just not sure yet. |
Also, as an alternative, user can also do the following right now where task1 publish and transition on succeeded to an empty task2. Task2 (no action defined) branches out with other publishes and transitions.
|
As per @m4dcoder suggestion, I am adding my 2 cents to publishing vars. Instead of having them as part of the transition model, I'd have it as part of the task model. So that once action is complete, regardless of whether it's success or failure, evaluate publish expressions, which then could be available to transition conditions. It'd make it simple to write workflows for everyone. |
@emptywee The issue here is that result of action execution can be different for success and failure. What if I want to publish x on success (only in the result of successful execution) and y on failure (only in the result of a failure execution)? In this case, the workflow engine will try to process the publish for x and y and will result in an expression evaluation failure in either case. |
@m4dcoder I don't know about those hypothetical scenarios where you'd want to publish different variables, but in my experience I've rarely needed it (if at all, to my recollection). I've been able to write pretty complex workflows with just using "publish" attribute of the mistral engine, dealing with all possible success/failure transitioning and variables publishing. However, I see many ways to deal with such scenarios with a single publish attribute in the task model. E.g.
Then it's up to you which variable to use later on. |
@emptywee You are not doing this doesn't mean it is not done by other users though. Among other use cases, it's common to publish certain variables on success but then publish stdout/err on failure to be logged or recorded. As for your example, it doesn't work because you are mixing condition with the expression that returns the value for the variable. In your example, var_on_success/failure will have value of True or False. |
@m4dcoder well, I expected this kind of response, thus was a bit hesitant to ignite this sort of debate :) Of course, if I needed to get rid of True/False, I'd make it:
I know the output I am gonna get in my expressions, and I'd account for that if I needed them to be very precise. For the sake of example I provided a very simple one. It's just, I don't know what's cheaper...: Perhaps, as a compromise, allow publishing in the task model and additional publishing in the transition model? I don't know if it's possible, though, from the Orquesta engine implementation standpoint of view. |
This type of design discussion is needed to understand requirements and evaluate solution proposals. The person who is going to implement this feature will be able to look at the dialogs here to understand how the decision was made. If we have this conversation in Slack, it will be lost. I tested the YAQL expressions and it works to my surprise. This also needs to be tested for Jinja since we support both YAQL and Jinja. IMO, the YAQL expressions are very ambiguous and I have not formed an opinion on this proposal yet. |
@m4dcoder I like YAQL, it allows to be pretty flexible when needed. Wish it could also let your handle exceptions :) Regardless, could you consider the compromise suggested? Let users publish at two stages: 1. after action is complete; 2. in the transition part (inside |
I don't have an opinion on your proposed solution atm. There is a workaround for the problem you stated. I am in no rush to make a decision on this especially introducing a new language element which I cannot rescind once I introduced it. This requires more thoughts and testing to make sure Jinja also works because we support both type of expressions. |
Well, the compromise where users would be able to publish in two places introduces no changes to YAQL/Jinja processing. Only that if |
P.S. As much as you don't want to rush make changes, I also don't want to use lengthy and bulky workarounds just to later re-do it the right way (if additional publish is added eventually). If you'd like users to migrate smoother to Orquesta, this little feature is a must have. P.P.S. Also retry-delay-count feature is long-awaited here :) |
Expressions in the So, I often end up splitting one task into two (or more). The first publishes the results in more convenient (intermediate) variables, and the second serves as a fork/decision node to evaluate those newly published (intermediate) vars, and the publish the vars that the rest of the workflow needs before continuing to the next set of tasks. One issue with this workaround is that it pollutes the context with variables that I only need for the next ( The nested-when block has a similar issue. So, using the next:
- when: <% succeeded() %>
publish:
- disk_space_usage: <% int(result().get(ctx().server_name).stdout.replace('%','')) %>
and:
- when: <% ctx().disk_space_usage >= ctx().disk_space_threshold and ctx().use_chatops %>
publish:
- full_msg: "{{ ctx().disk_space_usage }}% used ({{ 100 - ctx().disk_space_usage }}% free)"
do:
- send_chatops_confirm_disk_usage_too_high
- when: <% ctx().disk_space_usage >= ctx().disk_space_threshold %>
do:
- clean_up_logs
- restart_flaky_service If I never need the The mistral-like publish-first approach described by Igor (#116 (comment)) also suffers the same shortcoming; publishing a var that is meant to be used in the current task's task1:
action: example.randomly_fail
publish:
var_on_success: <% succeeded() and result().output.var1 or null %>
var_on_failure: <% not succeeded() and result().output.var2 or null %> So, I would like to see a way to not only create variables for use in What if we added a way for workflow authors to "override" what the This could work with either approach: (a) the nested-when approach, and (b) the publish-before-next approach. Here are the above examples using a new (a) nested when next:
- when: <% succeeded() %>
output:
- disk_space_usage: <% int(result().get(ctx().server_name).stdout.replace('%','')) %>
and:
- when: <% output().disk_space_usage >= ctx().disk_space_threshold and ctx().use_chatops %>
publish:
- full_msg: "{{ output().disk_space_usage }}% used ({{ 100 - output().disk_space_usage }}% free)"
do:
- send_chatops_confirm_disk_usage_too_high
- when: <% output().disk_space_usage >= ctx().disk_space_threshold %>
publish:
- disk_space_usage: <% output().disk_space_usage %>
do:
- clean_up_logs
- restart_flaky_service (b) define the variables before the next block task1:
action: example.foobar
input:
param: abcdef
output:
var_on_success: <% succeeded() and result().output.var1 or null %>
var_on_failure: <% not succeeded() and result().output.var2 or null %>
next:
- when: <% succeeded() and output().var_on_success = "qwerty" %>
do: ...
- when: <% succeeded() and output().var_on_success = "dvorak" %>
do: ... Instead of key |
I think there are two separate problems communicated here.
The current semantic for |
This issue has been brought up again in the community Slack channel. Please note that there is a workaround at #116 (comment) which uses intermediate tasks. So far, the Orquesta workflow definition language has avoided hardcoding on-success and on-error. I like to keep it that way. There are use cases where tasks completed successfully but the returned status code or the output data determines the state. So let's revisit a similar proposal from above and expand this here with scoped publish. The proposal is to introduce a new keyword Let's take the following workflow as an example, with branches S, F, SX, SY, and SYZ where SYZ means it falls under the umbrella of S and SY. tasks:
task1:
...
next:
# branch S
- when: <% succeeded() %>
publish:
- var1: blah
then:
# branch SX
- when: <% ctx().x = true %>
publish:
- var2: blahblah
do: task2
# branch SY
- when: <% ctx().y = true %>
publish:
- var3: blahblahblah
then:
# branch SYZ
- when: <ctx().z = true %>
do: task3
...
# branch F
- when: <% failed() %>
publish:
- var99: mehhhh
do: notify In the example above, var1 is published in S and available in SX, SY, and SYZ. The variable var2 is published in SX but since SY and SYZ is not under SX, var2 is not available in those branches. Branch F publishes var99 but it is not available in the scope of the branches under branch S.
As for local variables not to be published into the workflow context, how about prefixing the variable with an underscore and then reference with the tasks:
task1:
...
next:
- when: <% succeeded() %>
publish:
- _var10: fee fi
- _var20: fo fum
then:
- when: <% ctx().x = true %>
publish:
- poem: <% ctx()._var10 + " " + ctx()._var20 %>
do: task2
- when: <% ctx().y = true %>
do: task3
... |
I like Using an underscore prefix seems surprising at first, but I see how it prevents adding a new keyword to the workflow syntax. I like that there's only one place to define those vars, and the underscore is a nice scope indicator. |
I like the I assume that most Python developers will be very familiar with the leading-underscore-meaning-something-different convention, because that's a convention in Python. AIUI that is not a formal restriction of Python interpreters, it is merely a convention respected by most Python developers (but not all). However, I do not like the leading-underscore part of the Reasons:
Pardon my ignorance here, but I don't understand the reservation against adding keywords to the Orquesta syntax. The syntax is, as far as I can tell, very good at separating user-defined names (like published variable names and values) from syntactical keywords (like On the contrary - AFAICT that is an additional restriction on context variable names that did not exist before. So existing workflows that already define variables prefixed with underscores will break under the current A different way to handle variable scopes would be to extend the tasks:
task1:
...
next:
- when: <% succeeded() %>
publish:
# current, "short-form" syntax; still available in downstream contexts as the
# implicit "scope" defaults to "task" (see below)
- _var10: fee fi
# equivalent to ^^ syntax
# long-form syntax
# allows users to explicitly set variable scope
# publishing a variable with the same name more than once would not be allowed
# in practice, this is just to show an equivalent)
- name: _var10
value: fee fi
scope: task # set to "task" to include in downstream task variable contexts
# long-form syntax
# allows users to scope variable down to `then` transition
- name: _var20
value: fo fum
scope: transition # set to "transition" to remove from downstream task variable contexts
then:
... There would be no other acceptable values for the This sub-proposal has the benefits of not introducing any additional keywords to indicate transition variable scope, avoids the underscore-prefix restriction from the previous proposal, it should be additionally backwards compatible with existing workflows since it only extends the existing syntax, it makes it absolutely explicit when a variable will not be published to downstream task contexts, and it also gives us a way potential mechanism to extend the syntax if we would like to allow any additional variable scopes or other variable handling tricks in the future. The downsides to this option are that introduces an additional mechanism to publish variables and it forces users to write a lot of boilerplate when they need to specify Technically, the long-form syntax for I apologize if any if this is inaccurate (please feel free to correct me or simply ignore if so). It's been awhile since I was involved in StackStorm development and I'm not as up-to-speed on it as I used to be. |
Publishing the same var multiple times is a feature of the list of single item dicts. I purposely publish the same var multiple times in the same publish block. This allows me to break a long yaql or jinja expression into a series of smaller more understandable expressions. The added verbosity of An orquesta/docs/source/schemas/orquesta.json Line 280 in c548e49
What if we used something that is NOT a valid var name character (and not a special yaml char)? Here are several possibilities (we should only use one). publish:
- _var10: fee fi # _ is a valid var name char so no special meaning
- ~var20: fo fum # ~ is not a valid var name char. publish ~var_name access ctx().var_name
- %var30: I smell the # publish %var_name access ctx().var_name
- <var40>: blood of # or surround the name instead of prefixing it. publish <var_name> access ctx().var_name
- local:var50: an englishman # publish scope:var_name access ctx().var_name |
In light of @blag comment, I believe something like |
@copart-jafloyd I definitely agree - the long-form is definitely verbose and less readable. The unique variable name restriction is not important to my overall sub-proposal, so keeping that functionality working for you is totally fine, I'm just curious how/why you actually use that. Can you give an example of how you publish the same variable multiple times? I'm glad we're talking about new options, especially the point about using a non-allowed character as a sigil but I think anything that uses a symbol to indicate scope is going to confuse users and likely be difficult to search for. Out of those options, I like the Along that train of thought, trying to think outside the box a little bit here: the term "local" might not make it exactly clear as to what is considered "local". Local to the task being run? Local to the @emptywee I'm not sure I like the alternative Example: next:
- when: ...
publish:
- var1: fee fi
local: # or maybe this should be "transition:"?
- var2: fo fum
then:
- when: <% local().var2 and ctx().var1 %> |
@blag, yeah, right, we're talking about declaration here, my bad. |
Re YAML, you only have to wrap the I don't think Whatever the scope's name is, it will need to be documented. Borrowing a term or a symbol from a programming language is fine, as long as the term's definition, within the linguistic-context of orquesta, is documented. PS: @copart-jafloyd is me. I just posted the earlier comment from the wrong account. oops. |
Right now when writing check logic in orquesta I have to have a when block for each conditional. It's not graceful and harder to manage.
For ex:
Instead of ,
We have to do
It's not clean and harder to read/maintain. Especially in some of our workflows the conditionals are large and you end up having many statements with repeated keywords in them.
The text was updated successfully, but these errors were encountered: