Feature/dependency autoconfig (#2588)

* dependency autoconfig

* FE portion

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
Matt Hill
2024-04-03 11:48:26 -06:00
committed by GitHub
parent cc1f14e5e9
commit 3b9298ed2b
23 changed files with 262 additions and 170 deletions

View File

@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::ActionId; use crate::{ActionId, PackageId};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProcedureName { pub enum ProcedureName {
@@ -14,8 +14,8 @@ pub enum ProcedureName {
ActionMetadata, ActionMetadata,
RunAction(ActionId), RunAction(ActionId),
GetAction(ActionId), GetAction(ActionId),
QueryDependency(ActionId), QueryDependency(PackageId),
UpdateDependency(ActionId), UpdateDependency(PackageId),
Init, Init,
Uninit, Uninit,
} }

View File

@@ -6,4 +6,5 @@ export type CurrentDependencyInfo = {
icon: DataUrl; icon: DataUrl;
registryUrl: string; registryUrl: string;
versionSpec: string; versionSpec: string;
configSatisfied: boolean;
} & ({ kind: "exists" } | { kind: "running"; healthChecks: string[] }); } & ({ kind: "exists" } | { kind: "running"; healthChecks: string[] });

View File

@@ -1,4 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { PackageId } from "./PackageId";
export type DependencyConfigErrors = { [key: PackageId]: string };

View File

@@ -1,9 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { DependencyConfigErrors } from "./DependencyConfigErrors";
import type { MainStatus } from "./MainStatus"; import type { MainStatus } from "./MainStatus";
export type Status = { export type Status = { configured: boolean; main: MainStatus };
configured: boolean;
main: MainStatus;
dependencyConfigErrors: DependencyConfigErrors;
};

View File

@@ -18,7 +18,6 @@ export { CurrentDependencies } from "./CurrentDependencies";
export { CurrentDependencyInfo } from "./CurrentDependencyInfo"; export { CurrentDependencyInfo } from "./CurrentDependencyInfo";
export { DataUrl } from "./DataUrl"; export { DataUrl } from "./DataUrl";
export { Dependencies } from "./Dependencies"; export { Dependencies } from "./Dependencies";
export { DependencyConfigErrors } from "./DependencyConfigErrors";
export { DependencyKind } from "./DependencyKind"; export { DependencyKind } from "./DependencyKind";
export { DependencyRequirement } from "./DependencyRequirement"; export { DependencyRequirement } from "./DependencyRequirement";
export { DepInfo } from "./DepInfo"; export { DepInfo } from "./DepInfo";

View File

@@ -209,33 +209,24 @@ impl RpcContext {
self.services.init(&self).await?; self.services.init(&self).await?;
tracing::info!("Initialized Package Managers"); tracing::info!("Initialized Package Managers");
let mut all_dependency_config_errs = BTreeMap::new(); let mut updated_current_dependents = BTreeMap::new();
let peek = self.db.peek().await; let peek = self.db.peek().await;
for (package_id, package) in peek.as_public().as_package_data().as_entries()?.into_iter() { for (package_id, package) in peek.as_public().as_package_data().as_entries()?.into_iter() {
let package = package.clone(); let package = package.clone();
let current_dependencies = package.as_current_dependencies().de()?; let mut current_dependencies = package.as_current_dependencies().de()?;
all_dependency_config_errs.insert( compute_dependency_config_errs(self, &package_id, &mut current_dependencies).await?;
package_id.clone(), updated_current_dependents.insert(package_id.clone(), current_dependencies);
compute_dependency_config_errs(
self,
&peek,
&package_id,
&current_dependencies,
&Default::default(),
)
.await?,
);
} }
self.db self.db
.mutate(|v| { .mutate(|v| {
for (package_id, errs) in all_dependency_config_errs { for (package_id, deps) in updated_current_dependents {
if let Some(config_errors) = v if let Some(model) = v
.as_public_mut() .as_public_mut()
.as_package_data_mut() .as_package_data_mut()
.as_idx_mut(&package_id) .as_idx_mut(&package_id)
.map(|i| i.as_status_mut().as_dependency_config_errors_mut()) .map(|i| i.as_current_dependencies_mut())
{ {
config_errors.ser(&errs)?; model.ser(&deps)?;
} }
} }
Ok(()) Ok(())

View File

@@ -380,6 +380,7 @@ pub struct CurrentDependencyInfo {
pub registry_url: Url, pub registry_url: Url,
#[ts(type = "string")] #[ts(type = "string")]
pub version_spec: VersionRange, pub version_spec: VersionRange,
pub config_satisfied: bool,
} }
#[derive(Clone, Debug, Deserialize, Serialize, TS)] #[derive(Clone, Debug, Deserialize, Serialize, TS)]

View File

@@ -12,7 +12,6 @@ use crate::config::{Config, ConfigSpec, ConfigureContext};
use crate::context::RpcContext; use crate::context::RpcContext;
use crate::db::model::package::CurrentDependencies; use crate::db::model::package::CurrentDependencies;
use crate::prelude::*; use crate::prelude::*;
use crate::status::DependencyConfigErrors;
use crate::Error; use crate::Error;
pub fn dependency() -> ParentHandler { pub fn dependency() -> ParentHandler {
@@ -180,17 +179,23 @@ pub async fn configure_logic(
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn compute_dependency_config_errs( pub async fn compute_dependency_config_errs(
ctx: &RpcContext, ctx: &RpcContext,
db: &Peeked,
id: &PackageId, id: &PackageId,
current_dependencies: &CurrentDependencies, current_dependencies: &mut CurrentDependencies,
dependency_config: &BTreeMap<PackageId, Config>, ) -> Result<(), Error> {
) -> Result<DependencyConfigErrors, Error> { let service_guard = ctx.services.get(id).await;
let mut dependency_config_errs = BTreeMap::new(); let service = service_guard.as_ref().or_not_found(id)?;
for (dependency, _dep_info) in current_dependencies.0.iter() { for (dep_id, dep_info) in current_dependencies.0.iter_mut() {
// check if config passes dependency check // check if config passes dependency check
if let Some(error) = todo!() { let Some(dependency) = &*ctx.services.get(dep_id).await else {
dependency_config_errs.insert(dependency.clone(), error); continue;
} };
let dep_config = dependency.get_config().await?.config;
dep_info.config_satisfied = service
.dependency_config(dep_id.clone(), dep_config)
.await?
.is_none();
} }
Ok(DependencyConfigErrors(dependency_config_errs)) Ok(())
} }

View File

@@ -0,0 +1,37 @@
use std::time::Duration;
use models::{ActionId, ProcedureName};
use crate::action::ActionResult;
use crate::prelude::*;
use crate::service::{Service, ServiceActor};
use crate::util::actor::{BackgroundJobs, Handler};
struct Action {
id: ActionId,
input: Value,
}
impl Handler<Action> for ServiceActor {
type Response = Result<ActionResult, Error>;
async fn handle(
&mut self,
Action { id, input }: Action,
_: &mut BackgroundJobs,
) -> Self::Response {
let container = &self.0.persistent_container;
container
.execute::<ActionResult>(
ProcedureName::RunAction(id),
input,
Some(Duration::from_secs(30)),
)
.await
.with_kind(ErrorKind::Action)
}
}
impl Service {
pub async fn action(&self, id: ActionId, input: Value) -> Result<ActionResult, Error> {
self.actor.send(Action { id, input }).await?
}
}

View File

@@ -1,19 +1,52 @@
use std::time::Duration;
use models::ProcedureName; use models::ProcedureName;
use crate::config::action::ConfigRes;
use crate::config::ConfigureContext; use crate::config::ConfigureContext;
use crate::prelude::*; use crate::prelude::*;
use crate::service::Service; use crate::service::{Service, ServiceActor};
use crate::util::actor::{BackgroundJobs, Handler};
use crate::util::serde::NoOutput;
impl Service { struct Configure(ConfigureContext);
pub async fn configure( impl Handler<Configure> for ServiceActor {
&self, type Response = Result<(), Error>;
ConfigureContext { timeout, config }: ConfigureContext, async fn handle(
) -> Result<(), Error> { &mut self,
let container = &self.seed.persistent_container; Configure(ConfigureContext { timeout, config }): Configure,
_: &mut BackgroundJobs,
) -> Self::Response {
let container = &self.0.persistent_container;
container container
.execute::<Value>(ProcedureName::SetConfig, to_value(&config)?, timeout) .execute::<NoOutput>(ProcedureName::SetConfig, to_value(&config)?, timeout)
.await .await
.with_kind(ErrorKind::Action)?; .with_kind(ErrorKind::ConfigRulesViolation)?;
Ok(()) Ok(())
} }
} }
struct GetConfig;
impl Handler<GetConfig> for ServiceActor {
type Response = Result<ConfigRes, Error>;
async fn handle(&mut self, _: GetConfig, _: &mut BackgroundJobs) -> Self::Response {
let container = &self.0.persistent_container;
container
.execute::<ConfigRes>(
ProcedureName::GetConfig,
Value::Null,
Some(Duration::from_secs(30)), // TODO timeout
)
.await
.with_kind(ErrorKind::ConfigGen)
}
}
impl Service {
pub async fn configure(&self, ctx: ConfigureContext) -> Result<(), Error> {
self.actor.send(Configure(ctx)).await?
}
pub async fn get_config(&self) -> Result<ConfigRes, Error> {
self.actor.send(GetConfig).await?
}
}

View File

@@ -5,7 +5,6 @@ use crate::service::{Service, ServiceActor};
use crate::util::actor::{BackgroundJobs, Handler}; use crate::util::actor::{BackgroundJobs, Handler};
struct Start; struct Start;
#[async_trait::async_trait]
impl Handler<Start> for ServiceActor { impl Handler<Start> for ServiceActor {
type Response = (); type Response = ();
async fn handle(&mut self, _: Start, _: &mut BackgroundJobs) -> Self::Response { async fn handle(&mut self, _: Start, _: &mut BackgroundJobs) -> Self::Response {
@@ -22,7 +21,6 @@ impl Service {
} }
struct Stop; struct Stop;
#[async_trait::async_trait]
impl Handler<Stop> for ServiceActor { impl Handler<Stop> for ServiceActor {
type Response = (); type Response = ();
async fn handle(&mut self, _: Stop, _: &mut BackgroundJobs) -> Self::Response { async fn handle(&mut self, _: Stop, _: &mut BackgroundJobs) -> Self::Response {

View File

@@ -0,0 +1,61 @@
use std::time::Duration;
use imbl_value::json;
use models::{PackageId, ProcedureName};
use crate::prelude::*;
use crate::service::{Service, ServiceActor};
use crate::util::actor::{BackgroundJobs, Handler};
use crate::Config;
struct DependencyConfig {
dependency_id: PackageId,
remote_config: Option<Config>,
}
impl Handler<DependencyConfig> for ServiceActor {
type Response = Result<Option<Config>, Error>;
async fn handle(
&mut self,
DependencyConfig {
dependency_id,
remote_config,
}: DependencyConfig,
_: &mut BackgroundJobs,
) -> Self::Response {
let container = &self.0.persistent_container;
container
.sanboxed::<Option<Config>>(
ProcedureName::UpdateDependency(dependency_id.clone()),
json!({
"queryResults": container
.execute::<Value>(
ProcedureName::QueryDependency(dependency_id),
Value::Null,
Some(Duration::from_secs(30)),
)
.await
.with_kind(ErrorKind::Dependency)?,
"remoteConfig": remote_config,
}),
Some(Duration::from_secs(30)),
)
.await
.with_kind(ErrorKind::Dependency)
.map(|res| res.filter(|c| !c.is_empty()))
}
}
impl Service {
pub async fn dependency_config(
&self,
dependency_id: PackageId,
remote_config: Option<Config>,
) -> Result<Option<Config>, Error> {
self.actor
.send(DependencyConfig {
dependency_id,
remote_config,
})
.await?
}
}

View File

@@ -5,7 +5,7 @@ use chrono::{DateTime, Utc};
use clap::Parser; use clap::Parser;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use imbl::OrdMap; use imbl::OrdMap;
use models::{ActionId, HealthCheckId, PackageId, ProcedureName}; use models::{HealthCheckId, PackageId, ProcedureName};
use persistent_container::PersistentContainer; use persistent_container::PersistentContainer;
use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, Handler, HandlerArgs}; use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, Handler, HandlerArgs};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -13,7 +13,6 @@ use start_stop::StartStop;
use tokio::sync::Notify; use tokio::sync::Notify;
use ts_rs::TS; use ts_rs::TS;
use crate::action::ActionResult;
use crate::config::action::ConfigRes; use crate::config::action::ConfigRes;
use crate::context::{CliContext, RpcContext}; use crate::context::{CliContext, RpcContext};
use crate::core::rpc_continuations::RequestGuid; use crate::core::rpc_continuations::RequestGuid;
@@ -33,10 +32,13 @@ use crate::util::actor::{Actor, BackgroundJobs, SimpleActor};
use crate::util::serde::Pem; use crate::util::serde::Pem;
use crate::volume::data_dir; use crate::volume::data_dir;
mod action;
pub mod cli; pub mod cli;
mod config; mod config;
mod control; mod control;
mod dependencies;
pub mod persistent_container; pub mod persistent_container;
mod properties;
mod rpc; mod rpc;
pub mod service_effect_handler; pub mod service_effect_handler;
pub mod service_map; pub mod service_map;
@@ -302,43 +304,6 @@ impl Service {
Err(Error::new(eyre!("not yet implemented"), ErrorKind::Unknown)) Err(Error::new(eyre!("not yet implemented"), ErrorKind::Unknown))
} }
pub async fn get_config(&self) -> Result<ConfigRes, Error> {
let container = &self.seed.persistent_container;
container
.execute::<ConfigRes>(
ProcedureName::GetConfig,
Value::Null,
Some(Duration::from_secs(30)), // TODO timeout
)
.await
.with_kind(ErrorKind::ConfigGen)
}
// TODO DO the Action Get
pub async fn action(&self, id: ActionId, input: Value) -> Result<ActionResult, Error> {
let container = &self.seed.persistent_container;
container
.execute::<ActionResult>(
ProcedureName::RunAction(id),
input,
Some(Duration::from_secs(30)),
)
.await
.with_kind(ErrorKind::Action)
}
pub async fn properties(&self) -> Result<Value, Error> {
let container = &self.seed.persistent_container;
container
.execute::<Value>(
ProcedureName::Properties,
Value::Null,
Some(Duration::from_secs(30)),
)
.await
.with_kind(ErrorKind::Unknown)
}
pub async fn shutdown(self) -> Result<(), Error> { pub async fn shutdown(self) -> Result<(), Error> {
self.actor self.actor
.shutdown(crate::util::actor::PendingMessageStrategy::FinishAll { timeout: None }) // TODO timeout .shutdown(crate::util::actor::PendingMessageStrategy::FinishAll { timeout: None }) // TODO timeout

View File

@@ -0,0 +1,21 @@
use std::time::Duration;
use models::ProcedureName;
use crate::prelude::*;
use crate::service::Service;
impl Service {
// TODO: leave here or switch to Actor Message?
pub async fn properties(&self) -> Result<Value, Error> {
let container = &self.seed.persistent_container;
container
.execute::<Value>(
ProcedureName::Properties,
Value::Null,
Some(Duration::from_secs(30)),
)
.await
.with_kind(ErrorKind::Unknown)
}
}

View File

@@ -1,4 +1,4 @@
use std::collections::BTreeSet; use std::collections::{BTreeMap, BTreeSet};
use std::ffi::OsString; use std::ffi::OsString;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
@@ -1120,51 +1120,64 @@ async fn set_dependencies(
) -> Result<(), Error> { ) -> Result<(), Error> {
let ctx = ctx.deref()?; let ctx = ctx.deref()?;
let id = &ctx.id; let id = &ctx.id;
let service_guard = ctx.ctx.services.get(id).await;
let service = service_guard.as_ref().or_not_found(id)?;
let mut deps = BTreeMap::new();
for dependency in dependencies {
let (dep_id, kind, registry_url, version_spec) = match dependency {
DependencyRequirement::Exists {
id,
registry_url,
version_spec,
} => (
id,
CurrentDependencyKind::Exists,
registry_url,
version_spec,
),
DependencyRequirement::Running {
id,
health_checks,
registry_url,
version_spec,
} => (
id,
CurrentDependencyKind::Running { health_checks },
registry_url,
version_spec,
),
};
let icon = todo!();
let title = todo!();
let config_satisfied = if let Some(dep_service) = &*ctx.ctx.services.get(&dep_id).await {
service
.dependency_config(dep_id, dep_service.get_config().await?.config)
.await?
.is_none()
} else {
true
};
deps.insert(
dep_id,
CurrentDependencyInfo {
kind: CurrentDependencyKind::Exists,
registry_url,
version_spec,
icon,
title,
config_satisfied,
},
);
}
ctx.ctx ctx.ctx
.db .db
.mutate(|db| { .mutate(|db| {
let dependencies = CurrentDependencies(
dependencies
.into_iter()
.map(|dependency| match dependency {
DependencyRequirement::Exists {
id,
registry_url,
version_spec,
} => (
id,
CurrentDependencyInfo {
kind: CurrentDependencyKind::Exists,
registry_url,
version_spec,
icon: todo!(),
title: todo!(),
},
),
DependencyRequirement::Running {
id,
health_checks,
registry_url,
version_spec,
} => (
id,
CurrentDependencyInfo {
kind: CurrentDependencyKind::Running { health_checks },
registry_url,
version_spec,
icon: todo!(),
title: todo!(),
},
),
})
.collect(),
);
db.as_public_mut() db.as_public_mut()
.as_package_data_mut() .as_package_data_mut()
.as_idx_mut(id) .as_idx_mut(id)
.or_not_found(id)? .or_not_found(id)?
.as_current_dependencies_mut() .as_current_dependencies_mut()
.ser(&dependencies) .ser(&CurrentDependencies(deps))
}) })
.await .await
} }

View File

@@ -165,7 +165,6 @@ impl ServiceMap {
status: Status { status: Status {
configured: false, configured: false,
main: MainStatus::Stopped, main: MainStatus::Stopped,
dependency_config_errors: Default::default(),
}, },
marketplace_url: None, marketplace_url: None,
developer_key: Pem::new(developer_key), developer_key: Pem::new(developer_key),

View File

@@ -2,16 +2,14 @@ use std::sync::Arc;
use futures::FutureExt; use futures::FutureExt;
use super::TempDesiredState;
use crate::prelude::*; use crate::prelude::*;
use crate::service::transition::{TransitionKind, TransitionState}; use crate::service::transition::{TransitionKind, TransitionState};
use crate::service::{Service, ServiceActor}; use crate::service::{Service, ServiceActor};
use crate::util::actor::{BackgroundJobs, Handler}; use crate::util::actor::{BackgroundJobs, Handler};
use crate::util::future::RemoteCancellable; use crate::util::future::RemoteCancellable;
use super::TempDesiredState;
struct Restart; struct Restart;
#[async_trait::async_trait]
impl Handler<Restart> for ServiceActor { impl Handler<Restart> for ServiceActor {
type Response = (); type Response = ();
async fn handle(&mut self, _: Restart, jobs: &mut BackgroundJobs) -> Self::Response { async fn handle(&mut self, _: Restart, jobs: &mut BackgroundJobs) -> Self::Response {

View File

@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use imbl::OrdMap; use imbl::OrdMap;
use models::PackageId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ts_rs::TS; use ts_rs::TS;
@@ -18,23 +17,6 @@ pub mod health_check;
pub struct Status { pub struct Status {
pub configured: bool, pub configured: bool,
pub main: MainStatus, pub main: MainStatus,
#[serde(default)]
pub dependency_config_errors: DependencyConfigErrors,
}
#[derive(Clone, Debug, Deserialize, Serialize, HasModel, Default, TS)]
#[model = "Model<Self>"]
#[ts(export)]
pub struct DependencyConfigErrors(pub BTreeMap<PackageId, String>);
impl Map for DependencyConfigErrors {
type Key = PackageId;
type Value = String;
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
Ok(key)
}
fn key_string(key: &Self::Key) -> Result<imbl_value::InternedString, Error> {
Ok(key.clone().into())
}
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, TS)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, TS)]

View File

@@ -16,10 +16,13 @@ pub trait Actor: Send + 'static {
fn init(&mut self, jobs: &mut BackgroundJobs) {} fn init(&mut self, jobs: &mut BackgroundJobs) {}
} }
#[async_trait::async_trait]
pub trait Handler<M>: Actor { pub trait Handler<M>: Actor {
type Response: Any + Send; type Response: Any + Send;
async fn handle(&mut self, msg: M, jobs: &mut BackgroundJobs) -> Self::Response; fn handle(
&mut self,
msg: M,
jobs: &mut BackgroundJobs,
) -> impl Future<Output = Self::Response> + Send;
} }
#[async_trait::async_trait] #[async_trait::async_trait]

View File

@@ -110,7 +110,7 @@ export type VersionString = string
*/ */
export type DependencyConfig = { export type DependencyConfig = {
/** During autoconfigure, we have access to effects and local data. We are going to figure out all the data that we need and send it to update. For the sdk it is the desired delta */ /** During autoconfigure, we have access to effects and local data. We are going to figure out all the data that we need and send it to update. For the sdk it is the desired delta */
query(options: { effects: Effects; localConfig: unknown }): Promise<unknown> query(options: { effects: Effects }): Promise<unknown>
/** This is the second part. Given the query results off the previous function, we will determine what to change the remote config to. In our sdk normall we are going to use the previous as a deep merge. */ /** This is the second part. Given the query results off the previous function, we will determine what to change the remote config to. In our sdk normall we are going to use the previous as a deep merge. */
update(options: { update(options: {
queryResults: unknown queryResults: unknown

View File

@@ -1410,7 +1410,6 @@ export module Mock {
started: new Date().toISOString(), started: new Date().toISOString(),
health: {}, health: {},
}, },
dependencyConfigErrors: {},
}, },
actions: {}, // @TODO need mocks actions: {}, // @TODO need mocks
serviceInterfaces: { serviceInterfaces: {
@@ -1650,7 +1649,6 @@ export module Mock {
main: { main: {
status: 'stopped', status: 'stopped',
}, },
dependencyConfigErrors: {},
}, },
actions: {}, actions: {},
serviceInterfaces: { serviceInterfaces: {
@@ -1770,6 +1768,7 @@ export module Mock {
registryUrl: '', registryUrl: '',
versionSpec: '>=26.0.0', versionSpec: '>=26.0.0',
healthChecks: [], healthChecks: [],
configSatisfied: true,
}, },
}, },
hosts: {}, hosts: {},
@@ -1790,9 +1789,6 @@ export module Mock {
main: { main: {
status: 'stopped', status: 'stopped',
}, },
dependencyConfigErrors: {
'btc-rpc-proxy': 'Username not found',
},
}, },
actions: {}, actions: {},
serviceInterfaces: { serviceInterfaces: {
@@ -2015,6 +2011,7 @@ export module Mock {
registryUrl: 'https://registry.start9.com', registryUrl: 'https://registry.start9.com',
versionSpec: '>=26.0.0', versionSpec: '>=26.0.0',
healthChecks: [], healthChecks: [],
configSatisfied: true,
}, },
'btc-rpc-proxy': { 'btc-rpc-proxy': {
title: Mock.MockManifestBitcoinProxy.title, title: Mock.MockManifestBitcoinProxy.title,
@@ -2022,6 +2019,7 @@ export module Mock {
kind: 'exists', kind: 'exists',
registryUrl: 'https://community-registry.start9.com', registryUrl: 'https://community-registry.start9.com',
versionSpec: '>2.0.0', // @TODO versionSpec: '>2.0.0', // @TODO
configSatisfied: false,
}, },
}, },
hosts: {}, hosts: {},

View File

@@ -125,7 +125,6 @@ export const mockPatchData: DataModel = {
}, },
}, },
}, },
dependencyConfigErrors: {},
}, },
actions: {}, // @TODO actions: {}, // @TODO
serviceInterfaces: { serviceInterfaces: {
@@ -367,9 +366,6 @@ export const mockPatchData: DataModel = {
main: { main: {
status: 'stopped', status: 'stopped',
}, },
dependencyConfigErrors: {
'btc-rpc-proxy': 'This is a config unsatisfied error',
},
}, },
actions: {}, actions: {},
serviceInterfaces: { serviceInterfaces: {
@@ -590,6 +586,7 @@ export const mockPatchData: DataModel = {
registryUrl: 'https://registry.start9.com', registryUrl: 'https://registry.start9.com',
versionSpec: '>=26.0.0', versionSpec: '>=26.0.0',
healthChecks: [], healthChecks: [],
configSatisfied: true,
}, },
'btc-rpc-proxy': { 'btc-rpc-proxy': {
title: 'Bitcoin Proxy', title: 'Bitcoin Proxy',
@@ -598,6 +595,7 @@ export const mockPatchData: DataModel = {
registryUrl: 'https://community-registry.start9.com', registryUrl: 'https://community-registry.start9.com',
versionSpec: '>2.0.0', versionSpec: '>2.0.0',
healthChecks: [], healthChecks: [],
configSatisfied: false,
}, },
}, },
hosts: {}, hosts: {},

View File

@@ -83,20 +83,20 @@ export class DepErrorService {
} }
} }
const versionSpec = pkg.currentDependencies[depId].versionSpec const currentDep = pkg.currentDependencies[depId]
const depManifest = dep.stateInfo.manifest const depManifest = dep.stateInfo.manifest
// incorrect version // incorrect version
if (!this.emver.satisfies(depManifest.version, versionSpec)) { if (!this.emver.satisfies(depManifest.version, currentDep.versionSpec)) {
return { return {
type: 'incorrectVersion', type: 'incorrectVersion',
expected: versionSpec, expected: currentDep.versionSpec,
received: depManifest.version, received: depManifest.version,
} }
} }
// invalid config // invalid config
if (Object.values(pkg.status.dependencyConfigErrors).some(err => !!err)) { if (!currentDep.configSatisfied) {
return { return {
type: 'configUnsatisfied', type: 'configUnsatisfied',
} }
@@ -111,8 +111,6 @@ export class DepErrorService {
} }
} }
const currentDep = pkg.currentDependencies[depId]
// health check failure // health check failure
if (depStatus === 'running' && currentDep.kind === 'running') { if (depStatus === 'running' && currentDep.kind === 'running') {
for (let id of currentDep.healthChecks) { for (let id of currentDep.healthChecks) {