Skip to content

Commit 85d4cfa

Browse files
Support data, http, resource, and suspend templates in decorator syntax (#1501)
**Pull Request Checklist** - [x] Tests added - [x] Documentation/examples added - [x] [Good commit messages](https://cbea.ms/git-commit/) and/or PR title **Description of PR** Currently, decorator syntax only supports `steps`, `dags`, `script`, and `container`. This PR will add supports on `data`, `http`, `resource`, and `suspend_template`. --------- Signed-off-by: Ukjae Jeong <[email protected]> Signed-off-by: Ukjae Jeong <[email protected]> Signed-off-by: Elliot Gunton <[email protected]> Co-authored-by: Elliot Gunton <[email protected]>
1 parent fed792c commit 85d4cfa

21 files changed

+799
-32
lines changed

docs/examples/workflows/experimental/new_container_decorator.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ This example shows the use of the container decorator and special Input/Output c
3939
# We then use the decorators of the `Workflow` object
4040
# to set the entrypoint and create a Container template
4141
@w.set_entrypoint
42-
@w.container(
42+
@w.container_template(
4343
image="busybox",
4444
command=["sh", "-c"],
4545
args=["echo Hello {{inputs.parameters.user}} | tee /tmp/hello_world.txt"],
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# New Data Decorator
2+
3+
4+
5+
This example shows the use of the data decorator and special Input/Output classes.
6+
7+
8+
=== "Hera"
9+
10+
```python linenums="1"
11+
from typing_extensions import Annotated
12+
13+
from hera.expr import g, it
14+
from hera.shared import global_config
15+
from hera.workflows import (
16+
Artifact,
17+
Input,
18+
Output,
19+
S3Artifact,
20+
Workflow,
21+
)
22+
23+
global_config.experimental_features["decorator_syntax"] = True
24+
25+
26+
# We start by defining our Workflow
27+
w = Workflow(generate_name="data-workflow-")
28+
29+
30+
# This defines the template's inputs
31+
class MyInput(Input):
32+
bucket_name: str = "my-bucket"
33+
34+
35+
class MyOutput(Output):
36+
file: Annotated[str, Artifact(path="/file")]
37+
38+
39+
# We then use the decorators of the `Workflow` object
40+
# to set the entrypoint and create a Data template
41+
@w.set_entrypoint
42+
@w.data_template(
43+
source=S3Artifact(name="test-bucket", bucket=f"{g.inputs.parameters.bucket_name:$}"),
44+
transformations=[g.data.filter(it.ends_with("main.log"))], # type: ignore
45+
)
46+
def basic_hello_world(my_input: MyInput) -> MyOutput: ...
47+
```
48+
49+
=== "YAML"
50+
51+
```yaml linenums="1"
52+
apiVersion: argoproj.io/v1alpha1
53+
kind: Workflow
54+
metadata:
55+
generateName: data-workflow-
56+
spec:
57+
entrypoint: basic-hello-world
58+
templates:
59+
- name: basic-hello-world
60+
data:
61+
transformation:
62+
- expression: filter(data, {# endsWith 'main.log'})
63+
source:
64+
artifactPaths:
65+
name: test-bucket
66+
s3:
67+
bucket: '{{inputs.parameters.bucket_name}}'
68+
inputs:
69+
parameters:
70+
- name: bucket_name
71+
default: my-bucket
72+
outputs:
73+
artifacts:
74+
- name: file
75+
path: /file
76+
```
77+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# New Http Decorator
2+
3+
4+
5+
This example shows the use of the http decorator and special Input/Output classes.
6+
7+
8+
=== "Hera"
9+
10+
```python linenums="1"
11+
from typing_extensions import Annotated
12+
13+
from hera.expr import g
14+
from hera.shared import global_config
15+
from hera.workflows import Input, Output, Parameter, Workflow
16+
from hera.workflows.models import SuppliedValueFrom, ValueFrom
17+
18+
global_config.experimental_features["decorator_syntax"] = True
19+
20+
21+
# We start by defining our Workflow
22+
w = Workflow(generate_name="http-workflow-")
23+
24+
25+
# This defines the template's inputs
26+
class MyInput(Input):
27+
url: str = "https://example.com"
28+
29+
30+
class MyOutput(Output):
31+
approve: Annotated[
32+
str,
33+
Parameter(
34+
name="approve",
35+
value_from=ValueFrom(
36+
supplied=SuppliedValueFrom(),
37+
),
38+
),
39+
]
40+
41+
42+
# We then use the decorators of the `Workflow` object
43+
# to set the entrypoint and create a HTTP template
44+
@w.set_entrypoint
45+
@w.http_template(
46+
timeout_seconds=20,
47+
url=f"{g.inputs.parameters.url:$}",
48+
method="GET",
49+
headers=[{"name": "x-header-name", "value": "test-value"}],
50+
success_condition=str(g.response.body.contains("google")), # type: ignore
51+
body="test body",
52+
)
53+
def basic_hello_world(my_input: MyInput) -> MyOutput: ...
54+
```
55+
56+
=== "YAML"
57+
58+
```yaml linenums="1"
59+
apiVersion: argoproj.io/v1alpha1
60+
kind: Workflow
61+
metadata:
62+
generateName: http-workflow-
63+
spec:
64+
entrypoint: basic-hello-world
65+
templates:
66+
- name: basic-hello-world
67+
http:
68+
body: test body
69+
method: GET
70+
successCondition: response.body contains 'google'
71+
timeoutSeconds: 20
72+
url: '{{inputs.parameters.url}}'
73+
headers:
74+
- name: x-header-name
75+
value: test-value
76+
inputs:
77+
parameters:
78+
- name: url
79+
default: https://example.com
80+
outputs:
81+
parameters:
82+
- name: approve
83+
valueFrom:
84+
supplied: {}
85+
```
86+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# New Resource Decorator
2+
3+
4+
5+
This example shows the use of the resource decorator and special Input/Output classes.
6+
7+
8+
=== "Hera"
9+
10+
```python linenums="1"
11+
from typing_extensions import Annotated
12+
13+
from hera.shared import global_config
14+
from hera.workflows import Input, Output, Parameter, Workflow
15+
16+
global_config.experimental_features["decorator_syntax"] = True
17+
18+
19+
# We start by defining our Workflow
20+
w = Workflow(generate_name="resource-workflow-")
21+
22+
23+
# This defines the template's inputs
24+
class MyInput(Input):
25+
pvc_size: Annotated[
26+
int,
27+
Parameter(name="pvc-size"),
28+
] = 10
29+
30+
31+
class MyOutput(Output):
32+
pvc_name: Annotated[
33+
str,
34+
Parameter(
35+
name="pvc-name",
36+
value_from={"jsonPath": "{.metadata.name}"},
37+
),
38+
]
39+
40+
41+
# We then use the decorators of the `Workflow` object
42+
# to set the entrypoint and create a Resouruce template
43+
@w.set_entrypoint
44+
@w.resource_template(
45+
action="create",
46+
set_owner_reference=True,
47+
manifest="""apiVersion: v1
48+
kind: PersistentVolumeClaim
49+
metadata:
50+
generateName: pvc-example-
51+
spec:
52+
accessModes: ['ReadWriteOnce', 'ReadOnlyMany']
53+
resources:
54+
requests:
55+
storage: '{{inputs.parameters.pvc-size}}'
56+
""",
57+
)
58+
def basic_hello_world(my_input: MyInput) -> MyOutput: ...
59+
```
60+
61+
=== "YAML"
62+
63+
```yaml linenums="1"
64+
apiVersion: argoproj.io/v1alpha1
65+
kind: Workflow
66+
metadata:
67+
generateName: resource-workflow-
68+
spec:
69+
entrypoint: basic-hello-world
70+
templates:
71+
- name: basic-hello-world
72+
inputs:
73+
parameters:
74+
- name: pvc-size
75+
default: '10'
76+
outputs:
77+
parameters:
78+
- name: pvc-name
79+
valueFrom:
80+
jsonPath: '{.metadata.name}'
81+
resource:
82+
action: create
83+
manifest: |
84+
apiVersion: v1
85+
kind: PersistentVolumeClaim
86+
metadata:
87+
generateName: pvc-example-
88+
spec:
89+
accessModes: ['ReadWriteOnce', 'ReadOnlyMany']
90+
resources:
91+
requests:
92+
storage: '{{inputs.parameters.pvc-size}}'
93+
setOwnerReference: true
94+
```
95+
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# New Suspend Decorator
2+
3+
4+
5+
This example shows the use of the suspend decorator and special Input/Output classes.
6+
7+
8+
=== "Hera"
9+
10+
```python linenums="1"
11+
from typing_extensions import Annotated
12+
13+
from hera.shared import global_config
14+
from hera.workflows import Input, Output, Parameter, Workflow
15+
from hera.workflows.models import SuppliedValueFrom, ValueFrom
16+
17+
global_config.experimental_features["decorator_syntax"] = True
18+
19+
20+
# We start by defining our Workflow
21+
w = Workflow(generate_name="suspend-workflow-")
22+
23+
24+
# This defines the template's inputs
25+
class MyInput(Input):
26+
approve: Annotated[
27+
str,
28+
Parameter(
29+
description="Choose YES to continue workflow and deploy to production",
30+
enum=["YES", "NO"],
31+
),
32+
] = "NO"
33+
34+
35+
class MyOutput(Output):
36+
approve: Annotated[
37+
str,
38+
Parameter(
39+
name="approve",
40+
value_from=ValueFrom(
41+
supplied=SuppliedValueFrom(),
42+
),
43+
),
44+
]
45+
46+
47+
# We then use the decorators of the `Workflow` object
48+
# to set the entrypoint and create a Suspend template
49+
@w.set_entrypoint
50+
@w.suspend_template()
51+
def basic_hello_world(my_input: MyInput) -> MyOutput: ...
52+
```
53+
54+
=== "YAML"
55+
56+
```yaml linenums="1"
57+
apiVersion: argoproj.io/v1alpha1
58+
kind: Workflow
59+
metadata:
60+
generateName: suspend-workflow-
61+
spec:
62+
entrypoint: basic-hello-world
63+
templates:
64+
- name: basic-hello-world
65+
inputs:
66+
parameters:
67+
- name: approve
68+
default: 'NO'
69+
description: Choose YES to continue workflow and deploy to production
70+
enum:
71+
- 'YES'
72+
- 'NO'
73+
outputs:
74+
parameters:
75+
- name: approve
76+
valueFrom:
77+
supplied: {}
78+
suspend: {}
79+
```
80+

docs/user-guides/decorators.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ The decorators introduced in v5.16 are members of the `Workflow` class, meaning
1010

1111
* `dag`
1212
* `steps`
13-
* `container`
1413
* `script`
14+
* `container_template` (replacing the deprecated `container`)
15+
* `data_template`
16+
* `http_template`
17+
* `resource_template`
18+
* `suspend_template`
1519
* `set_entrypoint`
1620

1721
If you want to declare inputs and outputs in your templates, you must use the special `Input` and `Output` Pydantic
@@ -44,9 +48,9 @@ run as many _dependent_ templates in parallel as possible then use a DAG, which
4448
first. If you want to run templates sequentially, and have more control over the running order and when to parallelise,
4549
use `steps`.
4650

47-
## `container`
51+
## `container_template`
4852

49-
The `container` decorator is a convenient way to declare your container templates in Python, though the feature set is
53+
The `container_template` decorator is a convenient way to declare your container templates in Python, though the feature set is
5054
limited. You can specify the `command` and `args` in the decorator arguments, and the inputs and outputs in the function
5155
signature. You can leave the function as a stub or add some code to be able to run the function locally - consider it as
5256
a "mockable" function.
@@ -61,6 +65,16 @@ decorator.
6165
Using this decorator will enforce usage of the `RunnerScriptConstructor`, so you must ensure you use an image built from
6266
your code.
6367

68+
## `data_template`, `http_template`, `resource_template`, and `suspend_template`
69+
70+
These decorators allow you to define additional Argo Workflow template types in a Pythonic way. For more information on
71+
how to use these templates, please refer to the relevant examples.
72+
73+
- [`data_template` decorator example](../examples/workflows/experimental/new_data_decorator.md)
74+
- [`http_template` decorator example](../examples/workflows/experimental/new_http_decorator.md)
75+
- [`resource_template` decorator example](../examples/workflows/experimental/new_resource_decorator.md)
76+
- [`suspend_template` decorator example](../examples/workflows/experimental/new_suspend_decorator.md)
77+
6478
## `set_entrypoint`
6579

6680
The `set_entrypoint` decorator is a simple decorator with no arguments, used to set the `entrypoint` of the `Workflow` that you're declaring.

0 commit comments

Comments
 (0)