Skip to content

Commit 19363f6

Browse files
authored
Update loops examples and sidebar layout (#1410)
**Pull Request Checklist** - [x] 4 of 5 for #1408 - [x] Documentation/examples added - [x] [Good commit messages](https://cbea.ms/git-commit/) and/or PR title **Description of PR** Improve the loops examples. Also change the examples sidebar layout to lift up the sub folders to the top (i.e. removing the "idiomatic Hera examples" drop down). --------- Signed-off-by: Elliot Gunton <elliotgunton@gmail.com>
1 parent 18b394d commit 19363f6

29 files changed

+359
-268
lines changed

docs/examples/workflows-examples.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
# Workflows Examples
22

3-
Hera has complete feature parity with YAML as a definition language for Argo Workflows. To demonstrate the equivalency
4-
(and improvements that Hera offers), we provide two collections of examples.
3+
Hera has complete feature parity with YAML as a definition language for Argo Workflows.
4+
5+
The examples show off features and idiomatic usage of Hera such as the
6+
[Coinflip example](workflows/scripts/coinflip.md) recreated using the script decorator.
57

68
The "Upstream" collection contains examples
79
[directly from the Argo Workflows repository](https://github.com/argoproj/argo-workflows/tree/6e97c7d/examples), such as
810
the [DAG Diamond example](workflows/upstream/dag_diamond.md), to demonstrate how the YAML spec maps to Hera classes.
9-
These examples are generated and compared in Hera's CI/CD pipeline to ensure that all the examples are possible to
10-
recreate in Hera.
11+
They may not be written as idiomatic Hera code (for example, using `Container` when a script decorator would be
12+
preferred).
1113

1214
> If you'd like to contribute missing examples, please see the table below and follow the
1315
> [Contributing Guide](../CONTRIBUTING.md)!
1416
15-
The "Hera" collection shows off features and idiomatic usage of Hera such as the
16-
[Coinflip example](workflows/scripts/coinflip.md) recreated using the script decorator.
17-
1817
Explore the examples through the side bar!
1918

2019
## List of **missing** examples

docs/examples/workflows/loops/script_basic_loop.md renamed to docs/examples/workflows/loops/basic_fanout.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Script Basic Loop
2-
1+
# Basic Fanout
32

43

54

5+
This examples shows some basic hard-coded fan-outs.
66

77

88
=== "Hera"
@@ -36,11 +36,18 @@
3636
],
3737
)
3838

