From effcd5ea57978d34c196a40a0c2a057ec237ce49 Mon Sep 17 00:00:00 2001 From: Lucy C <12953208+elvece@users.noreply.github.com> Date: Thu, 14 Oct 2021 14:51:47 -0600 Subject: [PATCH] Feature/dry auto configure (#584) * rename variables for clarity * return altered dep config * add utils system image, move compat into system-images * rename variables for clarity * sync integration and add debug instrumentation * debugging * add trace instrumentation * fix compilation for instrumentation * fix potential deadlocking behavior * fix import * fix dep check response return * hook back up to rpc, was overwritten in rebase * fix package command * get proper package config * testing dep config * version/volume for dep * vars * compat debugs * clean up * remove tar Co-authored-by: Chris Guida --- appmgr/src/dependencies.rs | 169 +++++++++++++++++++++-- appmgr/src/lib.rs | 1 + system-images/compat/src/config/mod.rs | 25 ++-- system-images/compat/src/config/rules.rs | 64 ++++----- system-images/compat/src/main.rs | 12 +- 5 files changed, 210 insertions(+), 61 deletions(-) diff --git a/appmgr/src/dependencies.rs b/appmgr/src/dependencies.rs index 807ed6cdf..df8a43e58 100644 --- a/appmgr/src/dependencies.rs +++ b/appmgr/src/dependencies.rs @@ -1,23 +1,34 @@ use std::collections::BTreeMap; +use std::time::Duration; +use crate::util::display_none; use color_eyre::eyre::eyre; use emver::VersionRange; use futures::future::BoxFuture; use futures::FutureExt; -use patch_db::{DbHandle, DiffPatch, HasModel, Map, MapModel}; +use patch_db::{DbHandle, HasModel, Map, MapModel, PatchDbHandle}; +use rpc_toolkit::command; use serde::{Deserialize, Serialize}; +use tracing::instrument; use crate::action::{ActionImplementation, NoOutput}; use crate::config::Config; use crate::context::RpcContext; use crate::db::model::CurrentDependencyInfo; +use crate::error::ResultExt; use crate::s9pk::manifest::{Manifest, PackageId}; use crate::status::health_check::{HealthCheckId, HealthCheckResult}; use crate::status::{MainStatus, Status}; +use crate::util::display_serializable; use crate::util::Version; use crate::volume::Volumes; use crate::Error; +#[command(subcommands(configure))] +pub fn dependency() -> Result<(), Error> { + Ok(()) +} + #[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] #[serde(tag = "type")] @@ -78,6 +89,7 @@ impl DependencyError { } } } + #[instrument(skip(ctx, db))] pub fn try_heal<'a, Db: DbHandle>( self, ctx: &'a RpcContext, @@ -166,7 +178,7 @@ impl DependencyError { Config::default() }; if let Some(cfg_req) = &info.config { - if let Err(e) = cfg_req + if let Err(error) = cfg_req .check( ctx, id, @@ -174,15 +186,9 @@ impl DependencyError { &dependent_manifest.volumes, &dependency_config, ) - .await + .await? { - if e.kind == crate::ErrorKind::ConfigRulesViolation { - return Ok(Some(DependencyError::ConfigUnsatisfied { - error: format!("{}", e), - })); - } else { - return Err(e); - } + return Ok(Some(DependencyError::ConfigUnsatisfied { error })); } } DependencyError::NotRunning @@ -440,6 +446,146 @@ impl DependencyConfig { } } +#[command( + subcommands(self(configure_impl(async)), configure_dry), + display(display_none) +)] +pub async fn configure( + #[arg(rename = "dependent-id")] dependent_id: PackageId, + #[arg(rename = "dependency-id")] dependency_id: PackageId, +) -> Result<(PackageId, PackageId), Error> { + Ok((dependent_id, dependency_id)) +} + +pub async fn configure_impl( + ctx: RpcContext, + (pkg_id, dep_id): (PackageId, PackageId), +) -> Result<(), Error> { + let mut db = ctx.db.handle(); + let new_config = configure_logic(ctx.clone(), &mut db, (pkg_id, dep_id.clone())).await?; + Ok(crate::config::configure( + &ctx, + &mut db, + &dep_id, + Some(new_config), + &Some(Duration::from_secs(3)), + false, + &mut BTreeMap::new(), + &mut BTreeMap::new(), + ) + .await?) +} + +#[command(rename = "dry", display(display_serializable))] +#[instrument(skip(ctx))] +pub async fn configure_dry( + #[context] ctx: RpcContext, + #[parent_data] (pkg_id, dependency_id): (PackageId, PackageId), +) -> Result { + let mut db = ctx.db.handle(); + configure_logic(ctx, &mut db, (pkg_id, dependency_id)).await +} + +pub async fn configure_logic( + ctx: RpcContext, + db: &mut PatchDbHandle, + (pkg_id, dependency_id): (PackageId, PackageId), +) -> Result { + let pkg_model = crate::db::DatabaseModel::new() + .package_data() + .idx_model(&pkg_id) + .and_then(|m| m.installed()) + .expect(db) + .await + .with_kind(crate::ErrorKind::NotFound)?; + let pkg_version = pkg_model.clone().manifest().version().get(db, true).await?; + let pkg_volumes = pkg_model.clone().manifest().volumes().get(db, true).await?; + let dependency_model = crate::db::DatabaseModel::new() + .package_data() + .idx_model(&dependency_id) + .and_then(|m| m.installed()) + .expect(db) + .await + .with_kind(crate::ErrorKind::NotFound)?; + let dependency_config_action = dependency_model + .clone() + .manifest() + .config() + .get(db, true) + .await? + .to_owned() + .ok_or_else(|| { + Error::new( + eyre!("{} has no config", dependency_id), + crate::ErrorKind::NotFound, + ) + })?; + let dependency_version = dependency_model + .clone() + .manifest() + .version() + .get(db, true) + .await?; + let dependency_volumes = dependency_model + .clone() + .manifest() + .volumes() + .get(db, true) + .await?; + let dependencies = pkg_model + .clone() + .manifest() + .dependencies() + .get(db, true) + .await?; + + let dependency = dependencies + .get(&dependency_id) + .ok_or_else(|| { + Error::new( + eyre!( + "dependency for {} not found in the manifest for {}", + dependency_id, + pkg_id + ), + crate::ErrorKind::NotFound, + ) + })? + .config + .as_ref() + .ok_or_else(|| { + Error::new( + eyre!( + "dependency config for {} not found on {}", + dependency_id, + pkg_id + ), + crate::ErrorKind::NotFound, + ) + })?; + let config: Config = dependency_config_action + .get( + &ctx, + &dependency_id, + &*dependency_version, + &*dependency_volumes, + ) + .await? + .config + .ok_or_else(|| { + Error::new( + eyre!("no config get action found for {}", dependency_id), + crate::ErrorKind::NotFound, + ) + })?; + Ok(dependency + .auto_configure + .sandboxed(&ctx, &pkg_id, &pkg_version, &pkg_volumes, Some(config)) + .await? + .map_err(|e| Error::new(eyre!("{}", e.1), crate::ErrorKind::AutoConfigure))?) +} + +#[instrument(skip(db, current_dependencies))] pub async fn update_current_dependents< 'a, Db: DbHandle, @@ -542,6 +688,7 @@ pub async fn break_all_dependents_transitive<'a, Db: DbHandle>( Ok(()) } +#[instrument(skip(db))] pub fn break_transitive<'a, Db: DbHandle>( db: &'a mut Db, id: &'a PackageId, @@ -609,6 +756,7 @@ pub fn break_transitive<'a, Db: DbHandle>( .boxed() } +#[instrument(skip(ctx, db))] pub async fn heal_all_dependents_transitive<'a, Db: DbHandle>( ctx: &'a RpcContext, db: &'a mut Db, @@ -629,6 +777,7 @@ pub async fn heal_all_dependents_transitive<'a, Db: DbHandle>( Ok(()) } +#[instrument(skip(ctx, db))] pub fn heal_transitive<'a, Db: DbHandle>( ctx: &'a RpcContext, db: &'a mut Db, diff --git a/appmgr/src/lib.rs b/appmgr/src/lib.rs index 5bb92e55c..dfcecef7a 100644 --- a/appmgr/src/lib.rs +++ b/appmgr/src/lib.rs @@ -91,6 +91,7 @@ pub fn server() -> Result<(), RpcError> { control::stop, logs::logs, properties::properties, + dependencies::dependency ))] pub fn package() -> Result<(), RpcError> { Ok(()) diff --git a/system-images/compat/src/config/mod.rs b/system-images/compat/src/config/mod.rs index 36c9e3378..05c7d7a00 100644 --- a/system-images/compat/src/config/mod.rs +++ b/system-images/compat/src/config/mod.rs @@ -4,7 +4,7 @@ use std::path::Path; use beau_collector::BeauCollector; use embassy::config::action::SetResult; -use embassy::config::{Config, spec}; +use embassy::config::{spec, Config}; use linear_map::LinearMap; pub mod rules; @@ -28,7 +28,10 @@ pub fn validate_configuration( match rule_check { Ok(_) => { // create temp config file - serde_yaml::to_writer(std::fs::File::create(config_path.with_extension("tmp"))?, &config)?; + serde_yaml::to_writer( + std::fs::File::create(config_path.with_extension("tmp"))?, + &config, + )?; std::fs::rename(config_path.with_extension("tmp"), config_path)?; // return set result Ok(SetResult { @@ -37,7 +40,7 @@ pub fn validate_configuration( signal: Some(nix::sys::signal::SIGTERM), }) } - Err(e) => Err(anyhow!("{}", e)) + Err(e) => Err(anyhow!("{}", e)), } } @@ -58,28 +61,28 @@ pub fn validate_dependency_configuration( .bcollect::>(); match rule_check { Ok(_) => Ok(()), - Err(e) => Err(anyhow!("{}", e)) + Err(e) => Err(anyhow!("{}", e)), } } pub fn apply_dependency_configuration( - name: &str, + package_id: &str, config: Config, - parent_name: &str, - mut parent_config: Config, + dependency_id: &str, + mut dep_config: Config, rules_path: &Path, ) -> Result { let rules: Vec = serde_yaml::from_reader(std::fs::File::open(rules_path)?)?; let mut cfgs = LinearMap::new(); - cfgs.insert(parent_name, Cow::Owned(parent_config.clone())); - cfgs.insert(name, Cow::Owned(config.clone())); + cfgs.insert(dependency_id, Cow::Owned(dep_config.clone())); + cfgs.insert(package_id, Cow::Owned(config.clone())); let rule_check = rules .into_iter() - .map(|r| r.apply(parent_name, &mut parent_config, &mut cfgs)) + .map(|r| r.apply(dependency_id, &mut dep_config, &mut cfgs)) .bcollect::>(); match rule_check { - Ok(_) => Ok(parent_config), + Ok(_) => Ok(dep_config), Err(e) => Err(anyhow!("{}", e)), } } diff --git a/system-images/compat/src/config/rules.rs b/system-images/compat/src/config/rules.rs index 0bd25e27e..d3df0b7cf 100644 --- a/system-images/compat/src/config/rules.rs +++ b/system-images/compat/src/config/rules.rs @@ -7,8 +7,8 @@ use pest::Parser; use rand::SeedableRng; use serde_json::Value; -use embassy::config::Config; use embassy::config::util::STATIC_NULL; +use embassy::config::Config; #[derive(Parser)] #[grammar = "config/rule_parser.pest"] @@ -114,7 +114,7 @@ impl ConfigRuleEntry { cfgs: &LinearMap<&str, Cow>, ) -> Result<(), anyhow::Error> { if !(self.rule.compiled)(cfg, cfgs) { - return Err(anyhow::anyhow!("{}", self.description)) + return Err(anyhow::anyhow!("{}", self.description)); } Ok(()) } @@ -514,9 +514,8 @@ fn compile_var_rec(mut ident: Pairs) -> Option { Rule::sub_ident_regular_expr => { let idx = compile_str_expr(idx.into_inner().next().unwrap().into_inner()); Box::new(move |v, dep_cfg| match v { - Value::Object(o) => idx(&Config::default(), dep_cfg).map(|idx| { - idx.and_then(|idx| o.get(&idx)).unwrap_or(&STATIC_NULL) - }), + Value::Object(o) => idx(&Config::default(), dep_cfg) + .map(|idx| idx.and_then(|idx| o.get(&idx)).unwrap_or(&STATIC_NULL)), _ => VarRes::Exactly(&STATIC_NULL), }) } @@ -605,16 +604,15 @@ fn compile_var_mut_rec(mut ident: Pairs) -> Result, fa predicate(&cfg, cfgs) }) .next(), - Value::Object(o) => { - o.iter_mut() - .map(|(_, item)| item) - .filter(|item| { - let mut cfg = Config::default(); - cfg.insert(item_var.clone(), (*item).clone()); - predicate(&cfg, cfgs) - }) - .next() - } + Value::Object(o) => o + .iter_mut() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next(), _ => None, }) } @@ -631,16 +629,15 @@ fn compile_var_mut_rec(mut ident: Pairs) -> Result, fa predicate(&cfg, cfgs) }) .next_back(), - Value::Object(o) => { - o.iter_mut() - .map(|(_, item)| item) - .filter(|item| { - let mut cfg = Config::default(); - cfg.insert(item_var.clone(), (*item).clone()); - predicate(&cfg, cfgs) - }) - .next_back() - } + Value::Object(o) => o + .iter_mut() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next_back(), _ => None, }) } @@ -780,7 +777,7 @@ fn compile_num_var(var: Pairs) -> CompiledExpr> { Value::Number(n) => n.as_f64().unwrap(), Value::String(s) => match s.parse() { Ok(n) => n, - Err(_) => panic!("string cannot be parsed as an f64") + Err(_) => panic!("string cannot be parsed as an f64"), }, Value::Bool(b) => { if b { @@ -1045,11 +1042,12 @@ fn compile_value_expr(mut pairs: Pairs) -> CompiledExpr> { } Rule::num_expr => { let expr = compile_num_expr(expr.into_inner()); - Box::new(move |cfg, cfgs| expr(cfg, cfgs).map(|n| match - serde_json::Number::from_f64(n) { + Box::new(move |cfg, cfgs| { + expr(cfg, cfgs).map(|n| match serde_json::Number::from_f64(n) { Some(a) => Value::Number(a), - None => panic!("cannot coerce f64 into numberc type") - })) + None => panic!("cannot coerce f64 into numberc type"), + }) + }) } Rule::bool_expr => { let expr = compile_bool_expr(expr.into_inner()); @@ -1244,10 +1242,8 @@ mod test { let mut dependent_cfg = Config::default(); let mut dependency_cfg = Config::default(); let mut cfgs = LinearMap::new(); - dependent_cfg - .insert("foo".to_owned(), Value::String("bar".to_owned())); - dependency_cfg - .insert("foo".to_owned(), Value::String("bar!".to_owned())); + dependent_cfg.insert("foo".to_owned(), Value::String("bar".to_owned())); + dependency_cfg.insert("foo".to_owned(), Value::String("bar!".to_owned())); cfgs.insert("my-dependent", Cow::Borrowed(&dependent_cfg)); cfgs.insert("my-dependency", Cow::Borrowed(&dependency_cfg)); assert!((compile("'foo = '[my-dependent].foo + \"!\"") diff --git a/system-images/compat/src/main.rs b/system-images/compat/src/main.rs index 34aff1fdc..979572c28 100644 --- a/system-images/compat/src/main.rs +++ b/system-images/compat/src/main.rs @@ -238,7 +238,7 @@ fn inner_main() -> Result<(), anyhow::Error> { } } ("auto-configure", Some(sub_m)) => { - let parent_config = serde_yaml::from_reader(stdin())?; + let dep_config = serde_yaml::from_reader(stdin())?; let config = serde_yaml::from_reader( File::open( Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/config.yaml"), @@ -246,13 +246,13 @@ fn inner_main() -> Result<(), anyhow::Error> { .unwrap(), )?; 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(); + 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( - name, + package_id, config, - parent_name, - parent_config, + dependency_id, + dep_config, rules_path, ) { Ok(a) => {