forked from github-starred/komodo
begin implement periphery docker functions
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,4 +3,5 @@ node_modules
|
||||
dist
|
||||
.env
|
||||
|
||||
config.json
|
||||
config.json
|
||||
secrets.json
|
||||
8
.vscode/tasks.json
vendored
8
.vscode/tasks.json
vendored
@@ -44,6 +44,14 @@
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"label": "run periphery",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/periphery"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
|
||||
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -250,10 +250,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"docker_client",
|
||||
"docker",
|
||||
"dotenv",
|
||||
"envy",
|
||||
"git_client",
|
||||
"git",
|
||||
"mungos",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -484,10 +484,11 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docker_client"
|
||||
name = "docker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"axum",
|
||||
"bollard",
|
||||
"run_command",
|
||||
@@ -693,7 +694,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git_client"
|
||||
name = "git"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
@@ -1175,10 +1176,10 @@ dependencies = [
|
||||
"axum-extra",
|
||||
"bcrypt",
|
||||
"db_client",
|
||||
"docker_client",
|
||||
"docker",
|
||||
"dotenv",
|
||||
"envy",
|
||||
"git_client",
|
||||
"git",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"mungos",
|
||||
@@ -1201,10 +1202,10 @@ dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"bollard",
|
||||
"docker_client",
|
||||
"docker",
|
||||
"dotenv",
|
||||
"envy",
|
||||
"git_client",
|
||||
"git",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
||||
@@ -6,8 +6,8 @@ members = [
|
||||
"periphery",
|
||||
"builder",
|
||||
"lib/db_client",
|
||||
"lib/docker_client",
|
||||
"lib/git_client",
|
||||
"lib/docker",
|
||||
"lib/git",
|
||||
"lib/periphery_client",
|
||||
"lib/types"
|
||||
]
|
||||
@@ -6,8 +6,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
docker = { path = "../lib/docker" }
|
||||
git = { path = "../lib/git" }
|
||||
types = { path = "../lib/types" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5", features = ["ws"] }
|
||||
|
||||
@@ -6,8 +6,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
docker = { path = "../lib/docker" }
|
||||
git = { path = "../lib/git" }
|
||||
db = { package = "db_client", path = "../lib/db_client" }
|
||||
types = { path = "../lib/types" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
|
||||
@@ -25,5 +25,5 @@ pub fn load() -> CoreConfig {
|
||||
}
|
||||
|
||||
pub fn default_config_path() -> String {
|
||||
"./config.json".to_string()
|
||||
"/config/config.json".to_string()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "docker_client"
|
||||
name = "docker"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -11,4 +11,5 @@ run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
axum = "0.5"
|
||||
axum = "0.5"
|
||||
async_timing_util = "0.1.11"
|
||||
@@ -1,22 +1,24 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use axum::Extension;
|
||||
use bollard::{container::ListContainersOptions, Docker};
|
||||
use run_command::{async_run_command, CommandOutput};
|
||||
use types::{
|
||||
BasicContainerInfo, Conversion, Deployment, DockerRunArgs, EnvironmentVar, Log, RestartMode,
|
||||
BasicContainerInfo, Build, Conversion, Deployment, DockerRunArgs, EnvironmentVar, Log,
|
||||
RestartMode,
|
||||
};
|
||||
|
||||
pub type DeployExtension = Extension<Arc<DeployClient>>;
|
||||
pub type DockerExtension = Extension<Arc<DockerClient>>;
|
||||
|
||||
pub struct DeployClient {
|
||||
pub struct DockerClient {
|
||||
docker: Docker,
|
||||
}
|
||||
|
||||
impl DeployClient {
|
||||
pub fn extension() -> DeployExtension {
|
||||
let client = DeployClient {
|
||||
impl DockerClient {
|
||||
pub fn extension() -> DockerExtension {
|
||||
let client = DockerClient {
|
||||
docker: Docker::connect_with_local_defaults()
|
||||
.expect("failed to connect to docker daemon"),
|
||||
};
|
||||
@@ -50,28 +52,42 @@ impl DeployClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn docker_start(container_name: &str) -> (bool, Log) {
|
||||
let command = format!("start stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
// CONTAINER COMMANDS
|
||||
|
||||
pub fn parse_container_name(name: &str) -> String {
|
||||
name.to_lowercase().replace(" ", "_")
|
||||
}
|
||||
|
||||
pub async fn docker_stop(container_name: &str) -> (bool, Log) {
|
||||
pub async fn docker_start(container_name: &str) -> Log {
|
||||
let container_name = parse_container_name(container_name);
|
||||
let command = format!("docker start {container_name}");
|
||||
let start_ts = unix_timestamp_ms() as i64;
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker start", command, start_ts, output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop(container_name: &str) -> Log {
|
||||
let container_name = parse_container_name(container_name);
|
||||
let command = format!("docker stop {container_name}");
|
||||
let start_ts = unix_timestamp_ms() as i64;
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
output_into_log("docker stop", command, start_ts, output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_and_remove(container_name: &str) -> (bool, Log) {
|
||||
pub async fn docker_stop_and_remove(container_name: &str) -> Log {
|
||||
let container_name = parse_container_name(container_name);
|
||||
let command = format!("docker stop {container_name} && docker container rm {container_name}");
|
||||
let start_ts = unix_timestamp_ms() as i64;
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop and remove", output)
|
||||
output_into_log("docker stop and remove", command, start_ts, output)
|
||||
}
|
||||
|
||||
pub async fn deploy(deployment: &Deployment) -> (bool, Log) {
|
||||
let docker_run = docker_run_command(deployment);
|
||||
let output = async_run_command(&docker_run).await;
|
||||
output_into_log("docker run", output)
|
||||
pub async fn deploy(deployment: &Deployment) -> Log {
|
||||
let _ = docker_stop_and_remove(&parse_container_name(&deployment.name)).await;
|
||||
let command = docker_run_command(deployment);
|
||||
let start_ts = unix_timestamp_ms() as i64;
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker run", command, start_ts, output)
|
||||
}
|
||||
|
||||
pub fn docker_run_command(
|
||||
@@ -100,12 +116,7 @@ pub fn docker_run_command(
|
||||
let restart = parse_restart(restart);
|
||||
let environment = parse_environment(environment);
|
||||
let post_image = parse_post_image(post_image);
|
||||
format!("docker run -d {name}{container_user}{ports}{volumes}{network}{restart}{environment} {image}{post_image}")
|
||||
}
|
||||
|
||||
fn parse_container_name(name: &str) -> String {
|
||||
let name = name.to_lowercase().replace(" ", "_");
|
||||
format!("--name {name}")
|
||||
format!("docker run -d --name {name}{container_user}{ports}{volumes}{network}{restart}{environment} {image}{post_image}")
|
||||
}
|
||||
|
||||
fn parse_container_user(container_user: &Option<String>) -> String {
|
||||
@@ -156,12 +167,21 @@ fn parse_post_image(post_image: &Option<String>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn output_into_log(stage: &str, output: CommandOutput) -> (bool, Log) {
|
||||
// BUILD COMMANDS
|
||||
|
||||
pub async fn docker_build(_build: &Build) -> (bool, Log) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn output_into_log(stage: &str, command: String, start_ts: i64, output: CommandOutput) -> Log {
|
||||
let success = output.success();
|
||||
let log = Log {
|
||||
Log {
|
||||
stage: stage.to_string(),
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
};
|
||||
(success, log)
|
||||
command,
|
||||
success,
|
||||
start_ts,
|
||||
end_ts: unix_timestamp_ms() as i64,
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
use types::Build;
|
||||
|
||||
use crate::DockerClient;
|
||||
|
||||
impl DockerClient {
|
||||
pub async fn build(&self, build: Build) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use run_command::{async_run_command, CommandOutput};
|
||||
use types::{Deployment, Log};
|
||||
|
||||
use crate::DockerClient;
|
||||
|
||||
impl DockerClient {
|
||||
pub async fn deploy(&self, deployment: &Deployment) -> (bool, Log) {
|
||||
let docker_run = docker_run_command(deployment);
|
||||
let output = async_run_command(&docker_run).await;
|
||||
output_into_log("docker run", output)
|
||||
}
|
||||
|
||||
pub async fn docker_start_command(&self, container_name: &str) -> (bool, Log) {
|
||||
let command = format!("start stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_command(&self, container_name: &str) -> (bool, Log) {
|
||||
let command = format!("docker stop {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop", output)
|
||||
}
|
||||
|
||||
pub async fn docker_stop_and_remove(&self, container_name: &str) -> (bool, Log) {
|
||||
let command =
|
||||
format!("docker stop {container_name} && docker container rm {container_name}");
|
||||
let output = async_run_command(&command).await;
|
||||
output_into_log("docker stop and remove", output)
|
||||
}
|
||||
}
|
||||
|
||||
fn docker_run_command(deployment: &Deployment) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn output_into_log(stage: &str, output: CommandOutput) -> (bool, Log) {
|
||||
let success = output.success();
|
||||
let log = Log {
|
||||
stage: stage.to_string(),
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
};
|
||||
(success, log)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use axum::Extension;
|
||||
use bollard::{container::ListContainersOptions, Docker};
|
||||
use types::BasicContainerInfo;
|
||||
|
||||
mod build;
|
||||
mod deploy;
|
||||
|
||||
pub type DockerExtenstion = Extension<Arc<DockerClient>>;
|
||||
|
||||
pub struct DockerClient {
|
||||
client: Docker,
|
||||
}
|
||||
|
||||
impl DockerClient {
|
||||
pub fn extension() -> DockerExtenstion {
|
||||
let client = DockerClient {
|
||||
client: Docker::connect_with_local_defaults()
|
||||
.expect("failed to connect to docker daemon"),
|
||||
};
|
||||
Extension(Arc::new(client))
|
||||
}
|
||||
|
||||
pub async fn list_containers(&self) -> anyhow::Result<Vec<BasicContainerInfo>> {
|
||||
let res = self
|
||||
.client
|
||||
.list_containers(Some(ListContainersOptions::<String> {
|
||||
all: true,
|
||||
..Default::default()
|
||||
}))
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let info = BasicContainerInfo {
|
||||
name: s
|
||||
.names
|
||||
.ok_or(anyhow!("no names on container"))?
|
||||
.pop()
|
||||
.ok_or(anyhow!("no names on container (empty vec)"))?
|
||||
.replace("/", ""),
|
||||
state: s.state.unwrap().parse().unwrap(),
|
||||
status: s.status,
|
||||
};
|
||||
Ok::<_, anyhow::Error>(info)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<BasicContainerInfo>>>()?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "git_client"
|
||||
name = "git"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -134,7 +134,6 @@ pub struct Update {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub entity_id: Option<String>,
|
||||
pub operation: Operation,
|
||||
pub command: String,
|
||||
pub log: Vec<Log>,
|
||||
pub ts: i64,
|
||||
pub is_error: bool,
|
||||
@@ -179,8 +178,12 @@ pub struct BasicContainerInfo {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct Log {
|
||||
pub stage: String,
|
||||
pub command: String,
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
pub success: bool,
|
||||
pub start_ts: i64,
|
||||
pub end_ts: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
@@ -265,7 +268,9 @@ fn default_core_mongo_db_name() -> String {
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub struct PeripherySecrets {
|
||||
pub passkey: String,
|
||||
#[serde(default)]
|
||||
pub docker_accounts: DockerAccounts,
|
||||
#[serde(default)]
|
||||
pub github_accounts: GithubAccounts,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
types = { path = "../lib/types" }
|
||||
docker = { package = "docker_client", path = "../lib/docker_client" }
|
||||
docker = { path = "../lib/docker" }
|
||||
git = { path = "../lib/git" }
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
git = { package = "git_client", path = "../lib/git_client" }
|
||||
tokio = { version = "1.21", features = ["full"] }
|
||||
axum = { version = "0.5" }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
|
||||
55
periphery/src/api/container.rs
Normal file
55
periphery/src/api/container.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use axum::{
|
||||
extract::Path,
|
||||
http::StatusCode,
|
||||
routing::{get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use docker::{
|
||||
deploy, docker_start, docker_stop, docker_stop_and_remove, parse_container_name, DockerClient,
|
||||
DockerExtension,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use types::Deployment;
|
||||
|
||||
use crate::{helpers::handle_anyhow_error, response};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Container {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/list",
|
||||
get(|Extension(dc): DockerExtension| async move {
|
||||
let containers = dc.list_containers().await.map_err(handle_anyhow_error)?;
|
||||
response!(Json(containers))
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/start",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
Json(docker_start(&parse_container_name(&container.name)).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/stop",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
Json(docker_stop(&parse_container_name(&container.name)).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/remove",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
Json(docker_stop_and_remove(&parse_container_name(&container.name)).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/deploy",
|
||||
post(
|
||||
|Json(deployment): Json<Deployment>| async move { Json(deploy(&deployment).await) },
|
||||
),
|
||||
)
|
||||
.layer(DockerClient::extension())
|
||||
}
|
||||
1
periphery/src/api/mod.rs
Normal file
1
periphery/src/api/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod container;
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::fs::File;
|
||||
|
||||
use dotenv::dotenv;
|
||||
use serde::Deserialize;
|
||||
use types::PeripherySecrets;
|
||||
|
||||
@@ -12,6 +13,7 @@ struct Env {
|
||||
}
|
||||
|
||||
pub fn load() -> (u16, PeripherySecrets) {
|
||||
dotenv().ok();
|
||||
let env: Env = envy::from_env().expect("failed to parse env");
|
||||
let secrets_file = File::open(&env.secrets_path).expect("failed to find secrets");
|
||||
let secrets: PeripherySecrets =
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! response {
|
||||
($x:expr) => {
|
||||
Ok::<_, (axum::http::StatusCode, String)>($x)
|
||||
};
|
||||
}
|
||||
|
||||
use axum::http::StatusCode;
|
||||
|
||||
pub fn get_socket_addr(port: u16) -> SocketAddr {
|
||||
SocketAddr::from_str(&format!("0.0.0.0:{}", port)).expect("failed to parse socket addr")
|
||||
}
|
||||
|
||||
pub fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Internal Error: {err:#?}"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use axum::Router;
|
||||
use docker::DockerClient;
|
||||
use helpers::get_socket_addr;
|
||||
use axum::{extract::Path, http::StatusCode, routing::get, Extension, Json, Router};
|
||||
use docker::{DockerClient, DockerExtension};
|
||||
use helpers::{get_socket_addr, handle_anyhow_error};
|
||||
|
||||
mod api;
|
||||
mod config;
|
||||
mod deploy_client;
|
||||
mod helpers;
|
||||
|
||||
use api::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (port, secrets) = config::load();
|
||||
|
||||
let app = Router::new().layer(DockerClient::extension());
|
||||
let app = Router::new().nest("/container", container::router());
|
||||
|
||||
println!("starting montior periphery on port {port}");
|
||||
|
||||
axum::Server::bind(&get_socket_addr(port))
|
||||
.serve(app.into_make_service())
|
||||
|
||||
Reference in New Issue
Block a user