Skip to content

Commit

Permalink
feat: add /restart command
Browse files Browse the repository at this point in the history
  • Loading branch information
jiegec committed May 10, 2024
1 parent 797da1e commit 570a4da
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 101 deletions.
100 changes: 97 additions & 3 deletions server/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use crate::{
github::{get_crab_github_installation, get_packages_from_pr},
models::{NewJob, NewPipeline, Pipeline, User, Worker},
models::{Job, NewJob, NewPipeline, Pipeline, User, Worker},
DbPool, ABBS_REPO_LOCK, ALL_ARCH, ARGS,
};
use anyhow::anyhow;
use anyhow::Context;
use anyhow::{anyhow, bail};
use buildit_utils::github::{
get_archs, get_environment_requirement, resolve_packages, update_abbs,
};
use diesel::connection::{AnsiTransactionManager, TransactionManager};
use diesel::r2d2::PoolTransactionManager;
use diesel::{
dsl::count, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper,
dsl::count, ExpressionMethods, OptionalExtension, PgConnection, QueryDsl, RunQueryDsl,
SelectableHelper,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
Expand Down Expand Up @@ -347,3 +350,94 @@ pub async fn worker_status(pool: DbPool) -> anyhow::Result<Vec<Worker>> {
let workers = crate::schema::workers::dsl::workers.load::<Worker>(&mut conn)?;
Ok(workers)
}

async fn job_restart_in_transaction(job_id: i32, conn: &mut PgConnection) -> anyhow::Result<Job> {
let job = crate::schema::jobs::dsl::jobs
.find(job_id)
.get_result::<Job>(conn)?;
let pipeline = crate::schema::pipelines::dsl::pipelines
.find(job.pipeline_id)
.get_result::<Pipeline>(conn)?;

// job must be failed
if job.status != "failed" {
bail!("Cannot restart the job unless it was failed");
}

// create a new job
use crate::schema::jobs;
let mut new_job = NewJob {
pipeline_id: job.pipeline_id,
packages: job.packages,
arch: job.arch.clone(),
creation_time: chrono::Utc::now(),
status: "created".to_string(),
github_check_run_id: None,
require_min_core: None,
require_min_total_mem: None,
require_min_total_mem_per_core: None,
require_min_disk: None,
};

// create new github check run if the restarted job has one
if job.github_check_run_id.is_some() {
// authenticate with github app
match get_crab_github_installation().await {
Ok(Some(crab)) => {
match crab
.checks("AOSC-Dev", "aosc-os-abbs")
.create_check_run(format!("buildit {}", job.arch), &pipeline.git_sha)
.status(octocrab::params::checks::CheckRunStatus::Queued)
.send()
.await
{
Ok(check_run) => {
new_job.github_check_run_id = Some(check_run.id.0 as i64);
}
Err(err) => {
warn!("Failed to create check run: {}", err);
}
}
}
Ok(None) => {
// github app unavailable
}
Err(err) => {
warn!("Failed to get installation token: {}", err);
}
}
}

let new_job: Job = diesel::insert_into(jobs::table)
.values(&new_job)
.get_result(conn)
.context("Failed to create job")?;
Ok(new_job)
}

#[tracing::instrument(skip(pool))]
pub async fn job_restart(pool: DbPool, job_id: i32) -> anyhow::Result<Job> {
let mut conn = pool
.get()
.context("Failed to get db connection from pool")?;

// manually handle transaction, since we want to use async in transaction
PoolTransactionManager::<AnsiTransactionManager>::begin_transaction(&mut conn)?;
match job_restart_in_transaction(job_id, &mut conn).await {
Ok(new_job) => {
PoolTransactionManager::<AnsiTransactionManager>::commit_transaction(&mut conn)?;
return Ok(new_job);
}
Err(err) => {
match PoolTransactionManager::<AnsiTransactionManager>::rollback_transaction(&mut conn)
{
Ok(()) => {
return Err(err.into());
}
Err(rollback_err) => {
return Err(err.context(rollback_err).into());
}
}
}
}
}
27 changes: 26 additions & 1 deletion server/src/bot.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
api::{pipeline_new, pipeline_new_pr, pipeline_status, worker_status, JobSource},
api::{job_restart, pipeline_new, pipeline_new_pr, pipeline_status, worker_status, JobSource},
formatter::to_html_new_pipeline_summary,
github::{get_github_token, login_github},
models::{NewUser, User},
Expand Down Expand Up @@ -50,6 +50,8 @@ pub enum Command {
description = "Build lagging/missing packages for quality assurance: /qa arch lagging/missing"
)]
QA(String),
#[command(description = "Restart failed job: /restart job-id")]
Restart(String),
}

