mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
appmgr 0.3.0 rewrite pt 1
appmgr: split bins update cargo.toml and .gitignore context appmgr: refactor error module appmgr: context begin new s9pk format appmgr: add fields to manifest appmgr: start action abstraction appmgr: volume abstraction appmgr: improved volumes appmgr: install wip appmgr: health daemon appmgr: health checks appmgr: wip config get appmgr: secret store wip appmgr: config rewritten appmgr: delete non-reusable code appmgr: wip appmgr: please the borrow-checker appmgr: technically runs now appmgr: cli appmgr: clean up cli appmgr: rpc-toolkit in action appmgr: wrap up config appmgr: account for updates during install appmgr: fix: #308 appmgr: impl Display for Version appmgr: cleanup appmgr: set dependents on install appmgr: dependency health checks
This commit is contained in:
committed by
Aiden McClelland
parent
5741cf084f
commit
8954e3e338
@@ -1,68 +1,63 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use failure::ResultExt as _;
|
||||
use futures::stream::TryStreamExt;
|
||||
use lazy_static::lazy_static;
|
||||
use patch_db::DbHandle;
|
||||
use rpc_toolkit::command;
|
||||
use tokio_compat_02::FutureExt;
|
||||
|
||||
use crate::util::{to_yaml_async_writer, AsyncCompat, PersistencePath};
|
||||
use crate::Error;
|
||||
use crate::ResultExt as _;
|
||||
// mod v0_1_0;
|
||||
// mod v0_1_1;
|
||||
// mod v0_1_2;
|
||||
// mod v0_1_3;
|
||||
// mod v0_1_4;
|
||||
// mod v0_1_5;
|
||||
// mod v0_2_0;
|
||||
// mod v0_2_1;
|
||||
// mod v0_2_2;
|
||||
// mod v0_2_3;
|
||||
// mod v0_2_4;
|
||||
// mod v0_2_5;
|
||||
// mod v0_2_6;
|
||||
// mod v0_2_7;
|
||||
// mod v0_2_8;
|
||||
// mod v0_2_9;
|
||||
|
||||
mod v0_1_0;
|
||||
mod v0_1_1;
|
||||
mod v0_1_2;
|
||||
mod v0_1_3;
|
||||
mod v0_1_4;
|
||||
mod v0_1_5;
|
||||
mod v0_2_0;
|
||||
mod v0_2_1;
|
||||
mod v0_2_2;
|
||||
mod v0_2_3;
|
||||
mod v0_2_4;
|
||||
mod v0_2_5;
|
||||
mod v0_2_6;
|
||||
mod v0_2_7;
|
||||
mod v0_2_8;
|
||||
mod v0_2_9;
|
||||
// mod v0_2_10;
|
||||
// mod v0_2_11;
|
||||
// mod v0_2_12;
|
||||
|
||||
mod v0_2_10;
|
||||
mod v0_2_11;
|
||||
mod v0_2_12;
|
||||
mod v0_2_13;
|
||||
mod v0_2_14;
|
||||
mod v0_2_15;
|
||||
mod v0_2_16;
|
||||
// pub use v0_2_12::Version as Current;
|
||||
pub type Current = ();
|
||||
|
||||
pub use v0_2_16::Version as Current;
|
||||
use crate::context::{CliContext, EitherContext, RpcContext};
|
||||
use crate::util::{to_yaml_async_writer, AsyncCompat};
|
||||
use crate::{Error, ResultExt as _};
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum Version {
|
||||
V0_0_0(Wrapper<()>),
|
||||
V0_1_0(Wrapper<v0_1_0::Version>),
|
||||
V0_1_1(Wrapper<v0_1_1::Version>),
|
||||
V0_1_2(Wrapper<v0_1_2::Version>),
|
||||
V0_1_3(Wrapper<v0_1_3::Version>),
|
||||
V0_1_4(Wrapper<v0_1_4::Version>),
|
||||
V0_1_5(Wrapper<v0_1_5::Version>),
|
||||
V0_2_0(Wrapper<v0_2_0::Version>),
|
||||
V0_2_1(Wrapper<v0_2_1::Version>),
|
||||
V0_2_2(Wrapper<v0_2_2::Version>),
|
||||
V0_2_3(Wrapper<v0_2_3::Version>),
|
||||
V0_2_4(Wrapper<v0_2_4::Version>),
|
||||
V0_2_5(Wrapper<v0_2_5::Version>),
|
||||
V0_2_6(Wrapper<v0_2_6::Version>),
|
||||
V0_2_7(Wrapper<v0_2_7::Version>),
|
||||
V0_2_8(Wrapper<v0_2_8::Version>),
|
||||
V0_2_9(Wrapper<v0_2_9::Version>),
|
||||
V0_2_10(Wrapper<v0_2_10::Version>),
|
||||
V0_2_11(Wrapper<v0_2_11::Version>),
|
||||
V0_2_12(Wrapper<v0_2_12::Version>),
|
||||
V0_2_13(Wrapper<v0_2_13::Version>),
|
||||
V0_2_14(Wrapper<v0_2_14::Version>),
|
||||
V0_2_15(Wrapper<v0_2_15::Version>),
|
||||
V0_2_16(Wrapper<v0_2_16::Version>),
|
||||
// V0_1_0(Wrapper<v0_1_0::Version>),
|
||||
// V0_1_1(Wrapper<v0_1_1::Version>),
|
||||
// V0_1_2(Wrapper<v0_1_2::Version>),
|
||||
// V0_1_3(Wrapper<v0_1_3::Version>),
|
||||
// V0_1_4(Wrapper<v0_1_4::Version>),
|
||||
// V0_1_5(Wrapper<v0_1_5::Version>),
|
||||
// V0_2_0(Wrapper<v0_2_0::Version>),
|
||||
// V0_2_1(Wrapper<v0_2_1::Version>),
|
||||
// V0_2_2(Wrapper<v0_2_2::Version>),
|
||||
// V0_2_3(Wrapper<v0_2_3::Version>),
|
||||
// V0_2_4(Wrapper<v0_2_4::Version>),
|
||||
// V0_2_5(Wrapper<v0_2_5::Version>),
|
||||
// V0_2_6(Wrapper<v0_2_6::Version>),
|
||||
// V0_2_7(Wrapper<v0_2_7::Version>),
|
||||
// V0_2_8(Wrapper<v0_2_8::Version>),
|
||||
// V0_2_9(Wrapper<v0_2_9::Version>),
|
||||
// V0_2_10(Wrapper<v0_2_10::Version>),
|
||||
// V0_2_11(Wrapper<v0_2_11::Version>),
|
||||
// V0_2_12(Wrapper<v0_2_12::Version>),
|
||||
Other(emver::Version),
|
||||
}
|
||||
|
||||
@@ -73,39 +68,62 @@ where
|
||||
{
|
||||
type Previous: VersionT;
|
||||
fn new() -> Self;
|
||||
fn semver(&self) -> &'static emver::Version;
|
||||
async fn up(&self) -> Result<(), Error>;
|
||||
async fn down(&self) -> Result<(), Error>;
|
||||
async fn commit(&self) -> Result<(), Error> {
|
||||
let mut out = PersistencePath::from_ref("version").write(None).await?;
|
||||
to_yaml_async_writer(out.as_mut(), &self.semver()).await?;
|
||||
out.commit().await?;
|
||||
fn semver(&self) -> &'static crate::util::Version;
|
||||
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
|
||||
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
|
||||
async fn commit<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.version()
|
||||
.put(db, self.semver())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
async fn migrate_to<V: VersionT>(&self, version: &V) -> Result<(), Error> {
|
||||
async fn migrate_to<V: VersionT, Db: DbHandle>(
|
||||
&self,
|
||||
version: &V,
|
||||
db: &mut Db,
|
||||
) -> Result<(), Error> {
|
||||
match self.semver().cmp(version.semver()) {
|
||||
Ordering::Greater => self.rollback_to_unchecked(version).await,
|
||||
Ordering::Less => version.migrate_from_unchecked(self).await,
|
||||
Ordering::Greater => self.rollback_to_unchecked(version, db).await,
|
||||
Ordering::Less => version.migrate_from_unchecked(self, db).await,
|
||||
Ordering::Equal => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn migrate_from_unchecked<V: VersionT>(&self, version: &V) -> Result<(), Error> {
|
||||
async fn migrate_from_unchecked<V: VersionT, Db: DbHandle>(
|
||||
&self,
|
||||
version: &V,
|
||||
db: &mut Db,
|
||||
) -> Result<(), Error> {
|
||||
let previous = Self::Previous::new();
|
||||
if version.semver() != previous.semver() {
|
||||
previous.migrate_from_unchecked(version).await?;
|
||||
previous.migrate_from_unchecked(version, db).await?;
|
||||
}
|
||||
log::info!("{} -> {}", previous.semver(), self.semver());
|
||||
self.up().await?;
|
||||
self.commit().await?;
|
||||
log::info!(
|
||||
"{} -> {}",
|
||||
previous.semver().as_str(),
|
||||
self.semver().as_str()
|
||||
);
|
||||
self.up(db).await?;
|
||||
self.commit(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn rollback_to_unchecked<V: VersionT>(&self, version: &V) -> Result<(), Error> {
|
||||
async fn rollback_to_unchecked<V: VersionT, Db: DbHandle>(
|
||||
&self,
|
||||
version: &V,
|
||||
db: &mut Db,
|
||||
) -> Result<(), Error> {
|
||||
let previous = Self::Previous::new();
|
||||
log::info!("{} -> {}", self.semver(), previous.semver());
|
||||
self.down().await?;
|
||||
previous.commit().await?;
|
||||
log::info!(
|
||||
"{} -> {}",
|
||||
self.semver().as_str(),
|
||||
previous.semver().as_str()
|
||||
);
|
||||
self.down(db).await?;
|
||||
previous.commit(db).await?;
|
||||
if version.semver() != previous.semver() {
|
||||
previous.rollback_to_unchecked(version).await?;
|
||||
previous.rollback_to_unchecked(version, db).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -124,7 +142,7 @@ where
|
||||
T: VersionT,
|
||||
{
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let v = emver::Version::deserialize(deserializer)?;
|
||||
let v = crate::util::Version::deserialize(deserializer)?;
|
||||
let version = T::new();
|
||||
if &v == version.semver() {
|
||||
Ok(Wrapper(version))
|
||||
@@ -133,166 +151,38 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
const V0_0_0: emver::Version = emver::Version::new(0, 0, 0, 0);
|
||||
lazy_static! {
|
||||
static ref V0_0_0: crate::util::Version = emver::Version::new(0, 0, 0, 0).into();
|
||||
}
|
||||
#[async_trait]
|
||||
impl VersionT for () {
|
||||
type Previous = ();
|
||||
fn new() -> Self {
|
||||
()
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_0_0
|
||||
fn semver(&self) -> &'static crate::util::Version {
|
||||
&*V0_0_0
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn init() -> Result<(), failure::Error> {
|
||||
let _lock = PersistencePath::from_ref("").lock(true).await?;
|
||||
let vpath = PersistencePath::from_ref("version");
|
||||
if let Some(mut f) = vpath.maybe_read(false).await.transpose()? {
|
||||
let v: Version = crate::util::from_yaml_async_reader(&mut *f).await?;
|
||||
match v {
|
||||
Version::V0_0_0(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_1_0(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_1_1(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_1_2(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_1_3(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_1_4(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_1_5(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_0(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_1(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_2(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_3(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_4(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_5(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_6(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_7(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_8(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_9(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_10(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_11(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_12(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_13(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_14(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_15(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::V0_2_16(v) => v.0.migrate_to(&Current::new()).await?,
|
||||
Version::Other(_) => (),
|
||||
// TODO find some way to automate this?
|
||||
}
|
||||
} else {
|
||||
().migrate_to(&Current::new()).await?;
|
||||
}
|
||||
Ok(())
|
||||
pub async fn init() -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn self_update(requirement: emver::VersionRange) -> Result<(), Error> {
|
||||
let req_str: String = format!("{}", requirement)
|
||||
.chars()
|
||||
.filter(|c| !c.is_whitespace())
|
||||
.collect();
|
||||
let url = format!("{}/appmgr?spec={}", &*crate::SYS_REGISTRY_URL, req_str);
|
||||
log::info!("Fetching new version from {}", url);
|
||||
let response = reqwest::get(&url)
|
||||
.compat()
|
||||
.await
|
||||
.with_code(crate::error::NETWORK_ERROR)?
|
||||
.error_for_status()
|
||||
.with_code(crate::error::REGISTRY_ERROR)?;
|
||||
let tmp_appmgr_path = PersistencePath::from_ref("appmgr").tmp();
|
||||
if let Some(parent) = tmp_appmgr_path.parent() {
|
||||
if !parent.exists() {
|
||||
tokio::fs::create_dir_all(parent)
|
||||
.await
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
}
|
||||
}
|
||||
let mut f = tokio::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(&tmp_appmgr_path)
|
||||
.await
|
||||
.with_context(|e| format!("{}: {}", tmp_appmgr_path.display(), e))
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
tokio::io::copy(
|
||||
&mut AsyncCompat(
|
||||
response
|
||||
.bytes_stream()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||
.into_async_read(),
|
||||
),
|
||||
&mut f,
|
||||
)
|
||||
.await
|
||||
.no_code()?;
|
||||
drop(f);
|
||||
crate::ensure_code!(
|
||||
tokio::process::Command::new("chmod")
|
||||
.arg("700")
|
||||
.arg(&tmp_appmgr_path)
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
crate::error::FILESYSTEM_ERROR,
|
||||
"chmod failed"
|
||||
);
|
||||
let out = std::process::Command::new(&tmp_appmgr_path)
|
||||
.arg("semver")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.spawn()?
|
||||
.wait_with_output()
|
||||
.with_context(|e| format!("{} semver: {}", tmp_appmgr_path.display(), e))
|
||||
.no_code()?;
|
||||
let out_str = std::str::from_utf8(&out.stdout).no_code()?;
|
||||
log::info!("Migrating to version {}", out_str);
|
||||
let v: Version = serde_yaml::from_str(out_str)
|
||||
.with_context(|e| format!("{}: {:?}", e, out_str))
|
||||
.with_code(crate::error::SERDE_ERROR)?;
|
||||
match v {
|
||||
Version::V0_0_0(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_1_0(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_1_1(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_1_2(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_1_3(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_1_4(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_1_5(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_0(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_1(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_2(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_3(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_4(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_5(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_6(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_7(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_8(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_9(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_10(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_11(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_12(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_13(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_14(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_15(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::V0_2_16(v) => Current::new().migrate_to(&v.0).await?,
|
||||
Version::Other(_) => (),
|
||||
// TODO find some way to automate this?
|
||||
};
|
||||
let cur_path = std::path::Path::new("/usr/local/bin/appmgr");
|
||||
tokio::fs::rename(&tmp_appmgr_path, &cur_path)
|
||||
.await
|
||||
.with_context(|e| {
|
||||
format!(
|
||||
"{} -> {}: {}",
|
||||
tmp_appmgr_path.display(),
|
||||
cur_path.display(),
|
||||
e
|
||||
)
|
||||
})
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
|
||||
Ok(())
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[command(rename = "git-info", local)]
|
||||
pub fn git_info(#[context] _ctx: EitherContext) -> Result<String, Error> {
|
||||
Ok(
|
||||
git_version::git_version!(args = ["--always", "--abbrev=40", "--dirty=-modified"])
|
||||
.to_owned(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
|
||||
const V0_1_0: emver::Version = emver::Version::new(0, 1, 0, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = ();
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_1_0
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
tokio::fs::create_dir_all(Path::new(crate::PERSISTENCE_DIR).join("tor")).await?;
|
||||
tokio::fs::create_dir_all(Path::new(crate::PERSISTENCE_DIR).join("apps")).await?;
|
||||
tokio::fs::create_dir_all(Path::new(crate::TMP_DIR).join("tor")).await?;
|
||||
tokio::fs::create_dir_all(Path::new(crate::TMP_DIR).join("apps")).await?;
|
||||
let mut outfile = legacy::util::PersistencePath::from_ref("tor/torrc")
|
||||
.write()
|
||||
.await?;
|
||||
tokio::io::copy(
|
||||
&mut AsyncCompat(
|
||||
reqwest::get(&format!("{}/torrc?spec==0.0.0", &*crate::SYS_REGISTRY_URL))
|
||||
.compat()
|
||||
.await
|
||||
.with_context(|e| format!("GET {}/torrc: {}", &*crate::SYS_REGISTRY_URL, e))
|
||||
.with_code(crate::error::NETWORK_ERROR)?
|
||||
.error_for_status()
|
||||
.with_context(|e| format!("GET {}/torrc: {}", &*crate::SYS_REGISTRY_URL, e))
|
||||
.with_code(crate::error::REGISTRY_ERROR)?
|
||||
.bytes_stream()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||
.into_async_read(),
|
||||
),
|
||||
outfile.as_mut(),
|
||||
)
|
||||
.await
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
outfile.commit().await?;
|
||||
legacy::tor::set_svc(
|
||||
"start9-agent",
|
||||
legacy::tor::Service {
|
||||
ports: vec![5959],
|
||||
hidden_service_version: Default::default(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.no_code()?;
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod legacy {
|
||||
pub mod tor {
|
||||
use failure::{Error, ResultExt};
|
||||
use linear_map::LinearMap;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::tor::HiddenServiceVersion;
|
||||
|
||||
use super::util::PersistencePath;
|
||||
|
||||
pub const ETC_TOR_RC: &'static str = "/etc/tor/torrc";
|
||||
pub const HIDDEN_SERVICE_DIR_ROOT: &'static str = "/var/lib/tor";
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Service {
|
||||
pub ports: Vec<u16>,
|
||||
pub hidden_service_version: HiddenServiceVersion,
|
||||
}
|
||||
|
||||
async fn services_map(path: &PersistencePath) -> Result<LinearMap<String, Service>, Error> {
|
||||
use crate::util::Apply;
|
||||
Ok(path
|
||||
.maybe_read()
|
||||
.await
|
||||
.transpose()?
|
||||
.map(crate::util::from_yaml_async_reader)
|
||||
.apply(futures::future::OptionFuture::from)
|
||||
.await
|
||||
.transpose()?
|
||||
.unwrap_or_else(LinearMap::new))
|
||||
}
|
||||
|
||||
pub async fn write_services(
|
||||
hidden_services: &LinearMap<String, Service>,
|
||||
) -> Result<(), Error> {
|
||||
tokio::fs::copy(crate::TOR_RC, ETC_TOR_RC)
|
||||
.await
|
||||
.with_context(|e| format!("{} -> {}: {}", crate::TOR_RC, ETC_TOR_RC, e))?;
|
||||
let mut f = tokio::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.open(ETC_TOR_RC)
|
||||
.await?;
|
||||
f.write("\n".as_bytes()).await?;
|
||||
for (name, service) in hidden_services {
|
||||
f.write("\n".as_bytes()).await?;
|
||||
f.write(format!("# HIDDEN SERVICE FOR {}\n", name).as_bytes())
|
||||
.await?;
|
||||
f.write(
|
||||
format!(
|
||||
"HiddenServiceDir {}/app-{}/\n",
|
||||
HIDDEN_SERVICE_DIR_ROOT, name
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.await?;
|
||||
f.write(format!("{}\n", service.hidden_service_version).as_bytes())
|
||||
.await?;
|
||||
for port in &service.ports {
|
||||
f.write(format!("HiddenServicePort {} 127.0.0.1:{}\n", port, port).as_bytes())
|
||||
.await?;
|
||||
}
|
||||
f.write("\n".as_bytes()).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_svc(name: &str, service: Service) -> Result<(), Error> {
|
||||
log::info!(
|
||||
"Adding Tor hidden service {} to {}.",
|
||||
name,
|
||||
crate::SERVICES_YAML
|
||||
);
|
||||
let path = PersistencePath::from_ref(crate::SERVICES_YAML);
|
||||
let mut hidden_services = services_map(&path).await?;
|
||||
hidden_services.insert(name.to_owned(), service);
|
||||
let mut services_yaml = path.write().await?;
|
||||
crate::util::to_yaml_async_writer(services_yaml.as_mut(), &hidden_services).await?;
|
||||
services_yaml.write_all("\n".as_bytes()).await?;
|
||||
services_yaml.commit().await?;
|
||||
log::info!("Adding Tor hidden service {} to {}.", name, ETC_TOR_RC);
|
||||
write_services(&hidden_services).await?;
|
||||
log::info!("Restarting Tor.");
|
||||
let svc_exit = std::process::Command::new("service")
|
||||
.args(&["tor", "restart"])
|
||||
.status()?;
|
||||
ensure!(
|
||||
svc_exit.success(),
|
||||
"Failed to Restart Tor: {}",
|
||||
svc_exit.code().unwrap_or(0)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod util {
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::fs::File;
|
||||
|
||||
use crate::Error;
|
||||
use crate::ResultExt as _;
|
||||
use failure::ResultExt as _;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PersistencePath(PathBuf);
|
||||
impl PersistencePath {
|
||||
pub fn from_ref<P: AsRef<Path>>(p: P) -> Self {
|
||||
let path = p.as_ref();
|
||||
PersistencePath(if path.has_root() {
|
||||
path.strip_prefix("/").unwrap().to_owned()
|
||||
} else {
|
||||
path.to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tmp(&self) -> PathBuf {
|
||||
Path::new(crate::TMP_DIR).join(&self.0)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> PathBuf {
|
||||
Path::new(crate::PERSISTENCE_DIR).join(&self.0)
|
||||
}
|
||||
|
||||
pub async fn maybe_read(&self) -> Option<Result<File, Error>> {
|
||||
let path = self.path();
|
||||
if path.exists() {
|
||||
Some(
|
||||
File::open(&path)
|
||||
.await
|
||||
.with_context(|e| format!("{}: {}", path.display(), e))
|
||||
.with_code(crate::error::FILESYSTEM_ERROR),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn write(&self) -> Result<PersistenceFile, Error> {
|
||||
let path = self.path();
|
||||
if let Some(parent) = path.parent() {
|
||||
if !parent.exists() {
|
||||
tokio::fs::create_dir_all(parent).await?;
|
||||
}
|
||||
}
|
||||
Ok(if path.exists() {
|
||||
let path = self.tmp();
|
||||
if let Some(parent) = path.parent() {
|
||||
if !parent.exists() {
|
||||
tokio::fs::create_dir_all(parent).await?;
|
||||
}
|
||||
}
|
||||
PersistenceFile::new(File::create(path).await?, Some(self.clone()))
|
||||
} else {
|
||||
PersistenceFile::new(File::create(path).await?, None)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PersistenceFile {
|
||||
file: File,
|
||||
needs_commit: Option<PersistencePath>,
|
||||
}
|
||||
impl PersistenceFile {
|
||||
pub fn new(file: File, needs_commit: Option<PersistencePath>) -> Self {
|
||||
PersistenceFile { file, needs_commit }
|
||||
}
|
||||
/// Commits the file to the persistence directory.
|
||||
/// If this fails, the file was not saved.
|
||||
pub async fn commit(mut self) -> Result<(), Error> {
|
||||
if let Some(path) = self.needs_commit.take() {
|
||||
tokio::fs::rename(path.tmp(), path.path())
|
||||
.await
|
||||
.with_context(|e| {
|
||||
format!(
|
||||
"{} -> {}: {}",
|
||||
path.tmp().display(),
|
||||
path.path().display(),
|
||||
e
|
||||
)
|
||||
})
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for PersistenceFile {
|
||||
type Target = File;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.file
|
||||
}
|
||||
}
|
||||
impl std::ops::DerefMut for PersistenceFile {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.file
|
||||
}
|
||||
}
|
||||
impl AsRef<File> for PersistenceFile {
|
||||
fn as_ref(&self) -> &File {
|
||||
&*self
|
||||
}
|
||||
}
|
||||
impl AsMut<File> for PersistenceFile {
|
||||
fn as_mut(&mut self) -> &mut File {
|
||||
&mut *self
|
||||
}
|
||||
}
|
||||
impl Drop for PersistenceFile {
|
||||
fn drop(&mut self) {
|
||||
if let Some(path) = &self.needs_commit {
|
||||
log::warn!(
|
||||
"{} was dropped without being committed.",
|
||||
path.path().display()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
|
||||
const V0_1_1: emver::Version = emver::Version::new(0, 1, 1, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_1_0::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_1_1
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
log::info!("Update torrc");
|
||||
let mut outfile = crate::util::PersistencePath::from_ref("tor/torrc")
|
||||
.write(None)
|
||||
.await?;
|
||||
tokio::io::copy(
|
||||
&mut AsyncCompat(
|
||||
reqwest::get(&format!("{}/torrc?spec==0.1.1", &*crate::SYS_REGISTRY_URL))
|
||||
.compat()
|
||||
.await
|
||||
.with_context(|e| format!("GET {}/torrc: {}", &*crate::SYS_REGISTRY_URL, e))
|
||||
.with_code(crate::error::NETWORK_ERROR)?
|
||||
.error_for_status()
|
||||
.with_context(|e| format!("GET {}/torrc: {}", &*crate::SYS_REGISTRY_URL, e))
|
||||
.with_code(crate::error::REGISTRY_ERROR)?
|
||||
.bytes_stream()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||
.into_async_read(),
|
||||
),
|
||||
outfile.as_mut(),
|
||||
)
|
||||
.await
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
outfile.commit().await?;
|
||||
if !std::process::Command::new("docker")
|
||||
.arg("network")
|
||||
.arg("create")
|
||||
.arg("-d")
|
||||
.arg("bridge")
|
||||
.arg("--subnet=172.18.0.0/16")
|
||||
.arg("start9")
|
||||
.stdout(std::process::Stdio::null())
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
log::warn!("Failed to Create Network")
|
||||
}
|
||||
|
||||
match tokio::fs::remove_file(Path::new(crate::PERSISTENCE_DIR).join(crate::SERVICES_YAML))
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
.with_context(|e| format!("{}/{}: {}", crate::PERSISTENCE_DIR, crate::SERVICES_YAML, e))
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
crate::tor::reload().await?;
|
||||
|
||||
for app in crate::apps::list_info().await? {
|
||||
legacy::update::update(&app.0).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
let mut outfile = crate::util::PersistencePath::from_ref("tor/torrc")
|
||||
.write(None)
|
||||
.await?;
|
||||
|
||||
tokio::io::copy(
|
||||
&mut AsyncCompat(
|
||||
reqwest::get(&format!("{}/torrc?spec==0.1.0", &*crate::SYS_REGISTRY_URL))
|
||||
.compat()
|
||||
.await
|
||||
.with_context(|e| format!("GET {}/torrc: {}", &*crate::SYS_REGISTRY_URL, e))
|
||||
.no_code()?
|
||||
.error_for_status()
|
||||
.with_context(|e| format!("GET {}/torrc: {}", &*crate::SYS_REGISTRY_URL, e))
|
||||
.no_code()?
|
||||
.bytes_stream()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||
.into_async_read(),
|
||||
),
|
||||
outfile.as_mut(),
|
||||
)
|
||||
.await
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
outfile.commit().await?;
|
||||
|
||||
for app in crate::apps::list_info().await? {
|
||||
legacy::remove::remove(&app.0, false).await?;
|
||||
}
|
||||
let tor_svcs = crate::util::PersistencePath::from_ref(crate::SERVICES_YAML).path();
|
||||
if tor_svcs.exists() {
|
||||
tokio::fs::remove_file(&tor_svcs)
|
||||
.await
|
||||
.with_context(|e| format!("{}: {}", tor_svcs.display(), e))
|
||||
.with_code(crate::error::FILESYSTEM_ERROR)?;
|
||||
}
|
||||
if !std::process::Command::new("docker")
|
||||
.arg("network")
|
||||
.arg("rm")
|
||||
.arg("start9")
|
||||
.stdout(std::process::Stdio::null())
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
log::warn!("Failed to Remove Network");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod legacy {
|
||||
pub mod remove {
|
||||
use std::path::Path;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub async fn remove(name: &str, purge: bool) -> Result<(), Error> {
|
||||
log::info!("Removing app from manifest.");
|
||||
crate::apps::remove(name).await?;
|
||||
log::info!("Stopping docker container.");
|
||||
if !tokio::process::Command::new("docker")
|
||||
.args(&["stop", name])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(match log::max_level() {
|
||||
log::LevelFilter::Error => std::process::Stdio::null(),
|
||||
_ => std::process::Stdio::inherit(),
|
||||
})
|
||||
.status()
|
||||
.await?
|
||||
.success()
|
||||
{
|
||||
log::error!("Failed to Stop Docker Container");
|
||||
};
|
||||
log::info!("Removing docker container.");
|
||||
if !tokio::process::Command::new("docker")
|
||||
.args(&["rm", name])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(match log::max_level() {
|
||||
log::LevelFilter::Error => std::process::Stdio::null(),
|
||||
_ => std::process::Stdio::inherit(),
|
||||
})
|
||||
.status()
|
||||
.await?
|
||||
.success()
|
||||
{
|
||||
log::error!("Failed to Remove Docker Container");
|
||||
};
|
||||
if purge {
|
||||
log::info!("Removing tor hidden service.");
|
||||
crate::tor::rm_svc(name).await?;
|
||||
log::info!("Removing app metadata.");
|
||||
std::fs::remove_dir_all(Path::new(crate::PERSISTENCE_DIR).join("apps").join(name))?;
|
||||
log::info!("Destroying mounted volume.");
|
||||
std::fs::remove_dir_all(Path::new(crate::VOLUMES).join(name))?;
|
||||
log::info!("Pruning unused docker images.");
|
||||
crate::ensure_code!(
|
||||
std::process::Command::new("docker")
|
||||
.args(&["image", "prune", "-a", "-f"])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(match log::max_level() {
|
||||
log::LevelFilter::Error => std::process::Stdio::null(),
|
||||
_ => std::process::Stdio::inherit(),
|
||||
})
|
||||
.status()?
|
||||
.success(),
|
||||
3,
|
||||
"Failed to Prune Docker Images"
|
||||
);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub mod update {
|
||||
use crate::Error;
|
||||
pub async fn update(name_version: &str) -> Result<(), Error> {
|
||||
let name = name_version
|
||||
.split("@")
|
||||
.next()
|
||||
.ok_or_else(|| failure::format_err!("invalid app id"))?;
|
||||
crate::install::download_name(name_version).await?;
|
||||
super::remove::remove(name, false).await?;
|
||||
crate::install::install_name(name_version, true).await?;
|
||||
let config = crate::apps::config(name).await?;
|
||||
if let Some(cfg) = config.config {
|
||||
if config.spec.matches(&cfg).is_ok() {
|
||||
crate::apps::set_configured(name, true).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
use futures::StreamExt;
|
||||
use futures::TryStreamExt;
|
||||
use linear_map::LinearMap;
|
||||
|
||||
use super::*;
|
||||
|
||||
const V0_1_2: emver::Version = emver::Version::new(0, 1, 2, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_1_1::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_1_2
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
let app_info = legacy::apps::list_info().await?;
|
||||
for (name, _) in &app_info {
|
||||
let p = PersistencePath::from_ref("apps")
|
||||
.join(name)
|
||||
.join("manifest.yaml");
|
||||
let mut f = p.for_update().await?;
|
||||
let manifest: crate::manifest::ManifestV0 = crate::util::from_yaml_async_reader(&mut f)
|
||||
.await
|
||||
.no_code()?;
|
||||
let mut f = f.into_writer().await?;
|
||||
crate::util::to_yaml_async_writer(&mut f, &crate::manifest::Manifest::V0(manifest))
|
||||
.await
|
||||
.no_code()?;
|
||||
f.commit().await?;
|
||||
}
|
||||
|
||||
let p = PersistencePath::from_ref("apps.yaml");
|
||||
let exists = p.path().exists();
|
||||
let mut f = p.for_update().await?;
|
||||
let info: LinearMap<String, legacy::apps::AppInfo> = if exists {
|
||||
crate::util::from_yaml_async_reader(&mut f)
|
||||
.await
|
||||
.no_code()?
|
||||
} else {
|
||||
LinearMap::new()
|
||||
};
|
||||
let new_info: LinearMap<String, crate::apps::AppInfo> = futures::stream::iter(info)
|
||||
.then(|(name, i)| async move {
|
||||
let title = crate::apps::manifest(&name).await?.title;
|
||||
Ok::<_, Error>((
|
||||
name,
|
||||
crate::apps::AppInfo {
|
||||
title,
|
||||
version: i.version,
|
||||
tor_address: i.tor_address,
|
||||
configured: i.configured,
|
||||
recoverable: false,
|
||||
needs_restart: false,
|
||||
},
|
||||
))
|
||||
})
|
||||
.try_collect()
|
||||
.await?;
|
||||
let mut f = f.into_writer().await?;
|
||||
crate::util::to_yaml_async_writer(&mut f, &new_info)
|
||||
.await
|
||||
.no_code()?;
|
||||
f.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod legacy {
|
||||
pub mod apps {
|
||||
use linear_map::LinearMap;
|
||||
|
||||
use crate::util::from_yaml_async_reader;
|
||||
use crate::util::Apply;
|
||||
use crate::util::PersistencePath;
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AppInfo {
|
||||
pub version: emver::Version,
|
||||
pub tor_address: Option<String>,
|
||||
pub configured: bool,
|
||||
}
|
||||
|
||||
pub async fn list_info() -> Result<LinearMap<String, AppInfo>, Error> {
|
||||
let apps_path = PersistencePath::from_ref("apps.yaml");
|
||||
Ok(apps_path
|
||||
.maybe_read(false)
|
||||
.await
|
||||
.transpose()?
|
||||
.map(|mut f| async move { from_yaml_async_reader(&mut *f).await })
|
||||
.apply(futures::future::OptionFuture::from)
|
||||
.await
|
||||
.transpose()?
|
||||
.unwrap_or_else(LinearMap::new))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_1_3: emver::Version = emver::Version::new(0, 1, 3, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_1_2::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_1_3
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_1_4: emver::Version = emver::Version::new(0, 1, 4, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_1_3::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_1_4
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_1_5: emver::Version = emver::Version::new(0, 1, 5, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_1_4::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_1_5
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
use linear_map::LinearMap;
|
||||
|
||||
use super::*;
|
||||
use crate::util::{to_yaml_async_writer, PersistencePath};
|
||||
|
||||
const V0_2_0: emver::Version = emver::Version::new(0, 2, 0, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_1_5::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_0
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
let app_info: LinearMap<String, crate::apps::AppInfo> = legacy::apps::list_info()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|(id, ai)| {
|
||||
(
|
||||
id,
|
||||
crate::apps::AppInfo {
|
||||
title: ai.title,
|
||||
version: ai.version,
|
||||
tor_address: ai.tor_address,
|
||||
configured: ai.configured,
|
||||
recoverable: ai.recoverable,
|
||||
needs_restart: false,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut apps_file = PersistencePath::from_ref("apps.yaml").write(None).await?;
|
||||
to_yaml_async_writer(&mut *apps_file, &app_info).await?;
|
||||
apps_file.commit().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
let app_info: LinearMap<String, legacy::apps::AppInfo> = crate::apps::list_info()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|(id, ai)| {
|
||||
(
|
||||
id,
|
||||
legacy::apps::AppInfo {
|
||||
title: ai.title,
|
||||
version: ai.version,
|
||||
tor_address: ai.tor_address,
|
||||
configured: ai.configured,
|
||||
recoverable: ai.recoverable,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut apps_file = PersistencePath::from_ref("apps.yaml").write(None).await?;
|
||||
to_yaml_async_writer(&mut *apps_file, &app_info).await?;
|
||||
apps_file.commit().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod legacy {
|
||||
pub mod apps {
|
||||
use linear_map::LinearMap;
|
||||
|
||||
use crate::util::{from_yaml_async_reader, PersistencePath};
|
||||
use crate::Error;
|
||||
|
||||
fn not(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AppInfo {
|
||||
pub title: String,
|
||||
pub version: emver::Version,
|
||||
pub tor_address: Option<String>,
|
||||
pub configured: bool,
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "not")]
|
||||
pub recoverable: bool,
|
||||
}
|
||||
|
||||
pub async fn list_info() -> Result<LinearMap<String, AppInfo>, Error> {
|
||||
let apps_path = PersistencePath::from_ref("apps.yaml");
|
||||
let mut f = match apps_path.maybe_read(false).await.transpose()? {
|
||||
Some(a) => a,
|
||||
None => return Ok(LinearMap::new()),
|
||||
};
|
||||
from_yaml_async_reader(&mut *f).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_1: emver::Version = emver::Version::new(0, 2, 1, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_0::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_1
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_10: emver::Version = emver::Version::new(0, 2, 10, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_9::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_10
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
use super::*;
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
|
||||
const V0_2_11: emver::Version = emver::Version::new(0, 2, 11, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_10::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_11
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
crate::tor::write_lan_services(
|
||||
&crate::tor::services_map(&PersistencePath::from_ref(crate::SERVICES_YAML)).await?,
|
||||
)
|
||||
.await?;
|
||||
let svc_exit = std::process::Command::new("service")
|
||||
.args(&["nginx", "reload"])
|
||||
.status()?;
|
||||
crate::ensure_code!(
|
||||
svc_exit.success(),
|
||||
crate::error::GENERAL_ERROR,
|
||||
"Failed to Reload Nginx: {}",
|
||||
svc_exit
|
||||
.code()
|
||||
.or_else(|| { svc_exit.signal().map(|a| 128 + a) })
|
||||
.unwrap_or(0)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
use super::*;
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
|
||||
const V0_2_12: emver::Version = emver::Version::new(0, 2, 12, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_11::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_12
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
crate::tor::write_lan_services(
|
||||
&crate::tor::services_map(&PersistencePath::from_ref(crate::SERVICES_YAML)).await?,
|
||||
)
|
||||
.await?;
|
||||
let svc_exit = std::process::Command::new("service")
|
||||
.args(&["nginx", "reload"])
|
||||
.status()?;
|
||||
crate::ensure_code!(
|
||||
svc_exit.success(),
|
||||
crate::error::GENERAL_ERROR,
|
||||
"Failed to Reload Nginx: {}",
|
||||
svc_exit
|
||||
.code()
|
||||
.or_else(|| { svc_exit.signal().map(|a| 128 + a) })
|
||||
.unwrap_or(0)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_2: emver::Version = emver::Version::new(0, 2, 2, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_1::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_2
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_3: emver::Version = emver::Version::new(0, 2, 3, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_2::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_3
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_4: emver::Version = emver::Version::new(0, 2, 4, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_3::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_4
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_5: emver::Version = emver::Version::new(0, 2, 5, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_4::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_5
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
const V0_2_6: emver::Version = emver::Version::new(0, 2, 6, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_5::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_6
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use super::*;
|
||||
use crate::util::Invoke;
|
||||
|
||||
const V0_2_7: emver::Version = emver::Version::new(0, 2, 7, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_6::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_7
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
for (app_id, _) in crate::apps::list_info().await? {
|
||||
tokio::process::Command::new("docker")
|
||||
.arg("stop")
|
||||
.arg(&app_id)
|
||||
.invoke("Docker")
|
||||
.await?;
|
||||
tokio::process::Command::new("docker")
|
||||
.arg("update")
|
||||
.arg("--restart")
|
||||
.arg("no")
|
||||
.arg(&app_id)
|
||||
.invoke("Docker")
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use super::*;
|
||||
use crate::util::Invoke;
|
||||
|
||||
const V0_2_8: emver::Version = emver::Version::new(0, 2, 8, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_7::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_8
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
for (app_id, _) in crate::apps::list_info().await? {
|
||||
tokio::process::Command::new("docker")
|
||||
.arg("stop")
|
||||
.arg(&app_id)
|
||||
.invoke("Docker")
|
||||
.await?;
|
||||
tokio::process::Command::new("docker")
|
||||
.arg("update")
|
||||
.arg("--restart")
|
||||
.arg("no")
|
||||
.arg(&app_id)
|
||||
.invoke("Docker")
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
const V0_2_9: emver::Version = emver::Version::new(0, 2, 9, 0);
|
||||
|
||||
pub struct Version;
|
||||
#[async_trait]
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_2_8::Version;
|
||||
fn new() -> Self {
|
||||
Version
|
||||
}
|
||||
fn semver(&self) -> &'static emver::Version {
|
||||
&V0_2_9
|
||||
}
|
||||
async fn up(&self) -> Result<(), Error> {
|
||||
crate::tor::write_lan_services(
|
||||
&crate::tor::services_map(&PersistencePath::from_ref(crate::SERVICES_YAML)).await?,
|
||||
)
|
||||
.await?;
|
||||
tokio::fs::os::unix::symlink(
|
||||
crate::tor::ETC_NGINX_SERVICES_CONF,
|
||||
"/etc/nginx/sites-enabled/start9-services.conf",
|
||||
)
|
||||
.await
|
||||
.or_else(|e| {
|
||||
if e.kind() == std::io::ErrorKind::AlreadyExists {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})?;
|
||||
let svc_exit = std::process::Command::new("service")
|
||||
.args(&["nginx", "reload"])
|
||||
.status()?;
|
||||
crate::ensure_code!(
|
||||
svc_exit.success(),
|
||||
crate::error::GENERAL_ERROR,
|
||||
"Failed to Reload Nginx: {}",
|
||||
svc_exit
|
||||
.code()
|
||||
.or_else(|| { svc_exit.signal().map(|a| 128 + a) })
|
||||
.unwrap_or(0)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
async fn down(&self) -> Result<(), Error> {
|
||||
tokio::fs::remove_file("/etc/nginx/sites-enabled/start9-services.conf")
|
||||
.await
|
||||
.or_else(|e| match e {
|
||||
e if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
||||
e => Err(e),
|
||||
})?;
|
||||
tokio::fs::remove_file(crate::tor::ETC_NGINX_SERVICES_CONF)
|
||||
.await
|
||||
.or_else(|e| match e {
|
||||
e if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
||||
e => Err(e),
|
||||
})?;
|
||||
let svc_exit = std::process::Command::new("service")
|
||||
.args(&["nginx", "reload"])
|
||||
.status()?;
|
||||
crate::ensure_code!(
|
||||
svc_exit.success(),
|
||||
crate::error::GENERAL_ERROR,
|
||||
"Failed to Reload Nginx: {}",
|
||||
svc_exit
|
||||
.code()
|
||||
.or_else(|| { svc_exit.signal().map(|a| 128 + a) })
|
||||
.unwrap_or(0)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user