mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-02 05:23:14 +00:00
Fix/backups (#2659)
* fix master build (#2639) * feat: Change ts to use rsync Chore: Update the ts to use types over interface * feat: Get the rust and the js to do a backup * Wip: Got the backup working? * fix permissions * remove trixie list * update tokio to fix timer bug * fix error handling on backup * wip * remove idmap * run restore before init, and init with own version on restore --------- Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -34,6 +34,7 @@ use crate::util::actor::concurrent::ConcurrentActor;
|
||||
use crate::util::actor::Actor;
|
||||
use crate::util::io::create_file;
|
||||
use crate::util::serde::Pem;
|
||||
use crate::util::Never;
|
||||
use crate::volume::data_dir;
|
||||
|
||||
mod action;
|
||||
@@ -220,12 +221,13 @@ impl Service {
|
||||
tracing::error!("Error opening s9pk for install: {e}");
|
||||
tracing::debug!("{e:?}")
|
||||
}) {
|
||||
if let Ok(service) = Self::install(ctx.clone(), s9pk, None, None)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("Error installing service: {e}");
|
||||
tracing::debug!("{e:?}")
|
||||
})
|
||||
if let Ok(service) =
|
||||
Self::install(ctx.clone(), s9pk, None, None::<Never>, None)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("Error installing service: {e}");
|
||||
tracing::debug!("{e:?}")
|
||||
})
|
||||
{
|
||||
return Ok(Some(service));
|
||||
}
|
||||
@@ -257,6 +259,7 @@ impl Service {
|
||||
ctx.clone(),
|
||||
s9pk,
|
||||
Some(s.as_manifest().as_version().de()?),
|
||||
None::<Never>,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@@ -334,13 +337,35 @@ impl Service {
|
||||
pub async fn install(
|
||||
ctx: RpcContext,
|
||||
s9pk: S9pk,
|
||||
src_version: Option<models::VersionString>,
|
||||
mut src_version: Option<models::VersionString>,
|
||||
recovery_source: Option<impl GenericMountGuard>,
|
||||
progress: Option<InstallProgressHandles>,
|
||||
) -> Result<ServiceRef, Error> {
|
||||
let manifest = s9pk.as_manifest().clone();
|
||||
let developer_key = s9pk.as_archive().signer();
|
||||
let icon = s9pk.icon_data_url().await?;
|
||||
let service = Self::new(ctx.clone(), s9pk, StartStop::Stop).await?;
|
||||
if let Some(recovery_source) = recovery_source {
|
||||
service
|
||||
.actor
|
||||
.send(
|
||||
Guid::new(),
|
||||
transition::restore::Restore {
|
||||
path: recovery_source.path().to_path_buf(),
|
||||
},
|
||||
)
|
||||
.await??;
|
||||
recovery_source.unmount().await?;
|
||||
src_version = Some(
|
||||
service
|
||||
.seed
|
||||
.persistent_container
|
||||
.s9pk
|
||||
.as_manifest()
|
||||
.version
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
service
|
||||
.seed
|
||||
.persistent_container
|
||||
@@ -382,26 +407,6 @@ impl Service {
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
pub async fn restore(
|
||||
ctx: RpcContext,
|
||||
s9pk: S9pk,
|
||||
backup_source: impl GenericMountGuard,
|
||||
progress: Option<InstallProgressHandles>,
|
||||
) -> Result<ServiceRef, Error> {
|
||||
let service = Service::install(ctx.clone(), s9pk, None, progress).await?;
|
||||
|
||||
service
|
||||
.actor
|
||||
.send(
|
||||
Guid::new(),
|
||||
transition::restore::Restore {
|
||||
path: backup_source.path().to_path_buf(),
|
||||
},
|
||||
)
|
||||
.await??;
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn backup(&self, guard: impl GenericMountGuard) -> Result<(), Error> {
|
||||
let id = &self.seed.id;
|
||||
@@ -417,10 +422,11 @@ impl Service {
|
||||
.send(
|
||||
Guid::new(),
|
||||
transition::backup::Backup {
|
||||
path: guard.path().to_path_buf(),
|
||||
path: guard.path().join("data"),
|
||||
},
|
||||
)
|
||||
.await??;
|
||||
.await??
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -505,13 +511,21 @@ impl Actor for ServiceActor {
|
||||
}
|
||||
(Some(TransitionKind::Restarting), _, _) => MainStatus::Restarting,
|
||||
(Some(TransitionKind::Restoring), _, _) => MainStatus::Restoring,
|
||||
(Some(TransitionKind::BackingUp), _, Some(status)) => {
|
||||
(Some(TransitionKind::BackingUp), StartStop::Stop, Some(status)) => {
|
||||
seed.persistent_container.stop().await?;
|
||||
MainStatus::BackingUp {
|
||||
started: Some(status.started),
|
||||
health: status.health.clone(),
|
||||
}
|
||||
}
|
||||
(Some(TransitionKind::BackingUp), _, None) => MainStatus::BackingUp {
|
||||
(Some(TransitionKind::BackingUp), StartStop::Start, _) => {
|
||||
seed.persistent_container.start().await?;
|
||||
MainStatus::BackingUp {
|
||||
started: None,
|
||||
health: OrdMap::new(),
|
||||
}
|
||||
}
|
||||
(Some(TransitionKind::BackingUp), _, _) => MainStatus::BackingUp {
|
||||
started: None,
|
||||
health: OrdMap::new(),
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -277,7 +277,7 @@ impl PersistentContainer {
|
||||
backup_path: impl AsRef<Path>,
|
||||
mount_type: MountType,
|
||||
) -> Result<MountGuard, Error> {
|
||||
let backup_path: PathBuf = backup_path.as_ref().to_path_buf();
|
||||
let backup_path = backup_path.as_ref();
|
||||
let mountpoint = self
|
||||
.lxc_container
|
||||
.get()
|
||||
@@ -295,14 +295,14 @@ impl PersistentContainer {
|
||||
.arg(mountpoint.as_os_str())
|
||||
.invoke(ErrorKind::Filesystem)
|
||||
.await?;
|
||||
let bind = Bind::new(&backup_path);
|
||||
let mount_guard = MountGuard::mount(&bind, &mountpoint, mount_type).await;
|
||||
tokio::fs::create_dir_all(backup_path).await?;
|
||||
Command::new("chown")
|
||||
.arg("100000:100000")
|
||||
.arg(backup_path.as_os_str())
|
||||
.arg(backup_path)
|
||||
.invoke(ErrorKind::Filesystem)
|
||||
.await?;
|
||||
mount_guard
|
||||
let bind = Bind::new(backup_path);
|
||||
MountGuard::mount(&bind, &mountpoint, mount_type).await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
|
||||
@@ -265,35 +265,20 @@ impl ServiceMap {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(recovery_source) = recovery_source {
|
||||
*service = Some(
|
||||
Service::restore(
|
||||
ctx,
|
||||
s9pk,
|
||||
recovery_source,
|
||||
Some(InstallProgressHandles {
|
||||
finalization_progress,
|
||||
progress,
|
||||
}),
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
);
|
||||
} else {
|
||||
*service = Some(
|
||||
Service::install(
|
||||
ctx,
|
||||
s9pk,
|
||||
prev,
|
||||
Some(InstallProgressHandles {
|
||||
finalization_progress,
|
||||
progress,
|
||||
}),
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
*service = Some(
|
||||
Service::install(
|
||||
ctx,
|
||||
s9pk,
|
||||
prev,
|
||||
recovery_source,
|
||||
Some(InstallProgressHandles {
|
||||
finalization_progress,
|
||||
progress,
|
||||
}),
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
);
|
||||
drop(service);
|
||||
|
||||
sync_progress_task.await.map_err(|_| {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use futures::FutureExt;
|
||||
use models::ProcedureName;
|
||||
|
||||
@@ -19,7 +21,7 @@ pub(in crate::service) struct Backup {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
impl Handler<Backup> for ServiceActor {
|
||||
type Response = Result<(), Error>;
|
||||
type Response = Result<BoxFuture<'static, Result<(), Error>>, Error>;
|
||||
fn conflicts_with(_: &Backup) -> ConflictBuilder<Self> {
|
||||
ConflictBuilder::everything()
|
||||
.except::<GetConfig>()
|
||||
@@ -37,43 +39,31 @@ impl Handler<Backup> for ServiceActor {
|
||||
let path = backup.path.clone();
|
||||
let seed = self.0.clone();
|
||||
|
||||
let state = self.0.persistent_container.state.clone();
|
||||
let transition = RemoteCancellable::new(
|
||||
async move {
|
||||
temp.stop();
|
||||
let transition = RemoteCancellable::new(async move {
|
||||
temp.stop();
|
||||
current
|
||||
.wait_for(|s| s.running_status.is_none())
|
||||
.await
|
||||
.with_kind(ErrorKind::Unknown)?;
|
||||
|
||||
let backup_guard = seed
|
||||
.persistent_container
|
||||
.mount_backup(path, ReadWrite)
|
||||
.await?;
|
||||
seed.persistent_container
|
||||
.execute(id, ProcedureName::CreateBackup, Value::Null, None)
|
||||
.await?;
|
||||
backup_guard.unmount(true).await?;
|
||||
|
||||
if temp.restore().is_start() {
|
||||
current
|
||||
.wait_for(|s| s.running_status.is_none())
|
||||
.wait_for(|s| s.running_status.is_some())
|
||||
.await
|
||||
.with_kind(ErrorKind::Unknown)?;
|
||||
|
||||
let backup_guard = seed
|
||||
.persistent_container
|
||||
.mount_backup(path, ReadWrite)
|
||||
.await?;
|
||||
seed.persistent_container
|
||||
.execute(id, ProcedureName::CreateBackup, Value::Null, None)
|
||||
.await?;
|
||||
backup_guard.unmount(true).await?;
|
||||
|
||||
if temp.restore().is_start() {
|
||||
current
|
||||
.wait_for(|s| s.running_status.is_some())
|
||||
.await
|
||||
.with_kind(ErrorKind::Unknown)?;
|
||||
}
|
||||
drop(temp);
|
||||
state.send_modify(|s| {
|
||||
s.transition_state.take();
|
||||
});
|
||||
Ok::<_, Error>(())
|
||||
}
|
||||
.map(|x| {
|
||||
if let Err(err) = dbg!(x) {
|
||||
tracing::debug!("{:?}", err);
|
||||
tracing::warn!("{}", err);
|
||||
}
|
||||
}),
|
||||
);
|
||||
drop(temp);
|
||||
Ok::<_, Arc<Error>>(())
|
||||
});
|
||||
let cancel_handle = transition.cancellation_handle();
|
||||
let transition = transition.shared();
|
||||
let job_transition = transition.clone();
|
||||
@@ -92,9 +82,11 @@ impl Handler<Backup> for ServiceActor {
|
||||
if let Some(t) = old {
|
||||
t.abort().await;
|
||||
}
|
||||
match transition.await {
|
||||
None => Err(Error::new(eyre!("Backup canceled"), ErrorKind::Unknown)),
|
||||
Some(x) => Ok(x),
|
||||
}
|
||||
Ok(transition
|
||||
.map(|r| {
|
||||
r.ok_or_else(|| Error::new(eyre!("Backup canceled"), ErrorKind::Cancelled))?
|
||||
.map_err(|e| e.clone_output())
|
||||
})
|
||||
.boxed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,10 @@ impl TempDesiredRestore {
|
||||
}
|
||||
impl Drop for TempDesiredRestore {
|
||||
fn drop(&mut self) {
|
||||
self.0.send_modify(|s| s.temp_desired_state = None);
|
||||
self.0.send_modify(|s| {
|
||||
s.temp_desired_state.take();
|
||||
s.transition_state.take();
|
||||
});
|
||||
}
|
||||
}
|
||||
// impl Deref for TempDesiredState {
|
||||
|
||||
Reference in New Issue
Block a user