Skip to main content

clawft_kernel/workload_runtime/
seed_client.rs

1//! Seed HTTP calls shared by the adapter and the Seed operations
2//! ([`super::seed_ops`]): authenticated requests, the installed list and
3//! per-cog start / stop.
4
5use std::time::Duration;
6
7use serde_json::Value;
8
9use super::seed::SeedApiRuntime;
10use super::seed_http::Method;
11use super::seed_types::{API_TIMEOUT, InstalledCog, backend};
12use super::types::RuntimeError;
13use crate::workload_pkg::manifest::valid_cog_id;
14
15fn cog_path(id: &str, op: &str) -> Result<String, RuntimeError> {
16    if !valid_cog_id(id) {
17        return Err(RuntimeError::InvalidConfig(format!("bad cog id {id:?}")));
18    }
19    Ok(format!("/api/v1/apps/{id}/{op}"))
20}
21
22impl SeedApiRuntime {
23    /// Call the Seed API with the stored credential.
24    pub(super) async fn api(
25        &self,
26        method: Method,
27        path: &str,
28        body: Option<&Value>,
29        timeout: Duration,
30    ) -> Result<Value, RuntimeError> {
31        let token = self.creds.get(&self.cfg.node_id)?;
32        let (status, v) = self
33            .transport
34            .request(method, path, body, &token, timeout)
35            .await?;
36        if !(200..300).contains(&status) {
37            return Err(backend(path, status, &v));
38        }
39        Ok(v)
40    }
41
42    /// Installed cogs (`GET /api/v1/apps`).
43    pub async fn installed(&self) -> Result<Vec<InstalledCog>, RuntimeError> {
44        let v = self
45            .api(Method::Get, "/api/v1/apps", None, API_TIMEOUT)
46            .await?;
47        let list = v
48            .get("installed")
49            .and_then(Value::as_array)
50            .ok_or_else(|| RuntimeError::Backend("seed /api/v1/apps: no installed list".into()))?;
51        Ok(list
52            .iter()
53            .filter_map(|a| {
54                Some(InstalledCog {
55                    id: a.get("id")?.as_str()?.to_string(),
56                    version: a
57                        .get("version")
58                        .and_then(Value::as_str)
59                        .unwrap_or("")
60                        .to_string(),
61                    running: a.get("running").and_then(Value::as_bool).unwrap_or(false),
62                })
63            })
64            .collect())
65    }
66
67    /// Stop a cog by id (`POST /api/v1/apps/{id}/stop`).
68    pub async fn stop_cog(&self, id: &str) -> Result<(), RuntimeError> {
69        self.api(Method::Post, &cog_path(id, "stop")?, None, API_TIMEOUT)
70            .await
71            .map(|_| ())
72    }
73
74    /// Start a cog by id (`POST /api/v1/apps/{id}/start`).
75    pub async fn start_cog(&self, id: &str) -> Result<(), RuntimeError> {
76        self.api(Method::Post, &cog_path(id, "start")?, None, API_TIMEOUT)
77            .await
78            .map(|_| ())
79    }
80}