Skip to content

Commit 155d2f4

Browse files
author
Michael Buchar
committed
feat(task): pass scoped user secret names (#319)
Forward optional secret allowlists and step-local REANA hints from CWL workflows so job submissions preserve workflow defaults, step overrides, and the documented omitted-versus-empty semantics. Closes reanahub/reana#978
1 parent 5e8a9dd commit 155d2f4

4 files changed

Lines changed: 70 additions & 1 deletion

File tree

reana_workflow_engine_cwl/cwl_reana.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from cwltool.workflow import default_make_tool
3333
from reana_commons.api_client import JobControllerAPIClient as rjc_api_client
3434
from reana_commons.config import REANA_WORKFLOW_UMASK
35+
from reana_commons.k8s.secrets import resolve_secret_names
3536

3637
from reana_workflow_engine_cwl.config import LOGGING_MODULE, MOUNT_CVMFS
3738
from reana_workflow_engine_cwl.pipeline import Pipeline
@@ -51,6 +52,7 @@ def __init__(self, **kwargs):
5152
self.service = kwargs.get(
5253
"rjc_api_client", rjc_api_client("reana-job-controller")
5354
)
55+
self.workflow_resources = kwargs.get("workflow_resources") or {}
5456
if kwargs.get("basedir") is not None:
5557
self.basedir = kwargs.get("basedir")
5658
else:
@@ -280,6 +282,9 @@ def shouldquote(x):
280282
c4p_cpu_cores = self._get_hint("c4p_cpu_cores")
281283
c4p_memory_limit = self._get_hint("c4p_memory_limit")
282284
c4p_additional_requirements = self._get_hint("c4p_additional_requirements")
285+
secret_names = resolve_secret_names(
286+
self._get_hint("secret_names"), getattr(self, "workflow_resources", {})
287+
)
283288
create_body = {
284289
"image": container,
285290
"cmd": wrapped_cmd,
@@ -293,6 +298,7 @@ def shouldquote(x):
293298
"unpacked_img": unpacked_img,
294299
"voms_proxy": voms_proxy,
295300
"rucio": rucio,
301+
"secret_names": secret_names,
296302
"htcondor_max_runtime": htcondor_max_runtime,
297303
"htcondor_accounting_group": htcondor_accounting_group,
298304
"htcondor_request_cpus": htcondor_request_cpus,
@@ -332,6 +338,7 @@ def _get_hint(self, hint_name):
332338
def run(self, runtimeContext): # noqa: C901
333339
"""Run a job."""
334340
self._setup(runtimeContext)
341+
self.workflow_resources = runtimeContext.pipeline.workflow_resources
335342

336343
env = self.environment
337344
if not os.path.exists(self.tmpdir):

reana_workflow_engine_cwl/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def main(
4747
rjc_api_client,
4848
workflow_uuid,
4949
workflow_spec,
50+
workflow_resources,
5051
workflow_inputs,
5152
operational_options,
5253
working_dir,
@@ -127,7 +128,9 @@ def main(
127128
if parsed_args.debug:
128129
log.setLevel(logging.DEBUG)
129130

130-
pipeline = ReanaPipeline(rjc_api_client=rjc_api_client)
131+
pipeline = ReanaPipeline(
132+
rjc_api_client=rjc_api_client, workflow_resources=workflow_resources
133+
)
131134
log.info("starting the run..")
132135
db_log_writer = SQLiteHandler(workflow_uuid, publisher)
133136

reana_workflow_engine_cwl/tasks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def run_cwl_workflow_engine_adapter(
3232
workflow_uuid=None,
3333
workflow_workspace=None,
3434
workflow_json=None,
35+
workflow_resources=None,
3536
workflow_parameters=None,
3637
operational_options={},
3738
**kwargs,
@@ -43,6 +44,7 @@ def run_cwl_workflow_engine_adapter(
4344
rjc_api_client,
4445
workflow_uuid,
4546
workflow_json,
47+
workflow_resources,
4648
workflow_parameters,
4749
operational_options,
4850
workflow_workspace,

tests/test_cwl_reana.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# This file is part of REANA.
4+
# Copyright (C) 2026 CERN.
5+
#
6+
# REANA is free software; you can redistribute it and/or modify it
7+
# under the terms of the MIT License; see LICENSE file for more details.
8+
9+
"""REANA-Workflow-Engine-CWL tests."""
10+
11+
from types import SimpleNamespace
12+
13+
import pytest
14+
15+
from reana_workflow_engine_cwl.cwl_reana import ReanaPipelineJob
16+
17+
18+
@pytest.mark.parametrize(
19+
"step_secret_names,workflow_secret_names,expected_secret_names",
20+
[
21+
(None, None, None),
22+
(None, ["global"], ["global"]),
23+
([], ["global"], []),
24+
(["local"], ["global"], ["local"]),
25+
],
26+
)
27+
def test_create_task_msg_secret_names_resolution(
28+
step_secret_names, workflow_secret_names, expected_secret_names
29+
):
30+
"""Step hints should override or inherit workflow-global secret_names."""
31+
job = ReanaPipelineJob.__new__(ReanaPipelineJob)
32+
job.name = "fit"
33+
job.environment = {"HOME": "/tmp/outdir"}
34+
job.volumes = []
35+
job.command_line = ["echo", "hello"]
36+
job.stdin = None
37+
job.stdout = None
38+
job.stderr = None
39+
job.outdir = "/tmp/outdir"
40+
job.builder = SimpleNamespace(outdir="/tmp/outdir", bindings=[])
41+
job.workflow_resources = (
42+
{"secret_names": workflow_secret_names}
43+
if workflow_secret_names is not None
44+
else {}
45+
)
46+
job.hints = []
47+
if step_secret_names is not None:
48+
job.hints.append({"secret_names": step_secret_names})
49+
job.get_requirement = lambda name: (
50+
({"dockerPull": "docker.io/library/busybox"}, None)
51+
if name == "DockerRequirement"
52+
else (None, None)
53+
)
54+
55+
create_body = job.create_task_msg("/tmp/workspace", "workflow-uuid")
56+
57+
assert create_body["secret_names"] == expected_secret_names

0 commit comments

Comments
 (0)