From 8eea39dad650e050690f0c888c08707e082d8b49 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Thu, 21 Feb 2013 10:46:45 -0800 Subject: [PATCH] Add a script to test the Jenkins API. Using the extended versions of python-jenkins that are used by Zuul and devstack-gate, exercise all of the Jenkins API calls that are used by by Zuul, devstack-gate, and Jenkins Job Builder. (JJB doesn't need any extended methods. The extended methods used by devstack-gate have been upstreamed, but not released.) Change-Id: I9c3a4d7cfc96965ceb83b8c5ea4b12d4f2e07e12 Reviewed-on: https://review.openstack.org/22580 Reviewed-by: Paul Belanger Reviewed-by: Clark Boylan Reviewed-by: Jeremy Stanley Reviewed-by: Khai Do Approved: Clark Boylan Tested-by: Jenkins --- tools/jenkins-job.xml | 33 +++++++ tools/test-jenkins-api.py | 186 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 tools/jenkins-job.xml create mode 100755 tools/test-jenkins-api.py diff --git a/tools/jenkins-job.xml b/tools/jenkins-job.xml new file mode 100644 index 0000000000..e51bc57d85 --- /dev/null +++ b/tools/jenkins-job.xml @@ -0,0 +1,33 @@ + + + + + + false + + + + + UUID + + + + + + + %s + true + false + false + false + + true + + + sleep 30 + + + + + + diff --git a/tools/test-jenkins-api.py b/tools/test-jenkins-api.py new file mode 100755 index 0000000000..de3955ed00 --- /dev/null +++ b/tools/test-jenkins-api.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# +# Test all of the Jenkins API features used by the +# OpenStack Infrastructure project +# +# Copyright (C) 2013 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# +# See the License for the specific language governing permissions and +# limitations under the License. + +import jenkins +import urllib +import urllib2 +import urlparse +from pprint import pprint +import time +from uuid import uuid4 +import ConfigParser +import os +import re + +import sys +sys.path.insert(0, '../../zuul/zuul') +from launcher.jenkins import ExtendedJenkins + +sys.path.insert(0, '../../devstack-gate/') +from myjenkins import Jenkins as DGJenkins + +JOB_NAME = 'test-job' +NODE_NAME = 'test-node' + +class JenkinsTest(object): + def __init__(self): + self.config = ConfigParser.ConfigParser() + if len(sys.argv) < 2: + print "Usage: %s zuul.conf" % sys.argv[0] + sys.exit(1) + fp = sys.argv[1] + if os.path.exists(os.path.expanduser(fp)): + self.config.read(os.path.expanduser(fp)) + + server = self.config.get('jenkins', 'server') + user = self.config.get('jenkins', 'user') + apikey = self.config.get('jenkins', 'apikey') + + self.jenkins = ExtendedJenkins(server, user, apikey) + self.dgjenkins = DGJenkins(server, user, apikey) + + def nodeExists(self): + return self.dgjenkins.node_exists(NODE_NAME) + + def createNode(self): + assert not self.nodeExists() + priv_key = '/var/lib/jenkins/.ssh/id_rsa' + self.dgjenkins.create_node( + NODE_NAME, numExecutors=1, + nodeDescription='Test node', + remoteFS='/home/jenkins', + labels='testnode', + exclusive=True, + launcher='hudson.plugins.sshslaves.SSHLauncher', + launcher_params={'port': 22, + 'username': 'jenkins', + 'privatekey': priv_key, + 'host': 'nowhere.example.com'}) + assert self.nodeExists() + + def reconfigNode(self): + LABEL_RE = re.compile(r'') + config = self.dgjenkins.get_node_config(NODE_NAME) + assert '' in config + config = LABEL_RE.sub('', config) + self.dgjenkins.reconfig_node(NODE_NAME, config) + config = self.dgjenkins.get_node_config(NODE_NAME) + assert '' in config + + def deleteNode(self): + assert self.nodeExists() + self.dgjenkins.delete_node(NODE_NAME) + assert not self.nodeExists() + + def findBuildInQueue(self, build): + for item in self.jenkins.get_queue_info(): + if 'actions' not in item: + continue + for action in item['actions']: + if 'parameters' not in action: + continue + parameters = action['parameters'] + for param in parameters: + # UUID is deprecated in favor of ZUUL_UUID + if ((param['name'] in ['ZUUL_UUID', 'UUID']) + and build == param['value']): + return item + return False + + def addJob(self, quiet_period): + assert not self.jobExists() + xml = open('jenkins-job.xml').read() + xml = xml % quiet_period + + self.jenkins.create_job(JOB_NAME, xml) + assert self.jobExists() + + def reconfigJob(self, quiet_period): + assert self.jobExists() + xml = open('jenkins-job.xml').read() + xml = xml % quiet_period + + self.jenkins.reconfig_job(JOB_NAME, xml) + xml2 = self.jenkins.get_job_config(JOB_NAME) + s = '%s' % quiet_period + assert s in xml2 + + def jobExists(self): + return self.jenkins.job_exists(JOB_NAME) + + def deleteJob(self): + assert self.jobExists() + self.jenkins.delete_job(JOB_NAME) + assert not self.jobExists() + + def getJobs(self): + pprint(self.jenkins.get_jobs()) + + def testCancelQueue(self): + uuid = str(uuid4().hex) + self.jenkins.build_job(JOB_NAME, parameters=dict(UUID=uuid)) + + item = self.findBuildInQueue(uuid) + assert item + self.jenkins.cancel_queue(item['id']) + assert not self.findBuildInQueue(uuid) + + def testCancelBuild(self): + uuid = str(uuid4().hex) + self.jenkins.build_job(JOB_NAME, parameters=dict(UUID=uuid)) + + assert self.findBuildInQueue(uuid) + for x in range(60): + if not self.findBuildInQueue(uuid): + break + assert not self.findBuildInQueue(uuid) + time.sleep(1) + + buildno = self.jenkins.get_job_info(JOB_NAME)['lastBuild']['number'] + info = self.jenkins.get_build_info(JOB_NAME, buildno) + assert info['building'] + self.jenkins.stop_build(JOB_NAME, buildno) + time.sleep(1) + info = self.jenkins.get_build_info(JOB_NAME, buildno) + assert not info['building'] + + console_url = urlparse.urljoin(info['url'], 'consoleFull') + self.jenkins.jenkins_open(urllib2.Request(console_url)) + + self.jenkins.set_build_description(JOB_NAME, buildno, + "test description") + + info = self.jenkins.get_build_info(JOB_NAME, buildno) + assert info['description'] == 'test description' + +j = JenkinsTest() +if j.nodeExists(): + j.deleteNode() +j.createNode() +j.reconfigNode() +j.deleteNode() +if j.jobExists(): + j.deleteJob() +j.addJob(5) +j.reconfigJob(10) +j.testCancelQueue() +j.testCancelBuild() +j.deleteJob()