mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-02 05:23:14 +00:00
Gateways, domains, and new service interface (#3001)
* add support for inbound proxies * backend changes * fix file type * proxy -> tunnel, implement backend apis * wip start-tunneld * add domains and gateways, remove routers, fix docs links * dont show hidden actions * show and test dns * edit instead of chnage acme and change gateway * refactor: domains page * refactor: gateways page * domains and acme refactor * certificate authorities * refactor public/private gateways * fix fe types * domains mostly finished * refactor: add file control to form service * add ip util to sdk * domains api + migration * start service interface page, WIP * different options for clearnet domains * refactor: styles for interfaces page * minor * better placeholder for no addresses * start sorting addresses * best address logic * comments * fix unnecessary export * MVP of service interface page * domains preferred * fix: address comments * only translations left * wip: start-tunnel & fix build * forms for adding domain, rework things based on new ideas * fix: dns testing * public domain, max width, descriptions for dns * nix StartOS domains, implement public and private domains at interface scope * restart tor instead of reset * better icon for restart tor * dns * fix sort functions for public and private domains * with todos * update types * clean up tech debt, bump dependencies * revert to ts-rs v9 * fix all types * fix dns form * add missing translations * it builds * fix: comments (#3009) * fix: comments * undo default --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * fix: refactor legacy components (#3010) * fix: comments * fix: refactor legacy components * remove default again --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * more translations * wip * fix deadlock * coukd work * simple renaming * placeholder for empty service interfaces table * honor hidden form values * remove logs * reason instead of description * fix dns * misc fixes * implement toggling gateways for service interface * fix showing dns records * move status column in service list * remove unnecessary truthy check * refactor: refactor forms components and remove legacy Taiga UI package (#3012) * handle wh file uploads * wip: debugging tor * socks5 proxy working * refactor: fix multiple comments (#3013) * refactor: fix multiple comments * styling changes, add documentation to sidebar * translations for dns page * refactor: subtle colors * rearrange service page --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * fix file_stream and remove non-terminating test * clean up logs * support for sccache * fix gha sccache * more marketplace translations * install wizard clarity * stub hostnameInfo in migration * fix address info after setup, fix styling on SI page, new 040 release notes * remove tor logs from os * misc fixes * reset tor still not functioning... * update ts * minor styling and wording * chore: some fixes (#3015) * fix gateway renames * different handling for public domains * styling fixes * whole navbar should not be clickable on service show page * timeout getState request * remove links from changelog * misc fixes from pairing * use custom name for gateway in more places * fix dns parsing * closes #3003 * closes #2999 * chore: some fixes (#3017) * small copy change * revert hardcoded error for testing * dont require port forward if gateway is public * use old wan ip when not available * fix .const hanging on undefined * fix test * fix doc test * fix renames * update deps * allow specifying dependency metadata directly * temporarily make dependencies not cliackable in marketplace listings * fix socks bind * fix test --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: waterplea <alexander@inkin.ru>
This commit is contained in:
5
system-images/compat/.gitignore
vendored
5
system-images/compat/.gitignore
vendored
@@ -1,5 +0,0 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
.DS_Store
|
||||
.vscode
|
||||
/docker-images
|
||||
6120
system-images/compat/Cargo.lock
generated
6120
system-images/compat/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
[package]
|
||||
authors = ["Aiden McClelland <me@drbonez.dev>"]
|
||||
edition = "2018"
|
||||
name = "compat"
|
||||
version = "0.1.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.40", features = ["backtrace"] }
|
||||
beau_collector = "0.2.1"
|
||||
clap = "2.33.3"
|
||||
dashmap = "5.3.2"
|
||||
start-os = { path = "../../core/startos", default-features = false }
|
||||
emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", features = [
|
||||
"serde",
|
||||
] }
|
||||
failure = "0.1.8"
|
||||
indexmap = { version = "1.6.2", features = ["serde"] }
|
||||
imbl-value = "0.1.2"
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.4"
|
||||
linear-map = { version = "1.2", features = ["serde_impl"] }
|
||||
log = "0.4.11"
|
||||
nix = "0.25.0"
|
||||
pest = "2.1"
|
||||
pest_derive = "2.1"
|
||||
rand = "0.8.5"
|
||||
regex = "1.4.2"
|
||||
rust-argon2 = "1.0.0"
|
||||
serde = { version = "1.0.118", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.67"
|
||||
serde_yaml = "0.8.17"
|
||||
@@ -1 +0,0 @@
|
||||
FROM start9/compat
|
||||
@@ -1,24 +0,0 @@
|
||||
COMPAT_SRC := $(shell find ./src) Cargo.toml Cargo.lock
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
all: docker-images/aarch64.tar docker-images/x86_64.tar
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
rm -rf docker-images
|
||||
|
||||
docker-images:
|
||||
mkdir docker-images
|
||||
|
||||
docker-images/aarch64.tar: Dockerfile docker-images
|
||||
docker buildx build --build-arg ARCH=aarch64 --tag start9/x_system/compat --platform=linux/arm64 -o type=docker,dest=docker-images/aarch64.tar .
|
||||
|
||||
docker-images/x86_64.tar: Dockerfile docker-images
|
||||
docker buildx build --build-arg ARCH=x86_64 --tag start9/x_system/compat --platform=linux/amd64 -o type=docker,dest=docker-images/x86_64.tar .
|
||||
|
||||
target/aarch64-unknown-linux-musl/release/compat: $(COMPAT_SRC) ../../core/Cargo.lock
|
||||
ARCH=aarch64 ./build.sh
|
||||
|
||||
target/x86_64-unknown-linux-musl/release/compat: $(COMPAT_SRC) ../../core/Cargo.lock
|
||||
ARCH=x86_64 ./build.sh
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
shopt -s expand_aliases
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
ARCH=$(uname -m)
|
||||
fi
|
||||
|
||||
if [ "$0" != "./build.sh" ]; then
|
||||
>&2 echo "Must be run from compat directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USE_TTY=
|
||||
if tty -s; then
|
||||
USE_TTY="-it"
|
||||
fi
|
||||
|
||||
alias 'rust-musl-builder'='docker run $USE_TTY --rm -v "$HOME"/.cargo/registry:/root/.cargo/registry -v "$(pwd)":/home/rust/src messense/rust-musl-cross:${ARCH}-musl'
|
||||
|
||||
cd ../..
|
||||
rust-musl-builder sh -c "(git config --global --add safe.directory '*'; cd system-images/compat && cargo build --release --target=${ARCH}-unknown-linux-musl --no-default-features)"
|
||||
cd system-images/compat
|
||||
|
||||
sudo chown -R $USER target
|
||||
sudo chown -R $USER ~/.cargo
|
||||
@@ -1,79 +0,0 @@
|
||||
use std::{path::Path, process::Stdio};
|
||||
|
||||
use startos::disk::main::DEFAULT_PASSWORD;
|
||||
|
||||
pub fn create_backup(
|
||||
mountpoint: impl AsRef<Path>,
|
||||
data_path: impl AsRef<Path>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let mountpoint = std::fs::canonicalize(mountpoint)?;
|
||||
let data_path = std::fs::canonicalize(data_path)?;
|
||||
|
||||
let ignore_path = data_path.join(".backupignore");
|
||||
let exclude = if ignore_path.is_file() {
|
||||
std::fs::read_to_string(ignore_path)?
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let mut data_cmd = std::process::Command::new("duplicity");
|
||||
for exclude in exclude.lines().map(|s| s.trim()).filter(|s| !s.is_empty()) {
|
||||
if exclude.to_string().starts_with('!') {
|
||||
data_cmd
|
||||
.arg(format!(
|
||||
"--include={}",
|
||||
data_path
|
||||
.join(exclude.to_string().trim_start_matches('!'))
|
||||
.display()
|
||||
))
|
||||
.arg("--allow-source-mismatch");
|
||||
} else {
|
||||
data_cmd
|
||||
.arg(format!(
|
||||
"--exclude={}",
|
||||
data_path.join(exclude.to_string()).display()
|
||||
))
|
||||
.arg("--allow-source-mismatch");
|
||||
}
|
||||
}
|
||||
let data_output = data_cmd
|
||||
.env("PASSPHRASE", DEFAULT_PASSWORD)
|
||||
.arg(data_path)
|
||||
.arg(format!("file://{}", mountpoint.display().to_string()))
|
||||
.arg("--allow-source-mismatch")
|
||||
.stderr(Stdio::piped())
|
||||
.output()?;
|
||||
if !data_output.status.success() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"duplicity error: {}",
|
||||
String::from_utf8(data_output.stderr).unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn restore_backup(
|
||||
mountpoint: impl AsRef<Path>,
|
||||
data_path: impl AsRef<Path>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let mountpoint = std::fs::canonicalize(mountpoint)?;
|
||||
let data_path = std::fs::canonicalize(data_path)?;
|
||||
|
||||
let data_output = std::process::Command::new("duplicity")
|
||||
.arg("--allow-source-mismatch")
|
||||
.env("PASSPHRASE", DEFAULT_PASSWORD)
|
||||
.arg("--force")
|
||||
.arg(format!("file://{}", mountpoint.display().to_string()))
|
||||
.arg(&data_path)
|
||||
.stderr(Stdio::piped())
|
||||
.output()?;
|
||||
if !data_output.status.success() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"duplicity error: {}",
|
||||
String::from_utf8(data_output.stderr).unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::path::Path;
|
||||
|
||||
use beau_collector::BeauCollector;
|
||||
use linear_map::LinearMap;
|
||||
use startos::config::action::SetResult;
|
||||
use startos::config::{spec, Config};
|
||||
use startos::s9pk::manifest::PackageId;
|
||||
use startos::status::health_check::HealthCheckId;
|
||||
|
||||
pub mod rules;
|
||||
|
||||
use anyhow::anyhow;
|
||||
pub use rules::{ConfigRuleEntry, ConfigRuleEntryWithSuggestions};
|
||||
|
||||
use self::rules::ConfigRule;
|
||||
pub type DepInfo = HashMap<PackageId, DepRuleInfo>;
|
||||
#[derive(Clone, Debug, serde::Deserialize)]
|
||||
pub struct DepRuleInfo {
|
||||
condition: ConfigRule,
|
||||
health_checks: BTreeSet<HealthCheckId>,
|
||||
}
|
||||
|
||||
pub fn validate_configuration(
|
||||
name: &str,
|
||||
config: Config,
|
||||
rules_path: &Path,
|
||||
config_path: &Path,
|
||||
maybe_deps_path: Option<&str>,
|
||||
) -> Result<SetResult, anyhow::Error> {
|
||||
let rules: Vec<ConfigRuleEntry> = serde_yaml::from_reader(std::fs::File::open(rules_path)?)?;
|
||||
let mut cfgs = LinearMap::new();
|
||||
cfgs.insert(name, Cow::Borrowed(&config));
|
||||
|
||||
let mut depends_on = BTreeMap::new();
|
||||
if let Some(deps_path) = maybe_deps_path.map(Path::new) {
|
||||
if deps_path.exists() {
|
||||
let deps: DepInfo = serde_yaml::from_reader(std::fs::File::open(deps_path)?)?;
|
||||
// check if new config is set to depend on any optional dependencies
|
||||
depends_on.extend(
|
||||
deps.into_iter()
|
||||
.filter(|(_, data)| (data.condition.compiled)(&config, &cfgs))
|
||||
.map(|(pkg_id, data)| (pkg_id, data.health_checks)),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// check that all configuration rules
|
||||
let rule_check = rules
|
||||
.into_iter()
|
||||
.map(|r| r.check(&config, &cfgs))
|
||||
.bcollect::<Vec<_>>();
|
||||
match rule_check {
|
||||
Ok(_) => {
|
||||
// create temp config file
|
||||
serde_yaml::to_writer(
|
||||
std::fs::create_file(config_path.with_extension("tmp"))?,
|
||||
&config,
|
||||
)?;
|
||||
std::fs::rename(config_path.with_extension("tmp"), config_path)?;
|
||||
// return set result
|
||||
Ok(SetResult { depends_on })
|
||||
}
|
||||
Err(e) => Err(anyhow!("{}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_dependency_configuration(
|
||||
name: &str,
|
||||
config: &Option<Config>,
|
||||
parent_name: &str,
|
||||
parent_config: Config,
|
||||
rules_path: &Path,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let rules: Vec<ConfigRuleEntry> = serde_yaml::from_reader(std::fs::File::open(rules_path)?)?;
|
||||
let mut cfgs = LinearMap::new();
|
||||
cfgs.insert(parent_name, Cow::Borrowed(&parent_config));
|
||||
if let Some(config) = config {
|
||||
cfgs.insert(name, Cow::Borrowed(&config))
|
||||
} else {
|
||||
cfgs.insert(name, Cow::Owned(imbl_value::InOMap::new()))
|
||||
};
|
||||
let rule_check = rules
|
||||
.into_iter()
|
||||
.map(|r| r.check(&parent_config, &cfgs))
|
||||
.bcollect::<Vec<_>>();
|
||||
match rule_check {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(anyhow!("{}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_dependency_configuration(
|
||||
package_id: &str,
|
||||
config: Option<Config>,
|
||||
dependency_id: &str,
|
||||
mut dep_config: Config,
|
||||
rules_path: &Path,
|
||||
) -> Result<Config, anyhow::Error> {
|
||||
let rules: Vec<ConfigRuleEntryWithSuggestions> =
|
||||
serde_yaml::from_reader(std::fs::File::open(rules_path)?)?;
|
||||
let mut cfgs = LinearMap::new();
|
||||
cfgs.insert(dependency_id, Cow::Owned(dep_config.clone()));
|
||||
match config {
|
||||
Some(config) => cfgs.insert(package_id, Cow::Owned(config.clone())),
|
||||
None => cfgs.insert(package_id, Cow::Owned(imbl_value::InOMap::new())),
|
||||
};
|
||||
let rule_check = rules
|
||||
.into_iter()
|
||||
.map(|r| r.apply(dependency_id, &mut dep_config, &mut cfgs))
|
||||
.bcollect::<Vec<_>>();
|
||||
match rule_check {
|
||||
Ok(_) => Ok(dep_config),
|
||||
Err(e) => Err(anyhow!("{}", e)),
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
num = @{ int ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ int)? }
|
||||
int = @{ ("+" | "-")? ~ ASCII_DIGIT+ }
|
||||
|
||||
raw_string = @{ (!("\\" | "\"") ~ ANY)+ }
|
||||
predefined = @{ "n" | "r" | "t" | "\\" | "0" | "\"" | "'" }
|
||||
escape = @{ "\\" ~ predefined }
|
||||
str = @{ "\"" ~ (raw_string | escape)* ~ "\"" }
|
||||
|
||||
ident_char = @{ ASCII_ALPHANUMERIC | "-" }
|
||||
sub_ident = _{ sub_ident_regular | sub_ident_index | sub_ident_any | sub_ident_all | sub_ident_fn }
|
||||
sub_ident_regular = { sub_ident_regular_base | sub_ident_regular_expr }
|
||||
sub_ident_regular_base = @{ ASCII_ALPHA ~ ident_char* }
|
||||
sub_ident_regular_expr = ${ "[" ~ str_expr ~ "]" }
|
||||
sub_ident_index = { sub_ident_index_base | sub_ident_index_expr }
|
||||
sub_ident_index_base = @{ ASCII_DIGIT+ }
|
||||
sub_ident_index_expr = ${ "[" ~ num_expr ~ "]" }
|
||||
sub_ident_any = @{ "*" }
|
||||
sub_ident_all = @{ "&" }
|
||||
sub_ident_fn = ${ "[" ~ list_access_function ~ "]"}
|
||||
list_access_function = _{ list_access_function_first | list_access_function_last | list_access_function_any | list_access_function_all }
|
||||
list_access_function_first = !{ "first" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" }
|
||||
list_access_function_last = !{ "last" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" }
|
||||
list_access_function_any = !{ "any" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" }
|
||||
list_access_function_all = !{ "all" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" }
|
||||
|
||||
app_id = ${ "[" ~ sub_ident_regular ~ "]" }
|
||||
ident = _{ (app_id ~ ".")? ~ sub_ident_regular ~ ("." ~ sub_ident)* }
|
||||
bool_var = ${ ident ~ "?" }
|
||||
num_var = ${ "#" ~ ident }
|
||||
str_var = ${ "'" ~ ident }
|
||||
any_var = ${ ident }
|
||||
|
||||
bool_op = _{ and | or | xor }
|
||||
and = { "AND" }
|
||||
or = { "OR" }
|
||||
xor = { "XOR" }
|
||||
|
||||
num_cmp_op = _{ lt | lte | eq | neq | gt | gte }
|
||||
str_cmp_op = _{ lt | lte | eq | neq | gt | gte }
|
||||
lt = { "<" }
|
||||
lte = { "<=" }
|
||||
eq = { "=" }
|
||||
neq = { "!=" }
|
||||
gt = { ">" }
|
||||
gte = { ">=" }
|
||||
|
||||
num_op = _{ add | sub | mul | div | pow }
|
||||
str_op = _{ add }
|
||||
add = { "+" }
|
||||
sub = { "-" }
|
||||
mul = { "*" }
|
||||
div = { "/" }
|
||||
pow = { "^" }
|
||||
|
||||
num_expr = !{ num_term ~ (num_op ~ num_term)* }
|
||||
num_term = _{ num | num_var | "(" ~ num_expr ~ ")" }
|
||||
|
||||
str_expr = !{ str_term ~ (str_op ~ str_term)* }
|
||||
str_term = _{ str | str_var | "(" ~ str_expr ~ ")" }
|
||||
|
||||
num_cmp_expr = { num_expr ~ num_cmp_op ~ num_expr }
|
||||
str_cmp_expr = { str_expr ~ str_cmp_op ~ str_expr }
|
||||
|
||||
bool_expr = !{ bool_term ~ (bool_op ~ bool_term)* }
|
||||
inv_bool_expr = { "!(" ~ bool_expr ~ ")" }
|
||||
bool_term = _{ bool_var | "(" ~ bool_expr ~ ")" | inv_bool_expr | num_cmp_expr | str_cmp_expr }
|
||||
|
||||
val_expr = _{ any_var | str_expr | num_expr | bool_expr }
|
||||
|
||||
rule = _{ SOI ~ bool_expr ~ EOI }
|
||||
reference = _{ SOI ~ any_var ~ EOI }
|
||||
value = _{ SOI ~ val_expr ~ EOI }
|
||||
del_action = _{ SOI ~ "FROM" ~ any_var ~ "AS" ~ sub_ident_regular ~ "WHERE" ~ bool_expr ~ EOI }
|
||||
obj_key = _{ SOI ~ sub_ident_regular ~ EOI }
|
||||
|
||||
WHITESPACE = _{ " " | "\t" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,337 +0,0 @@
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{stdin, stdout},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
extern crate pest;
|
||||
#[macro_use]
|
||||
extern crate pest_derive;
|
||||
|
||||
mod backup;
|
||||
mod config;
|
||||
use anyhow::anyhow;
|
||||
use backup::{create_backup, restore_backup};
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use config::{
|
||||
apply_dependency_configuration, validate_configuration, validate_dependency_configuration,
|
||||
};
|
||||
use serde_json::json;
|
||||
use startos::config::action::ConfigRes;
|
||||
|
||||
const PROPERTIES_FALLBACK_MESSAGE: &str =
|
||||
"Could not find properties. The service might still be starting";
|
||||
pub enum CompatRes {
|
||||
SetResult,
|
||||
ConfigRes,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match inner_main() {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
log::debug!("{:?}", e.backtrace());
|
||||
drop(e);
|
||||
std::process::exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_main() -> Result<(), anyhow::Error> {
|
||||
let app = App::new("compat")
|
||||
.subcommand(
|
||||
SubCommand::with_name("config")
|
||||
.subcommand(
|
||||
SubCommand::with_name("get")
|
||||
.arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help("Path to the data mountpoint")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("spec")
|
||||
.help("The path to the config spec in the container")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("set")
|
||||
.arg(
|
||||
Arg::with_name("package_id")
|
||||
.help("The `id` field from the manifest file")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help("Path to the data mountpoint")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("assets")
|
||||
.help("Path to the rules file")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dependencies")
|
||||
.help("Path to rules for optional dependency config")
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("dependency")
|
||||
.subcommand(
|
||||
SubCommand::with_name("check")
|
||||
.arg(
|
||||
Arg::with_name("dependent_package_id")
|
||||
.help("Package identifier of this package (the child/depdendent)")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dependency_package_id")
|
||||
.help("Identifier of the dependency")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help(" ountpoint for the dependent's config file")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("assets")
|
||||
.help("Path to the dependency's config rules file")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("auto-configure")
|
||||
.arg(
|
||||
Arg::with_name("dependent_package_id")
|
||||
.help("Package identifier of this package (the child/depdendent)")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dependency_package_id")
|
||||
.help("Package identifier of the parent/dependency")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help("Mountpoint for the dependent's config file")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("assets")
|
||||
.help("Path to the dependency's config rules file")
|
||||
.required(true),
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("duplicity")
|
||||
.subcommand(
|
||||
SubCommand::with_name("create")
|
||||
.arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help("The backups mount point")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("datapath")
|
||||
.help("The path to the data to be backed up in the container")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("restore")
|
||||
.arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help("The backups mount point")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("datapath")
|
||||
.help("The path to the data to be restored to the container")
|
||||
.required(true),
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("properties").arg(
|
||||
Arg::with_name("mountpoint")
|
||||
.help("The data directory of the service to mount to.")
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::with_name("fallbackMessage")
|
||||
.help("The message to indicate that the startup is still working, or stats.yaml couldn't be found")
|
||||
.required(false),
|
||||
),
|
||||
);
|
||||
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 })?;
|
||||
Ok(())
|
||||
}
|
||||
("set", Some(sub_m)) => {
|
||||
let config = serde_yaml::from_reader(stdin())?;
|
||||
let cfg_path = Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9");
|
||||
if !cfg_path.exists() {
|
||||
std::fs::create_dir_all(&cfg_path).unwrap();
|
||||
};
|
||||
let rules_path = Path::new(sub_m.value_of("assets").unwrap());
|
||||
let name = sub_m.value_of("package_id").unwrap();
|
||||
let deps_path = sub_m.value_of("dependencies");
|
||||
match validate_configuration(
|
||||
&name,
|
||||
config,
|
||||
rules_path,
|
||||
&cfg_path.join("config.yaml"),
|
||||
deps_path,
|
||||
) {
|
||||
Ok(a) => {
|
||||
serde_yaml::to_writer(stdout(), &a)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
(subcmd, _) => {
|
||||
panic!("Unknown subcommand: {}", subcmd);
|
||||
}
|
||||
},
|
||||
("dependency", Some(sub_m)) => match sub_m.subcommand() {
|
||||
("check", Some(sub_m)) => {
|
||||
let parent_config = serde_yaml::from_reader(stdin())?;
|
||||
let cfg_path =
|
||||
Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/config.yaml");
|
||||
let config = if cfg_path.exists() {
|
||||
Some(serde_yaml::from_reader(File::open(cfg_path).unwrap()).unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rules_path = Path::new(sub_m.value_of("assets").unwrap());
|
||||
let name = sub_m.value_of("dependent_package_id").unwrap();
|
||||
let parent_name = sub_m.value_of("dependency_package_id").unwrap();
|
||||
match validate_dependency_configuration(
|
||||
name,
|
||||
&config,
|
||||
parent_name,
|
||||
parent_config,
|
||||
rules_path,
|
||||
) {
|
||||
Ok(a) => {
|
||||
serde_yaml::to_writer(stdout(), &a)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
// error string is configs rules failure description
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
("auto-configure", Some(sub_m)) => {
|
||||
let dep_config = serde_yaml::from_reader(stdin())?;
|
||||
let cfg_path =
|
||||
Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/config.yaml");
|
||||
let config = if cfg_path.exists() {
|
||||
Some(serde_yaml::from_reader(File::open(cfg_path).unwrap()).unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rules_path = Path::new(sub_m.value_of("assets").unwrap());
|
||||
let package_id = sub_m.value_of("dependent_package_id").unwrap();
|
||||
let dependency_id = sub_m.value_of("dependency_package_id").unwrap();
|
||||
match apply_dependency_configuration(
|
||||
package_id,
|
||||
config,
|
||||
dependency_id,
|
||||
dep_config,
|
||||
rules_path,
|
||||
) {
|
||||
Ok(a) => {
|
||||
serde_yaml::to_writer(stdout(), &a)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
(subcmd, _) => {
|
||||
panic!("Unknown subcommand: {}", subcmd);
|
||||
}
|
||||
},
|
||||
("duplicity", Some(sub_m)) => match sub_m.subcommand() {
|
||||
("create", Some(sub_m)) => {
|
||||
let res = create_backup(
|
||||
sub_m.value_of("mountpoint").unwrap(),
|
||||
sub_m.value_of("datapath").unwrap(),
|
||||
);
|
||||
match res {
|
||||
Ok(r) => {
|
||||
serde_yaml::to_writer(stdout(), &r)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("Could not create backup: {}", e)),
|
||||
}
|
||||
}
|
||||
("restore", Some(sub_m)) => {
|
||||
let res = restore_backup(
|
||||
sub_m.value_of("mountpoint").unwrap(),
|
||||
sub_m.value_of("datapath").unwrap(),
|
||||
);
|
||||
match res {
|
||||
Ok(r) => {
|
||||
serde_yaml::to_writer(stdout(), &r)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("Could not restore backup: {}", e)),
|
||||
}
|
||||
}
|
||||
(subcmd, _) => {
|
||||
panic!("Unknown subcommand: {}", subcmd);
|
||||
}
|
||||
},
|
||||
("properties", Some(sub_m)) => {
|
||||
let stats_path =
|
||||
Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/stats.yaml");
|
||||
let stats: serde_json::Value = if stats_path.exists() {
|
||||
serde_yaml::from_reader(File::open(stats_path).unwrap()).unwrap()
|
||||
} else {
|
||||
let fallback_message: &str = sub_m
|
||||
.value_of("fallbackMessage")
|
||||
.unwrap_or_else(|| PROPERTIES_FALLBACK_MESSAGE);
|
||||
json!({
|
||||
"version": 2i64,
|
||||
"data": {
|
||||
"Not Ready": {
|
||||
"type": "string",
|
||||
"value": fallback_message,
|
||||
"qr": false,
|
||||
"copyable": false,
|
||||
"masked": false,
|
||||
"description":"Fallback Message When Properties could not be found"
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
serde_json::to_writer(stdout(), &stats)?;
|
||||
Ok(())
|
||||
}
|
||||
(subcmd, _) => {
|
||||
panic!("Unknown subcommand: {}", subcmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user