FOREACH CI
Walks a list of resource identifiers held in the stash, loads each one as a live resource, and runs the ops nested underneath it once per entry with that resource in a loop variable.
The loading is the point. A list of identifiers on its own is a list of strings; after this op each
entry is the resource itself, so nested ops can read its fields with ${myitem.name} and ops that
expect a resource can take it directly. If all you need is to walk a list of plain strings, use
FOR eval or one of the file loops instead.
Settings¶
This op has no configuration form. Its Config tab holds a YAML editor carrying the op's two settings, which you edit as text and save with the rest of the op's properties. A freshly dropped op opens on this:
local_var: value
variable: stash_var
Both values are placeholders and neither is likely to match anything in your rule, so an op you drop and never open does nothing at all. The two keys come back sorted alphabetically each time you open the tab, whatever order you typed them in, and YAML comments are dropped when you save. Invalid YAML raises a dialog and blocks the save.
variable¶
The name of the stash variable holding the identifiers. A bare name, not ${stash_var}. Dots walk
into nested data, so job.servers works.
The value it points at can be a list, a single identifier on its own, or a comma-separated string.
Commas are split for you and spaces around them are dropped, so foo_mid, bar_mid and a two-entry
list behave identically.
Each entry may be a resource identifier, a resource that is already loaded, or a field:value
lookup, for example name:bar_server or moniker:baz. An entry that is already a resource is
reloaded from storage, so it reflects what is stored now rather than what was read earlier in the
rule. A field:value lookup searches every resource in the system rather than one class, so a name
that two resources share stops the rule with a message listing both identifiers.
Leave it blank, or point it at a variable that does not exist, and the loop runs zero times. Nothing
is logged and nothing fails. That silence is the first thing to check when a FOREACH CI block
appears to be ignored.
local_var¶
The name of the loop variable the current resource is placed in. Delete the line and it falls back
to value, which collides with almost everything, so give it a name of its own.
A variable of that name that already existed is shadowed for the duration of the loop and put back when the last pass finishes. Anything you want to survive the loop has to be collected into a different variable, with PUSH VAR or SET VAR.
Inside the body, read the resource's fields through it: ${myserver.name}, ${myserver.hostname}.
Static Analysis knows this variable is defined inside the block, so uses of it in the nested ops are
not reported as an unknown variable.
Loading and failure¶
Every entry is turned into a resource before the first pass begins, not lazily as the loop reaches it. One identifier that no longer exists stops the rule immediately, naming the identifier it could not find, and none of the valid entries ahead of it get processed. Filter the list before the loop when it may contain stale identifiers.
A failure inside the body ends the loop as well as the job. The remaining entries are never visited. Put TRY statement and CATCH statement inside the loop when one bad entry should not stop the others.
The loop writes nothing back. It hands the body a resource loaded from storage, and the body has to
save any change of its own; use Invoke Resource methods for that.
Return Key on the Options tab has no effect on this op, because the loop has no result to hand
back.
The job log gets one debug line for the op as a whole and nothing per pass. Add LOG Message in the body when you want to see which entry is being handled.
Combining with other ops¶
Build the list first. SET VAR to CI puts a resource into a variable, PUSH VAR grows a list one entry at a time, and services that select resources leave their results in the stash for this op to walk.
Feed the loop variable straight into an op that takes a server or another resource, such as Run a Remote Script or Ship File Remotely.
Skip entries with IF var condition THEN inside the body, testing a field of the loop variable.
For the identifiers themselves, see MID.
Examples¶
Restart a service on every server a previous op collected.
local_var: myserver
variable: bar_servers
-> LOG Message restarting on ${myserver.name}
-> Run a Remote Script server ${myserver}
Walk the projects a rule gathered and act on each one.
local_var: myproject
variable: foo_projects
-> IF var condition THEN
When: All
myproject.name NOT LIKE ^sandbox
-> Run a Remote Script