wip: possible second reimagining of manager
parent
5839d66213
commit
5073855696
|
@ -2,9 +2,12 @@ use super::{archive, package};
|
||||||
use crate::{db, error::Result};
|
use crate::{db, error::Result};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::{Arc, atomic::{AtomicBool, AtomicU32, Ordering}},
|
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, AtomicU32, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
@ -15,7 +18,7 @@ use sea_orm::{
|
||||||
use sea_query::{Expr, Query};
|
use sea_query::{Expr, Query};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::AsyncRead,
|
io::AsyncRead,
|
||||||
sync::{Mutex, Semaphore, RwLock, Notify},
|
sync::{Mutex, Notify, RwLock, Semaphore},
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -66,15 +69,17 @@ impl DistroMgr {
|
||||||
repos.get(&repo_id).map(Arc::clone)
|
repos.get(&repo_id).map(Arc::clone)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if state.is_none() {
|
if state.is_none() {
|
||||||
tracing::debug!("is none")
|
tracing::debug!("is none");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = state.unwrap();
|
let state = state.unwrap();
|
||||||
|
|
||||||
let res = state.sync_queued.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
|
let res =
|
||||||
|
state
|
||||||
|
.sync_queued
|
||||||
|
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
|
||||||
|
|
||||||
// Already a sync job scheduled, so this one can simply quit
|
// Already a sync job scheduled, so this one can simply quit
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
|
@ -376,7 +381,7 @@ impl DistroMgr {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((repo.id, pkg.name, pkg.version, pkg.arch))
|
Ok((repo.id, pkg.name, pkg.version, pkg.arch))
|
||||||
},
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
use super::{archive, package};
|
||||||
|
use crate::db;
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use sea_orm::{
|
||||||
|
ActiveModelTrait, ColumnTrait, Condition, DbConn, EntityTrait, JoinType, ModelTrait, NotSet,
|
||||||
|
QueryFilter, QuerySelect, Related, RelationTrait, Set, TransactionTrait,
|
||||||
|
};
|
||||||
|
use tokio::sync::{
|
||||||
|
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||||
|
Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PkgQueueMsg {
|
||||||
|
repo: i32,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single instance of this struct orchestrates everything related to managing packages files on
|
||||||
|
/// disk for all repositories in the server
|
||||||
|
pub struct RepoMgr {
|
||||||
|
repos_dir: PathBuf,
|
||||||
|
conn: DbConn,
|
||||||
|
pkg_queue: (
|
||||||
|
UnboundedSender<PkgQueueMsg>,
|
||||||
|
Mutex<UnboundedReceiver<PkgQueueMsg>>,
|
||||||
|
),
|
||||||
|
repos_lock: Mutex<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RepoMgr {
|
||||||
|
pub async fn new<P: AsRef<Path>>(repos_dir: P, conn: DbConn) -> crate::Result<Self> {
|
||||||
|
if !tokio::fs::try_exists(&repos_dir).await? {
|
||||||
|
tokio::fs::create_dir(&repos_dir).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (tx, rx) = unbounded_channel();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
repos_dir: repos_dir.as_ref().to_path_buf(),
|
||||||
|
conn,
|
||||||
|
pkg_queue: (tx, Mutex::new(rx)),
|
||||||
|
repos_lock: Mutex::new(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pkg_parse_task(&self) {
|
||||||
|
loop {
|
||||||
|
// Receive the next message and immediately drop the mutex afterwards. As long as the
|
||||||
|
// quue is empty, this will lock the mutex. This is okay, as the mutex will be unlocked
|
||||||
|
// as soon as a message is received, so another worker can pick up the mutex.
|
||||||
|
let mut recv = self.pkg_queue.1.lock().await;
|
||||||
|
let msg = recv.recv().await;
|
||||||
|
drop(recv);
|
||||||
|
|
||||||
|
if let Some(msg) = msg {
|
||||||
|
// TODO better handle this error (retry if failure wasn't because the package is
|
||||||
|
// faulty)
|
||||||
|
let _ = self
|
||||||
|
.add_pkg_from_path(msg.path, msg.repo)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| tracing::error!("{:?}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_pkg(&self, repo: i32, path: PathBuf) {
|
||||||
|
let _ = self.pkg_queue.0.send(PkgQueueMsg { path, repo });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_or_create_repo(&self, distro: &str, repo: &str) -> crate::Result<i32> {
|
||||||
|
let _guard = self.repos_lock.lock().await;
|
||||||
|
|
||||||
|
let distro_id: Option<i32> = db::Distro::find()
|
||||||
|
.filter(db::distro::Column::Name.eq(distro))
|
||||||
|
.select_only()
|
||||||
|
.column(db::distro::Column::Id)
|
||||||
|
.into_tuple()
|
||||||
|
.one(&self.conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let distro_id = if let Some(id) = distro_id {
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
let new_distro = db::distro::ActiveModel {
|
||||||
|
id: NotSet,
|
||||||
|
name: Set(distro.to_string()),
|
||||||
|
description: NotSet,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_distro.insert(&self.conn).await?.id
|
||||||
|
};
|
||||||
|
|
||||||
|
let repo_id: Option<i32> = db::Repo::find()
|
||||||
|
.filter(db::repo::Column::Name.eq(repo))
|
||||||
|
.select_only()
|
||||||
|
.column(db::repo::Column::Id)
|
||||||
|
.into_tuple()
|
||||||
|
.one(&self.conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let repo_id = if let Some(id) = repo_id {
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
let new_repo = db::repo::ActiveModel {
|
||||||
|
id: NotSet,
|
||||||
|
distro_id: Set(distro_id),
|
||||||
|
name: Set(repo.to_string()),
|
||||||
|
description: NotSet,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_repo.insert(&self.conn).await?.id
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(repo_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_pkg_from_path<P: AsRef<Path>>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
repo: i32,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
let path_clone = path.as_ref().to_path_buf();
|
||||||
|
let pkg = tokio::task::spawn_blocking(move || package::Package::open(path_clone))
|
||||||
|
.await
|
||||||
|
.unwrap()?;
|
||||||
|
|
||||||
|
// TODO prevent database from being updated but file failing to move to repo dir?
|
||||||
|
let pkg = db::query::package::insert(&self.conn, repo, pkg).await?;
|
||||||
|
|
||||||
|
let dest_path = self
|
||||||
|
.repos_dir
|
||||||
|
.join(repo.to_string())
|
||||||
|
.join(pkg.id.to_string());
|
||||||
|
tokio::fs::rename(path.as_ref(), dest_path).await?;
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"Added '{}-{}-{}' to repository {}",
|
||||||
|
pkg.name,
|
||||||
|
pkg.version,
|
||||||
|
pkg.arch,
|
||||||
|
repo,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
mod archive;
|
mod archive;
|
||||||
mod manager;
|
mod manager;
|
||||||
|
mod manager2;
|
||||||
pub mod package;
|
pub mod package;
|
||||||
|
|
||||||
pub use manager::DistroMgr;
|
pub use manager::DistroMgr;
|
||||||
|
|
Loading…
Reference in New Issue