Fix #105 - Add new cancel API call which can be used to cancel the module build.

This commit is contained in:
Jan Kaluza
2016-11-30 12:20:50 +01:00
parent 27484f2c32
commit 1e4e1be91e
6 changed files with 196 additions and 6 deletions

View File

@@ -231,6 +231,15 @@ class GenericBuilder(six.with_metaclass(ABCMeta)):
"""
raise NotImplementedError()
@abstractmethod
def cancel_build(self, task_id):
"""
:param task_id: Task ID returned by the build method.
Cancels the build.
"""
raise NotImplementedError()
@classmethod
@abstractmethod
def repo_from_tag(self, config, tag_name, arch):
@@ -568,6 +577,9 @@ chmod 644 %buildroot/%_rpmconfigdir/macros.d/macros.modules
reason = "Failed to submit artifact %s to Koji" % (artifact_name)
return task_id, state, reason, None
def cancel_build(self, task_id):
self.koji_session.cancelTask(task_id)
@classmethod
def repo_from_tag(cls, config, tag_name, arch):
"""
@@ -910,6 +922,8 @@ class CoprModuleBuilder(GenericBuilder):
raise ValueError(response["error"])
return response["repo"]
def cancel_build(self, task_id):
pass
class MockModuleBuilder(GenericBuilder):
"""
@@ -1216,6 +1230,9 @@ $repos
# @FIXME
return KojiModuleBuilder.get_disttag_srpm(disttag)
def cancel_build(self, task_id):
pass
GenericBuilder.register_backend_class(KojiModuleBuilder)
GenericBuilder.register_backend_class(CoprModuleBuilder)
GenericBuilder.register_backend_class(MockModuleBuilder)

View File

@@ -44,6 +44,57 @@ def get_rpm_release_from_tag(tag):
def get_artifact_from_srpm(srpm_path):
return os.path.basename(srpm_path).replace(".src.rpm", "")
def failed(config, session, msg):
"""
Called whenever a module enters the 'failed' state.
We cancel all the remaining component builds of a module
and stop the building.
"""
build = models.ModuleBuild.from_module_event(session, msg)
module_info = build.json()
if module_info['state'] != msg.module_build_state:
log.warn("Note that retrieved module state %r "
"doesn't match message module state %r" % (
module_info['state'], msg.module_build_state))
# This is ok.. it's a race condition we can ignore.
pass
unbuilt_components = [
c for c in build.component_builds
if (c.state != koji.BUILD_STATES['COMPLETE']
and c.state != koji.BUILD_STATES["FAILED"])
]
try:
groups = {
'build': build.resolve_profiles(session, 'buildroot'),
'srpm-build': build.resolve_profiles(session, 'srpm-buildroot'),
}
except ValueError:
reason = "Failed to gather buildroot groups from SCM."
log.exception(reason)
build.transition(config, state="failed", state_reason=reason)
session.commit()
raise
builder = module_build_service.builder.GenericBuilder.create(
build.owner, build.name, config.system, config, tag_name=build.koji_tag)
builder.buildroot_connect(groups)
for component in unbuilt_components:
if component.task_id:
builder.cancel_build(component.task_id)
component.state = koji.BUILD_STATES['FAILED']
component.state_reason = build.state_reason
session.add(component)
build.transition(config, state="failed")
session.commit()
def done(config, session, msg):
"""Called whenever a module enters the 'done' state.

View File

@@ -102,7 +102,7 @@ class MessageWorker(threading.Thread):
models.BUILD_STATES["init"]: NO_OP,
models.BUILD_STATES["wait"]: module_build_service.scheduler.handlers.modules.wait,
models.BUILD_STATES["build"]: NO_OP,
models.BUILD_STATES["failed"]: NO_OP,
models.BUILD_STATES["failed"]: module_build_service.scheduler.handlers.modules.failed,
models.BUILD_STATES["done"]: module_build_service.scheduler.handlers.modules.done, # XXX: DIRECT TRANSITION TO READY
models.BUILD_STATES["ready"]: NO_OP,
}

View File

@@ -35,7 +35,7 @@ from flask import request, jsonify
from flask.views import MethodView
from module_build_service import app, conf, log
from module_build_service import models
from module_build_service import models, db
from module_build_service.utils import pagination_metadata, filter_module_builds, submit_module_build
from module_build_service.errors import (
ValidationError, Unauthorized, NotFound)
@@ -60,6 +60,12 @@ api_v1 = {
'methods': ['GET'],
}
},
'module_build_cancel': {
'url': '/module-build-service/1/module-builds/cancel/<int:id>',
'options': {
'methods': ['PUT']
}
},
}
@@ -126,6 +132,32 @@ class ModuleBuildAPI(MethodView):
module = submit_module_build(username, url)
return jsonify(module.json()), 201
def put(self, id):
username = module_build_service.auth.get_username(request.environ)
if conf.require_packager:
module_build_service.auth.assert_is_packager(username, fas_kwargs=dict(
base_url=conf.fas_url,
username=conf.fas_username,
password=conf.fas_password))
if id is None:
raise NotFound('You must provide module build id.')
module = models.ModuleBuild.query.filter_by(id=id).first()
if not module:
raise NotFound('No such module found.')
if module.owner != username:
raise Unauthorized("You are not owner of this build and "
"therefore cannot cancel it.")
module.transition(conf, models.BUILD_STATES["failed"],
"Canceled by %s." % username)
db.session.add(module)
db.session.commit()
return jsonify(module.api_json()), 200
def register_api_v1():
""" Registers version 1 of Rida API. """