From 8771cc67b8342f0790c53d5bc6ceb42de6560687 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 28 Oct 2026 10:36:26 -0500 Subject: [PATCH 1/2] feat(argo-workflows): configure enroll-server via ConfigMap --- .../workflows/lint-workflow-templates.yaml | 22 ------- .../components/argo-events-workflows.md | 59 +++++++++++++++++++ workflows/argo-events/_lint-stubs/README.md | 34 ----------- .../_lint-stubs/enroll-post-hook.yaml | 23 -------- .../workflowtemplates/enroll-server.yaml | 30 ++++++---- 5 files changed, 77 insertions(+), 91 deletions(-) delete mode 100644 workflows/argo-events/_lint-stubs/README.md delete mode 100644 workflows/argo-events/_lint-stubs/enroll-post-hook.yaml diff --git a/.github/workflows/lint-workflow-templates.yaml b/.github/workflows/lint-workflow-templates.yaml index 8b2b62159..c737b5620 100644 --- a/.github/workflows/lint-workflow-templates.yaml +++ b/.github/workflows/lint-workflow-templates.yaml @@ -40,28 +40,6 @@ jobs: kubectl create namespace argo-events kubectl -n argo-events apply -f workflows/argo-events/workflowtemplates/ - - name: Deploy lint stubs for externally-provided templateRefs - run: | - # Some templates reference WorkflowTemplates that live outside this - # repo (site-specific extensions installed from undercloud-deploy, - # opted into per site). `argo lint` resolves templateRef names against - # the cluster, so those references fail to resolve here unless a - # placeholder is present. Any stub placed in the lint-stubs directory - # is deployed so the reference resolves; the stub bodies are - # deliberately minimal and are never shipped. Add a new external - # reference by dropping a file in that directory -- no change here. - stub_dir="workflows/argo-events/_lint-stubs" - shopt -s nullglob - stubs=("$stub_dir"/*.yaml) - if [ ${#stubs[@]} -gt 0 ]; then - for stub in "${stubs[@]}"; do - echo "Deploying lint stub: $stub" - kubectl -n argo-events apply -f "$stub" - done - else - echo "No lint stubs to deploy." - fi - - name: Argo Lint Workflow Template run: | set +e diff --git a/docs/deploy-guide/components/argo-events-workflows.md b/docs/deploy-guide/components/argo-events-workflows.md index c86cf51e2..8db019067 100644 --- a/docs/deploy-guide/components/argo-events-workflows.md +++ b/docs/deploy-guide/components/argo-events-workflows.md @@ -44,3 +44,62 @@ Required or commonly required items: Optional additions: - `Additional integration Secrets`: Add more Secrets beside the API token if individual workflows need their own credentials or endpoints. + +## Enroll Server Configuration + +Configure site-specific behavior for the `enroll-server` workflow by creating +an `enroll-server-config` ConfigMap in the `argo-events` namespace. The +`post_enroll_workflows` value is a JSON array of `WorkflowTemplate` names to run +after a server is enrolled: + +```yaml title="enroll-server-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: enroll-server-config + namespace: argo-events + labels: + workflows.argoproj.io/configmap-type: Parameter +data: + post_enroll_workflows: | + ["notify-enrollment", "configure-monitoring"] +``` + +The `workflows.argoproj.io/configmap-type: Parameter` label is required so the +Argo Workflows controller discovers the ConfigMap as a parameter source. + +Each named `WorkflowTemplate` must be in the same namespace and expose a `run` +template that accepts the enrolled server's `node_uuid`: + +{% raw %} + +```yaml title="workflowtemplate-notify-enrollment.yaml" +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: notify-enrollment + namespace: argo-events +spec: + templates: + - name: run + inputs: + parameters: + - name: node_uuid + container: + image: example.com/notify-enrollment:1.0.0 + args: + - "{{inputs.parameters.node_uuid}}" +``` + +{% endraw %} + +The hooks run in parallel as steps of the existing `enroll-server` workflow; +they do not create child `Workflow` resources. A failed hook fails the +post-enrollment step. If the ConfigMap or its key is absent, the hook list +defaults to empty and enrollment completes without running hooks. + +Only the referenced `run` template is imported by `templateRef`. Put required +pod configuration on that template, or arrange for it on the `enroll-server` +workflow; settings elsewhere in the referenced `WorkflowTemplate` spec are not +inherited. Hook pods use the service account of the parent `enroll-server` +workflow. diff --git a/workflows/argo-events/_lint-stubs/README.md b/workflows/argo-events/_lint-stubs/README.md deleted file mode 100644 index 99f973b91..000000000 --- a/workflows/argo-events/_lint-stubs/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Lint stubs - -Placeholder `WorkflowTemplate` manifests used **only** by the -`Lint Workflow Templates` GitHub Actions workflow -(`.github/workflows/lint-workflow-templates.yaml`). - -## Why this exists - -Some templates in `../workflowtemplates/` reference WorkflowTemplates that are -**not** part of this repository. They are site-specific extensions installed -from `undercloud-deploy`, and only where a site opts in. For example, -`enroll-server.yaml` references `enroll-post-hook` via `templateRef`. - -`argo lint` resolves `templateRef` names against the cluster. In CI the lint -runs against a throwaway kind cluster that only has this repo's templates -deployed, so any external reference fails to resolve and the lint reports a -false failure. - -To keep the lint meaningful without weakening it, the CI job deploys every stub -in this directory into the lint namespace before running `argo lint`. That lets -the external references resolve against a minimal placeholder. - -## Rules - -- These files are **lint-only**. They are never deployed to a real environment. -- Keep each stub minimal: the correct `kind`, `metadata.name`, and the - referenced template name plus its declared inputs are all that matter. -- This directory is prefixed with `_` so it is not picked up by the lint loop, - which globs `../workflowtemplates/*.yaml`. - -## Adding a new external reference - -When a template here starts referencing an out-of-repo WorkflowTemplate, add a -matching stub file in this directory. No change to the CI workflow is needed. diff --git a/workflows/argo-events/_lint-stubs/enroll-post-hook.yaml b/workflows/argo-events/_lint-stubs/enroll-post-hook.yaml deleted file mode 100644 index 27f12dae8..000000000 --- a/workflows/argo-events/_lint-stubs/enroll-post-hook.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Lint-only placeholder. NOT deployed to any real environment. -# -# enroll-server.yaml references the `enroll-post-hook` WorkflowTemplate via -# templateRef. The real implementation is site-specific and installed from -# undercloud-deploy only where a site opts in (e.g. SL1); it is intentionally -# absent from this repo. `argo lint` resolves templateRef names against the -# cluster, so this minimal stub is deployed into the lint cluster purely so the -# reference resolves. Its body is irrelevant to linting -- only the kind, name, -# and the `run` template with a `node_uuid` input matter. -apiVersion: argoproj.io/v1alpha1 -kind: WorkflowTemplate -metadata: - name: enroll-post-hook -spec: - entrypoint: run - templates: - - name: run - inputs: - parameters: - - name: node_uuid - container: - image: busybox - command: ["true"] diff --git a/workflows/argo-events/workflowtemplates/enroll-server.yaml b/workflows/argo-events/workflowtemplates/enroll-server.yaml index 396acd325..675411356 100644 --- a/workflows/argo-events/workflowtemplates/enroll-server.yaml +++ b/workflows/argo-events/workflowtemplates/enroll-server.yaml @@ -6,6 +6,10 @@ metadata: workflows.argoproj.io/title: Perform server discovery and update Ironic workflows.argoproj.io/description: | Defined in `workflows/argo-events/workflowtemplates/enroll-server.yaml` + + The workflow reads site-specific configuration from the + `enroll-server-config` ConfigMap. After enrollment, it runs the templates + listed in `post_enroll_workflows`. kind: WorkflowTemplate spec: serviceAccountName: workflow @@ -35,16 +39,20 @@ spec: value: calvin - name: external_cmdb_id value: "" - # Optional post-enrollment hook. "false" (default) = no hook, behaviour - # unchanged. A site opts in by setting "true" AND installing an - # `enroll-post-hook` WorkflowTemplate (entrypoint `run`, input param - # `node_uuid`) in the workflow namespace. The ref name is intentionally - # static: Argo resolves templateRef.name at parse time, so it cannot be a - # parameter. Each site decides what enroll-post-hook does. - - name: post_enroll_hook - value: "false" templates: - name: main + inputs: + parameters: + # A site can configure this value with a JSON array of + # WorkflowTemplate names. Each WorkflowTemplate must expose a `run` + # template that accepts `node_uuid`. If the ConfigMap is absent, no + # post-enrollment templates are run. + - name: post_enroll_workflows + valueFrom: + configMapKeyRef: + name: enroll-server-config + key: post_enroll_workflows + default: "[]" outputs: parameters: - name: node_uuid @@ -53,17 +61,15 @@ spec: steps: - - name: enroll-server template: enroll-server - # Runs only when the site enabled the hook. This step is a generic - # delegator and does not know about any specific integration. - - name: post-enroll - when: "{{workflow.parameters.post_enroll_hook}} == true" templateRef: - name: enroll-post-hook + name: "{{item}}" template: run arguments: parameters: - name: node_uuid value: "{{steps.enroll-server.outputs.parameters.node_uuid}}" + withParam: "{{inputs.parameters.post_enroll_workflows}}" - name: enroll-server outputs: parameters: From 5880001818546eb036b13b3e86a62e7bf81105d6 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 28 Oct 2026 12:50:23 -0500 Subject: [PATCH 2/2] feat(argo-workflows): add network device post-enroll hooks --- .../components/argo-events-workflows.md | 31 ++++++++----- .../tests/test_enroll_fw.py | 45 +++++++++++++++++-- .../tests/test_enroll_netdev.py | 31 +++++++++++++ .../tests/test_netdev_reconciler.py | 9 ++-- .../understack_workflows/main/enroll_fw.py | 16 +++++-- .../main/enroll_netdev.py | 10 ++++- .../understack_workflows/netdev_reconciler.py | 5 ++- .../workflowtemplates/enroll-fw.yaml | 37 +++++++++++++++ .../workflowtemplates/enroll-netdev.yaml | 37 +++++++++++++++ 9 files changed, 196 insertions(+), 25 deletions(-) diff --git a/docs/deploy-guide/components/argo-events-workflows.md b/docs/deploy-guide/components/argo-events-workflows.md index 8db019067..55b8597e7 100644 --- a/docs/deploy-guide/components/argo-events-workflows.md +++ b/docs/deploy-guide/components/argo-events-workflows.md @@ -45,12 +45,20 @@ Optional additions: - `Additional integration Secrets`: Add more Secrets beside the API token if individual workflows need their own credentials or endpoints. -## Enroll Server Configuration +## Enrollment Workflow Configuration -Configure site-specific behavior for the `enroll-server` workflow by creating -an `enroll-server-config` ConfigMap in the `argo-events` namespace. The -`post_enroll_workflows` value is a JSON array of `WorkflowTemplate` names to run -after a server is enrolled: +Each enrollment workflow can run site-specific templates after it enrolls an +Ironic node. Configure a workflow by creating its ConfigMap in the `argo-events` +namespace: + +| Workflow | ConfigMap | +| --- | --- | +| `enroll-server` | `enroll-server-config` | +| `enroll-fw` | `enroll-fw-config` | +| `enroll-netdev` | `enroll-netdev-config` | + +The `post_enroll_workflows` value is a JSON array of `WorkflowTemplate` names. +For example, the server configuration is: ```yaml title="enroll-server-config.yaml" apiVersion: v1 @@ -93,13 +101,12 @@ spec: {% endraw %} -The hooks run in parallel as steps of the existing `enroll-server` workflow; -they do not create child `Workflow` resources. A failed hook fails the -post-enrollment step. If the ConfigMap or its key is absent, the hook list -defaults to empty and enrollment completes without running hooks. +The hooks run in parallel as steps of the existing enrollment workflow; they do +not create child `Workflow` resources. A failed hook fails the post-enrollment +step. If the ConfigMap or its key is absent, the hook list defaults to empty and +enrollment completes without running hooks. Only the referenced `run` template is imported by `templateRef`. Put required -pod configuration on that template, or arrange for it on the `enroll-server` +pod configuration on that template, or arrange for it on the parent enrollment workflow; settings elsewhere in the referenced `WorkflowTemplate` spec are not -inherited. Hook pods use the service account of the parent `enroll-server` -workflow. +inherited. Hook pods use the service account of the parent workflow. diff --git a/python/understack-workflows/tests/test_enroll_fw.py b/python/understack-workflows/tests/test_enroll_fw.py index 7c1f8e6ef..89adefcc6 100644 --- a/python/understack-workflows/tests/test_enroll_fw.py +++ b/python/understack-workflows/tests/test_enroll_fw.py @@ -94,10 +94,13 @@ def _patch_paths(update_node_call) -> dict[str, Any]: def test_enroll_fw_hands_metadata_to_the_engine(mocker): _mock_client(mocker, node=None) # not found -> new node -> engine enrolls - engine = mocker.patch.object(enroll_fw.netdev_reconciler, "enroll") + engine = mocker.patch.object( + enroll_fw.netdev_reconciler, "enroll", return_value="node-123" + ) - enroll_fw.enroll_fw(**BASE_ARGS, **FW_FIELDS) + node_uuid = enroll_fw.enroll_fw(**BASE_ARGS, **FW_FIELDS) + assert node_uuid == "node-123" engine.assert_called_once_with( name="PA1410-026701008071", physical_network="f20-1-network", @@ -219,8 +222,9 @@ def test_active_node_updates_metadata_when_ports_match(mocker): client = _mock_client(mocker, node=_node(), node_ports=[_actual_port()]) engine = mocker.patch.object(enroll_fw.netdev_reconciler, "enroll") - enroll_fw.enroll_fw(**BASE_ARGS, **FW_FIELDS) + node_uuid = enroll_fw.enroll_fw(**BASE_ARGS, **FW_FIELDS) + assert node_uuid == "node-uuid" engine.assert_not_called() client.update_node.assert_called_once() paths = _patch_paths(client.update_node.call_args) @@ -365,6 +369,41 @@ def test_main_passes_default_or_explicit_driver(mocker, driver_args, expected): assert engine.call_args.kwargs["driver"] == expected +def test_main_emits_node_uuid_only_after_success(mocker, tmp_path): + output = tmp_path / "node-uuid" + mocker.patch.object(enroll_fw.helpers, "setup_logger") + mocker.patch( + "sys.argv", + [ + "enroll-fw", + "--name", + "fw1", + "--physical-network", + "net", + "--ports", + "[]", + "--resource-class", + "pa1410", + "--management-switch", + "switch1", + "--management-switch-port", + "eth1", + "--node-uuid-output", + str(output), + ], + ) + engine = mocker.patch.object(enroll_fw, "enroll_fw", return_value="node-123") + + enroll_fw.main() + + assert output.read_text() == "node-123\n" + output.unlink() + engine.side_effect = RuntimeError("enrollment failed") + with pytest.raises(RuntimeError, match="enrollment failed"): + enroll_fw.main() + assert not output.exists() + + def test_argument_parser_requires_resource_class(): parser = enroll_fw.argument_parser() with pytest.raises(SystemExit): diff --git a/python/understack-workflows/tests/test_enroll_netdev.py b/python/understack-workflows/tests/test_enroll_netdev.py index 0acb10bf1..dc652d2d1 100644 --- a/python/understack-workflows/tests/test_enroll_netdev.py +++ b/python/understack-workflows/tests/test_enroll_netdev.py @@ -1,3 +1,5 @@ +import pytest + from understack_workflows.main import enroll_netdev @@ -26,3 +28,32 @@ def test_argument_parser_defaults_resource_class_to_generic(): "intf": "e1", } ] + + +def test_main_emits_node_uuid_only_after_success(mocker, tmp_path): + output = tmp_path / "node-uuid" + mocker.patch( + "sys.argv", + [ + "enroll-netdev", + "--name", + "leaf01", + "--physical-network", + "net", + "--ports", + "[]", + "--node-uuid-output", + str(output), + ], + ) + mocker.patch.object(enroll_netdev.helpers, "setup_logger") + enroll = mocker.patch.object(enroll_netdev, "enroll", return_value="node-123") + + enroll_netdev.main() + + assert output.read_text() == "node-123\n" + output.unlink() + enroll.side_effect = RuntimeError("enrollment failed") + with pytest.raises(RuntimeError, match="enrollment failed"): + enroll_netdev.main() + assert not output.exists() diff --git a/python/understack-workflows/tests/test_netdev_reconciler.py b/python/understack-workflows/tests/test_netdev_reconciler.py index c78fdeec9..809857a60 100644 --- a/python/understack-workflows/tests/test_netdev_reconciler.py +++ b/python/understack-workflows/tests/test_netdev_reconciler.py @@ -80,8 +80,9 @@ def test_enroll_creates_node_ports_logs_and_makes_available(mocker, caplog): return_value=fake_ironic, ) - netdev_reconciler.enroll(**ENROLL_ARGS, resource_class=None) + node_uuid = netdev_reconciler.enroll(**ENROLL_ARGS, resource_class=None) + assert node_uuid == node.uuid fake_ironic.node.create.assert_called_once_with( automated_clean=False, driver="netdev", @@ -260,8 +261,9 @@ def test_enroll_patches_physical_network_drift_on_existing_port(mocker): return_value=fake_ironic, ) - netdev_reconciler.enroll(**ENROLL_ARGS) + node_uuid = netdev_reconciler.enroll(**ENROLL_ARGS) + assert node_uuid == "node-123" fake_ironic.port.update.assert_called_once_with( "uuid-port1", [{"op": "add", "path": "/physical_network", "value": "f20-1-network"}], @@ -472,8 +474,9 @@ def test_enroll_is_true_noop_when_available_and_everything_matches(mocker): return_value=fake_ironic, ) - netdev_reconciler.enroll(**ENROLL_ARGS) + node_uuid = netdev_reconciler.enroll(**ENROLL_ARGS) + assert node_uuid == "node-123" # Nothing drifted: no metadata patch, no port work, no state churn. fake_ironic.node.update.assert_not_called() fake_ironic.port.update.assert_not_called() diff --git a/python/understack-workflows/understack_workflows/main/enroll_fw.py b/python/understack-workflows/understack_workflows/main/enroll_fw.py index 88c4428d3..2a415c13e 100644 --- a/python/understack-workflows/understack_workflows/main/enroll_fw.py +++ b/python/understack-workflows/understack_workflows/main/enroll_fw.py @@ -1,6 +1,7 @@ import argparse import logging import os +from pathlib import Path from ironicclient.common.apiclient import exceptions as ironic_exceptions @@ -24,7 +25,7 @@ def main() -> None: helpers.setup_logger() args = argument_parser().parse_args() - enroll_fw( + node_uuid = enroll_fw( name=args.name, physical_network=args.physical_network, ports=netdev_reconciler.parse_ports_arg(args.ports), @@ -40,6 +41,9 @@ def main() -> None: model=args.model, ) + if args.node_uuid_output: + Path(args.node_uuid_output).write_text(node_uuid + "\n") + def _require_specific_resource_class(resource_class: str | None) -> str: """Return a normalized, purpose-made resource class. @@ -90,7 +94,7 @@ def enroll_fw( serial: str = "", vendor: str = "", model: str = "", -) -> None: +) -> str: driver = (driver or "").strip() if not driver: raise ValueError("enroll-fw requires a non-empty --driver") @@ -129,12 +133,12 @@ def enroll_fw( extra=extra, properties=properties, ) - return + return node.uuid # New or re-enrollable node: the engine writes the metadata inside the # enrollment lifecycle (at create, or patched before the node becomes # available), so the node is never allocatable in an under-described state. - netdev_reconciler.enroll( + return netdev_reconciler.enroll( name=name, physical_network=physical_network, ports=ports, @@ -255,6 +259,10 @@ def argument_parser(): prog=os.path.basename(__file__), description="Enroll a firewall (defaults to the panos hardware type)", ) + parser.add_argument( + "--node-uuid-output", + help="Write node UUID to this file after successful enrollment", + ) parser.add_argument("--name", required=True, help="Ironic node name") parser.add_argument( "--physical-network", diff --git a/python/understack-workflows/understack_workflows/main/enroll_netdev.py b/python/understack-workflows/understack_workflows/main/enroll_netdev.py index 94cf547f1..f27186c87 100644 --- a/python/understack-workflows/understack_workflows/main/enroll_netdev.py +++ b/python/understack-workflows/understack_workflows/main/enroll_netdev.py @@ -1,5 +1,6 @@ import argparse import os +from pathlib import Path from understack_workflows import helpers from understack_workflows.netdev_reconciler import DEFAULT_RESOURCE_CLASS @@ -24,7 +25,7 @@ def main() -> None: helpers.setup_logger() args = argument_parser().parse_args() - enroll( + node_uuid = enroll( name=args.name, physical_network=args.physical_network, ports=parse_ports_arg(args.ports), @@ -32,12 +33,19 @@ def main() -> None: resource_class=args.resource_class, ) + if args.node_uuid_output: + Path(args.node_uuid_output).write_text(node_uuid + "\n") + def argument_parser(): parser = argparse.ArgumentParser( prog=os.path.basename(__file__), description="Run the netdev enroll workflow", ) + parser.add_argument( + "--node-uuid-output", + help="Write node UUID to this file after successful enrollment", + ) parser.add_argument("--name", required=True, help="Ironic node name") parser.add_argument( "--physical-network", diff --git a/python/understack-workflows/understack_workflows/netdev_reconciler.py b/python/understack-workflows/understack_workflows/netdev_reconciler.py index 7e7e96f8a..527dfe327 100644 --- a/python/understack-workflows/understack_workflows/netdev_reconciler.py +++ b/python/understack-workflows/understack_workflows/netdev_reconciler.py @@ -146,7 +146,7 @@ def enroll( driver_info: dict | None = None, extra: dict | None = None, properties: dict | None = None, -) -> None: +) -> str: effective_resource_class = resource_class or DEFAULT_RESOURCE_CLASS effective_driver = driver or DEFAULT_DRIVER netdev_ports = build_netdev_ports(ports) @@ -257,7 +257,7 @@ def enroll( "[node:%s] Node already available and up to date, nothing to do", node.uuid, ) - return + return node.uuid for plan in pending: apply_port_plan( @@ -274,6 +274,7 @@ def enroll( name, node.uuid, ) + return node.uuid def warn_orphan_ports( diff --git a/workflows/argo-events/workflowtemplates/enroll-fw.yaml b/workflows/argo-events/workflowtemplates/enroll-fw.yaml index 352f069bc..ed7403f63 100644 --- a/workflows/argo-events/workflowtemplates/enroll-fw.yaml +++ b/workflows/argo-events/workflowtemplates/enroll-fw.yaml @@ -9,11 +9,17 @@ metadata: Reuses enroll-netdev for the node/port lifecycle and additionally records firewall management details in driver_info and the HA mate serial in extra. Defaults to the panos hardware type. + + The workflow reads site-specific configuration from the + `enroll-fw-config` ConfigMap. After enrollment, it runs the templates + listed in `post_enroll_workflows`. kind: WorkflowTemplate spec: serviceAccountName: workflow entrypoint: main volumes: + - name: enrollment-result + emptyDir: {} - name: baremetal-manage secret: secretName: baremetal-manage @@ -50,15 +56,44 @@ spec: value: "" templates: - name: main + inputs: + parameters: + - name: post_enroll_workflows + valueFrom: + configMapKeyRef: + name: enroll-fw-config + key: post_enroll_workflows + default: "[]" + outputs: + parameters: + - name: node_uuid + valueFrom: + parameter: "{{steps.enroll-fw.outputs.parameters.node_uuid}}" steps: - - name: enroll-fw template: enroll-fw + - - name: post-enroll + templateRef: + name: "{{item}}" + template: run + arguments: + parameters: + - name: node_uuid + value: "{{steps.enroll-fw.outputs.parameters.node_uuid}}" + withParam: "{{inputs.parameters.post_enroll_workflows}}" - name: enroll-fw + outputs: + parameters: + - name: node_uuid + valueFrom: + path: /run/enrollment/node-uuid container: image: ghcr.io/rackerlabs/understack/ironic-nautobot-client:latest command: - enroll-fw args: + - --node-uuid-output + - /run/enrollment/node-uuid - --name - "{{workflow.parameters.name}}" - --physical-network @@ -86,6 +121,8 @@ spec: - --model - "{{workflow.parameters.model}}" volumeMounts: + - mountPath: /run/enrollment + name: enrollment-result - mountPath: /etc/openstack name: baremetal-manage readOnly: true diff --git a/workflows/argo-events/workflowtemplates/enroll-netdev.yaml b/workflows/argo-events/workflowtemplates/enroll-netdev.yaml index 321498872..dc4d711f3 100644 --- a/workflows/argo-events/workflowtemplates/enroll-netdev.yaml +++ b/workflows/argo-events/workflowtemplates/enroll-netdev.yaml @@ -6,11 +6,17 @@ metadata: workflows.argoproj.io/title: Enroll a network device in Ironic workflows.argoproj.io/description: | Defined in `workflows/argo-events/workflowtemplates/enroll-netdev.yaml` + + The workflow reads site-specific configuration from the + `enroll-netdev-config` ConfigMap. After enrollment, it runs the templates + listed in `post_enroll_workflows`. kind: WorkflowTemplate spec: serviceAccountName: workflow entrypoint: main volumes: + - name: enrollment-result + emptyDir: {} - name: baremetal-manage secret: secretName: baremetal-manage @@ -32,15 +38,44 @@ spec: value: generic templates: - name: main + inputs: + parameters: + - name: post_enroll_workflows + valueFrom: + configMapKeyRef: + name: enroll-netdev-config + key: post_enroll_workflows + default: "[]" + outputs: + parameters: + - name: node_uuid + valueFrom: + parameter: "{{steps.enroll-netdev.outputs.parameters.node_uuid}}" steps: - - name: enroll-netdev template: enroll-netdev + - - name: post-enroll + templateRef: + name: "{{item}}" + template: run + arguments: + parameters: + - name: node_uuid + value: "{{steps.enroll-netdev.outputs.parameters.node_uuid}}" + withParam: "{{inputs.parameters.post_enroll_workflows}}" - name: enroll-netdev + outputs: + parameters: + - name: node_uuid + valueFrom: + path: /run/enrollment/node-uuid container: image: ghcr.io/rackerlabs/understack/ironic-nautobot-client:latest command: - enroll-netdev args: + - --node-uuid-output + - /run/enrollment/node-uuid - --name - "{{workflow.parameters.name}}" - --physical-network @@ -52,6 +87,8 @@ spec: - --resource-class - "{{workflow.parameters.resource_class}}" volumeMounts: + - mountPath: /run/enrollment + name: enrollment-result - mountPath: /etc/openstack name: baremetal-manage readOnly: true