diff --git a/src/events/types.rs b/src/events/types.rs index 1ae923bc..1633986a 100644 --- a/src/events/types.rs +++ b/src/events/types.rs @@ -11,6 +11,7 @@ use std::{ use cloudevents::{AttributesReader, Data, Event as CloudEvent, EventBuilder, EventBuilderV10}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use wasmcloud_control_interface::{ActorDescription, LabelsMap, ProviderDescriptions}; use crate::model::Manifest; @@ -553,27 +554,51 @@ event_impl!( id ); +// TODO(#TODO): Remove once wasmCloud v0.82 is released +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[serde(untagged)] +pub enum BackwardsCompatActors { + V81(HashMap), + V82(Vec), +} + +// TODO(#TODO): Remove once wasmCloud v0.82 is released +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[serde(untagged)] +pub enum BackwardsCompatProviders { + V81(Vec), + V82(ProviderDescriptions), +} + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct HostHeartbeat { - pub actors: HashMap, - pub friendly_name: String, - pub labels: HashMap, + /// Actors running on this host. + pub actors: BackwardsCompatActors, + /// Providers running on this host + pub providers: BackwardsCompatProviders, + /// The host's unique ID + #[serde(default, alias = "id")] + pub host_id: String, + /// The host's cluster issuer public key #[serde(default)] - pub annotations: BTreeMap, - pub providers: Vec, - pub uptime_human: String, - pub uptime_seconds: usize, + pub issuer: String, + /// The host's human-readable friendly name + pub friendly_name: String, + /// The host's labels + pub labels: LabelsMap, + /// The host version pub version: semver::Version, - // TODO: Parse as nkey? - #[serde(default)] - pub id: String, + /// The host uptime in human-readable form + pub uptime_human: String, + /// The host uptime in seconds + pub uptime_seconds: u64, } event_impl!( HostHeartbeat, "com.wasmcloud.lattice.host_heartbeat", source, - id + host_id ); #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] diff --git a/src/scaler/daemonscaler/mod.rs b/src/scaler/daemonscaler/mod.rs index 105e3786..d454585e 100644 --- a/src/scaler/daemonscaler/mod.rs +++ b/src/scaler/daemonscaler/mod.rs @@ -357,7 +357,6 @@ mod test { actors: HashMap::new(), friendly_name: "hey".to_string(), labels: HashMap::new(), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -633,7 +632,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-brooks-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -658,7 +656,6 @@ mod test { ("region".to_string(), "us-midwest-4".to_string()), ("label".to_string(), "value".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -679,7 +676,6 @@ mod test { ("cloud".to_string(), "purgatory".to_string()), ("location".to_string(), "edge".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -866,7 +862,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-brooks-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -888,7 +883,6 @@ mod test { ("region".to_string(), "us-brooks-1".to_string()), ("label".to_string(), "value".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -901,10 +895,10 @@ mod test { // Don't care about these events assert!(blobby_daemonscaler .handle_event(&Event::ProviderStarted(ProviderStarted { - annotations: BTreeMap::new(), claims: ProviderClaims::default(), contract_id: "".to_string(), image_ref: "".to_string(), + annotations: BTreeMap::default(), instance_id: "".to_string(), link_name: "".to_string(), public_key: "".to_string(), @@ -939,18 +933,18 @@ mod test { // Let a new host come online, should match the spread let modifying_event = HostHeartbeat { - actors: HashMap::new(), + actors: crate::events::BackwardsCompatActors::V82(vec![]), friendly_name: "hey".to_string(), + issuer: "".to_string(), labels: HashMap::from_iter([ ("cloud".to_string(), "purgatory".to_string()), ("location".to_string(), "edge".to_string()), ("region".to_string(), "us-brooks-1".to_string()), ]), - annotations: BTreeMap::new(), - providers: vec![], + providers: crate::events::BackwardsCompatProviders::V82(vec![]), uptime_seconds: 123, version: semver::Version::new(0, 63, 1), - id: host_id_three.to_string(), + host_id: host_id_three.to_string(), uptime_human: "time_is_a_human_construct".to_string(), }; diff --git a/src/scaler/daemonscaler/provider.rs b/src/scaler/daemonscaler/provider.rs index 1b0d57e9..7eaaf2c0 100644 --- a/src/scaler/daemonscaler/provider.rs +++ b/src/scaler/daemonscaler/provider.rs @@ -393,7 +393,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-noneofyourbusiness-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -415,7 +414,6 @@ mod test { ("cloud".to_string(), "real".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, diff --git a/src/scaler/spreadscaler/link.rs b/src/scaler/spreadscaler/link.rs index 0e105b07..76e7ee05 100644 --- a/src/scaler/spreadscaler/link.rs +++ b/src/scaler/spreadscaler/link.rs @@ -390,8 +390,11 @@ mod test { linkscaler_values_hash = compute_linkscaler_values_hash(&[("foo".to_string(), "nope".to_string())].into()) ); - assert_ne!(scaler.id(), id, "LinkScaler ID should be different when scalers have different configured values"); - + assert_ne!( + scaler.id(), + id, + "LinkScaler ID should be different when scalers have different configured values" + ); let scaler = LinkScaler::new( create_store(&lattice_id, &actor_ref, &provider_ref).await, @@ -412,7 +415,6 @@ mod test { provider_link_name = "default", actor_reference = actor_ref, provider_reference = provider_ref, - ); assert_eq!(scaler.id(), id, "LinkScaler ID should be the same when their type, model name, provider link name, actor reference, and provider reference are the same and they both have no values configured"); @@ -430,12 +432,10 @@ mod test { ); assert_ne!(scaler.id(), id, "Expected LinkScaler values hash to differiantiate scalers with the same type, model name, provider link name, actor reference, and provider reference"); - let mut scaler_id_tokens= scaler.id().split('-'); + let mut scaler_id_tokens = scaler.id().split('-'); scaler_id_tokens.next_back(); let scaler_id_tokens = scaler_id_tokens.collect::>().join("-"); assert_eq!(scaler_id_tokens, id, "Excluding the values hash, the LinkScaler ID should be the same when scalers have the same type, model name, provider link name, actor reference, and provider reference"); - - } #[tokio::test] @@ -563,7 +563,7 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-brooks-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { contract_id: "wasmcloud:httpserver".to_string(), link_name: "default".to_string(), diff --git a/src/scaler/spreadscaler/mod.rs b/src/scaler/spreadscaler/mod.rs index ae27f474..7bfcdfb3 100644 --- a/src/scaler/spreadscaler/mod.rs +++ b/src/scaler/spreadscaler/mod.rs @@ -617,7 +617,6 @@ mod test { actors: HashMap::new(), friendly_name: "hey".to_string(), labels: HashMap::new(), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -889,7 +888,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-brooks-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -914,7 +912,6 @@ mod test { ("region".to_string(), "us-midwest-4".to_string()), ("label".to_string(), "value".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -935,7 +932,6 @@ mod test { ("cloud".to_string(), "purgatory".to_string()), ("location".to_string(), "edge".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1048,7 +1044,6 @@ mod test { ("region".to_string(), "east".to_string()), ("resilient".to_string(), "true".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1141,7 +1136,7 @@ mod test { actors: HashMap::from_iter([(actor_id.to_string(), 10)]), friendly_name: "hey".to_string(), labels: HashMap::new(), - annotations: BTreeMap::new(), + providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1155,7 +1150,7 @@ mod test { actors: HashMap::from_iter([(actor_id.to_string(), 10)]), friendly_name: "hey2".to_string(), labels: HashMap::new(), - annotations: BTreeMap::new(), + providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1369,7 +1364,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-brooks-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1391,7 +1385,6 @@ mod test { ("region".to_string(), "us-midwest-4".to_string()), ("label".to_string(), "value".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1412,7 +1405,6 @@ mod test { ("cloud".to_string(), "purgatory".to_string()), ("location".to_string(), "edge".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -1426,8 +1418,8 @@ mod test { // Don't care about these events assert!(blobby_spreadscaler .handle_event(&Event::ProviderStarted(ProviderStarted { - annotations: BTreeMap::new(), claims: ProviderClaims::default(), + annotations: BTreeMap::default(), contract_id: "".to_string(), image_ref: "".to_string(), instance_id: "".to_string(), diff --git a/src/scaler/spreadscaler/provider.rs b/src/scaler/spreadscaler/provider.rs index 358411f3..4286a4e6 100644 --- a/src/scaler/spreadscaler/provider.rs +++ b/src/scaler/spreadscaler/provider.rs @@ -209,7 +209,7 @@ impl Scaler for ProviderSpreadScaler { contract_id: contract_id.to_string(), link_name: link_name.to_string(), public_key: provider_id.to_string(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }) }) .map(|(_host_id, host)| { @@ -457,7 +457,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-noneofyourbusiness-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -478,7 +477,6 @@ mod test { ("cloud".to_string(), "real".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -658,7 +656,7 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-noneofyourbusiness-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { contract_id: "prov:ider".to_string(), link_name: DEFAULT_LINK_NAME.to_string(), @@ -684,7 +682,6 @@ mod test { ("cloud".to_string(), "real".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -704,7 +701,6 @@ mod test { ("cloud".to_string(), "inthemiddle".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -724,7 +720,7 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-east-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { contract_id: "prov:ider".to_string(), link_name: DEFAULT_LINK_NAME.to_string(), @@ -887,7 +883,6 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-noneofyourbusiness-1".to_string()), ]), - annotations: BTreeMap::new(), providers: HashSet::new(), uptime_seconds: 123, version: None, @@ -908,12 +903,12 @@ mod test { ("cloud".to_string(), "real".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { public_key: provider_id.to_string(), contract_id: "prov:ider".to_string(), link_name: DEFAULT_LINK_NAME.to_string(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }]), uptime_seconds: 123, @@ -1024,12 +1019,12 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-noneofyourbusiness-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { public_key: provider_id.to_string(), contract_id: "prov:ider".to_string(), link_name: DEFAULT_LINK_NAME.to_string(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }]), uptime_seconds: 123, version: None, @@ -1050,12 +1045,12 @@ mod test { ("cloud".to_string(), "real".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { public_key: provider_id.to_string(), contract_id: "prov:ider".to_string(), link_name: DEFAULT_LINK_NAME.to_string(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }]), uptime_seconds: 123, @@ -1141,12 +1136,12 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-noneofyourbusiness-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { public_key: provider_id.to_string(), contract_id: "prov:ider".to_string(), link_name: DEFAULT_LINK_NAME.to_string(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }]), uptime_seconds: 123, version: None, @@ -1191,7 +1186,7 @@ mod test { ("cloud".to_string(), "fake".to_string()), ("region".to_string(), "us-yourhouse-1".to_string()), ]), - annotations: BTreeMap::new(), + providers: HashSet::from_iter([ProviderInfo { public_key: provider_id.to_string(), contract_id: "prov:ider".to_string(), diff --git a/src/storage/state.rs b/src/storage/state.rs index 113b8e24..27f53ebe 100644 --- a/src/storage/state.rs +++ b/src/storage/state.rs @@ -10,7 +10,8 @@ use serde::{Deserialize, Serialize}; use super::StateKind; use crate::events::{ - ActorStarted, ActorsStarted, HostHeartbeat, HostStarted, ProviderInfo, ProviderStarted, + ActorStarted, ActorsStarted, BackwardsCompatActors, BackwardsCompatProviders, HostHeartbeat, + HostStarted, ProviderInfo, ProviderStarted, }; /// A wasmCloud Capability provider @@ -277,9 +278,6 @@ pub struct Host { /// An arbitrary hashmap of string labels attached to the host pub labels: HashMap, - /// Additional annotations that have been added to the host - pub annotations: BTreeMap, - /// A set of running providers on the host pub providers: HashSet, @@ -329,15 +327,48 @@ impl From<&HostStarted> for Host { impl From for Host { fn from(value: HostHeartbeat) -> Self { + let actors = match value.actors.clone() { + BackwardsCompatActors::V81(actors) => actors, + // TODO(#TODO): Change the format of the [Host] to use the new format + BackwardsCompatActors::V82(actors) => actors + .into_iter() + .map(|actor| { + ( + actor.id, + actor + .instances + .into_iter() + .map(|instance| instance.max_concurrent as usize) + .sum(), + ) + }) + .collect(), + }; + + let providers = match value.providers { + BackwardsCompatProviders::V81(providers) => providers.into_iter().collect(), + BackwardsCompatProviders::V82(providers) => providers + .into_iter() + .map(|provider| ProviderInfo { + public_key: provider.id, + annotations: provider + .annotations + .map(|a| a.into_iter().collect()) + .unwrap_or_default(), + contract_id: provider.contract_id, + link_name: provider.link_name, + }) + .collect(), + }; + Host { - actors: value.actors, + actors, friendly_name: value.friendly_name, labels: value.labels, - annotations: value.annotations, - providers: value.providers.into_iter().collect(), - uptime_seconds: value.uptime_seconds, + providers, + uptime_seconds: value.uptime_seconds as usize, version: Some(value.version), - id: value.id, + id: value.host_id, last_seen: Utc::now(), } } @@ -345,15 +376,48 @@ impl From for Host { impl From<&HostHeartbeat> for Host { fn from(value: &HostHeartbeat) -> Self { + let actors = match value.actors.clone() { + BackwardsCompatActors::V81(actors) => actors, + // TODO(#TODO): Change the format of the [Host] to use the new format + BackwardsCompatActors::V82(actors) => actors + .into_iter() + .map(|actor| { + ( + actor.id, + actor + .instances + .into_iter() + .map(|instance| instance.max_concurrent as usize) + .sum(), + ) + }) + .collect(), + }; + + let providers = match value.providers.clone() { + BackwardsCompatProviders::V81(providers) => providers.iter().cloned().collect(), + BackwardsCompatProviders::V82(providers) => providers + .into_iter() + .map(|provider| ProviderInfo { + public_key: provider.id, + annotations: provider + .annotations + .map(|a| a.into_iter().collect()) + .unwrap_or_default(), + contract_id: provider.contract_id, + link_name: provider.link_name, + }) + .collect(), + }; + Host { - actors: value.actors.clone(), + actors, friendly_name: value.friendly_name.clone(), labels: value.labels.clone(), - annotations: value.annotations.clone(), - providers: value.providers.iter().cloned().collect(), - uptime_seconds: value.uptime_seconds, + providers, + uptime_seconds: value.uptime_seconds as usize, version: Some(value.version.clone()), - id: value.id.clone(), + id: value.host_id.clone(), last_seen: Utc::now(), } } diff --git a/src/workers/event.rs b/src/workers/event.rs index 90648b1a..8762de0f 100644 --- a/src/workers/event.rs +++ b/src/workers/event.rs @@ -208,57 +208,80 @@ where Ok(()) } - #[instrument(level = "debug", skip(self, host), fields(host_id = %host.id))] + #[instrument(level = "debug", skip(self, host), fields(host_id = %host.host_id))] async fn handle_host_heartbeat( &self, lattice_id: &str, host: &HostHeartbeat, ) -> anyhow::Result<()> { debug!("Updating store with current host heartbeat information"); - debug!("Requesting host inventory to supplement heartbeat"); - // NOTE(brooksmtownsend): Currently, the heartbeat does not tell us the instance IDs or annotations - // of actors, or the annotations of providers. We need to make an inventory request to get this - // information so we can properly update state. - let host_inventory = self.ctl_client.get_inventory(&host.id).await?; - let mut host_data = Host::from(host); - // Supplement host provider annotation data with annotations from inventory - host_data.providers = host_data - .providers - .into_iter() - .map(|mut info| { - if info.annotations.is_empty() { - match host_inventory.providers.iter().find(|p| { - p.id == info.public_key - && p.contract_id == info.contract_id - && p.link_name == info.link_name - }) { - Some(provider) if provider.annotations.is_some() => { - info.annotations = provider - .annotations - .clone() - .map(|annotations| annotations.into_iter().collect()) - .unwrap_or_default(); + match (&host.actors, &host.providers) { + // New heartbeat format, skip the inventory request and update the store + (BackwardsCompatActors::V82(actors), BackwardsCompatProviders::V82(providers)) => { + let host_data = Host::from(host); + self.store + .store(lattice_id, host.host_id.clone(), host_data) + .await?; + // TODO(brooksmtownsend): Make better choices with owned data + + // NOTE: We can return an error here and then nack because we'll just reupdate the host data + // with the exact same host heartbeat entry. There is no possibility of a duplicate + self.heartbeat_provider_update(lattice_id, host, providers.to_vec()) + .await?; + + // NOTE: We can return an error here and then nack because we'll just reupdate the host data + // with the exact same host heartbeat entry. There is no possibility of a duplicate + self.heartbeat_actor_update(lattice_id, host, actors.to_vec()) + .await?; + } + // Old heartbeat format, request inventory for supplemental information and update the store + _ => { + debug!("Requesting host inventory to supplement heartbeat"); + // NOTE(brooksmtownsend): Until wasmCloud 0.82, the heartbeat does not tell us the instance IDs or annotations + // of actors, or the annotations of providers. We need to make an inventory request to get this + // information so we can properly update state. + let host_inventory = self.ctl_client.get_inventory(&host.host_id).await?; + let mut host_data = Host::from(host); + // Supplement host provider annotation data with annotations from inventory + host_data.providers = host_data + .providers + .into_iter() + .map(|mut info| { + if info.annotations.is_empty() { + match host_inventory.providers.iter().find(|p| { + p.id == info.public_key + && p.contract_id == info.contract_id + && p.link_name == info.link_name + }) { + Some(provider) if provider.annotations.is_some() => { + info.annotations = provider + .annotations + .clone() + .map(|annotations| annotations.into_iter().collect()) + .unwrap_or_default(); + } + _ => (), + } } - _ => (), - } - } - info - }) - .collect(); - self.store - .store(lattice_id, host.id.clone(), host_data) - .await?; - - // NOTE: We can return an error here and then nack because we'll just reupdate the host data - // with the exact same host heartbeat entry. There is no possibility of a duplicate - self.heartbeat_provider_update(lattice_id, host, host_inventory.providers) - .await?; - - // NOTE: We can return an error here and then nack because we'll just reupdate the host data - // with the exact same host heartbeat entry. There is no possibility of a duplicate - self.heartbeat_actor_update(lattice_id, host, host_inventory.actors) - .await?; + info + }) + .collect(); + self.store + .store(lattice_id, host.host_id.clone(), host_data) + .await?; + + // NOTE: We can return an error here and then nack because we'll just reupdate the host data + // with the exact same host heartbeat entry. There is no possibility of a duplicate + self.heartbeat_provider_update(lattice_id, host, host_inventory.providers) + .await?; + + // NOTE: We can return an error here and then nack because we'll just reupdate the host data + // with the exact same host heartbeat entry. There is no possibility of a duplicate + self.heartbeat_actor_update(lattice_id, host, host_inventory.actors) + .await?; + } + } Ok(()) } @@ -477,7 +500,7 @@ where public_key: provider.public_key.to_owned(), // We don't have this information, nor do we need it since we don't hash based // on annotations - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }); self.store @@ -568,13 +591,27 @@ where &self, actors: &HashMap, host_id: &str, - instance_map: Vec<(ActorDescription, HashSet)>, + instance_map: Vec, ) -> anyhow::Result> { let claims = self.ctl_client.get_claims().await?; Ok(instance_map .into_iter() - .map(|(actor_description, instances)| { + .map(|actor_description| { + let instances = actor_description + .instances + .into_iter() + .map(|instance| { + let annotations = instance + .annotations + .map(|a| a.into_iter().collect()) + .unwrap_or_default(); + WadmActorInfo { + count: instance.max_concurrent as usize, + annotations, + } + }) + .collect(); if let Some(actor) = actors.get(&actor_description.id) { // Construct modified Actor with new instances included let mut new_instances = actor.instances.clone(); @@ -622,7 +659,7 @@ where .collect::>()) } - #[instrument(level = "debug", skip(self, host), fields(host_id = %host.id))] + #[instrument(level = "debug", skip(self, host), fields(host_id = %host.host_id))] async fn heartbeat_actor_update( &self, lattice_id: &str, @@ -632,62 +669,15 @@ where debug!("Fetching current actor state"); let actors = self.store.list::(lattice_id).await?; - let host_instances = inventory_actors - .into_iter() - .map(|actor_description| { - ( - ActorDescription { - id: actor_description.id, - image_ref: actor_description.image_ref, - name: actor_description.name, - // TODO(#191): Explicitly throwing away the instances list, we don't need it in the - // inventory format. As we resolve #191 we should be able to avoid the below folding - // logic and proceed with the instances list as-is on the host inventory. - ..Default::default() - }, - actor_description - .instances - .into_iter() - // The only thing we care about here are the annotations and the count - // associated with those annotations - .map(|instance| { - ( - instance - .annotations - .unwrap_or_default() - .into_iter() - .collect::>(), - instance.max_concurrent, - ) - }) - .fold(HashMap::new(), |mut map, (annotations, max_concurrent)| { - // NOTE(#191): This is for backwards compat with 0.78 as 0.79 changes this to be - // just a max concurrent value. This code should be removed probably by - // the time we release 0.80 - let plus = if max_concurrent == 0 { - 1_usize - } else { - max_concurrent as usize - }; - map.entry(annotations) - .and_modify(|count| *count += plus) - .or_insert(plus); - map - }) - .into_iter() - .map(|(annotations, count)| WadmActorInfo { count, annotations }) - .collect::>(), - ) - }) - .collect::)>>(); - // Compare stored Actors to the "true" list on this host, updating stored // Actors when they differ from the authoratative heartbeat - let actors_to_update = host_instances + let actors_to_update = inventory_actors .into_iter() - .filter_map(|(actor_description, instances)| { + .filter_map(|actor_description| { if actors .get(&actor_description.id) + // NOTE(brooksmtownsend): This code maps the actor to a boolean indicating if it's up-to-date with the heartbeat or not. + // If the actor matches what the heartbeat says, we return None, otherwise we return Some(actor_description). .map(|actor| { // If the stored reference isn't what we receive on the heartbeat, update actor_description @@ -696,26 +686,26 @@ where .is_some_and(|actor_ref| &actor.reference == actor_ref) && actor .instances - .get(&host.id) + .get(&host.host_id) .map(|store_instances| { - // NOTE(thomastaylor312): This is the weird part where we have to do - // custom equality checking because of how we did hashing. The only - // way to keep this data serializable was by storing it as a - // HashSet. A map of Annotations -> count would be better, but it's - // not serializable. Please note that there is some iteration - // involved, it is highly unlikely an actor would have more than 2 - // or 3 different instances running with different annotations - if store_instances.len() != instances.len() { - // Short circuit if we have more or less + // Update if the number of instances is different + if store_instances.len() != actor.instances.len() { return false; } - // Otherwise, check that all the annotations and counts are the same - instances.iter().all(|instance| { - store_instances - .get(&instance.annotations) - .map_or(false, |store_instance| { - instance.count == store_instance.count - }) + // Update if annotations or counts are different + actor_description.instances.iter().all(|instance| { + let annotations = instance + .annotations + .clone() + .map(|a| BTreeMap::from_iter(a.into_iter())) + .unwrap_or_default(); + store_instances.get(&annotations).map_or( + false, + |store_instance| { + instance.max_concurrent as usize + == store_instance.count + }, + ) }) }) .unwrap_or(false) @@ -724,14 +714,14 @@ where { None } else { - Some((actor_description, instances)) + Some(actor_description) } }) // actor ID to all instances on this host - .collect::)>>(); + .collect::>(); let actors_to_store = self - .populate_actor_info(&actors, &host.id, actors_to_update) + .populate_actor_info(&actors, &host.host_id, actors_to_update) .await?; trace!("Updating actors with new status from host"); @@ -741,11 +731,11 @@ where Ok(()) } - #[instrument(level = "debug", skip(self, host), fields(host_id = %host.id))] + #[instrument(level = "debug", skip(self, heartbeat), fields(host_id = %heartbeat.host_id))] async fn heartbeat_provider_update( &self, lattice_id: &str, - host: &HostHeartbeat, + heartbeat: &HostHeartbeat, inventory_providers: Vec, ) -> anyhow::Result<()> { debug!("Fetching current provider state"); @@ -772,7 +762,7 @@ where prov.reference = info.image_ref.clone().unwrap_or_default(); has_changes = true; } - if let Entry::Vacant(entry) = prov.hosts.entry(host.id.clone()) { + if let Entry::Vacant(entry) = prov.hosts.entry(heartbeat.host_id.clone()) { entry.insert(ProviderStatus::default()); has_changes = true; } @@ -791,7 +781,7 @@ where id: info.id.clone(), contract_id: info.contract_id.clone(), link_name: info.link_name.clone(), - hosts: [(host.id.clone(), ProviderStatus::default())].into(), + hosts: [(heartbeat.host_id.clone(), ProviderStatus::default())].into(), name: info.name.clone().unwrap_or_default(), reference: info.image_ref.clone().unwrap_or_default(), ..Default::default() @@ -1480,6 +1470,7 @@ mod test { // NOTE(brooksmtownsend): Painful manual manipulation of host inventory // to satisfy the way we currently query the inventory when handling heartbeats. + // TODO(#TODO): Remove this as we no longer need the inventory to handle a heartbeat *inventory.write().await = HashMap::from_iter([ ( host1_id.to_string(), @@ -1491,44 +1482,26 @@ mod test { id: actor1.public_key.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "1".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "2".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "1".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }, ActorDescription { id: actor2.public_key.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "3".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "4".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "2".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }, ], @@ -1566,44 +1539,26 @@ mod test { id: actor1.public_key.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "5".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "6".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "3".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }, ActorDescription { id: actor2.public_key.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "7".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "8".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "3".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }, ], @@ -1626,31 +1581,31 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::from([ + actors: BackwardsCompatActors::V81(HashMap::from([ (actor1.public_key.clone(), 2), (actor2.public_key.clone(), 2), - ]), + ])), friendly_name: "death-star-42".to_string(), labels: labels.clone(), - providers: vec![ + issuer: "".to_string(), + providers: BackwardsCompatProviders::V81(vec![ ProviderInfo { contract_id: provider1.contract_id.clone(), link_name: provider1.link_name.clone(), public_key: provider1.public_key.clone(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }, ProviderInfo { contract_id: provider2.contract_id.clone(), link_name: provider2.link_name.clone(), public_key: provider2.public_key.clone(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }, - ], + ]), uptime_human: "30s".into(), uptime_seconds: 30, version: semver::Version::parse("0.61.0").unwrap(), - id: host1_id.clone(), - annotations: BTreeMap::default(), + host_id: host1_id.clone(), }, ) .await @@ -1660,23 +1615,23 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::from([ + actors: BackwardsCompatActors::V81(HashMap::from([ (actor1.public_key.clone(), 2), (actor2.public_key.clone(), 2), - ]), + ])), + issuer: "".to_string(), friendly_name: "starkiller-base-2015".to_string(), labels: labels2.clone(), - providers: vec![ProviderInfo { + providers: BackwardsCompatProviders::V81(vec![ProviderInfo { contract_id: provider2.contract_id.clone(), link_name: provider2.link_name.clone(), public_key: provider2.public_key.clone(), - annotations: BTreeMap::new(), - }], + annotations: BTreeMap::default(), + }]), uptime_human: "30s".into(), uptime_seconds: 30, version: semver::Version::parse("0.61.0").unwrap(), - id: host2_id.clone(), - annotations: BTreeMap::default(), + host_id: host2_id.clone(), }, ) .await @@ -1796,22 +1751,13 @@ mod test { id: actor2.public_key.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "3".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "4".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "4".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }], host_id: host1_id.to_string(), @@ -1829,22 +1775,13 @@ mod test { id: actor2.public_key.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "7".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "8".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "5".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }], host_id: host2_id.to_string(), @@ -1860,20 +1797,23 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::from([(actor2.public_key.clone(), 2)]), + actors: BackwardsCompatActors::V81(HashMap::from([( + actor2.public_key.clone(), + 2, + )])), friendly_name: "death-star-42".to_string(), + issuer: "".to_string(), labels, - providers: vec![ProviderInfo { + providers: BackwardsCompatProviders::V81(vec![ProviderInfo { contract_id: provider1.contract_id.clone(), link_name: provider1.link_name.clone(), public_key: provider1.public_key.clone(), - annotations: BTreeMap::new(), - }], + annotations: BTreeMap::default(), + }]), uptime_human: "60s".into(), uptime_seconds: 60, version: semver::Version::parse("0.61.0").unwrap(), - id: host1_id.clone(), - annotations: BTreeMap::default(), + host_id: host1_id.clone(), }, ) .await @@ -1883,20 +1823,23 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::from([(actor2.public_key.clone(), 2)]), + actors: BackwardsCompatActors::V81(HashMap::from([( + actor2.public_key.clone(), + 2, + )])), friendly_name: "starkiller-base-2015".to_string(), labels: labels2, - providers: vec![ProviderInfo { + issuer: "".to_string(), + providers: BackwardsCompatProviders::V81(vec![ProviderInfo { contract_id: provider2.contract_id.clone(), link_name: provider2.link_name.clone(), public_key: provider2.public_key.clone(), - annotations: BTreeMap::new(), - }], + annotations: BTreeMap::default(), + }]), uptime_human: "60s".into(), uptime_seconds: 60, version: semver::Version::parse("0.61.0").unwrap(), - id: host2_id.clone(), - annotations: BTreeMap::default(), + host_id: host2_id.clone(), }, ) .await @@ -2024,22 +1967,13 @@ mod test { id: actor1_id.to_string(), image_ref: None, // The individual instances of this actor that are running - instances: vec![ - ActorInstance { - annotations: None, - instance_id: "1".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ActorInstance { - annotations: None, - instance_id: "2".to_string(), - revision: 0, - image_ref: None, - max_concurrent: 0, - }, - ], + instances: vec![ActorInstance { + annotations: None, + instance_id: "1".to_string(), + revision: 0, + image_ref: None, + max_concurrent: 2, + }], name: None, }, ActorDescription { @@ -2048,10 +1982,10 @@ mod test { // The individual instances of this actor that are running instances: vec![ActorInstance { annotations: None, - instance_id: "3".to_string(), + instance_id: "2".to_string(), revision: 0, image_ref: None, - max_concurrent: 0, + max_concurrent: 1, }], name: None, }, @@ -2075,20 +2009,23 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::from([(actor1_id.clone(), 2), (actor2_id.clone(), 1)]), + actors: BackwardsCompatActors::V81(HashMap::from([ + (actor1_id.clone(), 2), + (actor2_id.clone(), 1), + ])), friendly_name: "millenium_falcon-1977".to_string(), labels: HashMap::default(), - providers: vec![ProviderInfo { + issuer: "".to_string(), + providers: BackwardsCompatProviders::V81(vec![ProviderInfo { contract_id: "lightspeed".into(), link_name: link_name.clone(), public_key: provider_id.clone(), - annotations: BTreeMap::new(), - }], + annotations: BTreeMap::default(), + }]), uptime_human: "60s".into(), uptime_seconds: 60, version: semver::Version::parse("0.61.0").unwrap(), - id: host_id.clone(), - annotations: BTreeMap::default(), + host_id: host_id.clone(), }, ) .await @@ -2334,20 +2271,20 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::default(), + actors: BackwardsCompatActors::V82(vec![]), friendly_name: "tatooine-1977".to_string(), labels: HashMap::default(), - providers: vec![ProviderInfo { + issuer: "".to_string(), + providers: BackwardsCompatProviders::V81(vec![ProviderInfo { contract_id: contract_id.to_string(), link_name: link_name.to_string(), public_key: public_key.to_string(), - annotations: BTreeMap::new(), - }], + annotations: BTreeMap::default(), + }]), uptime_human: "60s".into(), uptime_seconds: 60, version: semver::Version::parse("0.61.0").unwrap(), - id: host_id.to_string(), - annotations: BTreeMap::default(), + host_id: host_id.to_string(), }, ) .await @@ -2447,14 +2384,14 @@ mod test { )])), revision: 0, image_ref: None, - max_concurrent: 0, + max_concurrent: 1, }, ActorInstance { instance_id: "2".to_string(), annotations: None, revision: 0, image_ref: None, - max_concurrent: 0, + max_concurrent: 1, }, ], }], @@ -2477,20 +2414,21 @@ mod test { .handle_host_heartbeat( lattice_id, &HostHeartbeat { - actors: HashMap::from([("jabba".to_string(), 2)]), + actors: BackwardsCompatActors::V81(HashMap::from([("jabba".to_string(), 2)])), friendly_name: "palace-1983".to_string(), labels: HashMap::default(), - providers: vec![ProviderInfo { + issuer: "".to_string(), + providers: BackwardsCompatProviders::V81(vec![ProviderInfo { contract_id: "jabba:jabba".to_string(), link_name: "default".to_string(), - annotations: BTreeMap::new(), + + annotations: BTreeMap::default(), public_key: "jabbatheprovider".to_string(), - }], + }]), uptime_human: "60s".into(), uptime_seconds: 60, version: semver::Version::parse("0.61.0").unwrap(), - id: host_id.to_string(), - annotations: BTreeMap::default(), + host_id: host_id.to_string(), }, ) .await @@ -2552,7 +2490,7 @@ mod test { contract_id: "jabba:jabba".to_string(), link_name: "default".to_string(), public_key: "jabbatheprovider".to_string(), - annotations: BTreeMap::new(), + annotations: BTreeMap::default(), }) .expect("provider to exist on host"); diff --git a/test/data/events.json b/test/data/events.json index fc3a595a..7c77939a 100644 --- a/test/data/events.json +++ b/test/data/events.json @@ -1,336 +1,382 @@ [ - { - "data": { - "annotations": {}, - "claims": { - "expires_human": "never", - "issuer": "ACOJJN6WUP4ODD75XEBKKTCCUJJCY5ZKQ56XVKYK4BEJWGVAOOQHZMCW", - "name": "HTTP Server", - "not_before_human": "immediately", - "tags": null, - "version": "0.16.0" - }, - "contract_id": "wasmcloud:httpserver", - "image_ref": "wasmcloud.azurecr.io/httpserver:0.16.0", - "instance_id": "a518251a-01f6-4ba6-b7f0-5d8458bc875e", - "link_name": "default", - "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M" - }, - "datacontenttype": "application/json", - "id": "5613ee03-8645-4ad8-a3aa-3428a33b7e96", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:21:09.011785Z", - "type": "com.wasmcloud.lattice.provider_started" + { + "data": { + "annotations": {}, + "claims": { + "expires_human": "never", + "issuer": "ACOJJN6WUP4ODD75XEBKKTCCUJJCY5ZKQ56XVKYK4BEJWGVAOOQHZMCW", + "name": "HTTP Server", + "not_before_human": "immediately", + "tags": null, + "version": "0.16.0" + }, + "contract_id": "wasmcloud:httpserver", + "image_ref": "wasmcloud.azurecr.io/httpserver:0.16.0", + "instance_id": "a518251a-01f6-4ba6-b7f0-5d8458bc875e", + "link_name": "default", + "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M" }, - { - "data": { - "link_name": "default", - "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", - "contract_id": "wasmcloud:httpserver" - }, - "datacontenttype": "application/json", - "id": "04369697-9397-46e3-99d7-277d867f9acc", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:21:14.113113Z", - "type": "com.wasmcloud.lattice.health_check_passed" + "datacontenttype": "application/json", + "id": "5613ee03-8645-4ad8-a3aa-3428a33b7e96", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:21:09.011785Z", + "type": "com.wasmcloud.lattice.provider_started" + }, + { + "data": { + "link_name": "default", + "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "contract_id": "wasmcloud:httpserver" }, - { - "data": { - "link_name": "default", - "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", - "contract_id": "wasmcloud:httpserver" - }, - "datacontenttype": "application/json", - "id": "04369697-9397-46e3-99d7-277d867f9acc", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:21:14.113113Z", - "type": "com.wasmcloud.lattice.health_check_failed" + "datacontenttype": "application/json", + "id": "04369697-9397-46e3-99d7-277d867f9acc", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:21:14.113113Z", + "type": "com.wasmcloud.lattice.health_check_passed" + }, + { + "data": { + "link_name": "default", + "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "contract_id": "wasmcloud:httpserver" }, - { - "data": { - "link_name": "default", - "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", - "contract_id": "wasmcloud:httpserver" - }, - "datacontenttype": "application/json", - "id": "04369697-9397-46e3-99d7-277d867f9acc", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:21:14.113113Z", - "type": "com.wasmcloud.lattice.health_check_status" + "datacontenttype": "application/json", + "id": "04369697-9397-46e3-99d7-277d867f9acc", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:21:14.113113Z", + "type": "com.wasmcloud.lattice.health_check_failed" + }, + { + "data": { + "link_name": "default", + "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "contract_id": "wasmcloud:httpserver" }, - { - "data": { - "annotations": {}, - "api_version": "n/a", - "claims": { - "call_alias": null, - "caps": [ - "wasmcloud:httpserver" - ], - "expires_human": "never", - "issuer": "ACOJJN6WUP4ODD75XEBKKTCCUJJCY5ZKQ56XVKYK4BEJWGVAOOQHZMCW", - "name": "Echo", - "not_before_human": "immediately", - "revision": 4, - "tags": [], - "version": "0.3.4" - }, - "image_ref": "wasmcloud.azurecr.io/echo:0.3.4", - "instance_id": "5ebc4920-cec4-44a6-a718-df4d43d94b09", - "public_key": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5" - }, - "datacontenttype": "application/json", - "id": "84ee3ab6-f951-4423-99b6-73ddf58bcd1d", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:21:38.222286Z", - "type": "com.wasmcloud.lattice.actor_started" + "datacontenttype": "application/json", + "id": "04369697-9397-46e3-99d7-277d867f9acc", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:21:14.113113Z", + "type": "com.wasmcloud.lattice.health_check_status" + }, + { + "data": { + "annotations": {}, + "api_version": "n/a", + "claims": { + "call_alias": null, + "caps": ["wasmcloud:httpserver"], + "expires_human": "never", + "issuer": "ACOJJN6WUP4ODD75XEBKKTCCUJJCY5ZKQ56XVKYK4BEJWGVAOOQHZMCW", + "name": "Echo", + "not_before_human": "immediately", + "revision": 4, + "tags": [], + "version": "0.3.4" + }, + "image_ref": "wasmcloud.azurecr.io/echo:0.3.4", + "instance_id": "5ebc4920-cec4-44a6-a718-df4d43d94b09", + "public_key": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5" }, - { - "data": { - "actors": { - "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5": 1 - }, - "friendly_name": "fragrant-flower-3913", - "labels": { - "hostcore.arch": "aarch64", - "hostcore.os": "macos", - "hostcore.osfamily": "unix" - }, - "providers": [ - { - "contract_id": "wasmcloud:httpserver", - "link_name": "default", - "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M" - } - ], - "uptime_human": "3 minutes, 2 seconds", - "uptime_seconds": 182, - "version": "0.60.0" - }, - "datacontenttype": "application/json", - "id": "ebd7b0d3-36a0-4011-b32a-ebccb9f270e3", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:21:48.838573Z", - "type": "com.wasmcloud.lattice.host_heartbeat" + "datacontenttype": "application/json", + "id": "84ee3ab6-f951-4423-99b6-73ddf58bcd1d", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:21:38.222286Z", + "type": "com.wasmcloud.lattice.actor_started" + }, + { + "data": { + "actors": { + "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5": 1 + }, + "friendly_name": "fragrant-flower-3913", + "labels": { + "hostcore.arch": "aarch64", + "hostcore.os": "macos", + "hostcore.osfamily": "unix" + }, + "providers": [ + { + "contract_id": "wasmcloud:httpserver", + "link_name": "default", + "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M" + } + ], + "uptime_human": "3 minutes, 2 seconds", + "uptime_seconds": 182, + "version": "0.81.0" }, - { - "data": { - "actor_id": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5", - "contract_id": "wasmcloud:httpserver", - "id": "EC986307FC72C45DBA4699F5AD52702B09800138ACA24BC53339B929423947B6", - "link_name": "default", - "provider_id": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", - "values": { - "address": "0.0.0.0:8080" + "datacontenttype": "application/json", + "id": "ebd7b0d3-36a0-4011-b32a-ebccb9f270e3", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:21:48.838573Z", + "type": "com.wasmcloud.lattice.host_heartbeat" + }, + { + "specversion": "1.0", + "id": "018cf459-24e3-c4fe-46f8-c68f5a3ea6bc", + "type": "com.wasmcloud.lattice.host_heartbeat", + "source": "ND4OAUIBDT4HITUV4UW7VJUUTVMDJJIH5FA3YKP6D6REASBU3ZNUKMEM", + "datacontenttype": "application/json", + "time": "2024-01-10T17:08:52.067116Z", + "data": { + "actors": [ + { + "id": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5", + "image_ref": "wasmcloud.azurecr.io/echo:0.3.4", + "instances": [ + { + "annotations": {}, + "image_ref": "wasmcloud.azurecr.io/echo:0.3.4", + "instance_id": "018cf458-073b-cd5f-87d5-f5e021b8aab1", + "max_instances": 1, + "revision": 4 } - }, - "datacontenttype": "application/json", - "id": "1d2ea745-5ae6-42f7-887b-f1a25bd7c19d", - "source": "n/a", - "specversion": "1.0", - "time": "2023-02-14T19:22:25.662024Z", - "type": "com.wasmcloud.lattice.linkdef_set" + ], + "name": "Echo" + } + ], + "friendly_name": "nameless-pine-6843", + "host_id": "ND4OAUIBDT4HITUV4UW7VJUUTVMDJJIH5FA3YKP6D6REASBU3ZNUKMEM", + "issuer": "CCKN5TESAQV2UUBNHLEHTZDNWXKDHWJTFDBTUSMPCTUB5UI47FNV4NZK", + "labels": { + "hostcore.arch": "aarch64", + "hostcore.os": "macos", + "hostcore.osfamily": "unix" + }, + "providers": [ + { + "annotations": {}, + "contract_id": "wasmcloud:httpserver", + "id": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "image_ref": "wasmcloud.azurecr.io/httpserver:0.19.1", + "link_name": "default", + "name": "HTTP Server", + "revision": 0 + } + ], + "uptime_human": "1m 30s", + "uptime_seconds": 90, + "version": "0.82.0" + } + }, + { + "data": { + "actor_id": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5", + "contract_id": "wasmcloud:httpserver", + "id": "EC986307FC72C45DBA4699F5AD52702B09800138ACA24BC53339B929423947B6", + "link_name": "default", + "provider_id": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "values": { + "address": "0.0.0.0:8080" + } }, - { - "data": { - "actor_id": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5", - "contract_id": "wasmcloud:httpserver", - "id": "EC986307FC72C45DBA4699F5AD52702B09800138ACA24BC53339B929423947B6", - "link_name": "default", - "provider_id": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", - "values": { - "address": "0.0.0.0:8080" - } - }, - "datacontenttype": "application/json", - "id": "e3169b21-1e16-48f8-baf4-337eafcf35ca", - "source": "n/a", - "specversion": "1.0", - "time": "2023-02-14T19:22:57.131368Z", - "type": "com.wasmcloud.lattice.linkdef_deleted" + "datacontenttype": "application/json", + "id": "1d2ea745-5ae6-42f7-887b-f1a25bd7c19d", + "source": "n/a", + "specversion": "1.0", + "time": "2023-02-14T19:22:25.662024Z", + "type": "com.wasmcloud.lattice.linkdef_set" + }, + { + "data": { + "actor_id": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5", + "contract_id": "wasmcloud:httpserver", + "id": "EC986307FC72C45DBA4699F5AD52702B09800138ACA24BC53339B929423947B6", + "link_name": "default", + "provider_id": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "values": { + "address": "0.0.0.0:8080" + } }, - { - "data": { - "instance_id": "5ebc4920-cec4-44a6-a718-df4d43d94b09", - "public_key": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5" - }, - "datacontenttype": "application/json", - "id": "fc22b1cb-5245-404c-9c36-a61ea8d3bac3", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:23:01.877353Z", - "type": "com.wasmcloud.lattice.actor_stopped" + "datacontenttype": "application/json", + "id": "e3169b21-1e16-48f8-baf4-337eafcf35ca", + "source": "n/a", + "specversion": "1.0", + "time": "2023-02-14T19:22:57.131368Z", + "type": "com.wasmcloud.lattice.linkdef_deleted" + }, + { + "data": { + "instance_id": "5ebc4920-cec4-44a6-a718-df4d43d94b09", + "public_key": "MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5" }, - { - "data": { - "contract_id": "wasmcloud:httpserver", - "instance_id": "a518251a-01f6-4ba6-b7f0-5d8458bc875e", - "link_name": "default", - "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", - "reason": "normal" - }, - "datacontenttype": "application/json", - "id": "ff992d44-d873-45cf-bf34-b07f9db530f3", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:23:04.461220Z", - "type": "com.wasmcloud.lattice.provider_stopped" + "datacontenttype": "application/json", + "id": "fc22b1cb-5245-404c-9c36-a61ea8d3bac3", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:23:01.877353Z", + "type": "com.wasmcloud.lattice.actor_stopped" + }, + { + "data": { + "contract_id": "wasmcloud:httpserver", + "instance_id": "a518251a-01f6-4ba6-b7f0-5d8458bc875e", + "link_name": "default", + "public_key": "VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M", + "reason": "normal" }, - { - "data": { - "labels": { - "hostcore.arch": "aarch64", - "hostcore.os": "macos", - "hostcore.osfamily": "unix" - } - }, - "datacontenttype": "application/json", - "id": "ae0b3e97-02f8-4792-a94b-6b00ce8e9cae", - "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", - "specversion": "1.0", - "time": "2023-02-14T19:24:43.967009Z", - "type": "com.wasmcloud.lattice.host_stopped" + "datacontenttype": "application/json", + "id": "ff992d44-d873-45cf-bf34-b07f9db530f3", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:23:04.461220Z", + "type": "com.wasmcloud.lattice.provider_stopped" + }, + { + "data": { + "labels": { + "hostcore.arch": "aarch64", + "hostcore.os": "macos", + "hostcore.osfamily": "unix" + } }, - { - "data": { - "friendly_name": "holy-wildflower-2227", - "labels": { - "hostcore.arch": "aarch64", - "hostcore.os": "macos", - "hostcore.osfamily": "unix" - } - }, - "datacontenttype": "application/json", - "id": "9e3ef37c-cf2c-4f9e-853d-ba3af0fc319c", - "source": "NCOGQNZLCJJS2V52A5434BX65RQFNWX5RMP6IIJJATKD3HVAPZJ2ZUXZ", - "specversion": "1.0", - "time": "2023-02-14T19:26:05.119347Z", - "type": "com.wasmcloud.lattice.host_started" + "datacontenttype": "application/json", + "id": "ae0b3e97-02f8-4792-a94b-6b00ce8e9cae", + "source": "NB6PMW4RGLBP3NAVUVO2IH34VFJFSX7LF7TJOQCDU4GGUGF3P57SZLPX", + "specversion": "1.0", + "time": "2023-02-14T19:24:43.967009Z", + "type": "com.wasmcloud.lattice.host_stopped" + }, + { + "data": { + "friendly_name": "holy-wildflower-2227", + "labels": { + "hostcore.arch": "aarch64", + "hostcore.os": "macos", + "hostcore.osfamily": "unix" + } }, - { - "data": { - "error": "\"Failed to fetch OCI bytes: Registry error: url https://wasmcloud.azurecr.io/v2/httpserver/manifests/latest, envelope: OCI API errors: [OCI API error: manifest tagged by \\\"latest\\\" is not found]\"", - "link_name": "default", - "provider_ref": "wasmcloud.azurecr.io/httpserver" - }, - "datacontenttype": "application/json", - "id": "5a55cf82-713a-4043-be51-ab532d1c8a4d", - "source": "NBPNYTVN27FQIEUI6M5L7I4MAJONGNB3E3JUZHQCMYI4JOHQKNKPN5SG", - "specversion": "1.0", - "time": "2023-03-05T00:14:53.990820Z", - "type": "com.wasmcloud.lattice.provider_start_failed" + "datacontenttype": "application/json", + "id": "9e3ef37c-cf2c-4f9e-853d-ba3af0fc319c", + "source": "NCOGQNZLCJJS2V52A5434BX65RQFNWX5RMP6IIJJATKD3HVAPZJ2ZUXZ", + "specversion": "1.0", + "time": "2023-02-14T19:26:05.119347Z", + "type": "com.wasmcloud.lattice.host_started" + }, + { + "data": { + "error": "\"Failed to fetch OCI bytes: Registry error: url https://wasmcloud.azurecr.io/v2/httpserver/manifests/latest, envelope: OCI API errors: [OCI API error: manifest tagged by \\\"latest\\\" is not found]\"", + "link_name": "default", + "provider_ref": "wasmcloud.azurecr.io/httpserver" }, - { - "data": { - "apiVersion": "core.oam.dev/v1beta1", - "kind": "Application", - "metadata": { - "name": "my-example-app", - "annotations": { - "version": "v0.0.1", - "description": "This is my app" - } + "datacontenttype": "application/json", + "id": "5a55cf82-713a-4043-be51-ab532d1c8a4d", + "source": "NBPNYTVN27FQIEUI6M5L7I4MAJONGNB3E3JUZHQCMYI4JOHQKNKPN5SG", + "specversion": "1.0", + "time": "2023-03-05T00:14:53.990820Z", + "type": "com.wasmcloud.lattice.provider_start_failed" + }, + { + "data": { + "apiVersion": "core.oam.dev/v1beta1", + "kind": "Application", + "metadata": { + "name": "my-example-app", + "annotations": { + "version": "v0.0.1", + "description": "This is my app" + } + }, + "spec": { + "components": [ + { + "name": "userinfo", + "type": "actor", + "properties": { + "image": "wasmcloud.azurecr.io/fake:1" }, - "spec": { - "components": [ + "traits": [ + { + "type": "spreadscaler", + "properties": { + "replicas": 4, + "spread": [ { - "name": "userinfo", - "type": "actor", - "properties": { - "image": "wasmcloud.azurecr.io/fake:1" - }, - "traits": [ - { - "type": "spreadscaler", - "properties": { - "replicas": 4, - "spread": [ - { - "name": "eastcoast", - "requirements": { - "zone": "us-east-1" - }, - "weight": 80 - }, - { - "name": "westcoast", - "requirements": { - "zone": "us-west-1" - }, - "weight": 20 - } - ] - } - }, - { - "type": "linkdef", - "properties": { - "target": "webcap", - "values": { - "port": "8080" - } - } - } - ] + "name": "eastcoast", + "requirements": { + "zone": "us-east-1" + }, + "weight": 80 }, { - "name": "webcap", - "type": "capability", - "properties": { - "contract": "wasmcloud:httpserver", - "image": "wasmcloud.azurecr.io/httpserver:0.13.1", - "link_name": "default" - } - }, - { - "name": "ledblinky", - "type": "capability", - "properties": { - "image": "wasmcloud.azurecr.io/ledblinky:0.0.1", - "contract": "wasmcloud:blinkenlights" - }, - "traits": [ - { - "type": "spreadscaler", - "properties": { - "replicas": 1, - "spread": [ - { - "name": "haslights", - "requirements": { - "ledenabled": "true" - } - } - ] - } - } - ] + "name": "westcoast", + "requirements": { + "zone": "us-west-1" + }, + "weight": 20 } - ] + ] + } + }, + { + "type": "linkdef", + "properties": { + "target": "webcap", + "values": { + "port": "8080" + } + } + } + ] + }, + { + "name": "webcap", + "type": "capability", + "properties": { + "contract": "wasmcloud:httpserver", + "image": "wasmcloud.azurecr.io/httpserver:0.13.1", + "link_name": "default" } - }, - "datacontenttype": "application/json", - "id": "5a55cf82-713a-4043-be51-ab532d1c8a4d", - "source": "wadm", - "specversion": "1.0", - "time": "2023-03-05T00:14:53.990820Z", - "type": "com.wadm.manifest_published" + }, + { + "name": "ledblinky", + "type": "capability", + "properties": { + "image": "wasmcloud.azurecr.io/ledblinky:0.0.1", + "contract": "wasmcloud:blinkenlights" + }, + "traits": [ + { + "type": "spreadscaler", + "properties": { + "replicas": 1, + "spread": [ + { + "name": "haslights", + "requirements": { + "ledenabled": "true" + } + } + ] + } + } + ] + } + ] + } }, - { - "data": { - "name": "foobar" - }, - "datacontenttype": "application/json", - "id": "5a55cf82-713a-4043-be51-ab532d1c8a4d", - "source": "wadm", - "specversion": "1.0", - "time": "2023-03-05T00:14:53.990820Z", - "type": "com.wadm.manifest_unpublished" - } -] \ No newline at end of file + "datacontenttype": "application/json", + "id": "5a55cf82-713a-4043-be51-ab532d1c8a4d", + "source": "wadm", + "specversion": "1.0", + "time": "2023-03-05T00:14:53.990820Z", + "type": "com.wadm.manifest_published" + }, + { + "data": { + "name": "foobar" + }, + "datacontenttype": "application/json", + "id": "5a55cf82-713a-4043-be51-ab532d1c8a4d", + "source": "wadm", + "specversion": "1.0", + "time": "2023-03-05T00:14:53.990820Z", + "type": "com.wadm.manifest_unpublished" + } +] diff --git a/tests/command_worker_integration.rs b/tests/command_worker_integration.rs index cf8e5870..dc445b5a 100644 --- a/tests/command_worker_integration.rs +++ b/tests/command_worker_integration.rs @@ -369,7 +369,6 @@ async fn test_annotation_stop() { "wasmcloud.azurecr.io/echo:0.3.4", "Should have started the correct actor" ); - // NOTE(#191): This should be changed to only check the max_concurrent value assert_eq!( inventory[0] .instances @@ -380,13 +379,7 @@ async fn test_annotation_stop() { .as_ref() .map(|annotations| !annotations.is_empty()) .unwrap_or(false)) - .map(|i| { - if i.max_concurrent == 0 { - 1 - } else { - i.max_concurrent - } - }) + .map(|i| { i.max_concurrent }) .sum::(), 2, "Should have started the correct number of actors"