Skip to content

Commit 83680b0

Browse files
author
Michael Buchar
committed
feat(task): pass scoped user secret names (reanahub#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 6a43f16 commit 83680b0

4 files changed

Lines changed: 68 additions & 2 deletions

File tree

reana_workflow_engine_cwl/cwl_reana.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@
3333
from reana_commons.api_client import JobControllerAPIClient as rjc_api_client
3434
from reana_commons.config import REANA_WORKFLOW_UMASK
3535

36+
try:
37+
from reana_commons.k8s.secrets import resolve_secret_names
38+
except ModuleNotFoundError:
39+
40+
def resolve_secret_names(scoped_secret_names, workflow_resources=None):
41+
"""Resolve a step-local allowlist against the workflow-global default."""
42+
if scoped_secret_names is not None:
43+
return scoped_secret_names
44+
workflow_resources = workflow_resources or {}
45+
return workflow_resources.get("secret_names")
46+
47+
3648
from reana_workflow_engine_cwl.config import LOGGING_MODULE, MOUNT_CVMFS
3749
from reana_workflow_engine_cwl.pipeline import Pipeline
3850
from reana_workflow_engine_cwl.poll import PollThread
@@ -51,6 +63,7 @@ def __init__(self, **kwargs):
5163
self.service = kwargs.get(
5264
"rjc_api_client", rjc_api_client("reana-job-controller")
5365
)
66+
self.workflow_resources = kwargs.get("workflow_resources") or {}
5467
if kwargs.get("basedir") is not None:
5568
self.basedir = kwargs.get("basedir")
5669
else:
@@ -310,6 +323,9 @@ def shouldquote(x):
310323
c4p_cpu_cores = self._get_hint("c4p_cpu_cores")
311324
c4p_memory_limit = self._get_hint("c4p_memory_limit")
312325
c4p_additional_requirements = self._get_hint("c4p_additional_requirements")
326+
secret_names = resolve_secret_names(
327+
self._get_hint("secret_names"), getattr(self, "workflow_resources", {})
328+
)
313329
create_body = {
314330
"image": container,
315331
"cmd": wrapped_cmd,
@@ -323,6 +339,7 @@ def shouldquote(x):
323339
"unpacked_img": unpacked_img,
324340
"voms_proxy": voms_proxy,
325341
"rucio": rucio,
342+
"secret_names": secret_names,
326343
"htcondor_max_runtime": htcondor_max_runtime,
327344
"htcondor_accounting_group": htcondor_accounting_group,
328345
"htcondor_request_cpus": htcondor_request_cpus,
@@ -362,6 +379,7 @@ def _get_hint(self, hint_name):
362379
def run(self, runtimeContext): # noqa: C901
363380
"""Run a job."""
364381
self._setup(runtimeContext)
382+
self.workflow_resources = runtimeContext.pipeline.workflow_resources
365383

366384
env = self.environment
367385
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: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
# REANA is free software; you can redistribute it and/or modify it
77
# under the terms of the MIT License; see LICENSE file for more details.
88

9-
"""REANA CWL job tests."""
9+
"""REANA Workflow Engine CWL tests."""
1010

1111
import subprocess
1212
from types import SimpleNamespace
1313

14+
import pytest
1415
import shellescape
1516

1617
from reana_workflow_engine_cwl.cwl_reana import ReanaPipelineJob
@@ -68,3 +69,45 @@ def test_initial_workdir_cleanup_preserves_inplace_update_symlinks(tmp_path):
6869
assert staged_input.read_text() == "generated output"
6970
assert staged_writable_input.is_symlink()
7071
assert staged_writable_input.read_text() == "writable input"
72+
73+
74+
@pytest.mark.parametrize(
75+
"step_secret_names,workflow_secret_names,expected_secret_names",
76+
[
77+
(None, None, None),
78+
(None, ["global"], ["global"]),
79+
([], ["global"], []),
80+
(["local"], ["global"], ["local"]),
81+
],
82+
)
83+
def test_create_task_msg_secret_names_resolution(
84+
step_secret_names, workflow_secret_names, expected_secret_names
85+
):
86+
"""Step hints should override or inherit workflow-global secret_names."""
87+
job = ReanaPipelineJob.__new__(ReanaPipelineJob)
88+
job.name = "fit"
89+
job.environment = {"HOME": "/tmp/outdir"}
90+
job.volumes = []
91+
job.command_line = ["echo", "hello"]
92+
job.stdin = None
93+
job.stdout = None
94+
job.stderr = None
95+
job.outdir = "/tmp/outdir"
96+
job.builder = SimpleNamespace(outdir="/tmp/outdir", bindings=[])
97+
job.workflow_resources = (
98+
{"secret_names": workflow_secret_names}
99+
if workflow_secret_names is not None
100+
else {}
101+
)
102+
job.hints = []
103+
if step_secret_names is not None:
104+
job.hints.append({"secret_names": step_secret_names})
105+
job.get_requirement = lambda name: (
106+
({"dockerPull": "docker.io/library/busybox"}, None)
107+
if name == "DockerRequirement"
108+
else (None, None)
109+
)
110+
111+
create_body = job.create_task_msg("/tmp/workspace", "workflow-uuid")
112+
113+
assert create_body["secret_names"] == expected_secret_names

0 commit comments

Comments
 (0)