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