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}