manta_server/service/
ephemeral_env.rs

1//! Ephemeral CFS environment provisioning — launches a temporary
2//! container booted from an existing IMS image and returns its
3//! hostname.
4
5use csm_rs::ShastaClient;
6use manta_backend_dispatcher::error::Error;
7
8use crate::server::common::app_context::InfraContext;
9use manta_shared::common::jwt_ops;
10
11const EPHEMERAL_IMAGE_NAME: &str = "__ephemeral_image";
12
13/// Create an ephemeral CFS environment and return the SSH hostname.
14pub async fn exec(
15  infra: &InfraContext<'_>,
16  token: &str,
17  image_id: &str,
18) -> Result<String, Error> {
19  let user_public_key_name =
20    jwt_ops::get_preferred_username(token).map_err(|e| {
21      Error::JwtMalformed(format!(
22        "claim 'preferred_user' not found in JWT token: {e}"
23      ))
24    })?;
25
26  tracing::info!("Looking for user '{}' public SSH key", user_public_key_name);
27
28  let shasta = ShastaClient::new(
29    infra.shasta_base_url,
30    infra.shasta_root_cert.to_vec(),
31    infra.socks5_proxy.map(|s| s.to_string()),
32  )
33  .map_err(|e| {
34    Error::BadRequest(format!("Could not build Shasta HTTP client: {e}"))
35  })?;
36
37  let user_public_ssh_id_value = if let Ok(Some(user_public_ssh_value)) = shasta
38    .ims_public_keys_v3_get_single(token, &user_public_key_name)
39    .await
40  {
41    user_public_ssh_value["id"].clone()
42  } else {
43    return Err(Error::NotFound(format!(
44      "User '{user_public_key_name}' does not have an SSH public key in Alps. \
45       Please contact platform sys admins."
46    )));
47  };
48
49  tracing::info!("SSH key found with ID {}", user_public_ssh_id_value);
50  tracing::info!(
51    "Creating ephemeral environment based on image ID {}",
52    image_id
53  );
54
55  let resp_json = shasta
56    .ims_job_post_customize(
57      token,
58      EPHEMERAL_IMAGE_NAME,
59      image_id,
60      user_public_ssh_id_value.as_str().ok_or_else(|| {
61        Error::MissingField("SSH key ID is not a string".to_string())
62      })?,
63    )
64    .await
65    .map_err(|e| {
66      Error::BadRequest(format!(
67        "Could not create ephemeral environment based on image ID {image_id}: {e}"
68      ))
69    })?;
70
71  let hostname = resp_json
72    .pointer("/ssh_containers/0/connection_info/customer_access/host")
73    .and_then(|v| v.as_str())
74    .ok_or_else(|| {
75      Error::MissingField(
76        "Failed to get SSH container hostname from ephemeral env response"
77          .to_string(),
78      )
79    })?
80    .to_string();
81
82  tracing::info!("Ephemeral environment created — SSH hostname: {}", hostname);
83
84  Ok(hostname)
85}