wasmcloud_control_interface/types/
host.rs

1//! Data types used for managing hosts on a wasmCloud lattice
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::types::component::ComponentDescription;
8use crate::types::provider::ProviderDescription;
9use crate::Result;
10
11/// A summary representation of a host
12#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
13#[non_exhaustive]
14pub struct Host {
15    /// NATS server host used for regular RPC
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub(crate) rpc_host: Option<String>,
18
19    /// NATS server host used for the control interface
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub(crate) ctl_host: Option<String>,
22
23    /// Human-friendly name for this host
24    #[serde(default)]
25    pub(crate) friendly_name: String,
26
27    /// Unique nkey public key for this host
28    #[serde(default)]
29    pub(crate) id: String,
30
31    /// JetStream domain (if applicable) in use by this host
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub(crate) js_domain: Option<String>,
34
35    /// Hash map of label-value pairs for this host
36    #[serde(default)]
37    pub(crate) labels: BTreeMap<String, String>,
38
39    /// The lattice that this host is a member of
40    #[serde(default)]
41    pub(crate) lattice: String,
42
43    /// Human-friendly uptime description
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub(crate) uptime_human: Option<String>,
46
47    /// Uptime in seconds
48    #[serde(default)]
49    pub(crate) uptime_seconds: u64,
50
51    /// Current wasmCloud Host software version
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub(crate) version: Option<String>,
54}
55
56impl Host {
57    /// Get the NATS server host used for RPC
58    pub fn rpc_host(&self) -> Option<&str> {
59        self.rpc_host.as_deref()
60    }
61
62    /// Get the NATS server host used for control interface commands
63    pub fn ctl_host(&self) -> Option<&str> {
64        self.ctl_host.as_deref()
65    }
66
67    /// Get the friendly name of the host
68    pub fn friendly_name(&self) -> &str {
69        &self.friendly_name
70    }
71
72    /// Get the ID of the host
73    pub fn id(&self) -> &str {
74        &self.id
75    }
76
77    /// Get the NATS Jetstream domain for the host
78    pub fn js_domain(&self) -> Option<&str> {
79        self.js_domain.as_deref()
80    }
81
82    /// Get the labels on the host
83    pub fn labels(&self) -> &BTreeMap<String, String> {
84        &self.labels
85    }
86
87    /// Get the lattice this host is a member of
88    pub fn lattice(&self) -> &str {
89        &self.lattice
90    }
91
92    /// Get a human friendly host uptime description
93    pub fn uptime_human(&self) -> Option<&str> {
94        self.uptime_human.as_deref()
95    }
96
97    /// Get the number of seconds the host has been up
98    pub fn uptime_seconds(&self) -> u64 {
99        self.uptime_seconds
100    }
101
102    /// Get the version of the host
103    pub fn version(&self) -> Option<&str> {
104        self.version.as_deref()
105    }
106
107    #[must_use]
108    pub fn builder() -> HostBuilder {
109        HostBuilder::default()
110    }
111}
112
113#[derive(Default, Clone, PartialEq, Eq)]
114#[non_exhaustive]
115pub struct HostBuilder {
116    rpc_host: Option<String>,
117    ctl_host: Option<String>,
118    friendly_name: Option<String>,
119    id: Option<String>,
120    js_domain: Option<String>,
121    labels: Option<BTreeMap<String, String>>,
122    lattice: Option<String>,
123    uptime_human: Option<String>,
124    uptime_seconds: Option<u64>,
125    version: Option<String>,
126}
127
128impl HostBuilder {
129    #[must_use]
130    pub fn rpc_host(mut self, v: String) -> Self {
131        self.rpc_host = Some(v);
132        self
133    }
134
135    #[must_use]
136    pub fn ctl_host(mut self, v: String) -> Self {
137        self.ctl_host = Some(v);
138        self
139    }
140
141    #[must_use]
142    pub fn friendly_name(mut self, v: String) -> Self {
143        self.friendly_name = Some(v);
144        self
145    }
146
147    #[must_use]
148    pub fn id(mut self, v: String) -> Self {
149        self.id = Some(v);
150        self
151    }
152
153    #[must_use]
154    pub fn js_domain(mut self, v: String) -> Self {
155        self.js_domain = Some(v);
156        self
157    }
158
159    #[must_use]
160    pub fn lattice(mut self, v: String) -> Self {
161        self.lattice = Some(v);
162        self
163    }
164
165    #[must_use]
166    pub fn labels(mut self, v: BTreeMap<String, String>) -> Self {
167        self.labels = Some(v);
168        self
169    }
170
171    #[must_use]
172    pub fn uptime_human(mut self, v: String) -> Self {
173        self.uptime_human = Some(v);
174        self
175    }
176
177    #[must_use]
178    pub fn uptime_seconds(mut self, v: u64) -> Self {
179        self.uptime_seconds = Some(v);
180        self
181    }
182
183    #[must_use]
184    pub fn version(mut self, v: String) -> Self {
185        self.version = Some(v);
186        self
187    }
188
189    pub fn build(self) -> Result<Host> {
190        Ok(Host {
191            friendly_name: self
192                .friendly_name
193                .ok_or_else(|| "friendly_name is required".to_string())?,
194            labels: self.labels.unwrap_or_default(),
195            uptime_human: self.uptime_human,
196            uptime_seconds: self
197                .uptime_seconds
198                .ok_or_else(|| "uptime_seconds is required".to_string())?,
199            rpc_host: self.rpc_host,
200            ctl_host: self.ctl_host,
201            id: self.id.ok_or_else(|| "id is required".to_string())?,
202            lattice: self
203                .lattice
204                .ok_or_else(|| "lattice is required".to_string())?,
205            js_domain: self.js_domain,
206            version: self.version,
207        })
208    }
209}
210
211/// Describes the known contents of a given host at the time of
212/// a query. Also used as a payload for the host heartbeat
213#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
214#[non_exhaustive]
215pub struct HostInventory {
216    /// Components running on this host.
217    #[serde(alias = "actors")]
218    pub(crate) components: Vec<ComponentDescription>,
219
220    /// Providers running on this host
221    pub(crate) providers: Vec<ProviderDescription>,
222
223    /// The host's unique ID
224    #[serde(default)]
225    pub(crate) host_id: String,
226
227    /// The host's human-readable friendly name
228    #[serde(default)]
229    pub(crate) friendly_name: String,
230
231    /// The host's labels
232    #[serde(default)]
233    pub(crate) labels: BTreeMap<String, String>,
234
235    /// The host version
236    #[serde(default)]
237    pub(crate) version: String,
238
239    /// The host uptime in human-readable form
240    #[serde(default)]
241    pub(crate) uptime_human: String,
242
243    /// The host uptime in seconds
244    #[serde(default)]
245    pub(crate) uptime_seconds: u64,
246}
247
248impl HostInventory {
249    /// Get information about providers in the inventory
250    pub fn components(&self) -> &Vec<ComponentDescription> {
251        self.components.as_ref()
252    }
253
254    /// Get information about providers in the inventory
255    pub fn providers(&self) -> &Vec<ProviderDescription> {
256        &self.providers
257    }
258
259    /// Get the ID of the host from which this inventory was returned
260    pub fn host_id(&self) -> &str {
261        &self.host_id
262    }
263
264    /// Get the friendly name of the host
265    pub fn friendly_name(&self) -> &str {
266        &self.friendly_name
267    }
268
269    /// Get the labels on the host
270    pub fn labels(&self) -> &BTreeMap<String, String> {
271        &self.labels
272    }
273
274    /// Get the version of the host
275    pub fn version(&self) -> &str {
276        &self.version
277    }
278
279    /// Get a human friendly host uptime description
280    pub fn uptime_human(&self) -> &str {
281        &self.uptime_human
282    }
283
284    /// Get the number of seconds the host has been up
285    pub fn uptime_seconds(&self) -> u64 {
286        self.uptime_seconds
287    }
288
289    #[must_use]
290    pub fn builder() -> HostInventoryBuilder {
291        HostInventoryBuilder::default()
292    }
293}
294
295#[derive(Default, Clone, PartialEq, Eq)]
296#[non_exhaustive]
297pub struct HostInventoryBuilder {
298    components: Option<Vec<ComponentDescription>>,
299    providers: Option<Vec<ProviderDescription>>,
300    host_id: Option<String>,
301    friendly_name: Option<String>,
302    labels: Option<BTreeMap<String, String>>,
303    version: Option<String>,
304    uptime_human: Option<String>,
305    uptime_seconds: Option<u64>,
306}
307
308impl HostInventoryBuilder {
309    #[must_use]
310    pub fn friendly_name(mut self, v: String) -> Self {
311        self.friendly_name = Some(v);
312        self
313    }
314
315    #[must_use]
316    pub fn host_id(mut self, v: String) -> Self {
317        self.host_id = Some(v);
318        self
319    }
320
321    #[must_use]
322    pub fn version(mut self, v: String) -> Self {
323        self.version = Some(v);
324        self
325    }
326
327    #[must_use]
328    pub fn components(mut self, v: Vec<ComponentDescription>) -> Self {
329        self.components = Some(v);
330        self
331    }
332
333    #[must_use]
334    pub fn providers(mut self, v: Vec<ProviderDescription>) -> Self {
335        self.providers = Some(v);
336        self
337    }
338
339    #[must_use]
340    pub fn uptime_human(mut self, v: String) -> Self {
341        self.uptime_human = Some(v);
342        self
343    }
344
345    #[must_use]
346    pub fn uptime_seconds(mut self, v: u64) -> Self {
347        self.uptime_seconds = Some(v);
348        self
349    }
350
351    #[must_use]
352    pub fn labels(mut self, v: BTreeMap<String, String>) -> Self {
353        self.labels = Some(v);
354        self
355    }
356
357    pub fn build(self) -> Result<HostInventory> {
358        Ok(HostInventory {
359            components: self.components.unwrap_or_default(),
360            providers: self.providers.unwrap_or_default(),
361            host_id: self
362                .host_id
363                .ok_or_else(|| "host_id is required".to_string())?,
364            friendly_name: self
365                .friendly_name
366                .ok_or_else(|| "friendly_name is required".to_string())?,
367            labels: self.labels.unwrap_or_default(),
368            version: self
369                .version
370                .ok_or_else(|| "version is required".to_string())?,
371            uptime_human: self
372                .uptime_human
373                .ok_or_else(|| "uptime_human is required".to_string())?,
374            uptime_seconds: self
375                .uptime_seconds
376                .ok_or_else(|| "uptime_seconds is required".to_string())?,
377        })
378    }
379}
380
381/// A label on a given host (ex. "arch=amd64")
382#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
383#[non_exhaustive]
384pub struct HostLabel {
385    /// Key of the label (`arch` in `arch=amd64`)
386    pub(crate) key: String,
387
388    /// Value of the label (`amd64` in `arch=amd64`)
389    pub(crate) value: String,
390}
391
392impl HostLabel {
393    /// Create a [`HostLabel`] from a key and value
394    pub fn from_kv(key: &str, value: &str) -> Self {
395        Self {
396            key: key.into(),
397            value: value.into(),
398        }
399    }
400
401    /// Get the host label key
402    pub fn key(&self) -> &str {
403        &self.key
404    }
405
406    /// Get the host label value
407    pub fn value(&self) -> &str {
408        &self.value
409    }
410}
411
412/// An identifier that represents a label on a given host (ex. "arch" in "arch=amd64")
413#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
414#[non_exhaustive]
415pub struct HostLabelIdentifier {
416    /// Key of the label (`arch` in `arch=amd64`)
417    pub(crate) key: String,
418}
419
420impl HostLabelIdentifier {
421    /// Create a [`HostLabelIdentifier`] from a key
422    pub fn from_key(key: &str) -> Self {
423        Self { key: key.into() }
424    }
425
426    /// Get the host label key
427    pub fn key(&self) -> &str {
428        &self.key
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use std::collections::BTreeMap;
435
436    use crate::{ComponentDescription, ProviderDescription};
437
438    use super::{Host, HostInventory};
439
440    #[test]
441    fn host_builder() {
442        assert_eq!(
443            Host {
444                rpc_host: Some("rpc_host".into()),
445                ctl_host: Some("ctl_host".into()),
446                friendly_name: "friendly_name".into(),
447                id: "id".into(),
448                js_domain: Some("js_domain".into()),
449                labels: BTreeMap::from([("a".into(), "b".into())]),
450                lattice: "lattice".into(),
451                uptime_human: Some("t".into()),
452                uptime_seconds: 1,
453                version: Some("1.0.0".into()),
454            },
455            Host::builder()
456                .rpc_host("rpc_host".into())
457                .ctl_host("ctl_host".into())
458                .friendly_name("friendly_name".into())
459                .id("id".into())
460                .js_domain("js_domain".into())
461                .labels(BTreeMap::from([("a".into(), "b".into())]))
462                .lattice("lattice".into())
463                .uptime_human("t".into())
464                .uptime_seconds(1)
465                .version("1.0.0".into())
466                .build()
467                .unwrap()
468        )
469    }
470
471    #[test]
472    fn host_inventory_builder() {
473        assert_eq!(
474            HostInventory {
475                components: Vec::from([ComponentDescription::default()]),
476                providers: Vec::from([ProviderDescription::default()]),
477                host_id: "host_id".into(),
478                friendly_name: "friendly_name".into(),
479                labels: BTreeMap::from([("a".into(), "b".into())]),
480                version: "1.0.0".into(),
481                uptime_human: "t".into(),
482                uptime_seconds: 1
483            },
484            HostInventory::builder()
485                .components(Vec::from([ComponentDescription::default()]))
486                .providers(Vec::from([ProviderDescription::default()]))
487                .host_id("host_id".into())
488                .friendly_name("friendly_name".into())
489                .labels(BTreeMap::from([("a".into(), "b".into())]))
490                .version("1.0.0".into())
491                .uptime_human("t".into())
492                .uptime_seconds(1)
493                .build()
494                .unwrap()
495        )
496    }
497}