- update the interface between the kojivmd and kojikamid

- allow --specfile and --patches to be passed to the VM
 - handle validation of urls in kojivmd
This commit is contained in:
Mike Bonnet 2010-07-19 16:23:31 -04:00
parent 0b485cd813
commit 38f74ed5a0
4 changed files with 84 additions and 38 deletions

View file

@ -177,6 +177,7 @@ class SCM(object):
sourcedir = '%s/%s' % (scmdir, self.module)
update_checkout_cmd = None
update_checkout_dir = None
if self.scmtype == 'CVS':
pserver = ':pserver:%s@%s:%s' % ((self.user or 'anonymous'), self.host, self.repository)
@ -203,6 +204,7 @@ class SCM(object):
sourcedir = '%s/%s' % (scmdir, checkout_path)
module_checkout_cmd = ['git', 'clone', '-n', gitrepo, sourcedir]
update_checkout_cmd = ['git', 'reset', '--hard', self.revision]
update_checkout_dir = sourcedir
# self.module may be empty, in which case the specfile should be in the top-level directory
if self.module:
@ -222,6 +224,7 @@ class SCM(object):
sourcedir = '%s/%s' % (scmdir, checkout_path)
module_checkout_cmd = ['git', 'clone', '-n', gitrepo, sourcedir]
update_checkout_cmd = ['git', 'reset', '--hard', self.revision]
update_checkout_dir = sourcedir
# self.module may be empty, in which case the specfile should be in the top-level directory
if self.module:
@ -256,7 +259,7 @@ class SCM(object):
if update_checkout_cmd:
# Currently only required for GIT checkouts
# Run the command in the directory the source was checked out into
ret, output = run(update_checkout_cmd, chdir=sourcedir)
ret, output = run(update_checkout_cmd, chdir=update_checkout_dir)
log(output)
if ret:
raise BuildError, 'Error running %s update command "%s": %s' % \
@ -332,18 +335,21 @@ class WindowsBuild(object):
def log(msg):
print >> sys.stderr, '%s: %s' % (datetime.datetime.now().ctime(), msg)
def run(cmd, chdir='.'):
def run(cmd, chdir=None):
shell = False
if isinstance(cmd, (str, unicode)) and len(cmd.split()) > 1:
shell = True
olddir = os.getcwd()
os.chdir(chdir)
olddir = None
if chdir:
olddir = os.getcwd()
os.chdir(chdir)
log('running command: %s' % cmd)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
close_fds=True, shell=shell)
ret = proc.wait()
output = proc.stdout.read()
os.chdir(olddir)
if olddir:
os.chdir(olddir)
return ret, output
def find_net_info():
@ -422,38 +428,50 @@ def get_options():
parser = OptionParser(usage=usage)
parser.add_option('-i', '--install', action='store_true', help='Install this daemon as a service', default=False)
parser.add_option('-u', '--uninstall', action='store_true', help='Uninstall this daemon if it was installed previously as a service', default=False)
parser.add_option('-s', '--scmurl', action='append', help='Forcibly specify an scmurl to checkout from', default=[])
parser.add_option('-s', '--scmurl', help='Forcibly specify an scmurl to checkout from')
(options, args) = parser.parse_args()
return options
def run_build(workdir, scmurls):
def apply_patches(src_dir, patch_dir):
patches = [patch for patch in os.listdir(patch_dir) if \
os.path.isfile(os.path.join(patch_dir, patch)) and \
not patch.startswith('.')]
if not patches:
raise BuildError, 'no patches found at %s' % patch_dir
patches.sort()
for patch in patches:
cmd = ['/usr/bin/patch', '--verbose', '-d', src_dir, '-p1', '-i', os.path.join(patch_dir, patch)]
ret, output = run(cmd)
if ret:
raise BuildError, 'error applying patches, output was: %s' % output
def run_build(workdir, source_url, task_opts):
"""run the build"""
if len(scmurls) == 2:
# should SCM.assert_allowed() be called on SCM objs?
specurl, srcurl = scmurls
spec_scm = SCM(specurl)
spec_dir = spec_scm.checkout(workdir)
src_scm = SCM(srcurl)
src_dir = src_scm.checkout(workdir)
elif len(scmurls) == 1:
specurl = scmurls[0]
spec_scm = SCM(specurl)
spec_dir = spec_scm.checkout(workdir)
src_scm, src_dir = spec_scm, spec_dir
src_scm = SCM(source_url)
src_dir = src_scm.checkout(os.path.join(workdir, 'source'))
if 'specfile' in task_opts:
spec_scm = SCM(task_opts['specfile'])
spec_dir = spec_scm.checkout(os.path.join(workdir, 'spec'))
else:
raise BuildError, 'Unexpected number of SCM URLs given: %s' % scmurls
spec_dir = src_dir
if 'patches' in task_opts:
patch_scm = SCM(task_opts['patches'])
patch_dir = patch_scm.checkout(os.path.join(workdir, 'patches'))
apply_patches(src_dir, patch_dir)
specfile = [spec for spec in os.listdir(spec_dir) if spec.endswith('.ini')]
if len(specfile) != 1:
raise BuildError, 'Exactly one .ini file must be found in a check out'
if len(specfile) == 0:
raise BuildError, 'No .ini file found'
elif len(specfile) > 1:
raise BuildError, 'Multiple .ini files found'
winbld = WindowsBuild(os.path.join(spec_dir, specfile[0]), src_dir)
return winbld.doAll(), src_dir
def flunk(server_report, server=None):
def flunk(server):
"""do the right thing when a build fails"""
exc_info = sys.exc_info()
tb = ''.join(traceback.format_exception(*exc_info))
if server_report:
if server is not None:
server.failTask(tb)
log(tb)
sys.exit(1)
@ -481,20 +499,27 @@ if __name__ == '__main__':
else:
print 'Successfully removed the %s service' % prog
sys.exit(0)
use_server, server = False, None
server = None
try:
if len(opts.scmurl) == 0:
use_server = True
source_url = None
task_opts = None
if opts.scmurl:
source_url = opts.scmurl
else:
server = get_mgmt_server()
scmurl = server.getTaskInfo()
opts.scmurl.append(scmurl)
info = server.getTaskInfo()
source_url = info[0]
if len(info) > 1:
task_opts = info[1]
if not task_opts:
task_opts = {}
workdir = '/tmp/workdir'
os.mkdir(workdir)
results, results_dir = run_build(workdir, opts.scmurl)
if use_server:
results, results_dir = run_build(workdir, source_url, task_opts)
if server is not None:
upload_results(server, results_dir, results)
server.closeTask(results)
log('Build results: %s' % results)
except:
flunk(use_server, server=server)
flunk(server)
sys.exit(0)

