Skip to main content

runnerd_core/
api.rs

1//! The daemon's JSON API, version 1: the request and response types every client deserializes, and the
2//! principal a transport authenticates. Plain data only: no I/O, no Docker or GitHub types, no secrets.
3//!
4//! Routes, all under the authenticated principal:
5//!
6//! | Route | Body |
7//! |---|---|
8//! | `GET /v1/status` | [`Status`] |
9//! | `POST /v1/repositories/{owner}/{name}/pause?`[`PauseQuery`] | [`Paused`]; 409 [`Busy`] while it runs a job |
10//! | `POST /v1/repositories/{owner}/{name}/resume` | none, 204 |
11//! | `POST /v1/profiles/{name}/reseed` | none, 202; 409 while it reseeds; `cache.reseeded` when done |
12//! | `POST /v1/caches/{name}/clean` | none, 204; 409 [`Busy`] while a runner uses it |
13//! | `GET /v1/config` | [`ConfigFile`] |
14//! | `POST /v1/config/check` | TOML in, [`ConfigCheck`] |
15//! | `PUT /v1/config` | TOML in, [`ConfigCheck`]; 422 [`ConfigCheck`] when invalid |
16//! | `GET /v1/doctor` | [`Doctor`] |
17//! | `POST /v1/shutdown` | [`ShutdownRequest`] in, 202 |
18//! | `GET /v1/jobs?`[`JobQuery`] | [`JobPage`] |
19//! | `GET /v1/jobs/{id}` | [`Job`] |
20//! | `GET /v1/jobs/{id}/samples` | [`SampleSeries`] |
21//! | `GET /v1/jobs/{id}/logs?`[`LogQuery`] | `text/plain`, one line per log line |
22//! | `GET /v1/host/samples?`[`HostSampleQuery`] | [`HostSamples`] |
23//! | `GET /v1/stats?`[`StatsQuery`] | [`Stats`] |
24//! | `GET /v1/caches` | [`Caches`] |
25//! | `GET /v1/caches/history?`[`CacheHistoryQuery`] | [`CacheHistory`] |
26//! | `GET /v1/events?`[`EventQuery`] | `text/event-stream` of [`EventEnvelope`] |
27//!
28//! Errors carry an [`ErrorResponse`].
29
30use serde::{Deserialize, Serialize};
31
32mod control;
33mod events;
34mod jobs;
35
36pub use control::{
37    Busy, Check, CheckStatus, ConfigCheck, ConfigFile, Doctor, PauseQuery, Paused, RunningJob,
38};
39pub use events::{Event, EventEnvelope, EventQuery};
40pub use jobs::{
41    Bucket, CacheHistory, CacheHistoryQuery, CacheKind, CacheSize, CacheVolume, Caches, HostSample,
42    HostSampleQuery, HostSamples, Job, JobMetrics, JobPage, JobQuery, JobRunner, JobState,
43    JobSummary, LogQuery, LogSummary, Sample, SampleSeries, Stats, StatsQuery, StatsRow, Step,
44    Timeline,
45};
46
47/// Who sent a request, as the transport authenticated it. Handlers get it from one place, so a new transport only
48/// has to produce one.
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "kebab-case", tag = "kind")]
51pub enum Principal {
52    /// The user the daemon runs as, on the local Unix socket.
53    LocalOwner {
54        /// The Unix user ID the socket's peer runs as.
55        uid: u32,
56    },
57}
58
59/// `GET /v1/status`: what the daemon serves and runs right now.
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct Status {
62    /// The daemon's version, from its `Cargo.toml`.
63    pub version: String,
64    /// Set once the daemon stops polling to shut down.
65    pub shutting_down: bool,
66    /// The memory budget and what runners use of it.
67    pub budget: Budget,
68    /// Every configured profile with its slots.
69    pub profiles: Vec<ProfileStatus>,
70    /// Every configured repository.
71    pub repositories: Vec<RepositoryStatus>,
72}
73
74/// The memory budget runners and the mirror share, in bytes.
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76pub struct Budget {
77    /// The systemd slice runner containers run in.
78    pub slice: String,
79    /// The slice's `MemoryMax`; `None` when it is unlimited.
80    pub memory_max: Option<u64>,
81    /// Memory caps of the running containers: runners, seed containers and the mirror.
82    pub memory_in_use: u64,
83}
84
85/// A runner profile and what runs in its slots.
86#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct ProfileStatus {
88    /// The name under `[profiles]` in `runnerd.toml`.
89    pub name: String,
90    /// The image its runners run, such as `runnerd-runner:base`.
91    pub image: String,
92    /// CPUs per slot.
93    pub cpus: u32,
94    /// Bytes per slot.
95    pub memory: u64,
96    /// Set while slot 1's layers become the profile's snapshot.
97    pub reseeding: bool,
98    /// Index 0 is slot 1.
99    pub slots: Vec<SlotStatus>,
100}
101
102/// One slot of a profile: room for one job.
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct SlotStatus {
105    /// 1-based.
106    pub slot: usize,
107    /// `None` when the slot is free.
108    pub runner: Option<RunnerStatus>,
109}
110
111/// A runner registered for one job, in its container.
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113pub struct RunnerStatus {
114    /// `owner/name` the runner is registered on.
115    pub repository: String,
116    /// The runner's name, also its container's.
117    pub name: String,
118    /// The runner's ID on GitHub.
119    pub runner_id: u64,
120    /// The Docker container ID.
121    pub container: String,
122    /// Whether GitHub reports it running a job.
123    pub busy: bool,
124    /// Seconds since the Unix epoch.
125    pub started_at: u64,
126}
127
128/// A configured repository and its queue.
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
130pub struct RepositoryStatus {
131    /// `owner/name` on GitHub.
132    pub name: String,
133    /// The profile its jobs run in.
134    pub profile: String,
135    /// The labels its runners register with.
136    pub labels: Vec<String>,
137    /// Paused repositories get no new runners; their running jobs finish.
138    pub paused: bool,
139    /// Queued jobs whose labels these runners match, at the last poll.
140    pub queued_jobs: usize,
141}
142
143/// `POST /v1/shutdown`.
144#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
145pub struct ShutdownRequest {
146    /// Kill running jobs and delete their runners instead of waiting for them.
147    #[serde(default)]
148    pub force: bool,
149}
150
151/// The body of every error response.
152#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
153pub struct ErrorResponse {
154    /// What went wrong, for people to read.
155    pub message: String,
156}