diff --git a/api/admin/admin.proto b/api/admin/admin.proto index c1057b0..f692119 100644 --- a/api/admin/admin.proto +++ b/api/admin/admin.proto @@ -50,6 +50,10 @@ message QueryListItem { string Description = 2; string VmStatus = 3; string TrustLevel = 4; + string VmType = 5; + string ServiceType = 6; + optional string VmName = 7; // None for host running services + optional string AgentName = 8; // None for agents } message QueryListResponse { diff --git a/client/src/endpoint.rs b/client/src/endpoint.rs index ba8b32a..d6cebed 100644 --- a/client/src/endpoint.rs +++ b/client/src/endpoint.rs @@ -95,7 +95,7 @@ impl EndpointConfig { let url = transport_config_to_url(&self.transport.address, self.tls.is_some()); info!("Connecting to {url}, TLS name {:?}", &self.tls); let mut endpoint = Endpoint::try_from(url.clone())? - .timeout(Duration::from_secs(5)) + .connect_timeout(Duration::from_millis(300)) .concurrency_limit(30); if let Some(tls) = &self.tls { endpoint = endpoint.tls_config(tls.client_config()?)?; diff --git a/common/src/query.rs b/common/src/query.rs index f107cea..d437d0b 100644 --- a/common/src/query.rs +++ b/common/src/query.rs @@ -1,4 +1,5 @@ // Types related to QueryList and Watch API +use super::types::{ServiceType, VmType}; use crate::pb; use pb::admin::watch_item::Status; @@ -33,6 +34,10 @@ pub struct QueryResult { pub description: String, //App name, some details pub status: VMStatus, pub trust_level: TrustLevel, + pub vm_type: VmType, + pub service_type: ServiceType, + pub vm_name: Option, + pub agent_name: Option, } impl QueryResult { @@ -51,6 +56,12 @@ impl TryFrom for QueryResult { .with_context(|| format!("While parsing vm_status {}", item.vm_status))?, trust_level: TrustLevel::from_str(item.trust_level.as_str()) .with_context(|| format!("While parsing trust_level {}", item.trust_level))?, + vm_type: VmType::from_str(item.vm_type.as_str()) + .with_context(|| format!("While parsing vm_type {}", item.vm_type))?, + service_type: ServiceType::from_str(item.service_type.as_str()) + .with_context(|| format!("While parsing service_type {}", item.service_type))?, + agent_name: item.agent_name, + vm_name: item.vm_name, }) } } @@ -62,6 +73,10 @@ impl From for pb::QueryListItem { description: val.description, vm_status: val.status.to_string(), trust_level: val.trust_level.to_string(), + vm_type: val.vm_type.to_string(), + service_type: val.service_type.to_string(), + agent_name: val.agent_name, + vm_name: val.vm_name, } } } diff --git a/common/src/types.rs b/common/src/types.rs index 0d4061a..dec80de 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -5,6 +5,8 @@ use crate::pb; use std::convert::{Into, TryFrom}; use anyhow::bail; +use serde::Serialize; +use strum::{Display, EnumString}; use tokio_vsock::VsockAddr; #[derive(Debug, Copy, Clone, PartialEq)] @@ -13,7 +15,7 @@ pub struct UnitType { pub service: ServiceType, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Serialize, EnumString, Display)] pub enum VmType { Host, AdmVM, @@ -21,7 +23,7 @@ pub enum VmType { AppVM, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Serialize, EnumString, Display)] pub enum ServiceType { Mgr, Svc, diff --git a/internal/pkgs/servicemanager/transport.go b/internal/pkgs/servicemanager/transport.go index 59ea4c7..0ebed88 100644 --- a/internal/pkgs/servicemanager/transport.go +++ b/internal/pkgs/servicemanager/transport.go @@ -59,7 +59,7 @@ func (s *SystemdControlServer) GetUnitStatus(ctx context.Context, req *systemd_a unitStatus, err := s.Controller.FindUnit(req.UnitName) if err != nil { log.Infof("[GetUnitStatus] Error finding unit: %v", err) - return nil, grpc_status.Error(grpc_codes.NotFound, "error fetching unit status") + return nil, grpc_status.Error(grpc_codes.NotFound, fmt.Sprintf("error fetching unit status: %s", req.UnitName)) } if len(unitStatus) != 1 { errStr := fmt.Sprintf("error, got %d units named %s", len(unitStatus), req.UnitName) diff --git a/src/admin/entry.rs b/src/admin/entry.rs index 30f2b87..c066dc9 100644 --- a/src/admin/entry.rs +++ b/src/admin/entry.rs @@ -10,10 +10,13 @@ use givc_common::types::*; #[derive(Debug, Clone, PartialEq)] pub enum Placement { // Service is a `givc-agent` and could be directly connected - Endpoint(EndpointEntry), + Endpoint { endpoint: EndpointEntry, vm: String }, // Service or application managed by specified agent - Managed(String), + Managed { vm: String, by: String }, + + // Running on host + Host, } #[derive(Debug, Clone, PartialEq)] @@ -26,14 +29,31 @@ pub struct RegistryEntry { } impl RegistryEntry { + pub fn agent_name(&self) -> Option<&str> { + match &self.placement { + Placement::Endpoint { .. } => Some(&self.name), + Placement::Managed { by, .. } => Some(by), + Placement::Host => None, + } + } + + pub fn vm_name(&self) -> Option<&str> { + match &self.placement { + Placement::Endpoint { vm, .. } => Some(vm), + Placement::Managed { vm, .. } => Some(vm), + Placement::Host => None, + } + } + pub fn agent(&self) -> anyhow::Result<&EndpointEntry> { match &self.placement { - Placement::Endpoint(endpoint) => Ok(endpoint), - Placement::Managed(by) => Err(anyhow!( + Placement::Endpoint { endpoint, .. } => Ok(endpoint), + Placement::Managed { by, .. } => Err(anyhow!( "Agent endpoint {} is managed by {}!", self.name, by )), + Placement::Host => Err(anyhow!("Its a host!")), } } } @@ -57,13 +77,16 @@ impl RegistryEntry { path: "bogus".to_string(), freezer_state: "bogus".to_string(), }, - placement: Placement::Endpoint(EndpointEntry { - address: EndpointAddress::Tcp { - addr: "127.0.0.1".to_string(), - port: 42, + placement: Placement::Endpoint { + endpoint: EndpointEntry { + address: EndpointAddress::Tcp { + addr: "127.0.0.1".to_string(), + port: 42, + }, + tls_name: "bogus".to_string(), }, - tls_name: "bogus".to_string(), - }), + vm: "bogus".into(), + }, watch: true, } } @@ -89,7 +112,10 @@ impl TryFrom for RegistryEntry { status, watch, r#type: ty, - placement: Placement::Endpoint(endpoint), + placement: Placement::Endpoint { + endpoint, + vm: "bogus".into(), + }, }) } } @@ -103,11 +129,17 @@ impl From for QueryResult { } else { VMStatus::PoweredOff }; + let vm_name = val.vm_name().map(|s| s.to_owned()); + let agent_name = val.agent_name().map(|s| s.to_owned()); QueryResult { name: val.name, description: val.status.description, status, trust_level: TrustLevel::default(), + vm_type: val.r#type.vm, + service_type: val.r#type.service, + vm_name, + agent_name, } } } diff --git a/src/admin/registry.rs b/src/admin/registry.rs index ca640ff..e2bc904 100644 --- a/src/admin/registry.rs +++ b/src/admin/registry.rs @@ -1,7 +1,7 @@ use std::collections::hash_map::HashMap; use std::sync::{Arc, Mutex}; -use super::entry::{Placement, RegistryEntry}; +use super::entry::RegistryEntry; use crate::types::*; use anyhow::{anyhow, bail}; use givc_common::query::{Event, QueryResult}; @@ -50,9 +50,12 @@ impl Registry { Some(entry) => { let cascade: Vec = state .values() - .filter_map(|re| match &re.placement { - Placement::Managed(within) if within == name => Some(re.name.clone()), - _ => None, + .filter_map(|re| { + if re.agent_name() == Some(name) || re.vm_name() == Some(name) { + Some(re.name.clone()) + } else { + None + } }) .collect(); for each in cascade { @@ -173,6 +176,7 @@ impl Registry { #[cfg(test)] mod tests { use super::*; + use crate::admin::entry::Placement; use crate::utils::naming::parse_application_name; #[test] @@ -204,11 +208,17 @@ mod tests { let r = Registry::new(); let foo = RegistryEntry::dummy("foo".to_string()); let bar = RegistryEntry { - placement: Placement::Managed("foo".into()), + placement: Placement::Managed { + by: "foo".into(), + vm: "foo-vm".into(), + }, ..RegistryEntry::dummy("bar".to_string()) }; let baz = RegistryEntry { - placement: Placement::Managed("foo".into()), + placement: Placement::Managed { + by: "foo".into(), + vm: "foo-vm".into(), + }, ..RegistryEntry::dummy("baz".to_string()) }; @@ -219,12 +229,12 @@ mod tests { assert!(r.contains("bar")); assert!(r.contains("baz")); - r.deregister("baz"); + r.deregister("baz")?; assert!(r.contains("foo")); assert!(r.contains("bar")); assert!(!r.contains("baz")); - r.deregister("foo"); + r.deregister("foo")?; assert!(!r.contains("foo")); assert!(!r.contains("bar")); diff --git a/src/admin/server.rs b/src/admin/server.rs index 838de24..1c02fda 100644 --- a/src/admin/server.rs +++ b/src/admin/server.rs @@ -109,14 +109,15 @@ impl AdminServiceImpl { pub fn endpoint(&self, entry: &RegistryEntry) -> anyhow::Result { let transport = match &entry.placement { - Placement::Managed(parent) => { + Placement::Managed { by: parent, .. } => { let parent = self.registry.by_name(parent)?; parent .agent() .with_context(|| "When get_remote_status()")? .to_owned() // Fail, if parent also `Managed` } - Placement::Endpoint(endpoint) => endpoint.clone(), // FIXME: avoid clone! + Placement::Endpoint { endpoint, .. } => endpoint.clone(), // FIXME: avoid clone! + Placement::Host => bail!("endpoint() called for Host"), // impossible, FIXME: should never happens atm }; let tls_name = transport.tls_name.clone(); Ok(EndpointConfig { @@ -223,6 +224,10 @@ impl AdminServiceImpl { } pub async fn handle_error(&self, entry: RegistryEntry) -> anyhow::Result<()> { + info!( + "Handling error for {} vm type {} service type {}", + entry.name, entry.r#type.vm, entry.r#type.service + ); match (entry.r#type.vm, entry.r#type.service) { (VmType::AppVM, ServiceType::App) => { if entry.status.is_exitted() { @@ -232,10 +237,11 @@ impl AdminServiceImpl { Ok(()) } (VmType::AppVM, ServiceType::Mgr) | (VmType::SysVM, ServiceType::Mgr) => { - let name = parse_service_name(&entry.name)?; - self.start_vm(name) - .await - .with_context(|| format!("handing error, by restart VM {}", entry.name))?; + if let Placement::Managed { vm: vm_name, .. } = entry.placement { + self.start_vm(&vm_name) + .await + .with_context(|| format!("handing error, by restart VM {}", entry.name))?; + } Ok(()) // FIXME: should use `?` from line above, why it didn't work? } (x, y) => { @@ -305,13 +311,12 @@ impl AdminServiceImpl { let name = req.app_name; let vm = req.vm_name.as_deref(); let vm_name = format_vm_name(&name, vm); - let systemd_agent = format_service_name(&name, vm); + let systemd_agent_name = format_service_name(&name, vm); - info!("Starting app {name} on {vm_name}"); - info!("Agent: {systemd_agent}"); + info!("Starting app {name} on {vm_name} via {systemd_agent_name}"); // Entry unused in "go" code - match self.registry.by_name(&systemd_agent) { + match self.registry.by_name(&systemd_agent_name) { std::result::Result::Ok(e) => e, Err(_) => { info!("Starting up VM {vm_name}"); @@ -319,13 +324,13 @@ impl AdminServiceImpl { .await .with_context(|| format!("Starting vm for {name}"))?; self.registry - .by_name(&systemd_agent) + .by_name(&systemd_agent_name) .context("after starting VM")? } }; - let endpoint = self.agent_endpoint(&systemd_agent)?; - let client = SystemDClient::new(endpoint.clone()); - let app_name = self.registry.create_unique_entry_name(&name.to_string()); + let endpoint = self.agent_endpoint(&systemd_agent_name)?; + let client = SystemDClient::new(endpoint); + let app_name = self.registry.create_unique_entry_name(&name); client.start_application(app_name.clone(), req.args).await?; let status = client.get_remote_status(app_name.clone()).await?; if status.active_state != "active" { @@ -340,7 +345,10 @@ impl AdminServiceImpl { vm: VmType::AppVM, service: ServiceType::App, }, - placement: Placement::Managed(systemd_agent), + placement: Placement::Managed { + by: systemd_agent_name, + vm: vm_name, + }, }; self.registry.register(app_entry); Ok(())