blueprint-manager 0.4.0-alpha.3

Tangle Blueprint manager and Runner
use super::BlueprintSourceHandler;
use super::{BlueprintArgs, BlueprintEnvVars};
use crate::config::BlueprintManagerContext;
use crate::error::{Error, Result};
use crate::rt::ResourceLimits;
use crate::rt::service::Service;
use crate::sources::types::ImageRegistryFetcher;
use blueprint_client_tangle::ConfidentialityPolicy;
use blueprint_core::info;
use blueprint_runner::config::BlueprintEnvironment;
use std::path::{Path, PathBuf};
use tokio::process::Command;

pub struct ContainerSource {
    pub fetcher: ImageRegistryFetcher,
    pub blueprint_id: u64,
    pub blueprint_name: String,
    resolved_image: Option<String>,
}

impl ContainerSource {
    #[must_use]
    pub fn new(fetcher: ImageRegistryFetcher, blueprint_id: u64, blueprint_name: String) -> Self {
        Self {
            fetcher,
            blueprint_id,
            blueprint_name,
            resolved_image: None,
        }
    }
}

impl BlueprintSourceHandler for ContainerSource {
    async fn fetch(&mut self, _cache_dir: &Path) -> Result<PathBuf> {
        if let Some(resolved_image) = &self.resolved_image {
            return Ok(PathBuf::from(resolved_image));
        }

        let registry = self.fetcher.registry.clone();
        let image = self.fetcher.image.clone();
        let tag = self.fetcher.tag.clone();

        let full = format!("{registry}/{image}:{tag}");
        info!("Pulling image {full}");

        let status = Command::new("docker")
            .arg("pull")
            .arg(&full)
            .status()
            .await
            .map_err(|e| Error::Other(e.to_string()))?;

        if !status.success() {
            return Err(Error::Other(format!(
                "Docker pull failed for image {full} with exit code: {:?}",
                status.code()
            )));
        }

        let ret = PathBuf::from(&full);
        self.resolved_image = Some(full);
        Ok(ret)
    }

    async fn spawn(
        &mut self,
        ctx: &BlueprintManagerContext,
        limits: ResourceLimits,
        _blueprint_config: &BlueprintEnvironment,
        _id: u32,
        env: BlueprintEnvVars,
        args: BlueprintArgs,
        confidentiality_policy: ConfidentialityPolicy,
        sub_service_str: &str,
        cache_dir: &Path,
        runtime_dir: &Path,
    ) -> Result<Service> {
        let image = self.fetch(cache_dir).await?;
        Service::new_container(
            ctx,
            limits,
            runtime_dir,
            sub_service_str,
            image.to_string_lossy().to_string(),
            env,
            args,
            confidentiality_policy,
            false,
        )
        .await
    }

    fn blueprint_id(&self) -> u64 {
        self.blueprint_id
    }

    fn name(&self) -> String {
        self.blueprint_name.clone()
    }
}