mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 18:31:52 +00:00
127 lines
3.9 KiB
Rust
127 lines
3.9 KiB
Rust
use std::collections::BTreeSet;
|
|
|
|
use color_eyre::eyre::eyre;
|
|
use emver::VersionRange;
|
|
use futures::{Future, FutureExt};
|
|
use indexmap::IndexMap;
|
|
use patch_db::HasModel;
|
|
use serde::{Deserialize, Serialize};
|
|
use tracing::instrument;
|
|
|
|
use crate::action::ActionImplementation;
|
|
use crate::context::RpcContext;
|
|
use crate::id::ImageId;
|
|
use crate::s9pk::manifest::PackageId;
|
|
use crate::util::Version;
|
|
use crate::volume::Volumes;
|
|
use crate::{Error, ResultExt};
|
|
|
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct Migrations {
|
|
pub from: IndexMap<VersionRange, ActionImplementation>,
|
|
pub to: IndexMap<VersionRange, ActionImplementation>,
|
|
}
|
|
impl Migrations {
|
|
#[instrument]
|
|
pub fn validate(&self, volumes: &Volumes, image_ids: &BTreeSet<ImageId>) -> Result<(), Error> {
|
|
for (version, migration) in &self.from {
|
|
migration.validate(volumes, image_ids, true).with_ctx(|_| {
|
|
(
|
|
crate::ErrorKind::ValidateS9pk,
|
|
format!("Migration from {}", version),
|
|
)
|
|
})?;
|
|
}
|
|
for (version, migration) in &self.to {
|
|
migration.validate(volumes, image_ids, true).with_ctx(|_| {
|
|
(
|
|
crate::ErrorKind::ValidateS9pk,
|
|
format!("Migration to {}", version),
|
|
)
|
|
})?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[instrument(skip(ctx))]
|
|
pub fn from<'a>(
|
|
&'a self,
|
|
ctx: &'a RpcContext,
|
|
version: &'a Version,
|
|
pkg_id: &'a PackageId,
|
|
pkg_version: &'a Version,
|
|
volumes: &'a Volumes,
|
|
) -> Option<impl Future<Output = Result<MigrationRes, Error>> + 'a> {
|
|
if let Some((_, migration)) = self
|
|
.from
|
|
.iter()
|
|
.find(|(range, _)| version.satisfies(*range))
|
|
{
|
|
Some(
|
|
migration
|
|
.execute(
|
|
ctx,
|
|
pkg_id,
|
|
pkg_version,
|
|
Some("Migration"), // Migrations cannot be executed concurrently
|
|
volumes,
|
|
Some(version),
|
|
false,
|
|
None,
|
|
)
|
|
.map(|r| {
|
|
r.and_then(|r| {
|
|
r.map_err(|e| {
|
|
Error::new(eyre!("{}", e.1), crate::ErrorKind::MigrationFailed)
|
|
})
|
|
})
|
|
}),
|
|
)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[instrument(skip(ctx))]
|
|
pub fn to<'a>(
|
|
&'a self,
|
|
ctx: &'a RpcContext,
|
|
version: &'a Version,
|
|
pkg_id: &'a PackageId,
|
|
pkg_version: &'a Version,
|
|
volumes: &'a Volumes,
|
|
) -> Option<impl Future<Output = Result<MigrationRes, Error>> + 'a> {
|
|
if let Some((_, migration)) = self.to.iter().find(|(range, _)| version.satisfies(*range)) {
|
|
Some(
|
|
migration
|
|
.execute(
|
|
ctx,
|
|
pkg_id,
|
|
pkg_version,
|
|
Some("Migration"),
|
|
volumes,
|
|
Some(version),
|
|
false,
|
|
None,
|
|
)
|
|
.map(|r| {
|
|
r.and_then(|r| {
|
|
r.map_err(|e| {
|
|
Error::new(eyre!("{}", e.1), crate::ErrorKind::MigrationFailed)
|
|
})
|
|
})
|
|
}),
|
|
)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct MigrationRes {
|
|
pub configured: bool,
|
|
}
|