Skip to content

Commit

Permalink
feat: periodically pull new runner image
Browse files Browse the repository at this point in the history
  • Loading branch information
VirtCode committed Apr 27, 2024
1 parent 0b9239f commit fee02d5
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
branches: ['main']
paths: ["runner/**", ".github/workflows/publish-runner.yml"]
workflow_dispatch:
release:
types: ['published']
schedule:
- cron: "0 22 * * *"

env:
REGISTRY: ghcr.io
Expand Down
51 changes: 50 additions & 1 deletion server/src/build/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use std::collections::HashMap;
use std::sync::{Arc};
use std::time::Duration;
use anyhow::{anyhow, Context};
use log::{debug, info, warn};
use hyper::body::HttpBody;
use log::{debug, error, info, warn};
use tokio::sync::RwLock;
use tokio_cron_scheduler::{Job, JobScheduler};
use uuid::Uuid;
use crate::build::Builder;
use crate::config::CONFIG;
use crate::package::Package;
use crate::runner::Runner;

/// this struct schedules builds for all packages
pub struct BuildScheduler {
Expand Down Expand Up @@ -119,4 +122,50 @@ async fn run(lock: Arc<RwLock<bool>>, builder: Arc<RwLock<Builder>>, force: bool
*lock.write().await = true;
builder.read().await.run_scheduled(&base, force, clean).await;
*lock.write().await = false;
}

/// Schedules the pulling of the runner image
pub struct ImageScheduler {
runner: Arc<RwLock<Runner>>,
sched: JobScheduler,
job: Uuid
}

impl ImageScheduler {
/// creates a new image scheduler
pub async fn new(runner: Arc<RwLock<Runner>>) -> anyhow::Result<Self> {
let mut s = Self {
runner,
sched: JobScheduler::new().await
.context("failed to initialize job scheduler")?,
job: Uuid::from_u128(0u128)
};

s.schedule().await?;
Ok(s)
}

async fn schedule(&mut self) -> anyhow::Result<()> {
let runner = self.runner.clone();

info!("scheduling image update");

let job = Job::new_async(CONFIG.schedule_image.as_str(), move |_, _| {
let runner = runner.clone();

Box::pin(async move {
info!("updating runner image");

if let Err(e) = runner.read().await.update_image().await {
error!("failed to update runner image: {e:#}");
} else {
info!("successfully updated runner image");
}
})
}).context("failed to schedule job image updating")?;

self.job = job.guid();

Ok(())
}
}
4 changes: 4 additions & 0 deletions server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub struct Config {
pub schedule_default: String,
/// scheduling of development packages
pub schedule_devel: String,
/// schedule for pulling the runner image
pub schedule_image: String,
/// container name prefix xxxxx-my-package
pub container_prefix: String,
/// runner docker image
Expand All @@ -44,6 +46,7 @@ impl Default for Config {

schedule_default: "0 0 0 * * *".to_string(), // 00:00 UTC every day
schedule_devel: "0 0 0 * * *".to_string(),
schedule_image: "0 0 0 * * *".to_string(),

container_prefix: "serene-aur-runner-".to_string(),
runner_image: "ghcr.io/virtcode/serene-aur-runner:main".to_string(),
Expand All @@ -68,6 +71,7 @@ impl Config {
repository_name: env::var("NAME").unwrap_or(default.repository_name),
own_repository_url: env::var("OWN_REPOSITORY_URL").ok().or(default.own_repository_url),

schedule_image: env::var("SCHEUDLE_IMAGE").unwrap_or(default.schedule_image),
schedule_devel: env::var("SCHEDULE_DEVEL").or(env::var("SCHEUDLE")).unwrap_or(default.schedule_devel.clone()),
schedule_default: env::var("SCHEUDLE").unwrap_or(default.schedule_default),

Expand Down
6 changes: 5 additions & 1 deletion server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use actix_web::web::Data;
use anyhow::Context;
use log::warn;
use tokio::sync::{RwLock};
use crate::build::schedule::BuildScheduler;
use crate::build::schedule::{BuildScheduler, ImageScheduler};
use crate::build::Builder;
use crate::config::CONFIG;
use crate::package::Package;
Expand Down Expand Up @@ -47,6 +47,10 @@ async fn main() -> anyhow::Result<()> {
// creating scheduler
let mut schedule = BuildScheduler::new(builder.clone()).await
.context("failed to start package scheduler")?;

// creating image scheduler
let _image_scheduler = ImageScheduler::new(runner.clone()).await
.context("failed to start image scheduler")?;

for package in Package::find_all(&db).await? {
schedule.schedule(&package).await
Expand Down
20 changes: 19 additions & 1 deletion server/src/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ use anyhow::{Context};
use async_tar::Archive;
use bollard::container::{Config, CreateContainerOptions, DownloadFromContainerOptions, ListContainersOptions, LogsOptions, StartContainerOptions, UploadToContainerOptions, WaitContainerOptions};
use bollard::Docker;
use bollard::image::CreateImageOptions;
use chrono::{DateTime, Utc};
use futures_util::{AsyncRead, StreamExt};
use futures_util::{AsyncRead, StreamExt, TryStreamExt};
use hyper::body::HttpBody;
use serde::{Deserialize, Serialize};
use tokio_util::io::StreamReader;
use tokio_util::compat::{TokioAsyncReadCompatExt};
Expand Down Expand Up @@ -152,6 +154,22 @@ impl Runner {
Ok(self.docker.create_container(Some(options), config).await?.id)
}

pub async fn update_image(&self) -> anyhow::Result<()> {
let results = self.docker.create_image(Some(CreateImageOptions {
from_image: CONFIG.runner_image.clone(),
..Default::default()
}), None, None)
.collect::<Vec<Result<_, _>>>().await;

// can this be directly collected into a result? probably... but streams suck
let _statuses = results.into_iter().collect::<Result<Vec<_>, _>>()
.context("failed to pull new docker image")?;

// we just make sure the stream is finished, and don't process the results (yet?)

Ok(())
}

/// cleans the container, i.e. removes it
pub async fn clean(&self, container: &ContainerId) -> anyhow::Result<()> {
self.docker.remove_container(&container, None).await
Expand Down

0 comments on commit fee02d5

Please sign in to comment.