Skip to content

Commit fc4efd3

Browse files
feat: Add a 'create secrets' command in the CLI
1 parent 0154391 commit fc4efd3

File tree

5 files changed

+115
-4
lines changed

5 files changed

+115
-4
lines changed

diracx-cli/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dirac = "diracx.cli:app"
3939
[project.entry-points."diracx.cli"]
4040
jobs = "diracx.cli.jobs:app"
4141
config = "diracx.cli.config:app"
42+
pilots = "diracx.cli.pilots:app"
4243

4344
[project.entry-points."diracx.cli.hidden"]
4445
internal = "diracx.cli.internal:app"

diracx-cli/src/diracx/cli/pilots.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from __future__ import annotations
2+
3+
__all__ = ("app",)
4+
5+
import asyncio
6+
import json
7+
from typing import Annotated, Optional
8+
9+
import typer
10+
11+
from diracx.client.aio import AsyncDiracClient
12+
13+
from .utils import AsyncTyper
14+
15+
app = AsyncTyper()
16+
17+
18+
async def installation_metadata():
19+
async with AsyncDiracClient() as api:
20+
return await api.well_known.get_installation_metadata()
21+
22+
23+
def vo_callback(vo: str | None) -> str:
24+
metadata = asyncio.run(installation_metadata())
25+
vos = list(metadata.virtual_organizations)
26+
if not vo:
27+
raise typer.BadParameter(
28+
f"VO must be specified, available options are: {' '.join(vos)}"
29+
)
30+
if vo not in vos:
31+
raise typer.BadParameter(
32+
f"Unknown VO {vo}, available options are: {' '.join(vos)}"
33+
)
34+
return vo
35+
36+
37+
@app.async_command()
38+
async def generate_pilot_secrets(
39+
vo: Annotated[
40+
str,
41+
typer.Argument(callback=vo_callback, help="Virtual Organization name"),
42+
],
43+
n: Annotated[
44+
int,
45+
typer.Argument(help="Number of secrets to generate."),
46+
],
47+
expiration_minutes: Optional[int] = typer.Option(
48+
60,
49+
help="Expiration in minutes of the secrets.",
50+
),
51+
max_use: Optional[int] = typer.Option(
52+
60,
53+
help="Number of uses max for a secret.",
54+
),
55+
):
56+
async with AsyncDiracClient() as api:
57+
secrets = await api.pilots.create_pilot_secrets(
58+
n=n,
59+
expiration_minutes=expiration_minutes,
60+
pilot_secret_use_count_max=max_use,
61+
vo=vo,
62+
)
63+
# Convert each model to dict
64+
secrets_dict = [secret.as_dict() for secret in secrets]
65+
66+
print(json.dumps(secrets_dict, indent=2))

diracx-client/src/diracx/client/patches/pilots/aio.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@
1616
from azure.core.tracing.decorator_async import distributed_trace_async
1717

1818
from ..._generated.aio.operations._operations import PilotsOperations as _PilotsOperations
19-
from ..._generated.models._models import PilotCredentialsInfo
19+
from ..._generated.models._models import PilotCredentialsInfo, PilotSecretsInfo
2020
from .common import (
2121
make_search_body,
2222
make_summary_body,
2323
make_add_pilot_stamps_body,
2424
make_update_pilot_fields_body,
25+
make_create_pilot_secrets_body,
2526
SearchKwargs,
2627
SummaryKwargs,
2728
AddPilotStampsKwargs,
28-
UpdatePilotFieldsKwargs
29+
UpdatePilotFieldsKwargs,
30+
CreatePilotSecretsKwargs,
2931
)
3032

3133
# We're intentionally ignoring overrides here because we want to change the interface.
@@ -52,3 +54,8 @@ async def add_pilot_stamps(self, **kwargs: Unpack[AddPilotStampsKwargs]) -> list
5254
async def update_pilot_fields(self, **kwargs: Unpack[UpdatePilotFieldsKwargs]) -> None:
5355
"""TODO"""
5456
return await super().update_pilot_fields(**make_update_pilot_fields_body(**kwargs))
57+
58+
@distributed_trace_async
59+
async def create_pilot_secrets(self, **kwargs: Unpack[CreatePilotSecretsKwargs]) -> list[PilotSecretsInfo]:
60+
"""TODO"""
61+
return await super().create_pilot_secrets(**make_create_pilot_secrets_body(**kwargs))