fn handle_archs_args(archs: Vec<&str>) -> Vec<&str> {
Expand Down Expand Up @@ -681,6 +683,29 @@ pub async fn answer(bot: Bot, msg: Message, cmd: Command, pool: DbPool) -> Respo
)
.await?;
}
Command::Restart(arguments) => match str::parse::<i32>(&arguments) {
Ok(job_id) => match job_restart(pool, job_id).await {
Ok(new_job) => {
bot_send_message_handle_length(
&bot,
&msg,
&format!("Restarted as job #{}", new_job.id),
)
.await?;
}
Err(err) => {
bot_send_message_handle_length(
&bot,
&msg,
&format!("Failed to restart job: {err}"),
)
.await?;
}
},
Err(err) => {
bot_send_message_handle_length(&bot, &msg, &format!("Bad job ID: {err}")).await?;
}
},
};

Ok(())
Expand Down
102 changes: 5 additions & 97 deletions server/src/routes/job.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
use crate::github::get_crab_github_installation;
use crate::models::{Job, NewJob, Pipeline, User, Worker};
use crate::models::{Job, Pipeline, User, Worker};
use crate::routes::{AnyhowError, AppState};
use anyhow::{bail, Context};
use anyhow::Context;
use axum::extract::{Json, Query, State};
use diesel::connection::{AnsiTransactionManager, TransactionManager};
use diesel::r2d2::PoolTransactionManager;
use diesel::{
Connection, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, PgConnection, QueryDsl,
RunQueryDsl,
Connection, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, RunQueryDsl,
};
use serde::{Deserialize, Serialize};
use tracing::warn;

#[derive(Deserialize)]
pub struct JobListRequest {
Expand Down Expand Up @@ -228,98 +223,11 @@ pub struct JobRestartRequest {
pub struct JobRestartResponse {
job_id: i32,
}
async fn job_restart_in_transaction(
payload: &JobRestartRequest,
conn: &mut PgConnection,
) -> anyhow::Result<Job> {
let job = crate::schema::jobs::dsl::jobs
.find(payload.job_id)
.get_result::<Job>(conn)?;
let pipeline = crate::schema::pipelines::dsl::pipelines
.find(job.pipeline_id)
.get_result::<Pipeline>(conn)?;

// job must be failed
if job.status != "failed" {
bail!("Cannot restart the job unless it was failed");
}

// create a new job
use crate::schema::jobs;
let mut new_job = NewJob {
pipeline_id: job.pipeline_id,
packages: job.packages,
arch: job.arch.clone(),
creation_time: chrono::Utc::now(),
status: "created".to_string(),
github_check_run_id: None,
require_min_core: None,
require_min_total_mem: None,
require_min_total_mem_per_core: None,
require_min_disk: None,
};

// create new github check run if the restarted job has one
if job.github_check_run_id.is_some() {
// authenticate with github app
match get_crab_github_installation().await {
Ok(Some(crab)) => {
match crab
.checks("AOSC-Dev", "aosc-os-abbs")
.create_check_run(format!("buildit {}", job.arch), &pipeline.git_sha)
.status(octocrab::params::checks::CheckRunStatus::Queued)
.send()
.await
{
Ok(check_run) => {
new_job.github_check_run_id = Some(check_run.id.0 as i64);
}
Err(err) => {
warn!("Failed to create check run: {}", err);
}
}
}
Ok(None) => {
// github app unavailable
}
Err(err) => {
warn!("Failed to get installation token: {}", err);
}
}
}

let new_job: Job = diesel::insert_into(jobs::table)
.values(&new_job)
.get_result(conn)
.context("Failed to create job")?;
Ok(new_job)
}

pub async fn job_restart(
State(AppState { pool, .. }): State<AppState>,
Json(payload): Json<JobRestartRequest>,
) -> Result<Json<JobRestartResponse>, AnyhowError> {
let mut conn = pool
.get()
.context("Failed to get db connection from pool")?;

// manually handle transaction, since we want to use async in transaction
PoolTransactionManager::<AnsiTransactionManager>::begin_transaction(&mut conn)?;
match job_restart_in_transaction(&payload, &mut conn).await {
Ok(new_job) => {
PoolTransactionManager::<AnsiTransactionManager>::commit_transaction(&mut conn)?;
return Ok(Json(JobRestartResponse { job_id: new_job.id }));
}
Err(err) => {
match PoolTransactionManager::<AnsiTransactionManager>::rollback_transaction(&mut conn)
{
Ok(()) => {
return Err(err.into());
}
Err(rollback_err) => {
return Err(err.context(rollback_err).into());
}
}
}
}
let new_job = crate::api::job_restart(pool, payload.job_id).await?;
return Ok(Json(JobRestartResponse { job_id: new_job.id }));
}

0 comments on commit 570a4da

Please sign in to comment.