This commit is contained in:
Aiden McClelland
2021-07-12 14:44:03 -06:00
parent a2188be4cd
commit d26e08014f
20 changed files with 3283 additions and 30 deletions

1
appmgr/.gitignore vendored
View File

@@ -3,3 +3,4 @@
.DS_Store
.vscode
secrets.db
*.s9pk

22
appmgr/Cargo.lock generated
View File

@@ -710,6 +710,7 @@ dependencies = [
"sha2 0.9.5",
"simple-logging",
"sqlx",
"tar",
"thiserror",
"tokio 1.9.0",
"tokio-compat-02",
@@ -1563,6 +1564,15 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
[[package]]
name = "openssl-src"
version = "111.15.0+1.1.1k"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.65"
@@ -1572,6 +1582,7 @@ dependencies = [
"autocfg",
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
@@ -2538,6 +2549,17 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tar"
version = "0.4.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d779dc6aeff029314570f666ec83f19df7280bb36ef338442cfa8c604021b80"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "tempfile"
version = "3.2.0"

View File

@@ -64,7 +64,7 @@ lazy_static = "1.4"
libc = "0.2.86"
log = "0.4.11"
nix = "0.20.0"
openssl = "0.10.30"
openssl = { version="0.10.30", features=["vendored"] }
patch-db = { version="*", path="../../patch-db/patch-db" }
pin-project = "1.0.6"
prettytable-rs = "0.8.0"
@@ -83,6 +83,7 @@ serde_yaml = "0.8.14"
sha2 = "0.9.3"
simple-logging = "2.0"
sqlx = { version="0.5", features=["runtime-tokio-rustls", "sqlite", "offline"] }
tar = "0.4.35"
thiserror = "1.0.24"
tokio = { version="1.5.0", features=["full"] }
tokio-compat-02 = "0.2.0"

Binary file not shown.

View File

@@ -77,6 +77,8 @@ impl DockerAction {
} else {
None
};
cmd.stdout(std::process::Stdio::piped());
cmd.stderr(std::process::Stdio::piped());
let mut handle = cmd.spawn().with_kind(crate::ErrorKind::Docker)?;
if let (Some(input), Some(stdin)) = (&input_buf, &mut handle.stdin) {
use tokio::io::AsyncWriteExt;
@@ -133,6 +135,8 @@ impl DockerAction {
} else {
None
};
cmd.stdout(std::process::Stdio::piped());
cmd.stderr(std::process::Stdio::piped());
let mut handle = cmd.spawn().with_kind(crate::ErrorKind::Docker)?;
if let (Some(input), Some(stdin)) = (&input_buf, &mut handle.stdin) {
use tokio::io::AsyncWriteExt;
@@ -177,9 +181,11 @@ impl DockerAction {
format!("service_{}_{}", pkg_id, version)
}
pub fn uncontainer_name(name: &str) -> Option<&str> {
name.strip_prefix("service_")
.and_then(|name| name.split("_").next())
pub fn uncontainer_name(name: &str) -> Option<(&str, Version)> {
name.trim_start_matches("/")
.strip_prefix("service_")
.and_then(|name| name.split_once("_"))
.and_then(|(id, version)| Some((id, version.parse().ok()?)))
}
fn docker_args<'a>(
@@ -196,18 +202,20 @@ impl DockerAction {
+ self.args.len(), // [ARG...]
);
for (volume_id, dst) in &self.mounts {
let src = if let Some(path) = volumes.get_path_for(pkg_id, volume_id) {
path
let volume = if let Some(v) = volumes.get(volume_id) {
v
} else {
continue;
};
let src = volume.path_for(pkg_id, pkg_version, volume_id);
res.push(OsStr::new("--mount").into());
res.push(
OsString::from(format!(
"type=bind,src={},dst={}",
dbg!(OsString::from(format!(
"type=bind,src={},dst={}{}",
src.display(),
dst.display()
))
dst.display(),
if volume.readonly() { ",readonly" } else { "" }
)))
.into(),
);
}

View File

@@ -7,7 +7,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::util::Version;
use crate::Error;
pub const SYSTEM_ID: Id<&'static str> = Id("SYSTEM");
pub const SYSTEM_ID: Id<&'static str> = Id("x_system");
#[derive(Debug, thiserror::Error)]
#[error("Invalid ID")]

View File

@@ -379,8 +379,6 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
let interface_info = manifest.interfaces.install(&mut sql_tx, pkg_id, ip).await?;
log::info!("Install {}@{}: Installed interfaces", pkg_id, version);
log::info!("Install {}@{}: Complete", pkg_id, version);
let static_files = StaticFiles::local(pkg_id, version, manifest.assets.icon_type());
let current_dependencies = manifest
.dependencies
@@ -489,11 +487,19 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
}
}
log::info!("Install {}@{}: Syncing Tor", pkg_id, version);
ctx.tor_controller.sync(&mut tx, &mut sql_tx).await?;
log::info!("Install {}@{}: Synced Tor", pkg_id, version);
#[cfg(feature = "avahi")]
ctx.mdns_controller.sync(&mut tx).await?;
{
log::info!("Install {}@{}: Syncing MDNS", pkg_id, version);
ctx.mdns_controller.sync(&mut tx).await?;
log::info!("Install {}@{}: Synced MDNS", pkg_id, version);
}
tx.commit(None).await?;
log::info!("Install {}@{}: Complete", pkg_id, version);
Ok(())
}

View File

@@ -18,6 +18,7 @@ pub struct S9pkPacker<
RInstructions: Read,
RIcon: Read,
RDockerImages: Read,
RAssets: Read,
> {
writer: W,
manifest: &'a Manifest,
@@ -25,6 +26,7 @@ pub struct S9pkPacker<
instructions: RInstructions,
icon: RIcon,
docker_images: RDockerImages,
assets: RAssets,
}
impl<
'a,
@@ -33,7 +35,8 @@ impl<
RInstructions: Read,
RIcon: Read,
RDockerImages: Read,
> S9pkPacker<'a, W, RLicense, RInstructions, RIcon, RDockerImages>
RAssets: Read,
> S9pkPacker<'a, W, RLicense, RInstructions, RIcon, RDockerImages, RAssets>
{
/// BLOCKING
pub fn pack(mut self, key: &ed25519_dalek::Keypair) -> Result<(), Error> {
@@ -93,13 +96,22 @@ impl<
position = new_pos;
// docker_images
std::io::copy(&mut self.docker_images, &mut writer)
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying App Image"))?;
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Docker Images"))?;
let new_pos = writer.stream_position()?;
header.table_of_contents.docker_images = FileSection {
position,
length: new_pos - position,
};
position = new_pos;
// docker_images
std::io::copy(&mut self.assets, &mut writer)
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Assets"))?;
let new_pos = writer.stream_position()?;
header.table_of_contents.assets = FileSection {
position,
length: new_pos - position,
};
position = new_pos;
// header
let (hash, _) = writer.finish();

View File

@@ -74,6 +74,7 @@ pub struct TableOfContents {
pub instructions: FileSection,
pub icon: FileSection,
pub docker_images: FileSection,
pub assets: FileSection,
}
impl TableOfContents {
pub fn serialize<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
@@ -81,7 +82,8 @@ impl TableOfContents {
+ (1 + "license".len() + 16)
+ (1 + "instructions".len() + 16)
+ (1 + "icon".len() + 16)
+ (1 + "docker_images".len() + 16)) as u32;
+ (1 + "docker_images".len() + 16)
+ (1 + "assets".len() + 16)) as u32;
writer.write_all(&u32::to_be_bytes(len))?;
self.manifest.serialize_entry("manifest", &mut writer)?;
self.license.serialize_entry("license", &mut writer)?;
@@ -90,6 +92,7 @@ impl TableOfContents {
self.icon.serialize_entry("icon", &mut writer)?;
self.docker_images
.serialize_entry("docker_images", &mut writer)?;
self.assets.serialize_entry("assets", &mut writer)?;
Ok(())
}
pub async fn deserialize<R: AsyncRead + Unpin>(mut reader: R) -> std::io::Result<Self> {
@@ -126,6 +129,7 @@ impl TableOfContents {
instructions: from_table(&table, "instructions")?,
icon: from_table(&table, "icon")?,
docker_images: from_table(&table, "docker_images")?,
assets: from_table(&table, "assets")?,
})
}
}

View File

@@ -139,11 +139,13 @@ pub struct Assets {
#[serde(default)]
pub license: Option<PathBuf>,
#[serde(default)]
pub instructions: Option<PathBuf>,
#[serde(default)]
pub icon: Option<PathBuf>,
#[serde(default)]
pub docker_images: Option<PathBuf>,
#[serde(default)]
pub instructions: Option<PathBuf>,
pub assets: Option<PathBuf>,
}
impl Assets {
pub fn license_path(&self) -> &Path {
@@ -152,6 +154,12 @@ impl Assets {
.map(|a| a.as_path())
.unwrap_or(Path::new("LICENSE.md"))
}
pub fn instructions_path(&self) -> &Path {
self.instructions
.as_ref()
.map(|a| a.as_path())
.unwrap_or(Path::new("INSTRUCTIONS.md"))
}
pub fn icon_path(&self) -> &Path {
self.icon
.as_ref()
@@ -171,11 +179,11 @@ impl Assets {
.map(|a| a.as_path())
.unwrap_or(Path::new("image.tar"))
}
pub fn instructions_path(&self) -> &Path {
self.instructions
pub fn assets_path(&self) -> &Path {
self.assets
.as_ref()
.map(|a| a.as_path())
.unwrap_or(Path::new("INSTRUCTIONS.md"))
.unwrap_or(Path::new("assets"))
}
}

View File

@@ -10,6 +10,7 @@ use crate::s9pk::builder::S9pkPacker;
use crate::s9pk::manifest::Manifest;
use crate::s9pk::reader::S9pkReader;
use crate::util::display_none;
use crate::volume::Volume;
use crate::{Error, ResultExt};
pub mod builder;
@@ -79,6 +80,22 @@ pub fn pack(#[context] ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Resu
)
})?,
)
.assets({
let mut assets = tar::Builder::new(Vec::new()); // TODO: Ideally stream this? best not to buffer in memory
for (asset_volume, _) in manifest
.volumes
.iter()
.filter(|(_, v)| matches!(v, &&Volume::Assets {}))
{
assets.append_dir_all(
asset_volume,
path.join(manifest.assets.assets_path()).join(asset_volume),
)?;
}
std::io::Cursor::new(assets.into_inner()?)
})
.build()
.pack(&ctx.developer_key()?)?;
outfile.sync_all()?;

View File

@@ -141,4 +141,8 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
pub async fn docker_images<'a>(&'a mut self) -> Result<ReadHandle<'a, R>, Error> {
Ok(self.read_handle(self.toc.docker_images).await?)
}
pub async fn assets<'a>(&'a mut self) -> Result<ReadHandle<'a, R>, Error> {
Ok(self.read_handle(self.toc.assets).await?)
}
}