39-
# We can still pass a list of dict values to `with_items`, but must serialize them
39+
# We can still pass a list of dict values to `with_items`, but must
40+
# serialize them using Hera's `serialize` function
4041
print_message(
4142
name="print-message-loop-with-items-list-of-dicts",
4243
arguments={"message": "{{item}}"},
43-
with_items=[serialize(item) for item in [{"my-key": "hello world"}, {"my-other-key": "goodbye world"}]],
44+
with_items=[
45+
serialize(item)
46+
for item in [
47+
{"my-key": "hello world"},
48+
{"my-other-key": "goodbye world"},
49+
]
50+
],
4451
)
4552
```
4653

docs/examples/workflows/loops/dynamic_fanout.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33

44

5-
This example showcases how clients can use Hera to dynamically generate tasks that process outputs from one task in
6-
parallel. This is useful for batch jobs and instances where clients do not know ahead of time how many tasks/entities
5+
This examples shows how to dynamically fan out over a list of values.
6+
7+
The fan-out task will consume the output of the previous task and process the list of values in parallel. We are
8+
printing to stdout to use the `result` output parameter, but generally you should use a named output parameter.
9+
10+
Dynamic fan-outs are useful for batch jobs and instances where clients do not know ahead of time how many tasks/entities
711
they may need to process.
812

913

@@ -16,19 +20,19 @@ they may need to process.
1620
@script()
1721
def generate():
1822
import json
23+
import random
1924
import sys
2025

21-
# this can be anything! e.g fetch from some API, then in parallel process all entities; chunk database records
22-
# and process them in parallel, etc.
23-
json.dump([i for i in range(10)], sys.stdout)
26+
# this can be anything! e.g fetch from some API, then in parallel process
27+
# all entities; chunk database records and process them in parallel, etc.
28+
json.dump([i for i in range(random.randint(8, 12))], sys.stdout)
2429

2530

2631
@script()
2732
def consume(value: int):
2833
print("Received value: {value}!".format(value=value))
2934

3035

31-
# assumes you used `hera.set_global_token` and `hera.set_global_host` so that the workflow can be submitted
3236
with Workflow(generate_name="dynamic-fanout-", entrypoint="d") as w:
3337
with DAG(name="d"):
3438
g = generate()
@@ -67,8 +71,9 @@ they may need to process.
6771
import sys
6872
sys.path.append(os.getcwd())
6973
import json
74+
import random
7075
import sys
71-
json.dump([i for i in range(10)], sys.stdout)
76+
json.dump([i for i in range(random.randint(8, 12))], sys.stdout)
7277
command:
7378
- python
7479
- name: consume

docs/examples/workflows/loops/dynamic_fanout_container.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33

44

5-
This example showcases how clients can use Hera to dynamically generate tasks that process outputs from one task in
6-
parallel. Differ from dynamic_fanout.py, this example uses a container to generate the tasks and the dynamically
7-
created tasks are also container only.
8-
More details can be found here: https://github.com/argoproj-labs/hera-workflows/issues/250
5+
This examples shows how to dynamically fan out over a list of values using `Container` templates.
6+
7+
The command `len=$((8 + RANDOM % 4)); json=$(seq 1 "$len" | paste -sd, -); echo "[$json]"` produces a list of random
8+
length (between 8 and 12), which is then echoed in json format. This matches the [dynamic fanout](dynamic-fanout.md)
9+
example that uses Python. In this example, we must specify the arguments as `Container` does not automatically match
10+
them for us.
911

1012

1113
=== "Hera"
@@ -16,7 +18,7 @@ More details can be found here: https://github.com/argoproj-labs/hera-workflows/
1618
generate = Container(
1719
name="generate",
1820
image="alpine:latest",
19-
command=["echo", '[{"value": "a"}, {"value": "b"}, {"value": "c"}]'],
21+
command=['len=$((8 + RANDOM % 4)); json=$(seq 1 "$len" | paste -sd, -); echo "[$json]"'],
2022
)
2123

2224
fanout = Container(
@@ -26,15 +28,13 @@ More details can be found here: https://github.com/argoproj-labs/hera-workflows/
2628
command=["echo", "{{inputs.parameters.value}}"],
2729
)
2830

29-
# assumes you used `hera.set_global_token` and `hera.set_global_host` so that the workflow can be submitted
30-
with Workflow(generate_name="dynamic-fanout-container-", entrypoint="d") as w:
31+
with Workflow(
32+
generate_name="dynamic-fanout-container-",
33+
entrypoint="d",
34+
) as w:
3135
with DAG(name="d"):
32-
# this can be anything! e.g. fetch from some API, then in parallel process all entities; chunk database records
33-
# and process them in parallel, etc.
3436
g = generate()
35-
f = fanout(
36-
arguments={"value": "{{item.value}}"}, with_param=g.result
37-
) # this make the task fan out over the `with_param`
37+
f = fanout(arguments={"value": "{{item}}"}, with_param=g.result)
3838
g >> f
3939
```
4040

@@ -60,13 +60,12 @@ More details can be found here: https://github.com/argoproj-labs/hera-workflows/
6060
arguments:
6161
parameters:
6262
- name: value
63-
value: '{{item.value}}'
63+
value: '{{item}}'
6464
- name: generate
6565
container:
6666
image: alpine:latest
6767
command:
68-
- echo
69-
- '[{"value": "a"}, {"value": "b"}, {"value": "c"}]'
68+
- len=$((8 + RANDOM % 4)); json=$(seq 1 "$len" | paste -sd, -); echo "[$json]"
7069
- name: fanout
7170
container:
7271
image: alpine:latest

docs/examples/workflows/loops/dynamic_fanout_fanin.md renamed to docs/examples/workflows/loops/fan_in.md

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
# Dynamic Fanout Fanin
1+
# Fan In
22

33

44

5+
This example shows how to collect values in a "fan-in" task after the fan-out.
56

