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