support_kit/deployments/
deployment_command.rs

1use clap::Parser;
2
3use crate::SupportControl;
4
5use super::DeploymentContext;
6
7#[derive(Clone, Copy, Parser, Debug, PartialEq)]
8#[clap(rename_all = "kebab-case")]
9pub enum DeploymentCommand {
10    Install,
11    Setup,
12    Build,
13    Start,
14    List,
15    Login,
16    Pull,
17    Push,
18    Restart,
19}
20
21impl DeploymentCommand {
22    #[tracing::instrument(skip(self, controller), level = "trace")]
23    pub async fn exec_remote(&self, controller: &SupportControl) -> crate::Result<()> {
24        Ok(exec_remote_container_op(&controller, *self).await?)
25    }
26
27    #[tracing::instrument(skip(self, controller), level = "trace")]
28    pub async fn exec_local(&self, controller: &SupportControl) -> crate::Result<()> {
29        Ok(exec_local_container_op(&controller, self).await?)
30    }
31}
32
33#[tracing::instrument(skip(controller, command), level = "trace")]
34pub async fn exec_local_container_op(
35    controller: &SupportControl,
36    command: &DeploymentCommand,
37) -> crate::Result<()> {
38    let deployment_context = DeploymentContext::from(controller);
39    tracing::trace!(command = ?command, "executing local container operation");
40    match command {
41        DeploymentCommand::Build => {
42            for image in deployment_context.images {
43                image.build()?.run()?;
44            }
45        }
46        DeploymentCommand::Install => {
47            deployment_context.get_install_script()?.run()?;
48            deployment_context.install_docker()?.run()?;
49        }
50        DeploymentCommand::List => deployment_context.list_containers()?.run()?,
51        DeploymentCommand::Login => deployment_context.login()?.run()?,
52        DeploymentCommand::Pull => {
53            for image in deployment_context.images {
54                image.pull()?.run()?;
55            }
56        }
57        DeploymentCommand::Push => {
58            for image in deployment_context.images {
59                image.push()?.run()?;
60            }
61        }
62        DeploymentCommand::Restart => {
63            let path = deployment_context.emit_config()?;
64            let path = if path.is_absolute() {
65                path
66            } else {
67                std::env::current_dir()
68                    .expect("unable to get current directory")
69                    .join(path)
70            };
71
72            for image in deployment_context.images {
73                image.pull()?.run()?;
74                image.kill_all()?.run()?;
75                image.start(path.clone())?.run()?;
76            }
77        }
78        DeploymentCommand::Setup => {
79            deployment_context.setup_cert_volume()?.run()?;
80
81            for image in deployment_context.images {
82                image.setup_log_volume()?.run()?;
83                image.setup_data_volume()?.run()?;
84                image.setup_config_volume()?.run()?;
85            }
86        }
87        DeploymentCommand::Start => {
88            let path = deployment_context.emit_config()?;
89            let path = if path.is_absolute() {
90                path
91            } else {
92                std::env::current_dir()
93                    .expect("unable to get current directory")
94                    .join(path)
95            };
96
97            for image in deployment_context.images {
98                image.start(path.clone())?.run()?;
99            }
100        }
101    }
102
103    Ok(())
104}
105
106#[tracing::instrument(skip(controller, command), level = "trace")]
107pub async fn exec_remote_container_op(
108    controller: &SupportControl,
109    command: DeploymentCommand,
110) -> crate::Result<()> {
111    let deployment_context = DeploymentContext::from(controller);
112    tracing::trace!(command = ?command, "executing remote container operation");
113    match command {
114        DeploymentCommand::Build => {
115            for image in deployment_context.images {
116                controller
117                    .on_remotes()
118                    .commands(image.build()?)
119                    .call()
120                    .await?;
121            }
122        }
123        DeploymentCommand::Install => {
124            controller
125                .on_remotes()
126                .commands(bon::vec![
127                    deployment_context.get_install_script()?,
128                    deployment_context.install_docker()?
129                ])
130                .call()
131                .await?;
132        }
133        DeploymentCommand::List => {
134            controller
135                .on_remotes()
136                .commands(deployment_context.list_containers()?)
137                .call()
138                .await?;
139        }
140        DeploymentCommand::Login => {
141            controller
142                .on_remotes()
143                .commands(deployment_context.login()?)
144                .call()
145                .await?;
146        }
147        DeploymentCommand::Pull => {
148            for image in deployment_context.images {
149                controller
150                    .on_remotes()
151                    .commands(image.pull()?)
152                    .call()
153                    .await?;
154            }
155        }
156        DeploymentCommand::Push => {}
157        DeploymentCommand::Restart => {
158            let path = deployment_context.emit_config()?;
159            let from_path = path.to_string_lossy();
160            let to_path = format!("./{name}.json", name = controller.config.name());
161
162            for host in deployment_context.hosts {
163                host.send_file(&from_path, &to_path)?.run()?;
164            }
165
166            for image in deployment_context.images {
167                controller
168                    .on_remotes()
169                    .commands(bon::vec![
170                        image.pull()?,
171                        image.kill_all()?,
172                        image.start(to_path.clone())?
173                    ])
174                    .call()
175                    .await?;
176            }
177        }
178        DeploymentCommand::Setup => {
179            let certs_volume = deployment_context.setup_cert_volume()?;
180
181            for image in deployment_context.images {
182                controller
183                    .on_remotes()
184                    .commands(bon::vec![
185                        certs_volume.clone(),
186                        image.setup_log_volume()?,
187                        image.setup_data_volume()?,
188                        image.setup_config_volume()?
189                    ])
190                    .call()
191                    .await?;
192            }
193        }
194        DeploymentCommand::Start => {
195            let path = deployment_context.emit_config()?;
196
197            let from_path = path.to_string_lossy();
198            let to_path = format!("{name}.container.json", name = controller.config.name());
199
200            for host in deployment_context.hosts {
201                host.send_file(&from_path, &to_path)?.run()?;
202            }
203
204            for image_controller in deployment_context.images {
205                controller
206                    .on_remotes()
207                    .commands(image_controller.start(to_path.clone())?)
208                    .call()
209                    .await?;
210            }
211        }
212    }
213
214    Ok(())
215}