diracx-client/src/diracx/client/patches/pilots/common.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"make_add_pilot_stamps_body",
1212
"UpdatePilotFieldsKwargs",
1313
"make_update_pilot_fields_body"
14+
"CreatePilotSecretsKwargs",
15+
"make_create_pilot_secrets_body"
1416
]
1517

1618
import json
@@ -146,3 +148,31 @@ def make_update_pilot_fields_body(**kwargs: Unpack[UpdatePilotFieldsKwargs]) ->
146148
result: UnderlyingUpdatePilotFields = {"body": BytesIO(json.dumps(body).encode("utf-8"))}
147149
result.update(cast(ResponseExtra, kwargs))
148150
return result
151+
152+
# ------------------ CreatePilotSecrets ------------------
153+
154+
class CreatePilotSecretsBody(TypedDict, total=False):
155+
n: int
156+
expiration_minutes: int | None
157+
pilot_secret_use_count_max: int | None
158+
vo: str
159+
160+
class CreatePilotSecretsKwargs(CreatePilotSecretsBody, ResponseExtra): ...
161+
162+
class UnderlyingCreatePilotSecrets(ResponseExtra, total=False):
163+
# FIXME: The autorest-generated has a bug that it expected IO[bytes] despite
164+
# the code being generated to support IO[bytes] | bytes.
165+
body: IO[bytes]
166+
167+
def make_create_pilot_secrets_body(**kwargs: Unpack[CreatePilotSecretsKwargs]) -> UnderlyingCreatePilotSecrets:
168+
body: CreatePilotSecretsBody = {}
169+
for key in CreatePilotSecretsBody.__optional_keys__:
170+
if key not in kwargs:
171+
continue
172+
key = cast(Literal["n", "expiration_minutes", "pilot_secret_use_count_max", "vo"], key)
173+
value = kwargs.pop(key)
174+
if value is not None:
175+
body[key] = value
176+
result: UnderlyingCreatePilotSecrets = {"body": BytesIO(json.dumps(body).encode("utf-8"))}
177+
result.update(cast(ResponseExtra, kwargs))
178+
return result

diracx-client/src/diracx/client/patches/pilots/sync.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@
1616
from azure.core.tracing.decorator import distributed_trace
1717

1818
from ..._generated.operations._operations import PilotsOperations as _PilotsOperations
19-
from ..._generated.models._models import PilotCredentialsInfo
19+
from ..._generated.models._models import PilotCredentialsInfo, PilotSecretsInfo
2020
from .common import (
2121
make_search_body,
2222
make_summary_body,
2323
make_add_pilot_stamps_body,
2424
make_update_pilot_fields_body,
25+
make_create_pilot_secrets_body,
2526
SearchKwargs,
2627
SummaryKwargs,
2728
AddPilotStampsKwargs,
28-
UpdatePilotFieldsKwargs
29+
UpdatePilotFieldsKwargs,
30+
CreatePilotSecretsKwargs,
2931
)
3032

3133
# We're intentionally ignoring overrides here because we want to change the interface.
@@ -52,3 +54,8 @@ def add_pilot_stamps(self, **kwargs: Unpack[AddPilotStampsKwargs]) -> list[Pilot
5254
def update_pilot_fields(self, **kwargs: Unpack[UpdatePilotFieldsKwargs]) -> None:
5355
"""TODO"""
5456
return super().update_pilot_fields(**make_update_pilot_fields_body(**kwargs))
57+
58+
@distributed_trace
59+
def create_pilot_secrets(self, **kwargs: Unpack[CreatePilotSecretsKwargs]) -> list[PilotSecretsInfo]:
60+
"""TODO"""
61+
return super().create_pilot_secrets(**make_create_pilot_secrets_body(**kwargs))

0 commit comments

Comments
 (0)