commit
321dcb6877
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "deploy-config"
|
||||
version = "0.1.0"
|
||||
authors = ["Zoran Zaric <zz@zoranzaric.de>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.99", features = [ "derive" ] }
|
||||
ssh2 = "0.3.3"
|
||||
url = "2.1.0"
|
||||
serde_yaml = "0.8.9"
|
||||
serde_json = "1.0.40"
|
||||
serde_with = "1.3.1"
|
||||
reqwest = "0.9.20"
|
@ -0,0 +1,298 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Display};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct DeployConfig {
|
||||
tasks: Vec<Task>,
|
||||
}
|
||||
|
||||
pub trait Deploy {
|
||||
fn deploy(&self) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub enum Task {
|
||||
#[serde(rename = "copy")]
|
||||
Copy {
|
||||
source: String,
|
||||
|
||||
#[serde(with = "serde_with::rust::display_fromstr")]
|
||||
target: Target,
|
||||
},
|
||||
|
||||
#[serde(rename = "trigger")]
|
||||
Trigger {
|
||||
#[serde(with = "serde_with::rust::display_fromstr")]
|
||||
method: TriggerMethod,
|
||||
url: String,
|
||||
parameters: Vec<TriggerParameter>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Deploy for Task {
|
||||
fn deploy(&self) -> Result<(), ()> {
|
||||
match self {
|
||||
Self::Copy { source, target } => match target.deploy(PathBuf::from(source)) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(()),
|
||||
},
|
||||
Self::Trigger {
|
||||
method,
|
||||
url,
|
||||
parameters,
|
||||
} => match method {
|
||||
&TriggerMethod::HttpGet => {
|
||||
let params: Vec<_> = parameters
|
||||
.iter()
|
||||
.map(|ref p| (p.name.clone(), p.value.clone()))
|
||||
.collect();
|
||||
|
||||
let resp = reqwest::Client::new().get(url).query(¶ms).send();
|
||||
|
||||
match resp {
|
||||
Ok(resp) => {
|
||||
if resp.status().is_success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
&TriggerMethod::HttpPost => {
|
||||
let params: HashMap<_, _> = parameters
|
||||
.iter()
|
||||
.map(|ref p| (p.name.clone(), p.value.clone()))
|
||||
.collect();
|
||||
|
||||
let resp = reqwest::Client::new().post(url).form(¶ms).send();
|
||||
|
||||
match resp {
|
||||
Ok(resp) => {
|
||||
if resp.status().is_success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Target {
|
||||
Scp {
|
||||
username: Option<String>,
|
||||
host: String,
|
||||
port: Option<u16>,
|
||||
path: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl FromStr for Target {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match Url::parse(s) {
|
||||
Ok(url) => match url.scheme() {
|
||||
"scp" => {
|
||||
let username = if url.username().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(url.username().into())
|
||||
};
|
||||
let host = match url.host_str() {
|
||||
Some(host_str) => host_str.into(),
|
||||
None => return Err("Host is missing".into()),
|
||||
};
|
||||
let port = url.port();
|
||||
let path = if url.path().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(url.path().into())
|
||||
};
|
||||
|
||||
Ok(Self::Scp {
|
||||
username,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
})
|
||||
}
|
||||
scheme => Err(format!("Unsupported scheme \"{}\"", scheme)),
|
||||
},
|
||||
Err(e) => Err(format!("{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// FIXME
|
||||
write!(f, "foo")
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn deploy(&self, source: PathBuf) -> Result<(), std::io::Error> {
|
||||
match self {
|
||||
Self::Scp {
|
||||
username,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
} => {
|
||||
let mut cmd = Command::new("scp");
|
||||
|
||||
let source: String = source.to_string_lossy().into();
|
||||
let cmd = cmd.arg(source);
|
||||
|
||||
let cmd = if let Some(port) = port {
|
||||
cmd.arg("-P").arg(format!("{}", port))
|
||||
} else {
|
||||
cmd
|
||||
};
|
||||
|
||||
let mut s = String::new();
|
||||
|
||||
if let Some(username) = username {
|
||||
s.push_str(&format!("{}@", username));
|
||||
}
|
||||
|
||||
s.push_str(&format!("{}:", host));
|
||||
|
||||
if let Some(path) = path {
|
||||
s.push_str(path);
|
||||
}
|
||||
|
||||
match cmd.output() {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"scp command failed with exit code {:?}",
|
||||
output.status.code()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum TriggerMethod {
|
||||
HttpGet,
|
||||
HttpPost,
|
||||
}
|
||||
|
||||
impl FromStr for TriggerMethod {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"http-get" => Ok(Self::HttpGet),
|
||||
"http-post" => Ok(Self::HttpPost),
|
||||
trigger_method => Err(format!("Unsupported trigger-method \"{}\"", trigger_method)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TriggerMethod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::HttpGet => write!(f, "http-get"),
|
||||
Self::HttpPost => write!(f, "http-post"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct TriggerParameter {
|
||||
name: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_parse_target() {
|
||||
let target = dbg!(Target::from_str("scp://foo@bar:/vol1/asdf")).unwrap();
|
||||
assert_eq!(
|
||||
Target::Scp {
|
||||
username: Some("foo".into()),
|
||||
host: "bar".into(),
|
||||
port: None,
|
||||
path: Some("/vol1/asdf".into()),
|
||||
},
|
||||
target
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_parse() {
|
||||
let s = r#"---
|
||||
tasks:
|
||||
- copy:
|
||||
source: hermes.war
|
||||
target: scp://compaxdev@delivery.comi.run:/vol1/delivery/aax2/microservices/tuadev/hermes/hermes.war
|
||||
- trigger:
|
||||
method: http-get
|
||||
url: http://172.29.110.20:8080/job/00_congo_neue_tuad_microservices_deployment_trigger_compax/buildWithParameters
|
||||
parameters:
|
||||
- name: token
|
||||
value: up9ul9oph5augh2oz8oak3aiSeemaxa
|
||||
- name: ms_visibility
|
||||
value: cob2b
|
||||
- name: warfile
|
||||
value: hermes
|
||||
"#;
|
||||
assert_eq!(
|
||||
DeployConfig {
|
||||
tasks: vec![Task::Copy {
|
||||
source: "hermes.war".into(),
|
||||
target: Target::Scp {
|
||||
username: Some("compaxdev".into()),
|
||||
host: "delivery.comi.run".into(),
|
||||
port: None,
|
||||
path: Some("/vol1/delivery/aax2/microservices/tuadev/hermes/hermes.war".into()),
|
||||
}
|
||||
}, Task::Trigger {
|
||||
method: TriggerMethod::HttpGet,
|
||||
url: "http://172.29.110.20:8080/job/00_congo_neue_tuad_microservices_deployment_trigger_compax/buildWithParameters".into(),
|
||||
parameters: vec![
|
||||
TriggerParameter {
|
||||
name: "token".into(),
|
||||
value: "up9ul9oph5augh2oz8oak3aiSeemaxa".into(),
|
||||
},
|
||||
TriggerParameter {
|
||||
name: "ms_visibility".into(),
|
||||
value: "cob2b".into(),
|
||||
},
|
||||
TriggerParameter {
|
||||
name: "warfile".into(),
|
||||
value: "hermes".into(),
|
||||
},
|
||||
],
|
||||
}],
|
||||
},
|
||||
serde_yaml::from_str(&s).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue