mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Feature/dependency autoconfig (#2588)
* dependency autoconfig * FE portion --------- Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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[] });
|
||||||
|
|||||||
@@ -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 };
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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,
|
|
||||||
¤t_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(())
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
37
core/startos/src/service/action.rs
Normal file
37
core/startos/src/service/action.rs
Normal 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?
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
61
core/startos/src/service/dependencies.rs
Normal file
61
core/startos/src/service/dependencies.rs
Normal 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?
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
21
core/startos/src/service/properties.rs
Normal file
21
core/startos/src/service/properties.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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: {},
|
||||||
|
|||||||
@@ -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: {},
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user