View file

@ -22,7 +22,7 @@
import koji
import koji.util
from koji.daemon import TaskManager
from koji.daemon import SCM, TaskManager
from koji.tasks import ServerExit, BaseTaskHandler
import sys
import logging
@ -125,6 +125,7 @@ def get_options():
'max_retries': 120,
'offline_retry': True,
'offline_retry_interval': 120,
'allowed_scms': '',
'cert': '/etc/kojivmd/client.crt',
'ca': '/etc/kojivmd/clientca.crt',
'serverca': '/etc/kojivmd/serverca.crt'}
@ -426,7 +427,7 @@ class VMTask(BaseTaskHandler):
thr.setDaemon(True)
thr.start()
def handler(self, name, task_info, opts=None):
def handler(self, name, source_url, opts=None):
"""
Clone the VM named "name", and provide the data in "task_info" to it.
Available options:
@ -437,7 +438,14 @@ class VMTask(BaseTaskHandler):
opts = {}
timeout = opts.get('timeout', 720)
self.task_info = task_info
self.task_info = [source_url, koji.util.dslice(opts, ['specfile', 'patches'],
strict=False)]
# opts may contain specfile and patches entries, both of which are
# urls.
# Verify the urls before passing them to the VM.
for url in [source_url] + self.task_info[1].values():
scm = SCM(url)
scm.assert_allowed(self.options.allowed_scms)
conn = libvirt.open(None)
clone_name = self.clone(conn, name)

View file

@ -17,6 +17,13 @@
; The URL for the xmlrpc server
server=http://hub.example.com/kojihub
; A space-separated list of hostname:repository[:use_common] tuples that kojivmd is authorized to checkout from (no quotes).
; Wildcards (as supported by fnmatch) are allowed.
; If use_common is specified and is one of "false", "no", "off", or "0" (without quotes), then kojid will not attempt to checkout
; a common/ dir when checking out sources from the source control system. Otherwise, it will attempt to checkout a common/
; dir, and will raise an exception if it cannot.
allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/*:no
; The mail host to use for sending email notifications
smtphost=example.com

View file

@ -15,14 +15,16 @@ parser.add_option('--cpus', help='Number of virtual CPUs to allocate to the VM (
parser.add_option('--mem', help='Amount of memory (in megabytes) to allocate to the VM (optional)',
type='int')
parser.add_option('--channel', help='Channel to create the task in', default='vm')
parser.add_option('--specfile', help='Alternate SCM URL of the specfile')
parser.add_option('--patches', help='SCM URL of patches to apply before build')
opts, args = parser.parse_args()
if len(args) < 2:
parser.error('You must specify a VM name and a command to run')
parser.error('You must specify a VM name and SCM URL')
vm_name = args[0]
cmd = ' '.join(args[1:])
scm_url = args[1]
session = koji.ClientSession(opts.server)
session.ssl_login(opts.cert, opts.ca, opts.server_ca)
@ -32,8 +34,12 @@ if opts.cpus:
task_opts['cpus'] = opts.cpus
if opts.mem:
task_opts['mem'] = opts.mem
if opts.specfile:
task_opts['specfile'] = opts.specfile
if opts.patches:
task_opts['patches'] = opts.patches
params = [vm_name, [cmd], task_opts]
params = [vm_name, scm_url, task_opts]
task_id = session.makeTask('vmExec', params, channel=opts.channel)