From patchwork Fri Mar 24 03:28:27 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Donnellan X-Patchwork-Id: 743045 X-Patchwork-Delegate: ruscur@russell.cc Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vq8735SYHz9s82 for ; Fri, 24 Mar 2017 14:29:51 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3vq87340mjzDqY4 for ; Fri, 24 Mar 2017 14:29:51 +1100 (AEDT) X-Original-To: snowpatch@lists.ozlabs.org Delivered-To: snowpatch@lists.ozlabs.org Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3vq86y4kT0zDqBv for ; Fri, 24 Mar 2017 14:29:46 +1100 (AEDT) Received: from pps.filterd (m0098399.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v2O3Srx0065617 for ; Thu, 23 Mar 2017 23:29:34 -0400 Received: from e23smtp08.au.ibm.com (e23smtp08.au.ibm.com [202.81.31.141]) by mx0a-001b2d01.pphosted.com with ESMTP id 29c57f9ybp-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 23 Mar 2017 23:29:34 -0400 Received: from localhost by e23smtp08.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 24 Mar 2017 13:29:31 +1000 Received: from d23relay09.au.ibm.com (202.81.31.228) by e23smtp08.au.ibm.com (202.81.31.205) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Fri, 24 Mar 2017 13:29:30 +1000 Received: from d23av04.au.ibm.com (d23av04.au.ibm.com [9.190.235.139]) by d23relay09.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v2O3TMmA51380380 for ; Fri, 24 Mar 2017 14:29:30 +1100 Received: from d23av04.au.ibm.com (localhost [127.0.0.1]) by d23av04.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id v2O3SvoH007488 for ; Fri, 24 Mar 2017 14:28:57 +1100 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av04.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id v2O3Svxt007208 for ; Fri, 24 Mar 2017 14:28:57 +1100 Received: from ajd.ozlabs.ibm.com (haven.au.ibm.com [9.192.254.114]) (using TLSv1.2 with cipher AES128-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.au.ibm.com (Postfix) with ESMTPSA id 67787A02D4 for ; Fri, 24 Mar 2017 14:28:33 +1100 (AEDT) From: Andrew Donnellan To: snowpatch@lists.ozlabs.org Date: Fri, 24 Mar 2017 14:28:27 +1100 X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170324032827.21648-1-andrew.donnellan@au1.ibm.com> References: <20170324032827.21648-1-andrew.donnellan@au1.ibm.com> X-TM-AS-MML: disable x-cbid: 17032403-0048-0000-0000-00000216C1F0 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17032403-0049-0000-0000-000047C27139 Message-Id: <20170324032827.21648-3-andrew.donnellan@au1.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-03-24_02:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=1 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1702020001 definitions=main-1703240030 Subject: [snowpatch] [PATCH v3 3/3] settings: improve parsing of jobs X-BeenThere: snowpatch@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Continuous Integration for patch-based workflows List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: snowpatch-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "snowpatch" Job entries in the config file are a mixture of parameters which should be passed straight through to Jenkins, and flags that have meaning to snowpatch. I don't really feel like splitting these two things apart in the config, as we already have enough levels of nesting. Instead, add our own deserialize method to do this more intelligently. This also serves as an example of how to do more advanced parsing such that we can reject invalid values at parse time. Thanks-to: David Tolnay # help with boilerplate code Signed-off-by: Andrew Donnellan Acked-by: Russell Currey --- v2->v3: * I figure I should just throw this patch in now... --- src/main.rs | 28 ++++++++--------- src/settings.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6ba550e..a0e6576 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,22 +108,18 @@ fn run_tests(settings: &Config, client: Arc, project: &Project, tag: &st token: settings.jenkins.token.clone(), }; let project = project.clone(); - for job_params in &project.jobs { - let job_name = job_params.get("job").unwrap(); - let job_title = settings::get_job_title(job_params); + for job in &project.jobs { let mut jenkins_params = Vec::<(&str, &str)>::new(); - for (param_name, param_value) in job_params.iter() { + for (param_name, param_value) in job.parameters.iter() { + // TODO(ajd): do this more neatly debug!("Param name {}, value {}", ¶m_name, ¶m_value); - match param_name.as_ref() { - // TODO: Validate special parameter names in config at start of program - "job" | "title" => { }, - "remote" => jenkins_params.push((param_value, &project.remote_uri)), - "branch" => jenkins_params.push((param_value, tag)), - _ => jenkins_params.push((param_name, param_value)), - } + jenkins_params.push((param_name, param_value)); } - info!("Starting job: {}", &job_title); - let res = jenkins.start_test(job_name, jenkins_params) + jenkins_params.push((&job.remote, &project.remote_uri)); + jenkins_params.push((&job.branch, tag)); + + info!("Starting job: {}", &job.title); + let res = jenkins.start_test(&job.job, jenkins_params) .unwrap_or_else(|err| panic!("Starting Jenkins test failed: {}", err)); debug!("{:?}", &res); let build_url_real; @@ -137,12 +133,12 @@ fn run_tests(settings: &Config, client: Arc, project: &Project, tag: &st debug!("Build URL: {}", build_url_real); jenkins.wait_build(&build_url_real); let test_result = jenkins.get_build_result(&build_url_real).unwrap(); - info!("Jenkins job for {}/{} complete.", branch_name, job_title); + info!("Jenkins job for {}/{} complete.", branch_name, job.title); results.push(TestResult { - test_name: format!("Test {} on branch {}", job_title.to_string(), + test_name: format!("Test {} on branch {}", job.title, branch_name.to_string()).to_string(), state: test_result, - url: Some(jenkins.get_results_url(&build_url_real, job_params)), + url: Some(jenkins.get_results_url(&build_url_real, &job.parameters)), summary: Some("TODO: get this summary from Jenkins".to_string()), }); } diff --git a/src/settings.rs b/src/settings.rs index 826f792..44c321c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -16,8 +16,11 @@ use toml; +use serde::de::{self, MapVisitor, Visitor, Deserializer, Deserialize}; + use git2::{Repository, Error}; +use std::fmt; use std::fs::File; use std::io::Read; use std::collections::BTreeMap; @@ -58,8 +61,8 @@ pub struct Project { pub test_all_branches: Option, pub remote_name: String, pub remote_uri: String, - pub jobs: Vec>, - pub push_results: bool + pub jobs: Vec, + pub push_results: bool, } impl Project { @@ -68,6 +71,87 @@ impl Project { } } +#[derive(Clone)] +pub struct Job { + pub job: String, + pub title: String, + pub remote: String, + pub branch: String, + pub parameters: BTreeMap, +} + +impl Deserialize for Job { + fn deserialize(deserializer: D) -> Result + where D: Deserializer + { + struct JobVisitor; + + impl Visitor for JobVisitor { + type Value = Job; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct Job with arbitrary fields") + } + + fn visit_map(self, mut visitor: V) -> Result + where V: MapVisitor + { + let mut job = None; + let mut title = None; + let mut remote = None; + let mut branch = None; + let mut parameters = BTreeMap::new(); + while let Some(key) = visitor.visit_key::()? { + match key.as_str() { + "job" => { + if job.is_some() { + return Err(de::Error::duplicate_field("job")); + } + job = Some(visitor.visit_value()?); + } + "title" => { + if title.is_some() { + return Err(de::Error::duplicate_field("title")); + } + title = Some(visitor.visit_value()?); + } + "remote" => { + if remote.is_some() { + return Err(de::Error::duplicate_field("remote")); + } + remote = Some(visitor.visit_value()?); + } + "branch" => { + if branch.is_some() { + return Err(de::Error::duplicate_field("branch")); + } + branch = Some(visitor.visit_value()?); + } + _ => { + parameters.insert(key, visitor.visit_value()?); + } + } + } + + let job: String = job.ok_or_else(|| de::Error::missing_field("job"))?; + let remote = remote.ok_or_else(|| de::Error::missing_field("remote"))?; + let branch = branch.ok_or_else(|| de::Error::missing_field("branch"))?; + let title = title.unwrap_or(job.clone()); + + Ok(Job { + job: job, + title: title, + remote: remote, + branch: branch, + parameters: parameters, + }) + } + } + + deserializer.deserialize_map(JobVisitor) + } +} + #[derive(Deserialize, Clone)] pub struct Config { pub git: Git, @@ -76,13 +160,6 @@ pub struct Config { pub projects: BTreeMap } -pub fn get_job_title(job: &BTreeMap) -> String { - match job.get("title") { - Some(title) => title.to_string(), - None => job.get("job").unwrap().to_string() - } -} - pub fn parse(path: &str) -> Config { let mut toml_config = String::new();