support_kit/deployments/
deployment_context.rs

1use std::path::PathBuf;
2
3use figment::providers::Serialized;
4
5use crate::{shell, Configuration, Registry, ShellCommand, SupportControl};
6
7use super::{HostDeploymentContext, ImageDeploymentContext};
8
9#[derive(Debug, Clone, bon::Builder)]
10pub struct DeploymentContext {
11    #[builder(default)]
12    pub figment: figment::Figment,
13    pub config: Configuration,
14    #[builder(default, into)]
15    pub images: Vec<ImageDeploymentContext>,
16    #[builder(default, into)]
17    pub hosts: Vec<HostDeploymentContext>,
18    pub registry: Registry,
19}
20
21impl DeploymentContext {
22    pub fn from_controller(controller: &SupportControl) -> Self {
23        let figment = controller.figment().unwrap_or_default();
24        let config = controller.config.clone();
25        let host_defs = config
26            .deployment
27            .clone()
28            .map(|deployment| deployment.hosts)
29            .unwrap_or_default();
30
31        let (image_defs, registry) = config
32            .deployment
33            .clone()
34            .and_then(|deployment| deployment.artifacts)
35            .and_then(|artifacts| artifacts.containers)
36            .map(|containers| (containers.images, containers.registry.unwrap_or_default()))
37            .unwrap_or_default();
38
39        let mut images = vec![];
40        let mut hosts = vec![];
41
42        for host in host_defs {
43            hosts.push(
44                HostDeploymentContext::builder()
45                    .config(config.clone())
46                    .host(host)
47                    .registry(registry.clone())
48                    .build(),
49            );
50        }
51
52        for image in image_defs {
53            images.push(
54                ImageDeploymentContext::builder()
55                    .config(config.clone())
56                    .image(image)
57                    .registry(registry.clone())
58                    .build(),
59            );
60        }
61
62        Self::builder()
63            .figment(figment)
64            .config(config)
65            .images(images)
66            .hosts(hosts)
67            .registry(registry)
68            .build()
69    }
70
71    #[tracing::instrument(skip(self), level = "trace")]
72    pub fn emit_config(&self) -> crate::Result<PathBuf> {
73        let path =
74            std::env::temp_dir().join(format!("{name}.container.json", name = self.config.name()));
75
76        let contents = serde_json::to_value(&self.config)?;
77        let all_configuration = self
78            .figment
79            .clone()
80            .merge(Serialized::from(contents, "default"))
81            .extract::<serde_json::Value>()?;
82
83        let contents = serde_json::to_string(&all_configuration)?;
84
85        tracing::debug!(path = ?path, contents = ?contents, "writing container config file");
86
87        std::fs::write(&path, contents).expect("Unable to write file");
88
89        Ok(path)
90    }
91
92    #[tracing::instrument(skip(self), level = "trace")]
93    pub fn setup_cert_volume(&self) -> crate::Result<ShellCommand> {
94        shell(format!("docker volume create certs"))
95    }
96
97    #[tracing::instrument(skip(self), level = "trace")]
98    pub fn setup_network(&self) -> crate::Result<ShellCommand> {
99        shell(format!(
100            "docker network create {name}-network",
101            name = self.config.name()
102        ))
103    }
104
105    /// Get the shell script from the [docker-install repo][docker-install].
106    ///
107    /// [docker-install]: https://github.com/docker/docker-install
108    #[tracing::instrument(skip(self), level = "trace")]
109    pub fn get_install_script(&self) -> crate::Result<ShellCommand> {
110        shell(format!(
111            "curl -fsSL https://get.docker.com -o get-docker.sh"
112        ))
113    }
114
115    /// Install Docker on the host machine.
116    #[tracing::instrument(skip(self), level = "trace")]
117    pub fn install_docker(&self) -> crate::Result<ShellCommand> {
118        shell(format!("sh get-docker.sh"))
119    }
120
121    #[tracing::instrument(skip(self), level = "trace")]
122    pub fn login(&self) -> crate::Result<ShellCommand> {
123        shell(format!(
124            "docker login {host} -u {account} -p {token}",
125            host = self.registry.host,
126            account = self.registry.account,
127            token = self.registry.token
128        ))
129    }
130
131    #[tracing::instrument(skip(self), level = "trace")]
132    pub fn list_containers(&self) -> crate::Result<ShellCommand> {
133        shell(format!("docker ps"))
134    }
135}
136
137impl From<&SupportControl> for DeploymentContext {
138    fn from(controller: &SupportControl) -> Self {
139        Self::from_controller(controller)
140    }
141}