init
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					/target
 | 
				
			||||||
 | 
					**/*.rs.bk
 | 
				
			||||||
 | 
					Cargo.lock
 | 
				
			||||||
							
								
								
									
										16
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -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"
 | 
				
			||||||
							
								
								
									
										298
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user