@@ -14,6 +14,11 @@
// ci.rs - CI backend interface definitions
//
+use std::collections::BTreeMap;
+use std::error::Error;
+
+use patchwork::TestState;
+
#[derive(Eq, PartialEq)]
pub enum BuildStatus {
Running,
@@ -23,4 +28,10 @@ pub enum BuildStatus {
pub trait CIBackend {
fn start_test(&self, job_name: &str, params: Vec<(&str, &str)>)
-> Result<String, &'static str>;
+ fn get_build_handle(&self, build_queue_entry: &str) -> Result<String, Box<Error>>;
+ fn get_build_result(&self, build_handle: &str) -> Result<TestState, Box<Error>>;
+ fn get_results_url(&self, build_handle: &str, job: &BTreeMap<String, String>) -> String;
+ fn get_description(&self, build_handle: &str, job: &BTreeMap<String, String>)
+ -> Option<String>;
+ fn wait_build(&self, build_handle: &str) -> Result<BuildStatus, Box<Error>>;
}
@@ -79,63 +79,8 @@ impl CIBackend for JenkinsBackend {
None => Err("No Location header returned"),
}
}
-}
-
-impl JenkinsBackend {
- fn headers(&self) -> HeaderMap {
- let mut headers = HeaderMap::new();
- if let Some(ref username) = self.username {
- if let Some(ref token) = self.token {
- headers.insert(
- AUTHORIZATION,
- format!(
- "Basic {}",
- base64::encode(&format!("{}:{}", username, token))
- )
- .parse()
- .unwrap(),
- );
- }
- };
- headers
- }
-
- fn get_url<U: IntoUrl>(&self, url: U) -> Result<Response, reqwest::Error> {
- self.reqwest_client.get(url).headers(self.headers()).send()
- }
- fn post_url<U: IntoUrl>(&self, url: U) -> Result<Response, reqwest::Error> {
- self.reqwest_client.post(url).headers(self.headers()).send()
- }
-
- fn get_api_json_object(&self, base_url: &str) -> Result<Value, Box<Error>> {
- let url = format!("{}api/json", base_url);
- let mut result_str = String::new();
- loop {
- let mut resp = match self.get_url(&url) {
- Ok(r) => r,
- Err(e) => {
- // TODO: We have to time out rather than spinning indefinitely
- warn!("Couldn't hit Jenkins API: {}", e);
- sleep(Duration::from_millis(JENKINS_POLLING_INTERVAL));
- continue;
- }
- };
-
- if resp.status().is_server_error() {
- // TODO: Timeout
- sleep(Duration::from_millis(JENKINS_POLLING_INTERVAL));
- continue;
- }
- resp.read_to_string(&mut result_str)
- .map_err(|e| format!("Couldn't read from server: {}", e))?;
- break;
- }
- serde_json::from_str(&result_str)
- .map_err(|e| format!("Couldn't parse JSON from Jenkins: {}", e).into())
- }
-
- pub fn get_build_handle(&self, build_queue_entry: &str) -> Result<String, Box<Error>> {
+ fn get_build_handle(&self, build_queue_entry: &str) -> Result<String, Box<Error>> {
loop {
let entry = self.get_api_json_object(build_queue_entry)?;
match entry.get("executable") {
@@ -156,15 +101,7 @@ impl JenkinsBackend {
}
}
- pub fn get_build_status(&self, build_handle: &str) -> Result<BuildStatus, Box<Error>> {
- match self.get_api_json_object(build_handle)?["building"].as_bool() {
- Some(true) => Ok(BuildStatus::Running),
- Some(false) => Ok(BuildStatus::Done),
- None => Err("Error getting build status".into()),
- }
- }
-
- pub fn get_build_result(&self, build_handle: &str) -> Result<TestState, Box<Error>> {
+ fn get_build_result(&self, build_handle: &str) -> Result<TestState, Box<Error>> {
match self
.get_api_json_object(build_handle)?
.get("result")
@@ -181,7 +118,7 @@ impl JenkinsBackend {
}
}
- pub fn get_results_url(&self, build_handle: &str, job: &BTreeMap<String, String>) -> String {
+ fn get_results_url(&self, build_handle: &str, job: &BTreeMap<String, String>) -> String {
let default_url = format!("{}/", build_handle);
match job.get("artifact") {
Some(artifact) => {
@@ -198,7 +135,7 @@ impl JenkinsBackend {
}
}
- pub fn get_description(
+ fn get_description(
&self,
build_handle: &str,
job: &BTreeMap<String, String>,
@@ -220,7 +157,7 @@ impl JenkinsBackend {
}
}
- pub fn wait_build(&self, build_handle: &str) -> Result<BuildStatus, Box<Error>> {
+ fn wait_build(&self, build_handle: &str) -> Result<BuildStatus, Box<Error>> {
// TODO: Implement a timeout?
while self.get_build_status(build_handle)? != BuildStatus::Done {
sleep(Duration::from_millis(JENKINS_POLLING_INTERVAL));
@@ -228,3 +165,66 @@ impl JenkinsBackend {
Ok(BuildStatus::Done)
}
}
+
+impl JenkinsBackend {
+ fn headers(&self) -> HeaderMap {
+ let mut headers = HeaderMap::new();
+ if let Some(ref username) = self.username {
+ if let Some(ref token) = self.token {
+ headers.insert(
+ AUTHORIZATION,
+ format!(
+ "Basic {}",
+ base64::encode(&format!("{}:{}", username, token))
+ )
+ .parse()
+ .unwrap(),
+ );
+ }
+ };
+ headers
+ }
+
+ fn get_url<U: IntoUrl>(&self, url: U) -> Result<Response, reqwest::Error> {
+ self.reqwest_client.get(url).headers(self.headers()).send()
+ }
+
+ fn post_url<U: IntoUrl>(&self, url: U) -> Result<Response, reqwest::Error> {
+ self.reqwest_client.post(url).headers(self.headers()).send()
+ }
+
+ fn get_api_json_object(&self, base_url: &str) -> Result<Value, Box<Error>> {
+ let url = format!("{}api/json", base_url);
+ let mut result_str = String::new();
+ loop {
+ let mut resp = match self.get_url(&url) {
+ Ok(r) => r,
+ Err(e) => {
+ // TODO: We have to time out rather than spinning indefinitely
+ warn!("Couldn't hit Jenkins API: {}", e);
+ sleep(Duration::from_millis(JENKINS_POLLING_INTERVAL));
+ continue;
+ }
+ };
+
+ if resp.status().is_server_error() {
+ // TODO: Timeout
+ sleep(Duration::from_millis(JENKINS_POLLING_INTERVAL));
+ continue;
+ }
+ resp.read_to_string(&mut result_str)
+ .map_err(|e| format!("Couldn't read from server: {}", e))?;
+ break;
+ }
+ serde_json::from_str(&result_str)
+ .map_err(|e| format!("Couldn't parse JSON from Jenkins: {}", e).into())
+ }
+
+ pub fn get_build_status(&self, build_handle: &str) -> Result<BuildStatus, Box<Error>> {
+ match self.get_api_json_object(build_handle)?["building"].as_bool() {
+ Some(true) => Ok(BuildStatus::Running),
+ Some(false) => Ok(BuildStatus::Done),
+ None => Err("Error getting build status".into()),
+ }
+ }
+}
Move the functions we actually use from the main code from the JenkinsBackend impl into the CIBackend trait. Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> --- src/ci.rs | 11 +++++ src/jenkins.rs | 136 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 79 insertions(+), 68 deletions(-)