ctfy.sdk.resources.nodes

client.nodes — the worker-node roster, for an admin bearer.

⚠️ Not public, and this file called it public for as long as the route had no gate. GET /nodes answers the internal fleet — each node's own address, its live load, its labels and its telemetry — so it is require_admin on the platform. The name stays here rather than moving under client.admin.nodes because moving it would break every caller to restate a fact the server already enforces.

Operator node management (register / remove / patch / invites / health) lives on ctfy.sdk.AdminClient under client.admin.nodes.

 1"""``client.nodes`` — the worker-node roster, for an admin bearer.
 2
 3⚠️ **Not public, and this file called it public for as long as the route
 4had no gate.** ``GET /nodes`` answers the internal fleet — each node's own
 5address, its live load, its labels and its telemetry — so it is
 6``require_admin`` on the platform. The name stays here rather than moving
 7under ``client.admin.nodes`` because moving it would break every caller to
 8restate a fact the server already enforces.
 9
10Operator node management (register / remove / patch / invites / health) lives
11on :class:`ctfy.sdk.AdminClient` under ``client.admin.nodes``.
12"""
13
14from __future__ import annotations
15
16from collections.abc import Sequence
17
18from ctfy.sdk._helpers import PagedList, _extract_items, _raise_for_status
19from ctfy.sdk.base import BaseHttpClient
20from ctfy.server.models import ImageConsistencyRow, NodeInfo
21
22
23class NodesResource:
24    """Read of the worker-node roster. Needs an admin bearer."""
25
26    def __init__(self, http: BaseHttpClient) -> None:
27        self._http = http
28
29    def list(self, offset: int = 0, limit: int = 50) -> PagedList[NodeInfo]:
30        resp = self._http.request("GET", "/nodes", params={"offset": offset, "limit": limit})
31        _raise_for_status(resp)
32        return _extract_items(resp.json(), NodeInfo)
33
34    def image_consistency(self) -> Sequence[ImageConsistencyRow]:
35        """Tags more than one node holds and disagrees on the content of (#1716.4).
36
37        Empty means the fleet agrees; a row names the tag and which node
38        holds which digest. Needs an admin bearer, like :meth:`list`.
39        """
40        resp = self._http.request("GET", "/nodes/image-consistency")
41        _raise_for_status(resp)
42        return [ImageConsistencyRow.model_validate(row) for row in resp.json()]
class NodesResource:
24class NodesResource:
25    """Read of the worker-node roster. Needs an admin bearer."""
26
27    def __init__(self, http: BaseHttpClient) -> None:
28        self._http = http
29
30    def list(self, offset: int = 0, limit: int = 50) -> PagedList[NodeInfo]:
31        resp = self._http.request("GET", "/nodes", params={"offset": offset, "limit": limit})
32        _raise_for_status(resp)
33        return _extract_items(resp.json(), NodeInfo)
34
35    def image_consistency(self) -> Sequence[ImageConsistencyRow]:
36        """Tags more than one node holds and disagrees on the content of (#1716.4).
37
38        Empty means the fleet agrees; a row names the tag and which node
39        holds which digest. Needs an admin bearer, like :meth:`list`.
40        """
41        resp = self._http.request("GET", "/nodes/image-consistency")
42        _raise_for_status(resp)
43        return [ImageConsistencyRow.model_validate(row) for row in resp.json()]

Read of the worker-node roster. Needs an admin bearer.

NodesResource(http: ctfy.sdk.base.BaseHttpClient)
27    def __init__(self, http: BaseHttpClient) -> None:
28        self._http = http
def list( self, offset: int = 0, limit: int = 50) -> ctfy.sdk._helpers.PagedList[ctfy.server.models.NodeInfo]:
30    def list(self, offset: int = 0, limit: int = 50) -> PagedList[NodeInfo]:
31        resp = self._http.request("GET", "/nodes", params={"offset": offset, "limit": limit})
32        _raise_for_status(resp)
33        return _extract_items(resp.json(), NodeInfo)
def image_consistency(self) -> Sequence[ctfy.server.models.ImageConsistencyRow]:
35    def image_consistency(self) -> Sequence[ImageConsistencyRow]:
36        """Tags more than one node holds and disagrees on the content of (#1716.4).
37
38        Empty means the fleet agrees; a row names the tag and which node
39        holds which digest. Needs an admin bearer, like :meth:`list`.
40        """
41        resp = self._http.request("GET", "/nodes/image-consistency")
42        _raise_for_status(resp)
43        return [ImageConsistencyRow.model_validate(row) for row in resp.json()]

Tags more than one node holds and disagrees on the content of (#1716.4).

Empty means the fleet agrees; a row names the tag and which node holds which digest. Needs an admin bearer, like list().