diff --git a/Cargo.lock b/Cargo.lock index 095c05bc8..56ad7e0d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1463,6 +1463,7 @@ name = "periphery_client" version = "0.1.0" dependencies = [ "anyhow", + "helpers", "reqwest", "serde", "serde_json", diff --git a/lib/helpers/src/git.rs b/lib/helpers/src/git.rs index 24958c947..774bf6622 100644 --- a/lib/helpers/src/git.rs +++ b/lib/helpers/src/git.rs @@ -1,59 +1,69 @@ use std::{path::PathBuf, str::FromStr}; use ::run_command::async_run_command; -use anyhow::{anyhow, Context}; +use anyhow::anyhow; use async_timing_util::unix_timestamp_ms; -use types::{Build, Deployment, GithubToken, Log}; +use serde::{Deserialize, Serialize}; +use types::{Build, Command, Deployment, GithubToken, GithubUsername, Log}; use crate::run_monitor_command; -pub async fn clone_build_repo( - Build { +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct CloneArgs { + name: String, + repo: Option, + branch: Option, + on_clone: Option, + pub github_account: Option, +} + +impl From<&Deployment> for CloneArgs { + fn from(d: &Deployment) -> Self { + CloneArgs { + name: d.name.clone(), + repo: d.repo.clone(), + branch: d.branch.clone(), + on_clone: d.on_clone.clone(), + github_account: d.github_account.clone(), + } + } +} + +impl From<&Build> for CloneArgs { + fn from(b: &Build) -> Self { + CloneArgs { + name: b.name.clone(), + repo: b.repo.clone(), + branch: b.branch.clone(), + on_clone: b.on_clone.clone(), + github_account: b.github_account.clone(), + } + } +} + +pub async fn clone_repo( + clone_args: impl Into, + repo_dir: &str, + access_token: Option, +) -> anyhow::Result> { + let CloneArgs { + name, repo, branch, on_clone, .. - }: &Build, - destination: &str, - access_token: Option, -) -> anyhow::Result> { + } = clone_args.into(); let repo = repo.as_ref().ok_or(anyhow!("build has no repo attached"))?; - let clone_log = clone(repo, destination, branch, access_token).await; + let mut repo_dir = PathBuf::from_str(repo_dir)?; + repo_dir.push(name); + let destination = repo_dir.display().to_string(); + let clone_log = clone(repo, &destination, &branch, access_token).await; let mut logs = vec![clone_log]; if let Some(command) = on_clone { - let mut path = PathBuf::from_str(destination) - .context("failed to parse destination path to pathbuf")?; - path.push(&command.path); + repo_dir.push(&command.path); let on_clone_log = run_monitor_command( "on clone", - format!("cd {} && {}", path.display(), command.command), - ) - .await; - logs.push(on_clone_log); - } - Ok(logs) -} - -pub async fn clone_deployment_repo( - Deployment { - repo, - branch, - on_clone, - .. - }: &Deployment, - destination: &str, - access_token: Option, -) -> anyhow::Result> { - let repo = repo.as_ref().ok_or(anyhow!("build has no repo attached"))?; - let clone_log = clone(repo, destination, branch, access_token).await; - let mut logs = vec![clone_log]; - if let Some(command) = on_clone { - let mut path = PathBuf::from_str(destination) - .context("failed to parse destination path to pathbuf")?; - path.push(&command.path); - let on_clone_log = run_monitor_command( - "on clone", - format!("cd {} && {}", path.display(), command.command), + format!("cd {} && {}", repo_dir.display(), command.command), ) .await; logs.push(on_clone_log); diff --git a/lib/periphery_client/Cargo.toml b/lib/periphery_client/Cargo.toml index 1c3bb2034..2b2e51c92 100644 --- a/lib/periphery_client/Cargo.toml +++ b/lib/periphery_client/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] types = { path = "../types" } +helpers = { path = "../helpers" } reqwest = { version = "0.11", features = ["json"] } serde = "1.0" serde_json = "1.0" diff --git a/lib/periphery_client/src/container.rs b/lib/periphery_client/src/container.rs new file mode 100644 index 000000000..af4eaf141 --- /dev/null +++ b/lib/periphery_client/src/container.rs @@ -0,0 +1,58 @@ +use serde_json::json; +use types::{BasicContainerInfo, Deployment, Log, Server}; + +use crate::PeripheryClient; + +impl PeripheryClient { + pub async fn container_list(&self, server: &Server) -> anyhow::Result> { + self.get_json(server, "/container/list").await + } + + pub async fn container_start( + &self, + server: &Server, + container_name: &str, + ) -> anyhow::Result { + self.post_json( + server, + &format!("/container/start"), + &json!({ "name": container_name }), + ) + .await + } + + pub async fn container_stop( + &self, + server: &Server, + container_name: &str, + ) -> anyhow::Result { + self.post_json( + server, + &format!("/container/stop"), + &json!({ "name": container_name }), + ) + .await + } + + pub async fn container_remove( + &self, + server: &Server, + container_name: &str, + ) -> anyhow::Result { + self.post_json( + server, + &format!("/container/remove"), + &json!({ "name": container_name }), + ) + .await + } + + pub async fn deploy(&self, server: &Server, deployment: &Deployment) -> anyhow::Result { + self.post_json(server, &format!("/container/deploy"), deployment) + .await + } + + pub async fn container_prune(&self, server: &Server) -> anyhow::Result { + self.post_json(server, "container/prune", &json!({})).await + } +} diff --git a/lib/periphery_client/src/git.rs b/lib/periphery_client/src/git.rs new file mode 100644 index 000000000..3227f72b7 --- /dev/null +++ b/lib/periphery_client/src/git.rs @@ -0,0 +1,15 @@ +use helpers::git::CloneArgs; +use types::{Log, Server}; + +use crate::PeripheryClient; + +impl PeripheryClient { + pub async fn clone( + &self, + server: &Server, + clone_args: impl Into, + ) -> anyhow::Result> { + let clone_args: CloneArgs = clone_args.into(); + self.post_json(server, "/git/clone", &clone_args).await + } +} diff --git a/lib/periphery_client/src/lib.rs b/lib/periphery_client/src/lib.rs index cc37eab07..d35580fcb 100644 --- a/lib/periphery_client/src/lib.rs +++ b/lib/periphery_client/src/lib.rs @@ -1,8 +1,12 @@ use anyhow::{anyhow, Context}; use reqwest::StatusCode; use serde::{de::DeserializeOwned, Serialize}; -use serde_json::json; -use types::{BasicContainerInfo, Deployment, Log, Server}; +use types::Server; + +mod container; +mod git; +mod network; +mod stats; pub struct PeripheryClient { http_client: reqwest::Client, @@ -15,52 +19,12 @@ impl PeripheryClient { } } - pub async fn container_list(&self, server: &Server) -> anyhow::Result> { - self.get_json(server, "/container/list").await + pub async fn get_github_accounts(&self, server: &Server) -> anyhow::Result> { + self.get_json(server, "/accounts/github").await } - pub async fn container_start( - &self, - server: &Server, - container_name: &str, - ) -> anyhow::Result { - self.post_json( - server, - &format!("/container/start"), - &json!({ "name": container_name }), - ) - .await - } - - pub async fn container_stop( - &self, - server: &Server, - container_name: &str, - ) -> anyhow::Result { - self.post_json( - server, - &format!("/container/stop"), - &json!({ "name": container_name }), - ) - .await - } - - pub async fn container_remove( - &self, - server: &Server, - container_name: &str, - ) -> anyhow::Result { - self.post_json( - server, - &format!("/container/remove"), - &json!({ "name": container_name }), - ) - .await - } - - pub async fn deploy(&self, server: &Server, deployment: &Deployment) -> anyhow::Result { - self.post_json(server, &format!("/container/deploy"), deployment) - .await + pub async fn get_docker_accounts(&self, server: &Server) -> anyhow::Result> { + self.get_json(server, "/accounts/docker").await } async fn get_json( diff --git a/lib/periphery_client/src/network.rs b/lib/periphery_client/src/network.rs new file mode 100644 index 000000000..fcffa8011 --- /dev/null +++ b/lib/periphery_client/src/network.rs @@ -0,0 +1,32 @@ +use serde_json::json; +use types::{Log, Server}; + +use crate::PeripheryClient; + +impl PeripheryClient { + pub async fn network_create( + &self, + server: &Server, + name: &str, + driver: Option, + ) -> anyhow::Result { + self.post_json( + server, + "/network/create", + &json!({ + "name": name, + "driver": driver + }), + ) + .await + } + + pub async fn network_delete(&self, server: &Server, name: &str) -> anyhow::Result { + self.post_json(server, "/network/delete", &json!({ "name": name })) + .await + } + + pub async fn network_prune(&self, server: &Server) -> anyhow::Result { + self.post_json(server, "/network/prune", &json!({})).await + } +} diff --git a/lib/periphery_client/src/stats.rs b/lib/periphery_client/src/stats.rs new file mode 100644 index 000000000..21e24d301 --- /dev/null +++ b/lib/periphery_client/src/stats.rs @@ -0,0 +1,13 @@ +use types::{Server, SystemStats}; + +use crate::PeripheryClient; + +impl PeripheryClient { + pub async fn get_system_stats(&self, server: &Server) -> anyhow::Result { + self.get_json(server, "/stats/system").await + } + + pub async fn get_docker_stats(&self, server: &Server) -> anyhow::Result { + self.get_json(server, "/stats/docker").await + } +} diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 44e36f2b4..30df7a88d 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -301,12 +301,18 @@ pub struct PeripheryConfig { pub docker_accounts: DockerAccounts, #[serde(default)] pub github_accounts: GithubAccounts, + #[serde(default = "default_repo_dir")] + pub repo_dir: String, } fn default_periphery_port() -> u16 { 9001 } +fn default_repo_dir() -> String { + "/repos".to_string() +} + #[derive(Deserialize, Debug)] pub struct UserCredentials { pub username: String, diff --git a/periphery/config.example.toml b/periphery/config.example.toml index 1afaa41c0..2d7b0cb66 100644 --- a/periphery/config.example.toml +++ b/periphery/config.example.toml @@ -1,10 +1,11 @@ -port = 9001 # optional. -is_builder = false +port = 9001 # optional. 9001 is default +is_builder = false # optional. false is default +repo_dir = "/repos" # optional. /repos is default -[github_accounts] +[github_accounts] # optional github_username1 = "github_token1" github_username2 = "github_token2" -[docker_accounts] +[docker_accounts] # optional docker_username1 = "docker_token1" docker_username2 = "docker_token2" \ No newline at end of file diff --git a/periphery/src/api/git.rs b/periphery/src/api/git.rs index 4e38923e2..11f3ca12e 100644 --- a/periphery/src/api/git.rs +++ b/periphery/src/api/git.rs @@ -1,5 +1,42 @@ -use axum::Router; +use anyhow::anyhow; +use axum::{routing::post, Extension, Json, Router}; +use helpers::{ + git::{self, CloneArgs}, + handle_anyhow_error, +}; +use types::{Build, Deployment, GithubToken, Log, PeripheryConfig}; + +use crate::PeripheryConfigExtension; pub fn router() -> Router { - Router::new() + Router::new().route( + "/clone", + post(|config, clone_args| async move { + clone(config, clone_args).await.map_err(handle_anyhow_error) + }), + ) +} + +async fn clone( + Extension(config): PeripheryConfigExtension, + Json(clone_args): Json, +) -> anyhow::Result>> { + let access_token = get_github_token(&clone_args.github_account, &config)?; + let logs = git::clone_repo(clone_args, &config.repo_dir, access_token).await?; + Ok(Json(logs)) +} + +fn get_github_token( + github_account: &Option, + config: &PeripheryConfig, +) -> anyhow::Result> { + match github_account { + Some(account) => match config.github_accounts.get(account) { + Some(token) => Ok(Some(token.to_owned())), + None => Err(anyhow!( + "did not find token in config for github account {account} " + )), + }, + None => Ok(None), + } }