use bollard::container::{Config, CreateContainerOptions};
use bollard::image::BuildImageOptions;
use bollard::models::{HostConfig, PortBinding};
use bollard::network::CreateNetworkOptions;
use bollard::service::{
EndpointPortConfig, EndpointSpec, Mount, MountTypeEnum, NetworkAttachmentConfig, ServiceSpec,
ServiceSpecMode, ServiceSpecModeReplicated, TaskSpec, TaskSpecContainerSpec,
TaskSpecContainerSpecFile, TaskSpecContainerSpecSecrets,
};
use bollard::Docker;
use futures_util::stream::StreamExt;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use super::log_exit;
use crate::service::JinxService;
pub async fn build_docker_image(client: Docker, jinx_service: &JinxService, bytes: Vec<u8>) {
let config = BuildImageOptions {
dockerfile: "Dockerfile",
t: &jinx_service.image_name,
..Default::default()
};
let mut image_build_stream = client.build_image(config, None, Some(bytes.into()));
while let Some(msg) = image_build_stream.next().await {
let message = match msg {
Ok(msg) => msg,
Err(err) => log_exit!("[DOCKER] Failed to get build message", err),
};
let stream = match message.stream {
Some(stream) => stream,
None => "".to_string(),
};
print!("{}", stream);
}
}
pub async fn create_jinx_network(client: Docker) {
let config = CreateNetworkOptions {
name: "jinx_network",
check_duplicate: true,
driver: "overlay",
internal: false,
..Default::default()
};
let network_id = match client.create_network(config).await {
Ok(id) => id,
Err(err) => log_exit!("[DOCKER] Failed to create jinx_network", err),
};
println!("Jinx network created: {:?}", network_id);
}
pub fn get_client() -> Docker {
let docker = match Docker::connect_with_socket_defaults() {
Ok(docker) => docker,
Err(err) => log_exit!("[DOCKER] Failed to connect to docker socket", err),
};
docker
}
pub fn get_dockerignore() -> Vec<String> {
let mut lines = vec![];
let current_dir = env::current_dir().expect("[DOCKER] Failed to get current directory");
let jinx_path = format!("{}/.dockerignore", current_dir.display());
let file = match File::open(jinx_path) {
Err(_err) => return lines,
Ok(file) => file,
};
let reader = BufReader::new(file);
for line in reader.lines() {
let ln = match line {
Err(err) => format!("Error: {}", err),
Ok(line) => line,
};
lines.push(ln);
}
lines
}
pub async fn create_service(client: Docker, jinx_service: &JinxService) {
let name = format!("{}{}", &jinx_service.name, "-jinx".to_string());
_create_service(client, jinx_service, name).await;
}
pub async fn create_jinx_proxy_service(client: Docker, jinx_service: &JinxService) {
let name = "jinx-proxy".to_string();
_create_service(client, jinx_service, name).await;
}
pub async fn run_image(
client: Docker,
image_name: &str,
ports: Vec<&str>,
vols: Vec<&str>,
envs: Option<Vec<&str>>,
cmds: Option<Vec<&str>>,
) {
let name = image_name.replace("/", "_");
let options = Some(CreateContainerOptions {
name: format!("jinx-{}", name),
});
let mut volumes: HashMap<&str, HashMap<(), ()>> = HashMap::new();
let mut port_bindings = HashMap::new();
for port in ports {
let split: Vec<&str> = port.split(':').collect();
let p = vec![PortBinding {
host_ip: None,
host_port: Some(split[0].to_string()),
}];
port_bindings.insert(split[1].to_string(), Some(p));
}
let host_config = HostConfig {
port_bindings: Some(port_bindings),
..Default::default()
};
for vol in vols {
volumes.insert(vol, HashMap::new());
}
let config = Config {
image: Some(image_name),
volumes: Some(volumes),
cmd: cmds,
env: envs,
host_config: Some(host_config),
..Default::default()
};
let container_id = match client.create_container(options, config).await {
Ok(id) => id,
Err(err) => log_exit!("[DOCKER] Failed to create image", err),
};
println!("Created container: {:?}", container_id.id);
match client
.start_container::<String>(&container_id.id, None)
.await
{
Ok(none) => none,
Err(err) => log_exit!("[DOCKER] Failed to start image", err),
};
println!("Started container: {:?}", container_id.id);
}
async fn _create_service(client: Docker, jinx_service: &JinxService, name: String) {
let networks = vec![NetworkAttachmentConfig {
target: Some("jinx_network".to_string()),
..Default::default()
}];
let mut ports = vec![];
if name.contains("jinx-proxy") {
ports.push(EndpointPortConfig {
target_port: Some(80),
published_port: Some(80),
..Default::default()
});
ports.push(EndpointPortConfig {
target_port: Some(443),
published_port: Some(443),
..Default::default()
});
} else {
ports.push(EndpointPortConfig {
target_port: Some(jinx_service.image_port),
published_port: jinx_service.published_port,
..Default::default()
});
}
let endpoint_spec = EndpointSpec {
ports: Some(ports),
..Default::default()
};
let mut mounts = vec![];
if jinx_service.image_volumes.is_some() {
let image_volumes = jinx_service.image_volumes.clone().unwrap();
for mount in image_volumes.iter() {
let split: Vec<&str> = mount.split(':').collect();
let m = Mount {
source: Some(split[0].to_string()),
target: Some(split[1].to_string()),
typ: Some(MountTypeEnum::BIND),
..Default::default()
};
mounts.push(m);
}
}
let mut envs = vec![];
if jinx_service.image_envs.is_some() {
envs = jinx_service.image_envs.clone().unwrap();
}
let mut secrets = vec![];
if jinx_service.image_secrets.is_some() {
let image_secrets = jinx_service.image_secrets.clone().unwrap();
for secret in image_secrets.iter() {
let split: Vec<&str> = secret.split(':').collect();
let s = TaskSpecContainerSpecSecrets {
secret_name: Some(split[0].to_string()),
secret_id: Some(split[1].to_string()),
file: Some(TaskSpecContainerSpecFile {
name: Some(split[0].to_string()),
uid: Some("0".to_string()),
gid: Some("0".to_string()),
mode: Some(292),
}),
};
secrets.push(s);
}
}
let service = ServiceSpec {
name: Some(name),
mode: Some(ServiceSpecMode {
replicated: Some(ServiceSpecModeReplicated { replicas: Some(1) }),
..Default::default()
}),
task_template: Some(TaskSpec {
container_spec: Some(TaskSpecContainerSpec {
image: Some(jinx_service.image_name.to_string()),
mounts: Some(mounts),
env: Some(envs.clone()),
secrets: Some(secrets),
..Default::default()
}),
..Default::default()
}),
networks: Some(networks),
endpoint_spec: Some(endpoint_spec),
..Default::default()
};
let service = match client.create_service(service, None).await {
Ok(svc) => svc,
Err(err) => log_exit!("[DOCKER] Failed to create jinx service", err),
};
let service_id = match service.id {
Some(id) => id,
None => "".to_string(),
};
println!("Jinx service created: {}", service_id);
}