Repository :
http://git.fedorahosted.org/cgit/copr.git
On branch : master
---------------------------------------------------------------
commit 26e3ba807e6bb807dd0f06c4a6b821ba8bb91fb3
Author: Adam Samalik <asamalik(a)redhat.com>
Date: Mon Jul 21 12:46:20 2014 +0200
FrontendCallback prettified
---------------------------------------------------------------
backend/backend/actions.py | 2 +-
backend/backend/callback.py | 93 ++++++++++++--------
backend/backend/dispatcher.py | 58 ++++++-------
.../coprs/views/backend_ns/backend_general.py | 6 +-
4 files changed, 86 insertions(+), 73 deletions(-)
diff --git a/backend/backend/actions.py b/backend/backend/actions.py
index 0600723..19e0270 100644
--- a/backend/backend/actions.py
+++ b/backend/backend/actions.py
@@ -118,4 +118,4 @@ class Action(object):
self.event("Action legal-flag: ignoring")
if "result" in result:
- self.frontend_callback.post_to_frontend({"actions": [result]})
+ self.frontend_callback.update({"actions": [result]})
diff --git a/backend/backend/callback.py b/backend/backend/callback.py
index d2312fe..937cd3e 100644
--- a/backend/backend/callback.py
+++ b/backend/backend/callback.py
@@ -1,62 +1,81 @@
import json
import requests
+from functools import wraps
+import time
class FrontendCallback(object):
+ """
+ Object to send data back to fronted
+ """
- """ Object to send data back to fronted """
-
- def __init__(self, opts):
+ def __init__(self, opts, log=None):
super(FrontendCallback, self).__init__()
self.frontend_url = opts.frontend_url
self.frontend_auth = opts.frontend_auth
+ self.log = log
self.msg = None
- def post_to_frontend(self, data):
- """ Send data to frontend """
-
- headers = {"content-type": "application/json"}
- url = "{0}/update/".format(self.frontend_url)
- auth = ("user", self.frontend_auth)
-
- self.msg = None
- try:
- r = requests.post(url, data=json.dumps(data), auth=auth,
- headers=headers)
- if r.status_code != 200:
- self.msg = "Failed to submit to frontend: {0}: {1}".format(
- r.status_code, r.text)
-
- except requests.RequestException, e:
- self.msg = "Post request failed: {0}".format(e)
-
- if self.msg:
- return False
- else:
- return True
-
- def build_starting(self, data):
+ def _post_to_frontend(self, data, url_path):
+ """
+ Make a request to the frontend
+ """
headers = {"content-type": "application/json"}
- url = "{0}/starting_build/".format(self.frontend_url)
+ url = "{0}/{1}/".format(self.frontend_url, url_path)
auth = ("user", self.frontend_auth)
self.msg = None
- r = None
+ response = None
try:
- r = requests.post(url, data=json.dumps(data), auth=auth,
+ response = requests.post(url, data=json.dumps(data), auth=auth,
headers=headers)
- if r.status_code != 200 or "canceled" not in r.json():
+ if response.status_code != 200:
self.msg = "Failed to submit to frontend: {0}: {1}".format(
- r.status_code, r.text)
+ response.status_code, response.text)
raise requests.RequestException(self.msg)
-
except requests.RequestException, e:
self.msg = "Post request failed: {0}".format(e)
raise
-
- return r.json()["canceled"]
-
-
+ return response
+
+
+ def _post_to_frontend_repeatedly(self, data, url_path, max_repeats=10):
+ """
+ Make a request max_repeats-time to the frontend
+ """
+ repeats = 0
+ while repeats <= max_repeats:
+ try:
+ response = self._post_to_frontend(data, url_path)
+ break
+ except requests.RequestException:
+ if self.log:
+ self.log(self.msg)
+ if repeats == max_repeats:
+ raise
+ repeats += 1
+ time.sleep(5)
+ return response
+
+
+ def update(self, data):
+ """
+ Send data to be updated in the frontend
+ """
+ self._post_to_frontend_repeatedly(data, "update")
+
+
+ def starting_build(self, build_id, chroot_name):
+ """
+ Announce to the frontend that a build is starting.
+ Return: True if the build can start
+ False if the build can not start (can be cancelled or deleted)
+ """
+ data = {"build_id": build_id, "chroot": chroot_name}
+ response = self._post_to_frontend_repeatedly(data, "starting_build")
+ if "can_start" not in response.json():
+ raise requests.RequestException("Bad respond from the frontend")
+ return response.json()["can_start"]
diff --git a/backend/backend/dispatcher.py b/backend/backend/dispatcher.py
index 310070b..04082d2 100644
--- a/backend/backend/dispatcher.py
+++ b/backend/backend/dispatcher.py
@@ -7,7 +7,6 @@ import Queue
import json
import subprocess
import multiprocessing
-import requests
import ansible
import ansible.utils
@@ -364,23 +363,11 @@ class Worker(multiprocessing.Process):
return jobdata
- # maybe we move this to the callback?
- def post_to_frontend(self, data):
- """send data to frontend"""
- i = 10
- while i > 0:
- result = self.frontend_callback.post_to_frontend(data)
- if not result:
- self.callback.log(self.frontend_callback.msg)
- i -= 1
- time.sleep(5)
- else:
- i = 0
- return result
- # maybe we move this to the callback?
def mark_started(self, job):
-
+ """
+ Send data about started build to the frontend
+ """
build = {"id": job.build_id,
"started_on": job.started_on,
"results": job.results,
@@ -389,13 +376,17 @@ class Worker(multiprocessing.Process):
}
data = {"builds": [build]}
- if not self.post_to_frontend(data):
+ try:
+ self.frontend_callback.update(data)
+ except:
raise errors.CoprWorkerError(
"Could not communicate to front end to submit status info")
- # maybe we move this to the callback?
- def return_results(self, job):
+ def return_results(self, job):
+ """
+ Send the build results to the frontend
+ """
self.callback.log(
"{0} status {1}. Took {2} seconds".format(
job.build_id, job.status, job.ended_on - job.started_on))
@@ -411,28 +402,31 @@ class Worker(multiprocessing.Process):
data = {"builds": [build]}
- if not self.post_to_frontend(data):
+ try:
+ self.frontend_callback.update(data)
+ except:
raise errors.CoprWorkerError(
"Could not communicate to front end to submit results")
os.unlink(job.jobfile)
- # maybe we move this to the callback?
- def build_starting(self, job):
- # FIXME very bad name....
- """ Is the build canceled? If not, set it as Starting and return
True
- """
- data = {"build_id": job.build_id,
- "chroot": job.chroot}
- result = None
+ def starting_build(self, job):
+ """
+ Announce to the frontend that a build is starting.
+ Return: True if the build can start
+ False if the build can not start (build is cancelled)
+ """
+ response = None
try:
- result = self.frontend_callback.build_starting(data)
- except requests.RequestException, e:
+ response = self.frontend_callback.starting_build(
+ job.build_id,
+ job.chroot)
+ except:
raise errors.CoprWorkerError(
"Could not communicate to front end to submit results")
- return result
+ return response
def pkg_built_before(self, pkgs, chroot, destdir):
@@ -480,7 +474,7 @@ class Worker(multiprocessing.Process):
# Checking whether the build is not cancelled
- if self.build_starting(job):
+ if not self.starting_build(job):
continue
diff --git a/frontend/coprs_frontend/coprs/views/backend_ns/backend_general.py
b/frontend/coprs_frontend/coprs/views/backend_ns/backend_general.py
index dd7e8c1..3c1b723 100644
--- a/frontend/coprs_frontend/coprs/views/backend_ns/backend_general.py
+++ b/frontend/coprs_frontend/coprs/views/backend_ns/backend_general.py
@@ -97,17 +97,17 @@ def starting_build():
Check if the build is not cancelled and set it to running state
"""
- result = {"canceled": True}
+ result = {"can_start": False}
if "build_id" in flask.request.json and "chroot" in
flask.request.json:
build =
builds_logic.BuildsLogic.get_by_id(flask.request.json["build_id"])
- if build and not build.cancelled:
+ if build and not build.canceled:
builds_logic.BuildsLogic.update_state_from_dict(build, {
"chroot":
flask.request.json["chroot"],
"status": 6}) # starting
db.session.commit()
- result["canceled"] = False
+ result["can_start"] = True
return flask.jsonify(result)