Compressing Branches With Conditionals and Loops
Goal
You fold tasks that kept multiplying through copy and paste into data and a loop, and learn to filter items with a condition.
Why it matters
If there are several nearly identical tasks, a mistake where just one of them differs is bound to happen, and it is hard to find by reading the code. If you reduce the tasks to one, the place to make a mistake also becomes one, and the data looks like a table, so a missing cell stands out. Three things commonly trip people up in practice when using loops. Log exposure — if you loop over a dictionary as is, every value inside it is printed in the log, so loop_control.label is needed. Idempotency — if the loop runs ten times, the damage from non-idempotency is ten times as large. Combinatorial explosion — the product of two lists grows quickly. And when you wait for something with until, put the judgment "if it doesn't work within this time, there is a real problem" into the retry count.
Steps
- In
/root/ans/loops/site.yml, useloopto create thealpha,beta, andgammadirectories under/root/ans/loops/simple/. There must be only one task. - Read
app_usersfrom/opt/lab/fixtures/ansible/users.ymland create each item'sdirwith itsmodepermission —/root/ans/loops/deploy(0750),/root/ans/loops/metrics(0755),/root/ans/loops/backup(0700). - Add
whento the same loop so that aPRIVILEGEDfile is left only in the directories of items stricter than0755—/root/ans/loops/deploy/PRIVILEGEDand/root/ans/loops/backup/PRIVILEGEDmust be created, and/root/ans/loops/metrics/PRIVILEGEDmust not. - Tidy up the log with the
labelofloop_controland save the execution output to/root/ans/loops/out/run.txt. The log must show an abbreviated label such asitem=deployand must not show the whole dictionary. - Create a waiting task that uses
until/retries/delay, and leave the success result in/root/ans/loops/out/ready.txtwith the contentready. - From combinations of environment (
dev,stage,prod) × component (app,worker), create 6 files named/root/ans/loops/matrix/<환경>-<컴포넌트>.conf(environment-component), for example/root/ans/loops/matrix/dev-app.confand/root/ans/loops/matrix/prod-worker.conf. Each file contains the linesenv=<환경>andcomponent=<컴포넌트>, where the placeholders stand for the environment and the component. - Gather the 6
.conffiles of the matrix with a pattern and copy them into/root/ans/loops/collected/. Do not write the file names one by one. - Create
/root/ans/loops/out/loops.json. It holdsusers(3 entries, each with a booleanprivileged) andmatrix_files(6). Also save the result of the second run to/root/ans/loops/out/run2.txt, and it must showchanged=0.
Notes
- Lab Pods start fresh for every lab. If
/root/ans/inventory/hosts.iniis missing, first recreate the same inventory you made in the first lab (web1, web2, db1,ansible_host=127.0.0.1,ansible_port=2222,ansible_user=root, with web and db in[prod:children]). For the structure, refer to/opt/lab/fixtures/ansible/inventory.sample.ini. - You can pass a variable as is, like
loop: "{{ app_users }}". - Build the combinations in the form
"{{ envs | product(components) | list }}". - Common mistake 1: a task inside
loopis not idempotent, so on the second run changed appears as many times as the loop count. - Common mistake 2: depending on the order of the pattern lookup result. The order is not guaranteed.
Create 3 directories with a list loop
In /root/ans/loops/site.yml, use loop to create the alpha, beta, and gamma directories under /root/ans/loops/simple/. There must be only one task.
Give a list to loop: and refer to each element as item. Do not copy the task.
Loop over a list of dictionaries
Read app_users from /opt/lab/fixtures/ansible/users.yml and create each item's dir with its mode permission — /root/ans/loops/deploy (0750), /root/ans/loops/metrics (0755), /root/ans/loops/backup (0700).
Read app_users from the fixture with vars_files. Access fields like item.name and item.mode.
Process only the items that match a condition
Add when to the same loop so that a PRIVILEGED file is left only in the directories of items stricter than 0755 — /root/ans/loops/deploy/PRIVILEGED and /root/ans/loops/backup/PRIVILEGED must be created, and /root/ans/loops/metrics/PRIVILEGED must not.
When loop and when are used together, the condition is evaluated for each item. Items whose mode is 0755 must be left out.
Tidy up the labels printed in the log
Tidy up the log with the label of loop_control and save the execution output to /root/ans/loops/out/run.txt. The log must show an abbreviated label such as item=deploy and must not show the whole dictionary.
Abbreviate with the label of loop_control. The whole dictionary must not appear in the log.
Retry until a condition becomes true
Create a waiting task that uses until/retries/delay, and leave the success result in /root/ans/loops/out/ready.txt with the content ready.
You need until, retries, and delay together. Leave the success result in a file.
Build all combinations of two lists
From combinations of environment (dev,stage,prod) × component (app,worker), create 6 files named /root/ans/loops/matrix/<환경>-<컴포넌트>.conf (environment-component), for example /root/ans/loops/matrix/dev-app.conf and /root/ans/loops/matrix/prod-worker.conf. Each file contains the lines env=<환경> and component=<컴포넌트>, where the placeholders stand for the environment and the component.
Build the combinations with the product filter. 3 environments × 2 components = 6 files.
Gather files with a pattern
Gather the 6 .conf files of the matrix with a pattern and copy them into /root/ans/loops/collected/. Do not write the file names one by one.
Do not write the file names one by one; get the list with a pattern. The content must be the same as the originals.
Loop result report and rerun check
Create /root/ans/loops/out/loops.json. It holds users (3 entries, each with a boolean privileged) and matrix_files (6). Also save the result of the second run to /root/ans/loops/out/run2.txt, and it must show changed=0.
Organize each user's privileged status and the number of combination files as JSON, and check that the second run shows changed=0.