mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
fix pack
This commit is contained in:
2
appmgr/Cargo.lock
generated
2
appmgr/Cargo.lock
generated
@@ -696,7 +696,7 @@ dependencies = [
|
|||||||
"patch-db",
|
"patch-db",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"prettytable-rs",
|
"prettytable-rs",
|
||||||
"rand 0.8.4",
|
"rand 0.7.3",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ openssl = "0.10.30"
|
|||||||
patch-db = { version="*", path="../../patch-db/patch-db" }
|
patch-db = { version="*", path="../../patch-db/patch-db" }
|
||||||
pin-project = "1.0.6"
|
pin-project = "1.0.6"
|
||||||
prettytable-rs = "0.8.0"
|
prettytable-rs = "0.8.0"
|
||||||
rand = "0.8.3"
|
rand = "0.7.3"
|
||||||
regex = "1.4.2"
|
regex = "1.4.2"
|
||||||
reqwest = { version="0.11.2", features=["stream", "json"] }
|
reqwest = { version="0.11.2", features=["stream", "json"] }
|
||||||
rpassword = "5.0.0"
|
rpassword = "5.0.0"
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ impl Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, HasModel)]
|
#[derive(Clone, Debug, Deserialize, Serialize, HasModel)]
|
||||||
#[serde(rename = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum ActionImplementation {
|
pub enum ActionImplementation {
|
||||||
Docker(DockerAction),
|
Docker(DockerAction),
|
||||||
|
|||||||
@@ -4,13 +4,35 @@ use embassy::Error;
|
|||||||
use rpc_toolkit::run_cli;
|
use rpc_toolkit::run_cli;
|
||||||
|
|
||||||
fn inner_main() -> Result<(), Error> {
|
fn inner_main() -> Result<(), Error> {
|
||||||
simple_logging::log_to_stderr(log::LevelFilter::Info);
|
|
||||||
run_cli!(
|
run_cli!(
|
||||||
embassy::main_api,
|
embassy::main_api,
|
||||||
app => app.name("Embassy CLI")
|
app => app
|
||||||
|
.name("Embassy CLI")
|
||||||
|
.arg(
|
||||||
|
clap::Arg::with_name("config")
|
||||||
|
.short("c")
|
||||||
|
.long("config")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::with_name("verbosity")
|
||||||
|
.short("v")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
.arg(Arg::with_name("host").long("host").short("h").takes_value(true))
|
.arg(Arg::with_name("host").long("host").short("h").takes_value(true))
|
||||||
.arg(Arg::with_name("port").long("port").short("p").takes_value(true)),
|
.arg(Arg::with_name("port").long("port").short("p").takes_value(true)),
|
||||||
matches => EitherContext::Cli(CliContext::init(matches)?),
|
matches => {
|
||||||
|
simple_logging::log_to_stderr(match matches.occurrences_of("verbosity") {
|
||||||
|
0 => log::LevelFilter::Off,
|
||||||
|
1 => log::LevelFilter::Error,
|
||||||
|
2 => log::LevelFilter::Warn,
|
||||||
|
3 => log::LevelFilter::Info,
|
||||||
|
4 => log::LevelFilter::Debug,
|
||||||
|
_ => log::LevelFilter::Trace,
|
||||||
|
});
|
||||||
|
EitherContext::Cli(CliContext::init(matches)?)
|
||||||
|
},
|
||||||
|code| if code < 0 { 1 } else { code }
|
|code| if code < 0 { 1 } else { code }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,33 @@ use embassy::Error;
|
|||||||
use rpc_toolkit::run_cli;
|
use rpc_toolkit::run_cli;
|
||||||
|
|
||||||
fn inner_main() -> Result<(), Error> {
|
fn inner_main() -> Result<(), Error> {
|
||||||
simple_logging::log_to_stderr(log::LevelFilter::Info);
|
|
||||||
run_cli!(
|
run_cli!(
|
||||||
embassy::portable_api,
|
embassy::portable_api,
|
||||||
app => app.name("Embassy SDK"),
|
app => app
|
||||||
matches => EitherContext::Cli(CliContext::init(matches)?),
|
.name("Embassy SDK")
|
||||||
|
.arg(
|
||||||
|
clap::Arg::with_name("config")
|
||||||
|
.short("c")
|
||||||
|
.long("config")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::with_name("verbosity")
|
||||||
|
.short("v")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
),
|
||||||
|
matches => {
|
||||||
|
simple_logging::log_to_stderr(match matches.occurrences_of("verbosity") {
|
||||||
|
0 => log::LevelFilter::Off,
|
||||||
|
1 => log::LevelFilter::Error,
|
||||||
|
2 => log::LevelFilter::Warn,
|
||||||
|
3 => log::LevelFilter::Info,
|
||||||
|
4 => log::LevelFilter::Debug,
|
||||||
|
_ => log::LevelFilter::Trace,
|
||||||
|
});
|
||||||
|
EitherContext::Cli(CliContext::init(matches)?)
|
||||||
|
},
|
||||||
|code| if code < 0 { 1 } else { code }
|
|code| if code < 0 { 1 } else { code }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ impl CharSet {
|
|||||||
self.0.iter().any(|r| r.0.contains(c))
|
self.0.iter().any(|r| r.0.contains(c))
|
||||||
}
|
}
|
||||||
pub fn gen<R: Rng>(&self, rng: &mut R) -> char {
|
pub fn gen<R: Rng>(&self, rng: &mut R) -> char {
|
||||||
let mut idx = rng.gen_range(0..self.1);
|
let mut idx = rng.gen_range(0, self.1);
|
||||||
for r in &self.0 {
|
for r in &self.0 {
|
||||||
if idx < r.1 {
|
if idx < r.1 {
|
||||||
return std::convert::TryFrom::try_from(
|
return std::convert::TryFrom::try_from(
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use reqwest::Proxy;
|
use reqwest::Proxy;
|
||||||
use rpc_toolkit::reqwest::{Client, Url};
|
use rpc_toolkit::reqwest::{Client, Url};
|
||||||
@@ -11,15 +13,17 @@ use rpc_toolkit::Context;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use super::rpc::RpcContextConfig;
|
use super::rpc::RpcContextConfig;
|
||||||
use crate::ResultExt;
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct CliContextConfig {
|
pub struct CliContextConfig {
|
||||||
#[serde(deserialize_with = "deserialize_host")]
|
#[serde(deserialize_with = "deserialize_host")]
|
||||||
pub host: Option<Host>,
|
pub host: Option<Host>,
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
#[serde(deserialize_with = "crate::util::deserialize_from_str_opt")]
|
#[serde(deserialize_with = "crate::util::deserialize_from_str_opt")]
|
||||||
pub proxy: Option<Url>,
|
pub proxy: Option<Url>,
|
||||||
|
pub developer_key_path: Option<PathBuf>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub server_config: RpcContextConfig,
|
pub server_config: RpcContextConfig,
|
||||||
}
|
}
|
||||||
@@ -29,13 +33,15 @@ pub struct CliContextSeed {
|
|||||||
pub host: Host,
|
pub host: Host,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
|
pub developer_key_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CliContext(Arc<CliContextSeed>);
|
pub struct CliContext(Arc<CliContextSeed>);
|
||||||
impl CliContext {
|
impl CliContext {
|
||||||
|
/// BLOCKING
|
||||||
pub fn init(matches: &ArgMatches) -> Result<Self, crate::Error> {
|
pub fn init(matches: &ArgMatches) -> Result<Self, crate::Error> {
|
||||||
let cfg_path = Path::new(crate::CONFIG_PATH);
|
let cfg_path = Path::new(matches.value_of("config").unwrap_or(crate::CONFIG_PATH));
|
||||||
let mut base = if cfg_path.exists() {
|
let mut base = if cfg_path.exists() {
|
||||||
serde_yaml::from_reader(
|
serde_yaml::from_reader(
|
||||||
File::open(cfg_path)
|
File::open(cfg_path)
|
||||||
@@ -76,8 +82,29 @@ impl CliContext {
|
|||||||
} else {
|
} else {
|
||||||
Client::new()
|
Client::new()
|
||||||
},
|
},
|
||||||
|
developer_key_path: base.developer_key_path.unwrap_or_else(|| {
|
||||||
|
cfg_path
|
||||||
|
.parent()
|
||||||
|
.unwrap_or(Path::new("/"))
|
||||||
|
.join(".developer_key")
|
||||||
|
}),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
/// BLOCKING
|
||||||
|
pub fn developer_key(&self) -> Result<ed25519_dalek::Keypair, Error> {
|
||||||
|
if !self.developer_key_path.exists() {
|
||||||
|
return Err(Error::new(anyhow!("Developer Key does not exist! Please run `embassy-sdk init` before running this command."), crate::ErrorKind::Uninitialized));
|
||||||
|
}
|
||||||
|
let mut keypair_buf = [0; ed25519_dalek::KEYPAIR_LENGTH];
|
||||||
|
File::open(&self.developer_key_path)?.read_exact(&mut keypair_buf)?;
|
||||||
|
Ok(ed25519_dalek::Keypair::from_bytes(&keypair_buf)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::ops::Deref for CliContext {
|
||||||
|
type Target = CliContextSeed;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Context for CliContext {
|
impl Context for CliContext {
|
||||||
fn host(&self) -> Host<&str> {
|
fn host(&self) -> Host<&str> {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ impl std::fmt::Display for DependencyError {
|
|||||||
if !comma {
|
if !comma {
|
||||||
comma = true;
|
comma = true;
|
||||||
} else {
|
} else {
|
||||||
write!(f, ", ");
|
write!(f, ", ")?;
|
||||||
}
|
}
|
||||||
write!(f, "{} @ {} {}", check, res.time, res.result)?;
|
write!(f, "{} @ {} {}", check, res.time, res.result)?;
|
||||||
}
|
}
|
||||||
|
|||||||
29
appmgr/src/developer/mod.rs
Normal file
29
appmgr/src/developer/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use ed25519_dalek::Keypair;
|
||||||
|
use rpc_toolkit::command;
|
||||||
|
|
||||||
|
use crate::context::EitherContext;
|
||||||
|
use crate::util::display_none;
|
||||||
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
|
#[command(cli_only, blocking, display(display_none))]
|
||||||
|
pub fn init(#[context] ctx: EitherContext) -> Result<(), Error> {
|
||||||
|
let ctx = ctx.as_cli().unwrap();
|
||||||
|
if !ctx.developer_key_path.exists() {
|
||||||
|
let parent = ctx.developer_key_path.parent().unwrap_or(Path::new("/"));
|
||||||
|
if !parent.exists() {
|
||||||
|
std::fs::create_dir_all(parent)
|
||||||
|
.with_ctx(|_| (crate::ErrorKind::Filesystem, parent.display().to_string()))?;
|
||||||
|
}
|
||||||
|
log::info!("Generating new developer key...");
|
||||||
|
let keypair = Keypair::generate(&mut rand::thread_rng());
|
||||||
|
log::info!("Writing key to {}", ctx.developer_key_path.display());
|
||||||
|
let mut dev_key_file = File::create(&ctx.developer_key_path)?;
|
||||||
|
dev_key_file.write_all(&keypair.to_bytes())?;
|
||||||
|
dev_key_file.sync_all()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ pub enum ErrorKind {
|
|||||||
RateLimited = 37,
|
RateLimited = 37,
|
||||||
InvalidRequest = 38,
|
InvalidRequest = 38,
|
||||||
MigrationFailed = 39,
|
MigrationFailed = 39,
|
||||||
|
Uninitialized = 40,
|
||||||
}
|
}
|
||||||
impl ErrorKind {
|
impl ErrorKind {
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
@@ -89,6 +90,7 @@ impl ErrorKind {
|
|||||||
RateLimited => "Rate Limited",
|
RateLimited => "Rate Limited",
|
||||||
InvalidRequest => "Invalid Request",
|
InvalidRequest => "Invalid Request",
|
||||||
MigrationFailed => "Migration Failed",
|
MigrationFailed => "Migration Failed",
|
||||||
|
Uninitialized => "Uninitialized",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub mod config;
|
|||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod dependencies;
|
pub mod dependencies;
|
||||||
|
pub mod developer;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod id;
|
pub mod id;
|
||||||
pub mod install;
|
pub mod install;
|
||||||
@@ -46,12 +47,19 @@ pub fn echo(#[context] _ctx: EitherContext, #[arg] message: String) -> Result<St
|
|||||||
Ok(message)
|
Ok(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(subcommands(config::config, version::git_info, echo, s9pk::pack, s9pk::verify))]
|
#[command(subcommands(
|
||||||
|
config::config,
|
||||||
|
version::git_info,
|
||||||
|
echo,
|
||||||
|
s9pk::pack,
|
||||||
|
s9pk::verify,
|
||||||
|
developer::init
|
||||||
|
))]
|
||||||
pub fn main_api(#[context] ctx: EitherContext) -> Result<EitherContext, RpcError> {
|
pub fn main_api(#[context] ctx: EitherContext) -> Result<EitherContext, RpcError> {
|
||||||
Ok(ctx)
|
Ok(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(subcommands(version::git_info, s9pk::pack, s9pk::verify))]
|
#[command(subcommands(version::git_info, s9pk::pack, s9pk::verify, developer::init))]
|
||||||
pub fn portable_api(#[context] ctx: EitherContext) -> Result<EitherContext, RpcError> {
|
pub fn portable_api(#[context] ctx: EitherContext) -> Result<EitherContext, RpcError> {
|
||||||
Ok(ctx)
|
Ok(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use torut::onion::TorSecretKeyV3;
|
|||||||
use crate::db::model::{InterfaceAddressMap, InterfaceAddresses, InterfaceInfo};
|
use crate::db::model::{InterfaceAddressMap, InterfaceAddresses, InterfaceInfo};
|
||||||
use crate::id::Id;
|
use crate::id::Id;
|
||||||
use crate::s9pk::manifest::PackageId;
|
use crate::s9pk::manifest::PackageId;
|
||||||
|
use crate::util::Port;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
@@ -99,7 +100,7 @@ impl<S: AsRef<str>> AsRef<Path> for InterfaceId<S> {
|
|||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
pub tor_config: Option<TorConfig>,
|
pub tor_config: Option<TorConfig>,
|
||||||
pub lan_config: Option<IndexMap<u16, LanPortConfig>>,
|
pub lan_config: Option<IndexMap<Port, LanPortConfig>>,
|
||||||
pub ui: bool,
|
pub ui: bool,
|
||||||
pub protocols: Vec<String>,
|
pub protocols: Vec<String>,
|
||||||
}
|
}
|
||||||
@@ -107,7 +108,7 @@ pub struct Interface {
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct TorConfig {
|
pub struct TorConfig {
|
||||||
pub port_mapping: IndexMap<u16, u16>,
|
pub port_mapping: IndexMap<Port, Port>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ impl TorControllerInner {
|
|||||||
.port_mapping
|
.port_mapping
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(external, internal)| {
|
.map(|(external, internal)| {
|
||||||
(*external, SocketAddr::from((config.ip, *internal)))
|
(external.0, SocketAddr::from((config.ip, internal.0)))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.iter(),
|
.iter(),
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
|
use digest::Digest;
|
||||||
|
use sha2::Sha512;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
use super::header::{FileSection, Header};
|
use super::header::{FileSection, Header};
|
||||||
use super::manifest::Manifest;
|
use super::manifest::Manifest;
|
||||||
|
use super::SIG_CONTEXT;
|
||||||
|
use crate::util::HashWriter;
|
||||||
use crate::{Error, ResultExt};
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
#[derive(TypedBuilder)]
|
#[derive(TypedBuilder)]
|
||||||
@@ -32,7 +36,7 @@ impl<
|
|||||||
> S9pkPacker<'a, W, RLicense, RInstructions, RIcon, RDockerImages>
|
> S9pkPacker<'a, W, RLicense, RInstructions, RIcon, RDockerImages>
|
||||||
{
|
{
|
||||||
/// BLOCKING
|
/// BLOCKING
|
||||||
pub fn pack(mut self) -> Result<(), Error> {
|
pub fn pack(mut self, key: &ed25519_dalek::Keypair) -> Result<(), Error> {
|
||||||
let header_pos = self.writer.stream_position()?;
|
let header_pos = self.writer.stream_position()?;
|
||||||
if header_pos != 0 {
|
if header_pos != 0 {
|
||||||
log::warn!("Appending to non-empty file.");
|
log::warn!("Appending to non-empty file.");
|
||||||
@@ -45,57 +49,63 @@ impl<
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let mut position = self.writer.stream_position()?;
|
let mut position = self.writer.stream_position()?;
|
||||||
|
|
||||||
|
let mut writer = HashWriter::new(Sha512::new(), &mut self.writer);
|
||||||
// manifest
|
// manifest
|
||||||
serde_cbor::to_writer(&mut self.writer, self.manifest).with_ctx(|_| {
|
serde_cbor::to_writer(&mut writer, self.manifest).with_ctx(|_| {
|
||||||
(
|
(
|
||||||
crate::ErrorKind::Serialization,
|
crate::ErrorKind::Serialization,
|
||||||
"Serializing Manifest (CBOR)",
|
"Serializing Manifest (CBOR)",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let new_pos = self.writer.stream_position()?;
|
let new_pos = writer.stream_position()?;
|
||||||
header.table_of_contents.manifest = FileSection {
|
header.table_of_contents.manifest = FileSection {
|
||||||
position,
|
position,
|
||||||
length: new_pos - position,
|
length: new_pos - position,
|
||||||
};
|
};
|
||||||
position = new_pos;
|
position = new_pos;
|
||||||
// license
|
// license
|
||||||
std::io::copy(&mut self.license, &mut self.writer)
|
std::io::copy(&mut self.license, &mut writer)
|
||||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying License"))?;
|
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying License"))?;
|
||||||
let new_pos = self.writer.stream_position()?;
|
let new_pos = writer.stream_position()?;
|
||||||
header.table_of_contents.license = FileSection {
|
header.table_of_contents.license = FileSection {
|
||||||
position,
|
position,
|
||||||
length: new_pos - position,
|
length: new_pos - position,
|
||||||
};
|
};
|
||||||
position = new_pos;
|
position = new_pos;
|
||||||
// instructions
|
// instructions
|
||||||
std::io::copy(&mut self.instructions, &mut self.writer)
|
std::io::copy(&mut self.instructions, &mut writer)
|
||||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Instructions"))?;
|
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Instructions"))?;
|
||||||
let new_pos = self.writer.stream_position()?;
|
let new_pos = writer.stream_position()?;
|
||||||
header.table_of_contents.instructions = FileSection {
|
header.table_of_contents.instructions = FileSection {
|
||||||
position,
|
position,
|
||||||
length: new_pos - position,
|
length: new_pos - position,
|
||||||
};
|
};
|
||||||
position = new_pos;
|
position = new_pos;
|
||||||
// icon
|
// icon
|
||||||
std::io::copy(&mut self.icon, &mut self.writer)
|
std::io::copy(&mut self.icon, &mut writer)
|
||||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Icon"))?;
|
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Icon"))?;
|
||||||
let new_pos = self.writer.stream_position()?;
|
let new_pos = writer.stream_position()?;
|
||||||
header.table_of_contents.icon = FileSection {
|
header.table_of_contents.icon = FileSection {
|
||||||
position,
|
position,
|
||||||
length: new_pos - position,
|
length: new_pos - position,
|
||||||
};
|
};
|
||||||
position = new_pos;
|
position = new_pos;
|
||||||
// docker_images
|
// docker_images
|
||||||
std::io::copy(&mut self.docker_images, &mut self.writer)
|
std::io::copy(&mut self.docker_images, &mut writer)
|
||||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying App Image"))?;
|
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying App Image"))?;
|
||||||
let new_pos = self.writer.stream_position()?;
|
let new_pos = writer.stream_position()?;
|
||||||
header.table_of_contents.docker_images = FileSection {
|
header.table_of_contents.docker_images = FileSection {
|
||||||
position,
|
position,
|
||||||
length: new_pos - position,
|
length: new_pos - position,
|
||||||
};
|
};
|
||||||
position = new_pos;
|
position = new_pos;
|
||||||
|
|
||||||
// header
|
// header
|
||||||
|
let (hash, _) = writer.finish();
|
||||||
self.writer.seek(SeekFrom::Start(header_pos))?;
|
self.writer.seek(SeekFrom::Start(header_pos))?;
|
||||||
|
header.pubkey = key.public.clone();
|
||||||
|
header.signature = key.sign_prehashed(hash, Some(SIG_CONTEXT))?;
|
||||||
header
|
header
|
||||||
.serialize(&mut self.writer)
|
.serialize(&mut self.writer)
|
||||||
.with_ctx(|_| (crate::ErrorKind::Serialization, "Writing Header"))?;
|
.with_ctx(|_| (crate::ErrorKind::Serialization, "Writing Header"))?;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::Error;
|
|||||||
pub const MAGIC: [u8; 2] = [59, 59];
|
pub const MAGIC: [u8; 2] = [59, 59];
|
||||||
pub const VERSION: u8 = 1;
|
pub const VERSION: u8 = 1;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
pub pubkey: PublicKey,
|
pub pubkey: PublicKey,
|
||||||
pub signature: Signature,
|
pub signature: Signature,
|
||||||
@@ -76,14 +77,11 @@ pub struct TableOfContents {
|
|||||||
}
|
}
|
||||||
impl TableOfContents {
|
impl TableOfContents {
|
||||||
pub fn serialize<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
pub fn serialize<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||||
let len: u32 = 16 // size of FileSection
|
let len: u32 = ((1 + "manifest".len() + 16)
|
||||||
* (
|
+ (1 + "license".len() + 16)
|
||||||
1 + // manifest
|
+ (1 + "instructions".len() + 16)
|
||||||
1 + // license
|
+ (1 + "icon".len() + 16)
|
||||||
1 + // instructions
|
+ (1 + "docker_images".len() + 16)) as u32;
|
||||||
1 + // icon
|
|
||||||
1 // docker_images
|
|
||||||
);
|
|
||||||
writer.write_all(&u32::to_be_bytes(len))?;
|
writer.write_all(&u32::to_be_bytes(len))?;
|
||||||
self.manifest.serialize_entry("manifest", &mut writer)?;
|
self.manifest.serialize_entry("manifest", &mut writer)?;
|
||||||
self.license.serialize_entry("license", &mut writer)?;
|
self.license.serialize_entry("license", &mut writer)?;
|
||||||
@@ -153,7 +151,8 @@ impl FileSection {
|
|||||||
if read == 0 {
|
if read == 0 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let label = vec![0; label_len[0] as usize];
|
let mut label = vec![0; label_len[0] as usize];
|
||||||
|
reader.read_exact(&mut label).await?;
|
||||||
let mut pos = [0; 8];
|
let mut pos = [0; 8];
|
||||||
reader.read_exact(&mut pos).await?;
|
reader.read_exact(&mut pos).await?;
|
||||||
let mut len = [0; 8];
|
let mut len = [0; 8];
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ pub struct Manifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Assets {
|
pub struct Assets {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub license: Option<PathBuf>,
|
pub license: Option<PathBuf>,
|
||||||
@@ -168,7 +169,7 @@ impl Assets {
|
|||||||
self.docker_images
|
self.docker_images
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|a| a.as_path())
|
.map(|a| a.as_path())
|
||||||
.unwrap_or(Path::new("images.tar"))
|
.unwrap_or(Path::new("image.tar"))
|
||||||
}
|
}
|
||||||
pub fn instructions_path(&self) -> &Path {
|
pub fn instructions_path(&self) -> &Path {
|
||||||
self.instructions
|
self.instructions
|
||||||
|
|||||||
@@ -20,8 +20,11 @@ pub mod reader;
|
|||||||
pub const SIG_CONTEXT: &'static [u8] = b"s9pk";
|
pub const SIG_CONTEXT: &'static [u8] = b"s9pk";
|
||||||
|
|
||||||
#[command(cli_only, display(display_none), blocking)]
|
#[command(cli_only, display(display_none), blocking)]
|
||||||
pub fn pack(#[context] _ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Result<(), Error> {
|
pub fn pack(#[context] ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Result<(), Error> {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let ctx = ctx.as_cli().unwrap();
|
||||||
|
|
||||||
let path = if let Some(path) = path {
|
let path = if let Some(path) = path {
|
||||||
path
|
path
|
||||||
@@ -44,12 +47,40 @@ pub fn pack(#[context] _ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Res
|
|||||||
S9pkPacker::builder()
|
S9pkPacker::builder()
|
||||||
.manifest(&manifest)
|
.manifest(&manifest)
|
||||||
.writer(&mut outfile)
|
.writer(&mut outfile)
|
||||||
.license(File::open(path.join(manifest.assets.license_path()))?)
|
.license(
|
||||||
.icon(File::open(path.join(manifest.assets.icon_path()))?)
|
File::open(path.join(manifest.assets.license_path())).with_ctx(|_| {
|
||||||
.instructions(File::open(path.join(manifest.assets.instructions_path()))?)
|
(
|
||||||
.docker_images(File::open(path.join(manifest.assets.docker_images_path()))?)
|
crate::ErrorKind::Filesystem,
|
||||||
|
manifest.assets.license_path().display().to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
.icon(
|
||||||
|
File::open(path.join(manifest.assets.icon_path())).with_ctx(|_| {
|
||||||
|
(
|
||||||
|
crate::ErrorKind::Filesystem,
|
||||||
|
manifest.assets.icon_path().display().to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
.instructions(
|
||||||
|
File::open(path.join(manifest.assets.instructions_path())).with_ctx(|_| {
|
||||||
|
(
|
||||||
|
crate::ErrorKind::Filesystem,
|
||||||
|
manifest.assets.instructions_path().display().to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
.docker_images(
|
||||||
|
File::open(path.join(manifest.assets.docker_images_path())).with_ctx(|_| {
|
||||||
|
(
|
||||||
|
crate::ErrorKind::Filesystem,
|
||||||
|
manifest.assets.docker_images_path().display().to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
.pack()?;
|
.pack(&ctx.developer_key()?)?;
|
||||||
outfile.sync_all()?;
|
outfile.sync_all()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<InstallProgressTracker<R>> {
|
|||||||
}
|
}
|
||||||
impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
|
impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
|
||||||
pub async fn validate(&mut self) -> Result<(), Error> {
|
pub async fn validate(&mut self) -> Result<(), Error> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
pub async fn from_reader(mut rdr: R) -> Result<Self, Error> {
|
pub async fn from_reader(mut rdr: R) -> Result<Self, Error> {
|
||||||
let header = Header::deserialize(&mut rdr).await?;
|
let header = Header::deserialize(&mut rdr).await?;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use std::time::Duration;
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use digest::Digest;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use sqlx::{Executor, Sqlite};
|
use sqlx::{Executor, Sqlite};
|
||||||
@@ -620,7 +621,7 @@ impl<W: std::fmt::Write> std::io::Write for FmtWriter<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum IoFormat {
|
pub enum IoFormat {
|
||||||
Json,
|
Json,
|
||||||
JsonPretty,
|
JsonPretty,
|
||||||
@@ -698,6 +699,7 @@ impl IoFormat {
|
|||||||
.with_kind(crate::ErrorKind::Serialization),
|
.with_kind(crate::ErrorKind::Serialization),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// BLOCKING
|
||||||
pub fn from_reader<R: std::io::Read, T: for<'de> Deserialize<'de>>(
|
pub fn from_reader<R: std::io::Read, T: for<'de> Deserialize<'de>>(
|
||||||
&self,
|
&self,
|
||||||
mut reader: R,
|
mut reader: R,
|
||||||
@@ -714,7 +716,9 @@ impl IoFormat {
|
|||||||
}
|
}
|
||||||
IoFormat::Toml | IoFormat::TomlPretty => {
|
IoFormat::Toml | IoFormat::TomlPretty => {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
reader.read_to_string(&mut s);
|
reader
|
||||||
|
.read_to_string(&mut s)
|
||||||
|
.with_kind(crate::ErrorKind::Deserialization)?;
|
||||||
serde_toml::from_str(&s).with_kind(crate::ErrorKind::Deserialization)
|
serde_toml::from_str(&s).with_kind(crate::ErrorKind::Deserialization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,3 +813,56 @@ impl<T> Container<T> {
|
|||||||
*self.0.write().await = None;
|
*self.0.write().await = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct HashWriter<H: Digest, W: std::io::Write> {
|
||||||
|
hasher: H,
|
||||||
|
writer: W,
|
||||||
|
}
|
||||||
|
impl<H: Digest, W: std::io::Write> HashWriter<H, W> {
|
||||||
|
pub fn new(hasher: H, writer: W) -> Self {
|
||||||
|
HashWriter { hasher, writer }
|
||||||
|
}
|
||||||
|
pub fn finish(self) -> (H, W) {
|
||||||
|
(self.hasher, self.writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: Digest, W: std::io::Write> std::io::Write for HashWriter<H, W> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
let written = self.writer.write(buf)?;
|
||||||
|
self.hasher.update(&buf[..written]);
|
||||||
|
Ok(written)
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
self.writer.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: Digest, W: std::io::Write> std::ops::Deref for HashWriter<H, W> {
|
||||||
|
type Target = W;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: Digest, W: std::io::Write> std::ops::DerefMut for HashWriter<H, W> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Port(pub u16);
|
||||||
|
impl<'de> Deserialize<'de> for Port {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserialize_from_str(deserializer).map(Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Serialize for Port {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serialize_display(&self.0, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user