Hera Developer Features
Here we showcase more features that help developers write Workflows in Hera. Note that these features do not exist in Argo as they are specific to Hera, being a Python library.
Set Class Defaults
You are able to set basic default values for any attributes in Hera’s custom classes, such as Script
, Container
and
Workflow
, by using hera.shared.global_config.set_class_defaults
. You pass the class you want to set defaults on, and
then kwargs for all the attributes you want to set (with their respective values). For example, if you wanted to set some
default values for any Container
objects you create, you would do:
from hera.shared import global_config
from hera.workflows import Container
global_config.set_class_defaults(
Container,
image="my-image:latest",
image_pull_policy="Always",
command=["cowsay"],
)
And then use the Container
class in your Workflows as normal, but now the Container
will have pre-populated default
attributes when created:
with Workflow(name="w") as w:
do_a_cowsay = Container(name="cowsay-container", args=["Hello, world!"])
with Steps(name="steps"):
do_a_cowsay()
Notice how we do not need to set image
or command
!
Pre-Build Hooks
If class defaults don’t meet your needs, Hera also offers a pre-build hook feature through
hera.shared.register_pre_build_hook
with huge flexibility to do pre-build processing on any type of template
or
Workflow
. For example, it can be used to conditionally set the image
of a Script
, or set which cluster to submit a
Workflow
to.
To use this feature, you can write a function that takes an object of type template
or Workflow
, does some
processing on the object, then returns it.
For a simple example, we’ll write a function that adds an annotation with a key of “hera-annotation”, and value of “This workflow was written in Hera!”
from hera.shared import register_pre_build_hook
from hera.workflows import Workflow
@register_pre_build_hook
def set_workflow_default_labels(workflow: Workflow) -> Workflow:
if workflow.annotations is None:
workflow.annotations = {}
workflow.annotations["hera-annotation"] = "This workflow was written in Hera!"
return workflow
Now, any time build
is called on the Workflow (e.g. to submit it or dump it to yaml), it will add in the annotation!
Load YAML from File
Hera’s Workflow
classes offer a collection of to
and from
functions for dict
, yaml
and file
. This
means you can load YAML files and manipulate them as Hera objects!
with Workflow.from_file("./workflow.yaml") as w:
w.entrypoint = "my-new-dag-entrypoint"
with DAG(name="my-new-dag-entrypoint"):
... # Add some tasks!
w.create() # And submit to Argo directly from Hera!
The following are all valid assertions:
with Workflow(name="w") as w:
pass
assert w == Workflow.from_dict(w.to_dict())
assert w == Workflow.from_yaml(w.to_yaml())
assert w == Workflow.from_file(w.to_file())
Submit WorkflowTemplates and ClusterWorkflowTemplates as Workflows
This feature is available for WorkflowTemplates
and ClusterWorkflowTemplates
, and helps you, as a dev, iterate on
your WorkflowTemplate
until it’s ready to be deployed. Calling create_as_workflow
on a WorkflowTemplate
will
create a Workflow
on the fly which is submitted to the Argo cluster directly and given a generated name, meaning you
don’t need to first submit the WorkflowTemplate
itself! What this means is you don’t need to keep deleting your
WorkflowTemplate
and submitting it again, to then run argo submit --from WorkflowTemplate/my-wt
while iterating
on your WorkflowTemplate
.
with WorkflowTemplate(
name="my-wt",
namespace="my-namespace",
workflows_service=ws,
) as wt:
cowsay = Container(name="cowsay", image="docker/whalesay", command=["cowsay", "foo"])
with Steps(name="steps"):
cowsay()
wt.create_as_workflow(generate_name="my-wt-test-1-") # submitted and given a generated name by Argo like "my-wt-test-1-abcde"
wt.create_as_workflow() # submitted and given a generated name by Argo like "my-wtabcde"
wt.create_as_workflow() # submitted and given a generated name by Argo like "my-wtvwxyz"
generate_name
is an optional parameter in case you want to control the exact value of the generated name, similarly to
the regular Workflow
, otherwise the name of the WorkflowTemplate
will be used verbatim for generate_name
. The
Workflow submitted will always use generate_name
so that you can call it multiple times in a row without naming
conflicts.