7+
This also works for the `result` output parameter (as long as nothing else is in stdout!).
68

79

810
=== "Hera"
@@ -20,11 +22,18 @@
2022
json.dump([{"value": i} for i in range(10)], sys.stdout)
2123

2224

23-
@script(outputs=[Parameter(name="value", value_from=ValueFrom(path="/tmp/value"))])
24-
def fanout(object: dict):
25-
print("Received object: {object}!".format(object=object))
26-
# Output the content of the "value" key in the object
27-
value = object["value"]
25+
@script(
26+
outputs=[
27+
Parameter(
28+
name="value",
29+
value_from=ValueFrom(path="/tmp/value"),
30+
)
31+
]
32+
)
33+
def fanout(my_dict: dict):
34+
print("Received object: {my_dict}!".format(my_dict=my_dict))
35+
# Output the content of the "value" key in the dict
36+
value = my_dict["value"]
2837
with open("/tmp/value", "w") as f:
2938
f.write(str(value))
3039

@@ -34,12 +43,11 @@
3443
print("Received values: {values}!".format(values=values))
3544

3645

37-
# assumes you used `hera.set_global_token` and `hera.set_global_host` so that the workflow can be submitted
38-
with Workflow(generate_name="dynamic-fanout-fanin", entrypoint="d") as w:
46+
with Workflow(generate_name="fan-in-", entrypoint="d") as w:
3947
with DAG(name="d"):
4048
g = generate()
4149
fout = fanout(with_param=g.result)
42-
fin = fanin(arguments=fout.get_parameter("value").with_name("values"))
50+
fin = fanin(arguments={"values": fout.get_parameter("value")})
4351
g >> fout >> fin
4452
```
4553

@@ -49,7 +57,7 @@
4957
apiVersion: argoproj.io/v1alpha1
5058
kind: Workflow
5159
metadata:
52-
generateName: dynamic-fanout-fanin
60+
generateName: fan-in-
5361
spec:
5462
entrypoint: d
5563
templates:
@@ -64,7 +72,7 @@
6472
withParam: '{{tasks.generate.outputs.result}}'
6573
arguments:
6674
parameters:
67-
- name: object
75+
- name: my_dict
6876
value: '{{item}}'
6977
- name: fanin
7078
depends: fanout
@@ -88,7 +96,7 @@
8896
- name: fanout
8997
inputs:
9098
parameters:
91-
- name: object
99+
- name: my_dict
92100
outputs:
93101
parameters:
94102
- name: value
@@ -101,11 +109,11 @@
101109
import sys
102110
sys.path.append(os.getcwd())
103111
import json
104-
try: object = json.loads(r'''{{inputs.parameters.object}}''')
105-
except: object = r'''{{inputs.parameters.object}}'''
112+
try: my_dict = json.loads(r'''{{inputs.parameters.my_dict}}''')
113+
except: my_dict = r'''{{inputs.parameters.my_dict}}'''
106114

107-
print('Received object: {object}!'.format(object=object))
108-
value = object['value']
115+
print('Received object: {my_dict}!'.format(my_dict=my_dict))
116+
value = my_dict['value']
109117
with open('/tmp/value', 'w') as f:
110118
f.write(str(value))
111119
command:

docs/examples/workflows/loops/dynamic_fanout_extra_kwargs.md renamed to docs/examples/workflows/loops/fanout_extra_kwargs.md

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
# Dynamic Fanout Extra Kwargs
1+
# Fanout Extra Kwargs
22

33

44

5-
This example showcases how clients can use Hera to dynamically generate tasks that process outputs from one task in
6-
parallel. This is useful for batch jobs and instances where clients do not know ahead of time how many tasks/entities
7-
they may need to process. In addition to the fanout, this example showcases how one can set up extra parameters for
8-
the job to dictate what the fanout should execute over.
5+
This example shows how to use a fan-out over one argument, while keeping the others the same.
96

107

118
=== "Hera"
@@ -19,8 +16,8 @@ the job to dictate what the fanout should execute over.
1916
import json
2017
import sys
2118

22-
# this can be anything! e.g fetch from some API, then in parallel process all entities; chunk database records
23-
# and process them in parallel, etc.
19+
# this can be anything! e.g fetch from some API, then in parallel process
20+
# all entities; chunk database records and process them in parallel, etc.
2421
json.dump([i for i in range(10)], sys.stdout)
2522

2623

@@ -35,23 +32,34 @@ the job to dictate what the fanout should execute over.
3532
)
3633

3734

38-
# assumes you used `hera.set_global_token` and `hera.set_global_host` so that the workflow can be submitted
3935
with Workflow(generate_name="dynamic-fanout-", entrypoint="d") as w:
4036
with DAG(name="d"):
4137
g = generate()
42-
# the following fanout will occur over the items in the list that is returned from the generate script
43-
# the `extra_param1` will take the `hello world` value while `extra_param2` will hold the default value of 42
44-
c1 = consume(name="c1", with_param=g.result, arguments={"value": "{{item}}", "extra_param1": "hello world"})
4538

46-
# the following fanout will occur over the items in the list that is returned from the generate script
47-
# the `extra_param1` will take the `hello world` value while `extra_param2` will hold the default value of 123
39+
# We use `value` to fan-out over the values from `generate`, while the
40+
# other arguments remain the same for all fan-out tasks.
41+
# `extra_param1` is set here, while `extra_param1` has a default
42+
# value of 42 in the script
43+
c1 = consume(
44+
name="c1",
45+
with_param=g.result,
46+
arguments={
47+
"value": "{{item}}",
48+
"extra_param1": "hello world",
49+
},
50+
)
51+
52+
# Here is the same fan-out, except we are also setting `extra_param2`
4853
c2 = consume(
4954
name="c2",
5055
with_param=g.result,
51-
arguments={"value": "{{item}}", "extra_param1": "hello world", "extra_param2": "123"},
56+
arguments={
57+
"value": "{{item}}",
58+
"extra_param1": "hello world",
59+
"extra_param2": "123",
60+
},
5261
)
53-
g >> c1
54-
g >> c2
62+
g >> [c1, c2]
5563
```
5664

