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 <paul.belanger@polybeacon.com>
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Reviewed-by: Khai Do <zaro0508@gmail.com>
Approved: Clark Boylan <clark.boylan@gmail.com>
Tested-by: Jenkins
This commit is contained in:
James E. Blair 2013-02-21 10:46:45 -08:00 committed by Jenkins
parent 6bfa26e179
commit 8eea39dad6
2 changed files with 219 additions and 0 deletions

33
tools/jenkins-job.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- A test job config for test-jenkins-api.py -->
<project>
<actions/>
<description></description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>UUID</name>
<description></description>
<defaultValue></defaultValue>
</hudson.model.StringParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<quietPeriod>%s</quietPeriod>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers class="vector"/>
<concurrentBuild>true</concurrentBuild>
<builders>
<hudson.tasks.Shell>
<command>sleep 30
</command>
</hudson.tasks.Shell>
</builders>
<publishers/>
<buildWrappers/>
</project>

186
tools/test-jenkins-api.py Executable file
View File

@ -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'<label>.*</label>')
config = self.dgjenkins.get_node_config(NODE_NAME)
assert '<label>testnode</label>' in config
config = LABEL_RE.sub('<label>devstack-used</label>', config)
self.dgjenkins.reconfig_node(NODE_NAME, config)
config = self.dgjenkins.get_node_config(NODE_NAME)
assert '<label>devstack-used</label>' 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 = '<quietPeriod>%s</quietPeriod>' % 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()