Skip to content

Commit eaeb0a2

Browse files
committed
feat(campaign): generate additional namespaced names server-side
1 parent c33cf1e commit eaeb0a2

File tree

6 files changed

+61
-57
lines changed

6 files changed

+61
-57
lines changed

src/lsst/cmservice/client/loaders.py

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _upsert_spec_block(
7979
if spec_block and not allow_update:
8080
return spec_block
8181

82-
logger.info(f"""Loading spec_block {spec_name} as {key}""")
82+
logger.info(f"Loading spec_block {spec_name} as {key}")
8383

8484
# A spec that "includes" another spec is effectively declaring a clone
8585
# of the referenced spec that should have already been "loaded" such
@@ -143,47 +143,6 @@ def _upsert_spec_block(
143143
},
144144
)
145145

146-
# scripts and steps are lists, not mappings
147-
if "scripts" in block_data:
148-
# the 'name' of the script should be namespaced; the 'spec_block'
149-
# of the script is a key to its specification alias within the
150-
# campaign, which itself references a namespaced spec_block.
151-
block_data["scripts"] = [
152-
deep_update(
153-
s,
154-
{
155-
"Script": {
156-
"name": str(uuid5(namespace, s["Script"]["name"])),
157-
"spec_block": s["Script"]["spec_block"],
158-
"prerequisites": [
159-
str(uuid5(namespace, dep)) for dep in s["Script"].get("prerequisites", [])
160-
],
161-
}
162-
},
163-
)
164-
for s in block_data["scripts"]
165-
]
166-
167-
if "steps" in block_data:
168-
# steps are found in a campaign spec_block; the 'spec_block' and
169-
# 'step' should be namespaced. Any names used in a prerequisites
170-
# should be namespaced.
171-
block_data["steps"] = [
172-
deep_update(
173-
s,
174-
{
175-
"Step": {
176-
"name": str(uuid5(namespace, s["Step"]["name"])),
177-
"spec_block": str(uuid5(namespace, s["Step"]["spec_block"])),
178-
"prerequisites": [
179-
str(uuid5(namespace, dep)) for dep in s["Step"].get("prerequisites", [])
180-
],
181-
}
182-
},
183-
)
184-
for s in block_data["steps"]
185-
]
186-
187146
handler = block_data.pop("handler", None)
188147
if spec_block is None:
189148
return self._parent.spec_block.create(

src/lsst/cmservice/db/script.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ async def get_create_kwargs(
168168
parent_name = kwargs["parent_name"]
169169
name = kwargs["name"]
170170
spec_block_name = kwargs["spec_block_name"]
171+
original_name = kwargs.get("original_name", name)
171172
except KeyError as e:
172173
raise CMMissingRowCreateInputError(f"Missing input to create Script: {e}") from e
173174
attempt = kwargs.get("attempt", 0)
@@ -177,11 +178,13 @@ async def get_create_kwargs(
177178
if isinstance(parent_level, int):
178179
parent_level = LevelEnum(parent_level)
179180

181+
# The fullname should reflect the element's original shortname not its
182+
# namespaced name
180183
ret_dict = {
181184
"parent_level": parent_level,
182185
"name": name,
183186
"attempt": attempt,
184-
"fullname": f"{parent_name}/{name}_{attempt:03}",
187+
"fullname": f"{parent_name}/{original_name}_{attempt:03}",
185188
"method": ScriptMethodEnum[kwargs.get("method", "default")],
186189
"handler": kwargs.get("handler"),
187190
"data": kwargs.get("data", {}),

src/lsst/cmservice/db/step.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ async def get_create_kwargs(
161161
parent_name = kwargs["parent_name"]
162162
name = kwargs["name"]
163163
spec_block_name = kwargs["spec_block_name"]
164+
original_name = kwargs.get("original_name", name)
164165
except KeyError as e:
165166
raise CMMissingRowCreateInputError(f"Missing input to create Step: {e}") from e
166167

@@ -173,7 +174,7 @@ async def get_create_kwargs(
173174
"spec_block_id": spec_block.id,
174175
"parent_id": campaign.id,
175176
"name": name,
176-
"fullname": f"{campaign.fullname}/{name}",
177+
"fullname": f"{campaign.fullname}/{original_name}",
177178
"handler": kwargs.get("handler"),
178179
"data": kwargs.get("data", {}),
179180
"child_config": kwargs.get("child_config", {}),

src/lsst/cmservice/handlers/element_handler.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ async def prepare(
157157
assert isinstance(spec_block.scripts, Iterable)
158158
assert isinstance(element_campaign.data, dict)
159159

160+
if element_campaign.data.get("namespace"):
161+
campaign_namespace = UUID(element_campaign.data.get("namespace"))
162+
else:
163+
campaign_namespace = None
164+
165+
# Campaigns, Steps, Groups, and Jobs may have Scripts
160166
for script_item in spec_block.scripts:
161167
try:
162168
script_vals = script_item["Script"].copy()
@@ -165,6 +171,9 @@ async def prepare(
165171
test_type_and_raise(script_vals, dict, "ElementHandler Script yaml tag")
166172
try:
167173
script_name = script_vals.pop("name")
174+
namespaced_script_name = (
175+
str(uuid5(campaign_namespace, script_name)) if campaign_namespace else script_name
176+
)
168177
except KeyError as msg:
169178
raise CMYamlParseError(f"Unnnamed Script block {script_vals}") from msg
170179

@@ -174,29 +183,34 @@ async def prepare(
174183

175184
# If the spec_aliases does not have a key for the current script
176185
# name, then it is not an alias.
177-
if (script_spec_block_name in spec_aliases) or (not element_campaign.data.get("namespace")):
186+
if (script_spec_block_name in spec_aliases) or (not campaign_namespace):
178187
script_spec_block_name = spec_aliases.get(script_spec_block_name, script_spec_block_name)
179188
else:
180189
# generate a namespaced name from the current campaign
181-
campaign_namespace = UUID(element_campaign.data.get("namespace"))
182190
script_spec_block_name = str(uuid5(campaign_namespace, script_spec_block_name))
191+
183192
new_script = await Script.create_row(
184193
session,
185194
parent_level=element.level,
186195
spec_block_name=script_spec_block_name,
187196
parent_name=element.fullname,
188-
name=script_name,
197+
name=namespaced_script_name,
198+
original_name=script_name,
189199
**script_vals,
190200
)
191201
await session.refresh(new_script, attribute_names=["id"])
192-
script_ids_dict[script_name] = new_script.id
193-
prereq_pairs += [(script_name, prereq_) for prereq_ in script_vals.get("prerequisites", [])]
202+
script_ids_dict[namespaced_script_name] = new_script.id
203+
204+
prereq_list = [
205+
str(uuid5(campaign_namespace, prereq)) if campaign_namespace else prereq
206+
for prereq in script_vals.get("prerequisites", [])
207+
]
208+
prereq_pairs += [(namespaced_script_name, prereq) for prereq in prereq_list]
194209

195210
for depend_name, prereq_name in prereq_pairs:
196211
prereq_id = script_ids_dict[prereq_name]
197212
depend_id = script_ids_dict[depend_name]
198-
_new_depend = await self._add_prerequisite(session, depend_id, prereq_id)
199-
# await session.refresh(new_depend)
213+
_ = await self._add_prerequisite(session, depend_id, prereq_id)
200214

201215
await element.update_values(session, status=StatusEnum.prepared)
202216
return (True, StatusEnum.prepared)

src/lsst/cmservice/handlers/functions.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from collections.abc import Mapping
3-
from typing import Any
3+
from typing import TYPE_CHECKING, Any
4+
from uuid import UUID, uuid5
45

56
import yaml
67
from anyio import Path
@@ -296,6 +297,14 @@ async def add_steps(
296297

297298
step_ids_dict = {step_.name: step_.id for step_ in current_steps}
298299

300+
if TYPE_CHECKING:
301+
assert isinstance(campaign.data, dict)
302+
303+
if campaign.data.get("namespace"):
304+
campaign_namespace = UUID(campaign.data.get("namespace"))
305+
else:
306+
campaign_namespace = None
307+
299308
prereq_pairs = []
300309
for step_ in step_config_list:
301310
try:
@@ -304,24 +313,38 @@ async def add_steps(
304313
raise CMYamlParseError(f"Expecting Step not: {step_.keys()}") from msg
305314
child_name_ = step_config_.pop("name")
306315
spec_block_name = step_config_.pop("spec_block")
316+
307317
if spec_block_name is None: # pragma: no cover
308318
raise CMYamlParseError(
309319
f"Step {child_name_} of {campaign.fullname} does contain 'spec_block'",
310320
)
321+
322+
namespaced_step_name = (
323+
str(uuid5(campaign_namespace, child_name_)) if campaign_namespace else child_name_
324+
)
325+
namespaced_spec_block_name = (
326+
str(uuid5(campaign_namespace, spec_block_name)) if campaign_namespace else spec_block_name
327+
)
328+
311329
spec_block_name = spec_aliases.get(spec_block_name, spec_block_name)
312330
step_config_ = deep_update(step_config_, child_config.get(child_name_, {}))
313331

314332
new_step = await Step.create_row(
315333
session,
316-
name=child_name_,
317-
spec_block_name=spec_block_name,
334+
name=namespaced_step_name,
335+
spec_block_name=namespaced_spec_block_name,
336+
original_name=child_name_,
318337
parent_name=campaign.fullname,
319338
**step_config_,
320339
)
321340
await session.refresh(new_step)
322-
step_ids_dict[child_name_] = new_step.id
323-
prereqs_names = step_config_.pop("prerequisites", [])
324-
prereq_pairs += [(child_name_, prereq_) for prereq_ in prereqs_names]
341+
step_ids_dict[namespaced_step_name] = new_step.id
342+
343+
prereq_list = [
344+
str(uuid5(campaign_namespace, prereq)) if campaign_namespace else prereq
345+
for prereq in step_config_.pop("prerequisites", [])
346+
]
347+
prereq_pairs += [(namespaced_step_name, prereq) for prereq in prereq_list]
325348

326349
for depend_name, prereq_name in prereq_pairs:
327350
prereq_id = step_ids_dict[prereq_name]

src/lsst/cmservice/routers/queues.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66

77
from .. import db, models
88
from ..common.errors import CMMissingIDError
9+
from ..common.logging import LOGGER
910
from . import wrappers
1011

12+
logger = LOGGER.bind(module=__name__)
13+
1114
# Template specialization
1215
# Specify the pydantic model for the table
1316
ResponseModelClass = models.Queue
@@ -72,6 +75,7 @@ async def process_element(
7275
except CMMissingIDError as msg:
7376
raise HTTPException(status_code=404, detail=f"{str(msg)}") from msg
7477
except Exception as msg:
78+
logger.exception()
7579
raise HTTPException(status_code=500, detail=f"{str(msg)}") from msg
7680
return can_continue
7781

0 commit comments

Comments
 (0)