Merge "Remove trailing spaces in debug log"
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 21d3bae..4c5a624 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -114,6 +114,11 @@
starting jobs for a change. Used by zuul-server only.
``status_url=https://zuul.example.com/status``
+**status_expiry**
+ Zuul will cache the status.json file for this many seconds. This is an
+ optional value and ``1`` is used by default.
+ ``status_expiry=1``
+
**url_pattern**
If you are storing build logs external to the system that originally
ran jobs and wish to link to those logs when Zuul makes comments on
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index a953ffb..6e65774 100755
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -14,8 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from cStringIO import StringIO
-import gzip
import json
import logging
import os
@@ -1927,7 +1925,7 @@
self.assertEqual(self.history[4].pipeline, 'check')
self.assertEqual(self.history[5].pipeline, 'check')
- def test_json_status(self, compressed=False):
+ def test_json_status(self):
"Test that we can retrieve JSON status info"
self.worker.hold_jobs_in_build = True
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
@@ -1938,13 +1936,14 @@
port = self.webapp.server.socket.getsockname()[1]
req = urllib2.Request("http://localhost:%s/status.json" % port)
- if compressed:
- req.add_header("accept-encoding", "gzip")
f = urllib2.urlopen(req)
+ headers = f.info()
+ self.assertIn('Content-Length', headers)
+ self.assertIn('Content-Type', headers)
+ self.assertEqual(headers['Content-Type'],
+ 'application/json; charset=UTF-8')
+ self.assertIn('Last-Modified', headers)
data = f.read()
- if compressed:
- gz = gzip.GzipFile(fileobj=StringIO(data))
- data = gz.read()
self.worker.hold_jobs_in_build = False
self.worker.release()
@@ -1968,9 +1967,6 @@
self.assertIn('project-test1', status_jobs)
self.assertIn('project-test2', status_jobs)
- def test_json_status_gzip(self):
- self.test_json_status(True)
-
def test_merging_queues(self):
"Test that transitively-connected change queues are merged"
self.config.set('zuul', 'layout_config',
diff --git a/zuul/cmd/__init__.py b/zuul/cmd/__init__.py
index e17ad5b..8ac3368 100644
--- a/zuul/cmd/__init__.py
+++ b/zuul/cmd/__init__.py
@@ -15,6 +15,8 @@
# under the License.
import ConfigParser
+import cStringIO
+import extras
import logging
import logging.config
import os
@@ -22,6 +24,8 @@
import sys
import traceback
+yappi = extras.try_import('yappi')
+
# No zuul imports here because they pull in paramiko which must not be
# imported until after the daemonization.
# https://github.com/paramiko/paramiko/issues/59
@@ -36,6 +40,17 @@
log_str += "".join(traceback.format_stack(stack_frame))
log = logging.getLogger("zuul.stack_dump")
log.debug(log_str)
+ if yappi:
+ if not yappi.is_running():
+ yappi.start()
+ else:
+ yappi.stop()
+ yappi_out = cStringIO.StringIO()
+ yappi.get_func_stats().print_all(out=yappi_out)
+ yappi.get_thread_stats().print_all(out=yappi_out)
+ log.debug(yappi_out.getvalue())
+ yappi_out.close()
+ yappi.clear_stats()
signal.signal(signal.SIGUSR2, stack_dump_handler)
@@ -47,7 +62,7 @@
def _get_version(self):
from zuul.version import version_info as zuul_version_info
- return "Zuul version: %s" % zuul_version_info.version_string()
+ return "Zuul version: %s" % zuul_version_info.release_string()
def read_config(self):
self.config = ConfigParser.ConfigParser()
diff --git a/zuul/cmd/server.py b/zuul/cmd/server.py
index 06ea780..d7de85a 100755
--- a/zuul/cmd/server.py
+++ b/zuul/cmd/server.py
@@ -163,7 +163,11 @@
merger = zuul.merger.client.MergeClient(self.config, self.sched)
gerrit = zuul.trigger.gerrit.Gerrit(self.config, self.sched)
timer = zuul.trigger.timer.Timer(self.config, self.sched)
- webapp = zuul.webapp.WebApp(self.sched)
+ if self.config.has_option('zuul', 'status_expiry'):
+ cache_expiry = self.config.getint('zuul', 'status_expiry')
+ else:
+ cache_expiry = 1
+ webapp = zuul.webapp.WebApp(self.sched, cache_expiry=cache_expiry)
rpc = zuul.rpclistener.RPCListener(self.config, self.sched)
gerrit_reporter = zuul.reporter.gerrit.Reporter(gerrit)
smtp_reporter = zuul.reporter.smtp.Reporter(
diff --git a/zuul/webapp.py b/zuul/webapp.py
index cd02d20..4d6115f 100644
--- a/zuul/webapp.py
+++ b/zuul/webapp.py
@@ -15,6 +15,7 @@
import logging
import threading
+import time
from paste import httpserver
import webob
from webob import dec
@@ -23,10 +24,13 @@
class WebApp(threading.Thread):
log = logging.getLogger("zuul.WebApp")
- def __init__(self, scheduler, port=8001):
+ def __init__(self, scheduler, port=8001, cache_expiry=1):
threading.Thread.__init__(self)
self.scheduler = scheduler
self.port = port
+ self.cache_expiry = cache_expiry
+ self.cache_time = 0
+ self.cache = None
self.daemon = True
self.server = httpserver.serve(dec.wsgify(self.app), host='0.0.0.0',
port=self.port, start_loop=False)
@@ -40,14 +44,18 @@
def app(self, request):
if request.path != '/status.json':
raise webob.exc.HTTPNotFound()
- try:
- ret = self.scheduler.formatStatusJSON()
- except:
- self.log.exception("Exception formatting status:")
- raise
- response = webob.Response(body=ret, content_type='application/json')
+ if (not self.cache or
+ (time.time() - self.cache_time) > self.cache_expiry):
+ try:
+ self.cache = self.scheduler.formatStatusJSON()
+ # Call time.time() again because formatting above may take
+ # longer than the cache timeout.
+ self.cache_time = time.time()
+ except:
+ self.log.exception("Exception formatting status:")
+ raise
+ response = webob.Response(body=self.cache,
+ content_type='application/json')
response.headers['Access-Control-Allow-Origin'] = '*'
- response.headers['Vary'] = 'Accept-Encoding'
- if 'gzip' in request.headers.get('accept-encoding', ()):
- response.encode_content('gzip')
+ response.last_modified = self.cache_time
return response