1use bollard::container::{Config, CreateContainerOptions};
2use bollard::image::BuildImageOptions;
3use bollard::models::{HostConfig, PortBinding};
4use bollard::network::CreateNetworkOptions;
5use bollard::service::{
6 EndpointPortConfig, EndpointSpec, Mount, MountTypeEnum, NetworkAttachmentConfig, ServiceSpec,
7 ServiceSpecMode, ServiceSpecModeReplicated, TaskSpec, TaskSpecContainerSpec,
8 TaskSpecContainerSpecFile, TaskSpecContainerSpecSecrets,
9};
10use bollard::Docker;
11use futures_util::stream::StreamExt;
12use std::collections::HashMap;
13use std::env;
14use std::fs::File;
15use std::io::BufRead;
16use std::io::BufReader;
17
18use super::log_exit;
19use crate::service::JinxService;
20
21pub async fn build_docker_image(client: Docker, jinx_service: &JinxService, bytes: Vec<u8>) {
23 let config = BuildImageOptions {
25 dockerfile: "Dockerfile",
26 t: &jinx_service.image_name,
27 ..Default::default()
28 };
29
30 let mut image_build_stream = client.build_image(config, None, Some(bytes.into()));
31
32 while let Some(msg) = image_build_stream.next().await {
33 let message = match msg {
34 Ok(msg) => msg,
35 Err(err) => log_exit!("[DOCKER] Failed to get build message", err),
36 };
37 let stream = match message.stream {
38 Some(stream) => stream,
39 None => "".to_string(),
40 };
41
42 print!("{}", stream);
43 }
44}
45
46pub async fn create_jinx_network(client: Docker) {
48 let config = CreateNetworkOptions {
50 name: "jinx_network",
51 check_duplicate: true,
52 driver: "overlay",
53 internal: false,
54 ..Default::default()
55 };
56
57 let network_id = match client.create_network(config).await {
58 Ok(id) => id,
59 Err(err) => log_exit!("[DOCKER] Failed to create jinx_network", err),
60 };
61
62 println!("Jinx network created: {:?}", network_id);
63}
64
65pub fn get_client() -> Docker {
67 let docker = match Docker::connect_with_socket_defaults() {
68 Ok(docker) => docker,
69 Err(err) => log_exit!("[DOCKER] Failed to connect to docker socket", err),
70 };
71
72 docker
73}
74
75pub fn get_dockerignore() -> Vec<String> {
77 let mut lines = vec![];
78
79 let current_dir = env::current_dir().expect("[DOCKER] Failed to get current directory");
81
82 let jinx_path = format!("{}/.dockerignore", current_dir.display());
84 let file = match File::open(jinx_path) {
85 Err(_err) => return lines,
86 Ok(file) => file,
87 };
88
89 let reader = BufReader::new(file);
91
92 for line in reader.lines() {
94 let ln = match line {
95 Err(err) => format!("Error: {}", err),
96 Ok(line) => line,
97 };
98 lines.push(ln);
99 }
100
101 lines
102}
103
104pub async fn create_service(client: Docker, jinx_service: &JinxService) {
106 let name = format!("{}{}", &jinx_service.name, "-jinx".to_string());
108
109 _create_service(client, jinx_service, name).await;
110}
111
112pub async fn create_jinx_proxy_service(client: Docker, jinx_service: &JinxService) {
114 let name = "jinx-proxy".to_string();
116
117 _create_service(client, jinx_service, name).await;
118}
119
120pub async fn run_image(
122 client: Docker,
123 image_name: &str,
124 ports: Vec<&str>,
125 vols: Vec<&str>,
126 envs: Option<Vec<&str>>,
127 cmds: Option<Vec<&str>>,
128) {
129 let name = image_name.replace("/", "_");
130 let options = Some(CreateContainerOptions {
131 name: format!("jinx-{}", name),
132 });
133
134 let mut volumes: HashMap<&str, HashMap<(), ()>> = HashMap::new();
135
136 let mut port_bindings = HashMap::new();
137 for port in ports {
138 let split: Vec<&str> = port.split(':').collect();
139 let p = vec![PortBinding {
140 host_ip: None,
141 host_port: Some(split[0].to_string()),
142 }];
143
144 port_bindings.insert(split[1].to_string(), Some(p));
145 }
146
147 let host_config = HostConfig {
148 port_bindings: Some(port_bindings),
149 ..Default::default()
150 };
151
152 for vol in vols {
153 volumes.insert(vol, HashMap::new());
154 }
155
156 let config = Config {
157 image: Some(image_name),
158 volumes: Some(volumes),
159 cmd: cmds,
160 env: envs,
161 host_config: Some(host_config),
162 ..Default::default()
163 };
164
165 let container_id = match client.create_container(options, config).await {
166 Ok(id) => id,
167 Err(err) => log_exit!("[DOCKER] Failed to create image", err),
168 };
169
170 println!("Created container: {:?}", container_id.id);
171
172 match client
173 .start_container::<String>(&container_id.id, None)
174 .await
175 {
176 Ok(none) => none,
177 Err(err) => log_exit!("[DOCKER] Failed to start image", err),
178 };
179
180 println!("Started container: {:?}", container_id.id);
181}
182
183async fn _create_service(client: Docker, jinx_service: &JinxService, name: String) {
184 let networks = vec![NetworkAttachmentConfig {
186 target: Some("jinx_network".to_string()),
187 ..Default::default()
188 }];
189
190 let mut ports = vec![];
192 if name.contains("jinx-proxy") {
193 ports.push(EndpointPortConfig {
194 target_port: Some(80),
195 published_port: Some(80),
196 ..Default::default()
197 });
198 ports.push(EndpointPortConfig {
199 target_port: Some(443),
200 published_port: Some(443),
201 ..Default::default()
202 });
203 } else {
204 ports.push(EndpointPortConfig {
205 target_port: Some(jinx_service.image_port),
206 published_port: jinx_service.published_port,
207 ..Default::default()
208 });
209 }
210
211 let endpoint_spec = EndpointSpec {
212 ports: Some(ports),
213 ..Default::default()
214 };
215
216 let mut mounts = vec![];
218 if jinx_service.image_volumes.is_some() {
219 let image_volumes = jinx_service.image_volumes.clone().unwrap();
220
221 for mount in image_volumes.iter() {
222 let split: Vec<&str> = mount.split(':').collect();
223 let m = Mount {
224 source: Some(split[0].to_string()),
225 target: Some(split[1].to_string()),
226 typ: Some(MountTypeEnum::BIND),
227 ..Default::default()
228 };
229 mounts.push(m);
230 }
231 }
232
233 let mut envs = vec![];
235 if jinx_service.image_envs.is_some() {
236 envs = jinx_service.image_envs.clone().unwrap();
237 }
238
239 let mut secrets = vec![];
241 if jinx_service.image_secrets.is_some() {
242 let image_secrets = jinx_service.image_secrets.clone().unwrap();
243 for secret in image_secrets.iter() {
244 let split: Vec<&str> = secret.split(':').collect();
245 let s = TaskSpecContainerSpecSecrets {
246 secret_name: Some(split[0].to_string()),
247 secret_id: Some(split[1].to_string()),
248 file: Some(TaskSpecContainerSpecFile {
249 name: Some(split[0].to_string()),
250 uid: Some("0".to_string()),
251 gid: Some("0".to_string()),
252 mode: Some(292),
253 }),
254 };
255 secrets.push(s);
256 }
257 }
258
259 let service = ServiceSpec {
261 name: Some(name),
262 mode: Some(ServiceSpecMode {
263 replicated: Some(ServiceSpecModeReplicated { replicas: Some(1) }),
264 ..Default::default()
265 }),
266 task_template: Some(TaskSpec {
267 container_spec: Some(TaskSpecContainerSpec {
268 image: Some(jinx_service.image_name.to_string()),
269 mounts: Some(mounts),
270 env: Some(envs.clone()),
271 secrets: Some(secrets),
272 ..Default::default()
273 }),
274 ..Default::default()
275 }),
276 networks: Some(networks),
277 endpoint_spec: Some(endpoint_spec),
278 ..Default::default()
279 };
280
281 let service = match client.create_service(service, None).await {
282 Ok(svc) => svc,
283 Err(err) => log_exit!("[DOCKER] Failed to create jinx service", err),
284 };
285 let service_id = match service.id {
286 Some(id) => id,
287 None => "".to_string(),
288 };
289
290 println!("Jinx service created: {}", service_id);
291}