| 423 | 423 | | /// `create_registration_token`. |
| 424 | 424 | | pub async fn create_registration_token(&self, a: CreateRegistrationTokenArgs) -> Result<Outcome<RegistrationToken>> { |
| 425 | 425 | | let place = check!(self.runner_place(&a.actor, &a.owner, true).await?); |
| 426 | + | // A workspace g1t staff put a hold on registers no new runners. |
| 427 | + | if self.standing(&place.workspace).await.is_some_and(|standing| standing.held) { |
| 428 | + | return Ok(fail( |
| 429 | + | FailureCode::Paused, |
| 430 | + | "g1t staff put a hold on this workspace, so it cannot register new runners. Write to hey@flagon.io if you think this is a mistake.", |
| 431 | + | )); |
| 432 | + | } |
| 426 | 433 | | let made = self |
| 427 | 434 | | .db |
| 428 | 435 | | .prepare("SELECT COUNT(*) AS n FROM runner_registrations WHERE workspace = ? AND created_at > ?") |
| ⋯ |
| 1174 | 1181 | | job: JobRow, |
| 1175 | 1182 | | run_repo: String, |
| 1176 | 1183 | | } |
| 1177 | | − | let queued = self.db.prepare(sql).bind(&binds)?.all().await?.results::<Queued>()?; |
| 1184 | + | let mut queued = self.db.prepare(sql).bind(&binds)?.all().await?.results::<Queued>()?; |
| 1185 | + | // A workspace without the plan runs FREE_SELF_HOSTED_JOBS of its jobs |
| 1186 | + | // on its own runners at once (limits.rs). Billing is asked only once |
| 1187 | + | // that many are going, so a runner's poll stays cheap. |
| 1188 | + | if !queued.is_empty() && self.limits.free_self_hosted_jobs > 0 { |
| 1189 | + | let running = self |
| 1190 | + | .db |
| 1191 | + | .prepare("SELECT COUNT(*) AS n FROM jobs WHERE status = 'in_progress' AND runner_id IS NOT NULL AND lower(namespace) = ?") |
| 1192 | + | .bind(&[runner.workspace.as_str().into()])? |
| 1193 | + | .first::<Count>(None) |
| 1194 | + | .await? |
| 1195 | + | .map_or(0, |c| c.n); |
| 1196 | + | if !crate::limits::self_hosted_room(running as u64, self.limits.free_self_hosted_jobs) |
| 1197 | + | && self.standing(&runner.workspace).await.is_some_and(|standing| !standing.has_plan) |
| 1198 | + | { |
| 1199 | + | // Its workflow jobs wait for one to finish; anything else the |
| 1200 | + | // runner takes (below) is not held back. |
| 1201 | + | queued.clear(); |
| 1202 | + | } |
| 1203 | + | } |
| 1178 | 1204 | | for Queued { job, run_repo } in queued { |
| 1179 | 1205 | | let stored: Vec<String> = job.labels.as_deref().and_then(|l| serde_json::from_str(l).ok()).unwrap_or_default(); |
| 1180 | 1206 | | let wanted = Wanted::from_stored(&stored); |