5765
=== "YAML"

docs/examples/workflows/loops/dynamic_fanout_json_payload.md renamed to docs/examples/workflows/loops/json_payload_fanout.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
# Dynamic Fanout Json Payload
1+
# Json Payload Fanout
22

33

44

5-
This example showcases how clients can use Hera to dynamically generate tasks that process outputs from one task in
6-
parallel. This is useful for batch jobs and instances where clients do not know ahead of time how many tasks/entities
7-
they may need to process. The fanout occurs over independent JSON payloads coming from the generate script
5+
This example shows how you can fan-out over a JSON payload (a JSON list of dicts), and let Hera match the arguments for you.
86

97

108
=== "Hera"
@@ -18,8 +16,8 @@ they may need to process. The fanout occurs over independent JSON payloads comin
1816
import json
1917
import sys
2018

21-
# this can be anything! e.g fetch from some API, then in parallel process all entities; chunk database records
22-
# and process them in parallel, etc.
19+
# this can be anything! e.g fetch from some API, then in parallel process
20+
# all entities; chunk database records and process them in parallel, etc.
2321
json.dump([{"p1": i + 1, "p2": i + 2, "p3": i + 3} for i in range(10)], sys.stdout)
2422

2523

@@ -28,8 +26,7 @@ they may need to process. The fanout occurs over independent JSON payloads comin
2826
print("Received p1={p1}, p2={p2}, p3={p3}".format(p1=p1, p2=p2, p3=p3))
2927

3028

31-
# assumes you used `hera.set_global_token` and `hera.set_global_host` so that the workflow can be submitted
32-
with Workflow(generate_name="dynamic-fanout-", entrypoint="d") as w:
29+
with Workflow(generate_name="json-payload-fanout-", entrypoint="d") as w:
3330
with DAG(name="d"):
3431
g = generate()
3532
c = consume(with_param=g.result)
@@ -42,7 +39,7 @@ they may need to process. The fanout occurs over independent JSON payloads comin
4239
apiVersion: argoproj.io/v1alpha1
4340
kind: Workflow
4441
metadata:
45-
generateName: dynamic-fanout-
42+
generateName: json-payload-fanout-
4643
spec:
4744
entrypoint: d
4845
templates:

0 commit comments

Comments
 (0)