Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the groups endpoint #91

Merged
merged 3 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions firecrest/AsyncClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,22 @@ async def whoami(self, machine=None) -> Optional[str]:
# Invalid token, cannot retrieve username
return None

async def groups(self, machine) -> t.UserId:
"""Returns the output of the `id` command, user and group ids.

:calls: GET `/utilities/whoami`

.. warning:: This is available only for FirecREST>=1.15.0
"""
resp = await self._get_request(
endpoint="/utilities/whoami",
additional_headers={"X-Machine-Name": machine},
params={
"groups": True
}
)
return self._json_response([resp], 200)["output"]

# Compute
async def submit(
self,
Expand Down
16 changes: 16 additions & 0 deletions firecrest/BasicClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,22 @@ def whoami(self, machine=None) -> Optional[str]:
# Invalid token, cannot retrieve username
return None

def groups(self, machine) -> t.UserId:
"""Returns the output of the `id` command, user and group ids.

:calls: GET `/utilities/whoami`

.. warning:: This is available only for FirecREST>=1.15.0
"""
resp = self._get_request(
endpoint="/utilities/whoami",
additional_headers={"X-Machine-Name": machine},
params={
"groups": True
}
)
return self._json_response([resp], 200)["output"]

# Compute
def _submit_request(self, machine: str, job_script, local_file, account=None, env_vars=None):
data = {}
Expand Down
28 changes: 28 additions & 0 deletions firecrest/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,34 @@ def whoami(
raise typer.Exit(code=1)


@app.command(rich_help_panel="Utilities commands")
def id(
config_from_parent: str = typer.Option(None,
callback=config_parent_load_callback,
is_eager=True,
hidden=True
),
system: str = typer.Option(
None,
"-s",
"--system",
help="The name of the system where the `id` command will run.",
envvar="FIRECREST_SYSTEM",
),
):
"""Return the identity of the user in the remote system.
"""
try:
result = client.groups(system)
user = f"{result['user']['id']}({result['user']['name']})"
group = f"{result['group']['id']}({result['group']['name']})"
all_groups = ",".join([f"{g['id']}({g['name']})" for g in result["groups"]])
console.print(f"uid={user} gid={group} groups={all_groups}")
except Exception as e:
examine_exeption(e)
raise typer.Exit(code=1)


class TransferType(str, Enum):
direct = "direct"
external = "external"
Expand Down
13 changes: 13 additions & 0 deletions firecrest/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,16 @@ class JobSubmit(TypedDict):
job_file_out: str
jobid: int
result: str


class Id(TypedDict):
name: str
id: str


class UserId(TypedDict):
"""A record from the `id` command"""

user: Id
group: Id
groups: list[Id]
49 changes: 48 additions & 1 deletion tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,21 @@ def whoami_handler(request: Request):
content_type="application/json",
)

groups = request.args.get("groups", False)
if groups:
ret = {
"description": "User information",
"output": {
"group": {"id": "1000", "name": "group1"},
"groups": [{"id": "1000", "name": "group1"}, {"id": "1001", "name": "group2"}],
"user": {"id": "10000", "name": "test_user"},
}
}
else:
ret = {"description": "Success on whoami operation.", "output": "username"}

return Response(
json.dumps({"description": "Success on whoami operation.", "output": "username"}),
json.dumps(ret),
status=200,
content_type="application/json",
)
Expand Down Expand Up @@ -1510,3 +1523,37 @@ def test_whoami_invalid_machine(valid_client):
def test_whoami_invalid_client(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
invalid_client.whoami("cluster1")


def test_cli_whoami(valid_credentials):
args = valid_credentials + ["whoami", "--system", "cluster1"]
result = runner.invoke(cli.app, args=args)
stdout = common.clean_stdout(result.stdout)
assert result.exit_code == 0
assert "username" in stdout


def test_groups(valid_client):
assert valid_client.groups("cluster1") == {
"group": {"id": "1000", "name": "group1"},
"groups": [{"id": "1000", "name": "group1"}, {"id": "1001", "name": "group2"}],
"user": {"id": "10000", "name": "test_user"},
}


def test_groups_invalid_machine(valid_client):
with pytest.raises(firecrest.HeaderException):
valid_client.groups("cluster2")


def test_groups_invalid_client(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
invalid_client.groups("cluster1")


def test_cli_id(valid_credentials):
args = valid_credentials + ["id", "--system", "cluster1"]
result = runner.invoke(cli.app, args=args)
stdout = common.clean_stdout(result.stdout)
assert result.exit_code == 0
assert "uid=10000(test_user) gid=1000(group1) groups=1000(group1),1001(group2)" in stdout
22 changes: 22 additions & 0 deletions tests/test_utilities_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,3 +651,25 @@ async def test_whoami_invalid_machine(valid_client):
async def test_whoami_invalid_client(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
await invalid_client.whoami("cluster1")


@pytest.mark.asyncio
async def test_groups(valid_client):
assert await valid_client.groups("cluster1") == {
"group": {"id": "1000", "name": "group1"},
"groups": [{"id": "1000", "name": "group1"}, {"id": "1001", "name": "group2"}],
"user": {"id": "10000", "name": "test_user"},
}


@pytest.mark.asyncio
async def test_groups_invalid_machine(valid_client):
with pytest.raises(firecrest.HeaderException):
await valid_client.groups("cluster2")


@pytest.mark.asyncio
async def test_groups_invalid_client(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
await invalid_client.groups("cluster1")

Loading