begin implement periphery docker functions

This commit is contained in:
beckerinj
2022-11-08 02:37:37 -05:00
parent b936ff3e36
commit ced8aa661b
21 changed files with 169 additions and 162 deletions

3
.gitignore vendored
View File

@@ -3,4 +3,5 @@ node_modules
dist
.env
config.json
config.json
secrets.json

8
.vscode/tasks.json vendored
View File

@@ -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
View File

@@ -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",

View File

@@ -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"
]

View File

@@ -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"] }

View File

@@ -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"] }

View File

@@ -25,5 +25,5 @@ pub fn load() -> CoreConfig {
}
pub fn default_config_path() -> String {
"./config.json".to_string()
"/config/config.json".to_string()
}

View File

@@ -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"

View File

@@ -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,
}
}

View File

@@ -1,9 +0,0 @@
use types::Build;
use crate::DockerClient;
impl DockerClient {
pub async fn build(&self, build: Build) -> anyhow::Result<()> {
todo!()
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -1,5 +1,5 @@
[package]
name = "git_client"
name = "git"
version = "0.1.0"
edition = "2021"

View File

@@ -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,
}

View File

@@ -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"] }

View 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
View File

@@ -0,0 +1 @@
pub mod container;

View File

@@ -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 =

View File

@@ -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:#?}"),
)
}

View File

@@ -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())