View File

@@ -65,7 +65,7 @@ pub async fn synchronize_all(ctx: &RpcContext) -> Result<(), Error> {
let mut fuckening = false;
for summary in info {
let id = if let Some(id) = summary.names.iter().flatten().find_map(|s| {
DockerAction::uncontainer_name(s.as_str()).and_then(|id| pkg_ids.take(id))
DockerAction::uncontainer_name(s.as_str()).and_then(|(id, _)| pkg_ids.take(id))
}) {
id
} else {

View File

@@ -518,6 +518,15 @@ impl std::fmt::Display for Version {
write!(f, "{}", self.string)
}
}
impl std::str::FromStr for Version {
type Err = <emver::Version as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Version {
string: s.to_owned(),
version: s.parse()?,
})
}
}
impl From<emver::Version> for Version {
fn from(v: emver::Version) -> Self {
Version {

View File

@@ -9,6 +9,7 @@ use serde::{Deserialize, Deserializer, Serialize};
use crate::id::{Id, IdUnchecked};
use crate::net::interface::InterfaceId;
use crate::s9pk::manifest::PackageId;
use crate::util::Version;
pub mod disk;
@@ -65,16 +66,18 @@ where
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct CustomVolumeId<S: AsRef<str> = String>(Id<S>);
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Volumes(IndexMap<VolumeId, Volume>);
impl Volumes {
pub fn get_path_for(&self, pkg_id: &PackageId, volume_id: &VolumeId) -> Option<PathBuf> {
pub fn get_path_for(
&self,
pkg_id: &PackageId,
version: &Version,
volume_id: &VolumeId,
) -> Option<PathBuf> {
self.0
.get(volume_id)
.map(|volume| volume.path_for(pkg_id, volume_id))
.map(|volume| volume.path_for(pkg_id, version, volume_id))
}
pub fn to_readonly(&self) -> Self {
Volumes(
@@ -121,6 +124,8 @@ pub enum Volume {
readonly: bool,
},
#[serde(rename_all = "kebab-case")]
Assets {},
#[serde(rename_all = "kebab-case")]
Pointer {
package_id: PackageId,
volume_id: VolumeId,
@@ -134,22 +139,31 @@ pub enum Volume {
Backup { readonly: bool },
}
impl Volume {
pub fn path_for(&self, pkg_id: &PackageId, volume_id: &VolumeId) -> PathBuf {
pub fn path_for(&self, pkg_id: &PackageId, version: &Version, volume_id: &VolumeId) -> PathBuf {
match self {
Volume::Data { .. } => Path::new(PKG_VOLUME_DIR)
.join(pkg_id)
.join("volumes")
.join(volume_id),
Volume::Assets {} => Path::new(PKG_VOLUME_DIR)
.join(pkg_id)
.join("assets")
.join(version.as_str())
.join(volume_id),
Volume::Pointer {
package_id,
volume_id,
path,
..
} => Path::new(PKG_VOLUME_DIR)
} => dbg!(Path::new(PKG_VOLUME_DIR)
.join(package_id)
.join("volumes")
.join(volume_id)
.join(path),
.join(if path.is_absolute() {
path.strip_prefix("/").unwrap()
} else {
path.as_ref()
})),
Volume::Certificate { interface_id } => Path::new(PKG_VOLUME_DIR)
.join(pkg_id)
.join("certificates")
@@ -174,6 +188,7 @@ impl Volume {
pub fn readonly(&self) -> bool {
match self {
Volume::Data { readonly } => *readonly,
Volume::Assets {} => true,
Volume::Pointer { readonly, .. } => *readonly,
Volume::Certificate { .. } => true,
Volume::Backup { readonly } => *readonly,

4
compat/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/target
**/*.rs.bk
.DS_Store
.vscode

3079
compat/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

13
compat/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "compat"
version = "0.1.0"
authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = "2.33.3"
serde = { version="1.0.118", features=["derive", "rc"] }
serde_yaml = "0.8.17"
embassy-os = { path="../appmgr", default-features=false }

5
compat/Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM alpine:latest
ADD ./target/aarch64-unknown-linux-musl/release/compat /usr/local/bin/compat
ENTRYPOINT ["compat"]

45
compat/src/main.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::{fs::File, io::stdout, path::Path};
use clap::{App, Arg, SubCommand};
use embassy::config::action::{ConfigRes, SetResult};
fn main() {
let app = App::new("compat").subcommand(
SubCommand::with_name("config").subcommand(
SubCommand::with_name("get")
.arg(
Arg::with_name("mountpoint")
.help("The `mount` field from manifest.yaml")
.required(true),
)
.arg(
Arg::with_name("spec")
.help("The path to the config spec in the container")
.required(true),
),
),
);
let matches = app.get_matches();
match matches.subcommand() {
("config", Some(sub_m)) => match sub_m.subcommand() {
("get", Some(sub_m)) => {
let cfg_path =
Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/config.yaml");
let cfg = if cfg_path.exists() {
Some(serde_yaml::from_reader(File::open(cfg_path).unwrap()).unwrap())
} else {
None
};
let spec_path = Path::new(sub_m.value_of("spec").unwrap());
let spec = serde_yaml::from_reader(File::open(spec_path).unwrap()).unwrap();
serde_yaml::to_writer(stdout(), &ConfigRes { config: cfg, spec }).unwrap();
}
(subcmd, _) => {
panic!("unknown subcommand: {}", subcmd);
}
},
(subcmd, _) => {
panic!("unknown subcommand: {}", subcmd);
}
}
}