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

Allow node search with tags #64

Merged
merged 1 commit into from
Jan 13, 2025
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
14 changes: 11 additions & 3 deletions nodeman/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response, status
from jwcrypto.jwk import JWK
from jwcrypto.jws import JWS, InvalidJWSSignature
from mongoengine import Q
from opentelemetry import metrics, trace
from pydantic_core import ValidationError

Expand Down Expand Up @@ -205,11 +206,18 @@ def get_node_information(name: str, username: Annotated[str, Depends(get_current
status.HTTP_200_OK: {"model": NodeCollection},
},
tags=["backend"],
response_model_exclude_none=False,
)
def get_all_nodes(username: Annotated[str, Depends(get_current_username)]) -> NodeCollection:
def get_all_nodes(username: Annotated[str, Depends(get_current_username)], tags: str | None = None) -> NodeCollection:
"""Get all nodes"""
logging.info("%s queried for all nodes", username, extra={"username": username})
return NodeCollection(nodes=[NodeInformation.from_db_model(node) for node in TapirNode.objects(deleted=None)])
query = Q(deleted=None)
if tags:
query_tags = sorted(set(tags.split(",")))
logging.info("%s queried for nodes with tags %s", username, query_tags, extra={"username": username})
query &= Q(tags__all=sorted(set(query_tags)))
else:
logging.info("%s queried for all nodes", username, extra={"username": username})
return NodeCollection(nodes=[NodeInformation.from_db_model(node) for node in TapirNode.objects(query)])


@router.get(
Expand Down
30 changes: 30 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from nodeman.x509 import RSA_EXPONENT, CertificateAuthorityClient, generate_ca_certificate, generate_x509_csr

ADMIN_TEST_NODE_COUNT = 100
ADMIN_TEST_NODE_COUNT_TAGS = 10
BACKEND_CREDENTIALS = ("username", "password")

PrivateKey = ec.EllipticCurvePrivateKey | rsa.RSAPublicKey | Ed25519PrivateKey | Ed448PrivateKey
Expand Down Expand Up @@ -407,6 +408,35 @@ def test_admin() -> None:
assert response.status_code == status.HTTP_204_NO_CONTENT


def test_admin_tags() -> None:
client = get_test_client()
client.auth = BACKEND_CREDENTIALS

server = ""

for node_number in range(ADMIN_TEST_NODE_COUNT_TAGS):
tags = ["odd"] if node_number % 2 else ["even"]
if node_number == 0:
tags.append("zero")
response = client.post(urljoin(server, "/api/v1/node"), json={"tags": tags})
assert response.status_code == status.HTTP_201_CREATED

# half of the nodes should have tag even
response = client.get(urljoin(server, "/api/v1/nodes"), params={"tags": "even"})
assert response.status_code == status.HTTP_200_OK
assert len(response.json()["nodes"]) == ADMIN_TEST_NODE_COUNT_TAGS // 2

# exactly one node should have tags even & zero
response = client.get(urljoin(server, "/api/v1/nodes"), params={"tags": "even,zero"})
assert response.status_code == status.HTTP_200_OK
assert len(response.json()["nodes"]) == 1

# no nodes should have both tags even & odd
response = client.get(urljoin(server, "/api/v1/nodes"), params={"tags": "even,odd"})
assert response.status_code == status.HTTP_200_OK
assert len(response.json()["nodes"]) == 0


def test_backend_authentication() -> None:
client = get_test_client()
server = ""
Expand Down
Loading