Merge "Allow test_playbook to run long"
diff --git a/doc/source/admin/components.rst b/doc/source/admin/components.rst
index ba14752..84ebc10 100644
--- a/doc/source/admin/components.rst
+++ b/doc/source/admin/components.rst
@@ -660,6 +660,16 @@
Base URL on which the websocket service is exposed, if different
than the base URL of the web app.
+ .. attr:: stats_url
+
+ Base URL from which statistics emitted via statsd can be queried.
+
+ .. attr:: stats_type
+ :default: graphite
+
+ Type of server hosting the statistics information. Currently only
+ 'graphite' is supported by the dashboard.
+
.. attr:: static_cache_expiry
:default: 3600
diff --git a/etc/status/public_html/jquery.zuul.js b/etc/status/public_html/jquery.zuul.js
index 50dbed5..ac8a302 100644
--- a/etc/status/public_html/jquery.zuul.js
+++ b/etc/status/public_html/jquery.zuul.js
@@ -49,7 +49,7 @@
options = $.extend({
'enabled': true,
'graphite_url': '',
- 'source': 'status.json',
+ 'source': 'status',
'msg_id': '#zuul_msg',
'pipelines_id': '#zuul_pipelines',
'queue_events_num': '#zuul_queue_events_num',
diff --git a/etc/status/public_html/zuul.app.js b/etc/status/public_html/zuul.app.js
index bf90a4d..6e35eb3 100644
--- a/etc/status/public_html/zuul.app.js
+++ b/etc/status/public_html/zuul.app.js
@@ -55,7 +55,7 @@
var demo = location.search.match(/[?&]demo=([^?&]*)/),
source_url = location.search.match(/[?&]source_url=([^?&]*)/),
source = demo ? './status-' + (demo[1] || 'basic') + '.json-sample' :
- 'status.json';
+ 'status';
source = source_url ? source_url[1] : source;
var zuul = $.zuul({
diff --git a/requirements.txt b/requirements.txt
index 7057c5a..47c0f5e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,6 @@
git+https://github.com/sigmavirus24/github3.py.git@develop#egg=Github3.py
PyYAML>=3.1.0
Paste
-WebOb>=1.2.3
paramiko>=2.0.1
GitPython>=2.1.8
python-daemon>=2.0.4,<2.1.0
diff --git a/tests/unit/test_web.py b/tests/unit/test_web.py
index b5ebe9f..602209f 100644
--- a/tests/unit/test_web.py
+++ b/tests/unit/test_web.py
@@ -22,20 +22,30 @@
import urllib
import time
import socket
-from unittest import skip
-
-import webob
import zuul.web
from tests.base import ZuulTestCase, FIXTURE_DIR
-class TestWeb(ZuulTestCase):
+class FakeConfig(object):
+
+ def __init__(self, config):
+ self.config = config or {}
+
+ def has_option(self, section, option):
+ return option in self.config.get(section, {})
+
+ def get(self, section, option):
+ return self.config.get(section, {}).get(option)
+
+
+class BaseTestWeb(ZuulTestCase):
tenant_config_file = 'config/single-tenant/main.yaml'
+ config_ini_data = {}
def setUp(self):
- super(TestWeb, self).setUp()
+ super(BaseTestWeb, self).setUp()
self.executor_server.hold_jobs_in_build = True
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
A.addApproval('Code-Review', 2)
@@ -45,10 +55,13 @@
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
+ self.zuul_ini_config = FakeConfig(self.config_ini_data)
# Start the web server
self.web = zuul.web.ZuulWeb(
listen_address='127.0.0.1', listen_port=0,
- gear_server='127.0.0.1', gear_port=self.gearman_server.port)
+ gear_server='127.0.0.1', gear_port=self.gearman_server.port,
+ info=zuul.model.WebInfo.fromConfig(self.zuul_ini_config)
+ )
loop = asyncio.new_event_loop()
loop.set_debug(True)
ws_thread = threading.Thread(target=self.web.run, args=(loop,))
@@ -75,7 +88,10 @@
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
- super(TestWeb, self).tearDown()
+ super(BaseTestWeb, self).tearDown()
+
+
+class TestWeb(BaseTestWeb):
def test_web_status(self):
"Test that we can retrieve JSON status info"
@@ -89,7 +105,7 @@
self.waitUntilSettled()
req = urllib.request.Request(
- "http://localhost:%s/tenant-one/status.json" % self.port)
+ "http://localhost:%s/tenant-one/status" % self.port)
f = urllib.request.urlopen(req)
headers = f.info()
self.assertIn('Content-Length', headers)
@@ -184,7 +200,6 @@
"http://localhost:%s/status/foo" % self.port)
self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, req)
- @skip("This is not supported by zuul-web")
def test_web_find_change(self):
# can we filter by change id
req = urllib.request.Request(
@@ -213,24 +228,84 @@
f = urllib.request.urlopen(req)
self.assertEqual(f.read(), public_pem)
- @skip("This may not apply to zuul-web")
- def test_web_custom_handler(self):
- def custom_handler(path, tenant_name, request):
- return webob.Response(body='ok')
-
- self.webapp.register_path('/custom', custom_handler)
- req = urllib.request.Request(
- "http://localhost:%s/custom" % self.port)
- f = urllib.request.urlopen(req)
- self.assertEqual(b'ok', f.read())
-
- self.webapp.unregister_path('/custom')
- self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, req)
-
- @skip("This returns a 500")
def test_web_404_on_unknown_tenant(self):
req = urllib.request.Request(
- "http://localhost:{}/non-tenant/status.json".format(self.port))
+ "http://localhost:{}/non-tenant/status".format(self.port))
e = self.assertRaises(
urllib.error.HTTPError, urllib.request.urlopen, req)
self.assertEqual(404, e.code)
+
+
+class TestInfo(BaseTestWeb):
+
+ def setUp(self):
+ super(TestInfo, self).setUp()
+ web_config = self.config_ini_data.get('web', {})
+ self.websocket_url = web_config.get('websocket_url')
+ self.stats_url = web_config.get('stats_url')
+ statsd_config = self.config_ini_data.get('statsd', {})
+ self.stats_prefix = statsd_config.get('prefix')
+
+ def test_info(self):
+ req = urllib.request.Request(
+ "http://localhost:%s/info" % self.port)
+ f = urllib.request.urlopen(req)
+ info = json.loads(f.read().decode('utf8'))
+ self.assertEqual(
+ info, {
+ "info": {
+ "endpoint": "http://localhost:%s" % self.port,
+ "capabilities": {
+ "job_history": False
+ },
+ "stats": {
+ "url": self.stats_url,
+ "prefix": self.stats_prefix,
+ "type": "graphite",
+ },
+ "websocket_url": self.websocket_url,
+ }
+ })
+
+ def test_tenant_info(self):
+ req = urllib.request.Request(
+ "http://localhost:%s/tenant-one/info" % self.port)
+ f = urllib.request.urlopen(req)
+ info = json.loads(f.read().decode('utf8'))
+ self.assertEqual(
+ info, {
+ "info": {
+ "endpoint": "http://localhost:%s" % self.port,
+ "tenant": "tenant-one",
+ "capabilities": {
+ "job_history": False
+ },
+ "stats": {
+ "url": self.stats_url,
+ "prefix": self.stats_prefix,
+ "type": "graphite",
+ },
+ "websocket_url": self.websocket_url,
+ }
+ })
+
+
+class TestWebSocketInfo(TestInfo):
+
+ config_ini_data = {
+ 'web': {
+ 'websocket_url': 'wss://ws.example.com'
+ }
+ }
+
+
+class TestGraphiteUrl(TestInfo):
+
+ config_ini_data = {
+ 'statsd': {
+ 'prefix': 'example'
+ },
+ 'web': {
+ 'stats_url': 'https://graphite.example.com',
+ }
+ }
diff --git a/tools/zuul-changes.py b/tools/zuul-changes.py
index d258354..cdedf51 100755
--- a/tools/zuul-changes.py
+++ b/tools/zuul-changes.py
@@ -24,7 +24,7 @@
parser.add_argument('pipeline', help='The name of the Zuul pipeline')
options = parser.parse_args()
-data = urllib2.urlopen('%s/status.json' % options.url).read()
+data = urllib2.urlopen('%s/status' % options.url).read()
data = json.loads(data)
for pipeline in data['pipelines']:
diff --git a/zuul/cmd/web.py b/zuul/cmd/web.py
index abdb1cb..8b0e3ee 100755
--- a/zuul/cmd/web.py
+++ b/zuul/cmd/web.py
@@ -20,6 +20,7 @@
import threading
import zuul.cmd
+import zuul.model
import zuul.web
from zuul.lib.config import get_default
@@ -33,8 +34,11 @@
self.web.stop()
def _run(self):
+ info = zuul.model.WebInfo.fromConfig(self.config)
+
params = dict()
+ params['info'] = info
params['listen_address'] = get_default(self.config,
'web', 'listen_address',
'127.0.0.1')
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 270b91c..df6336d 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -1754,20 +1754,22 @@
config_path)
return config_path
- def loadConfig(self, config_path, project_key_dir):
- abide = model.Abide()
-
+ def readConfig(self, config_path):
config_path = self.expandConfigPath(config_path)
with open(config_path) as config_file:
self.log.info("Loading configuration from %s" % (config_path,))
data = yaml.safe_load(config_file)
- config = model.UnparsedAbideConfig()
- config.extend(data)
base = os.path.dirname(os.path.realpath(config_path))
+ unparsed_abide = model.UnparsedAbideConfig(base)
+ unparsed_abide.extend(data)
+ return unparsed_abide
- for conf_tenant in config.tenants:
+ def loadConfig(self, unparsed_abide, project_key_dir):
+ abide = model.Abide()
+ for conf_tenant in unparsed_abide.tenants:
# When performing a full reload, do not use cached data.
- tenant = self.tenant_parser.fromYaml(base, project_key_dir,
+ tenant = self.tenant_parser.fromYaml(unparsed_abide.base,
+ project_key_dir,
conf_tenant, old_tenant=None)
abide.tenants[tenant.name] = tenant
return abide
diff --git a/zuul/connection/__init__.py b/zuul/connection/__init__.py
index 86f14d6..1c62f4d 100644
--- a/zuul/connection/__init__.py
+++ b/zuul/connection/__init__.py
@@ -75,11 +75,14 @@
still in use. Anything in our cache that isn't in the supplied
list should be safe to remove from the cache."""
- def getWebHandlers(self, zuul_web):
+ def getWebHandlers(self, zuul_web, info):
"""Return a list of web handlers to register with zuul-web.
:param zuul.web.ZuulWeb zuul_web:
Zuul Web instance.
+ :param zuul.model.WebInfo info:
+ The WebInfo object for the Zuul Web instance. Can be used by
+ plugins to toggle API capabilities.
:returns: List of `zuul.web.handler.BaseWebHandler` instances.
"""
return []
diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py
index 6dfcdd3..772ba9b 100644
--- a/zuul/driver/github/githubconnection.py
+++ b/zuul/driver/github/githubconnection.py
@@ -1141,7 +1141,7 @@
return statuses
- def getWebHandlers(self, zuul_web):
+ def getWebHandlers(self, zuul_web, info):
return [GithubWebhookHandler(self, zuul_web, 'POST', 'payload')]
def validateWebConfig(self, config, connections):
diff --git a/zuul/driver/sql/sqlconnection.py b/zuul/driver/sql/sqlconnection.py
index 501a2c5..e931301 100644
--- a/zuul/driver/sql/sqlconnection.py
+++ b/zuul/driver/sql/sqlconnection.py
@@ -125,9 +125,10 @@
return zuul_buildset_table, zuul_build_table
- def getWebHandlers(self, zuul_web):
+ def getWebHandlers(self, zuul_web, info):
+ info.capabilities.job_history = True
return [
- SqlWebHandler(self, zuul_web, 'GET', '/{tenant}/builds.json'),
+ SqlWebHandler(self, zuul_web, 'GET', '/{tenant}/builds'),
StaticHandler(zuul_web, '/{tenant}/builds.html'),
]
diff --git a/zuul/model.py b/zuul/model.py
index 763eb66..44e8d06 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -24,6 +24,7 @@
import textwrap
from zuul import change_matcher
+from zuul.lib.config import get_default
MERGER_MERGE = 1 # "git merge"
MERGER_MERGE_RESOLVE = 2 # "git merge -s resolve"
@@ -2446,8 +2447,10 @@
An Abide is a collection of tenants.
"""
- def __init__(self):
+ def __init__(self, base=None):
self.tenants = []
+ self.known_tenants = set()
+ self.base = base
def extend(self, conf):
if isinstance(conf, UnparsedAbideConfig):
@@ -2465,6 +2468,8 @@
key, value = list(item.items())[0]
if key == 'tenant':
self.tenants.append(value)
+ if 'name' in value:
+ self.known_tenants.add(value['name'])
else:
raise ConfigItemUnknownError()
@@ -3178,3 +3183,80 @@
td = self._getTD(build)
td.add(elapsed, result)
td.save()
+
+
+class Capabilities(object):
+ """The set of capabilities this Zuul installation has.
+
+ Some plugins add elements to the external API. In order to
+ facilitate consumers knowing if functionality is available
+ or not, keep track of distinct capability flags.
+ """
+ def __init__(self, job_history=False):
+ self.job_history = job_history
+
+ def __repr__(self):
+ return '<Capabilities 0x%x %s>' % (id(self), self._renderFlags())
+
+ def _renderFlags(self):
+ d = self.toDict()
+ return " ".join(['{k}={v}'.format(k=k, v=v) for (k, v) in d.items()])
+
+ def copy(self):
+ return Capabilities(**self.toDict())
+
+ def toDict(self):
+ d = dict()
+ d['job_history'] = self.job_history
+ return d
+
+
+class WebInfo(object):
+ """Information about the system needed by zuul-web /info."""
+
+ def __init__(self, websocket_url=None, endpoint=None,
+ capabilities=None, stats_url=None,
+ stats_prefix=None, stats_type=None):
+ self.capabilities = capabilities or Capabilities()
+ self.websocket_url = websocket_url
+ self.stats_url = stats_url
+ self.stats_prefix = stats_prefix
+ self.stats_type = stats_type
+ self.endpoint = endpoint
+ self.tenant = None
+
+ def __repr__(self):
+ return '<WebInfo 0x%x capabilities=%s>' % (
+ id(self), str(self.capabilities))
+
+ def copy(self):
+ return WebInfo(
+ websocket_url=self.websocket_url,
+ endpoint=self.endpoint,
+ stats_url=self.stats_url,
+ stats_prefix=self.stats_prefix,
+ stats_type=self.stats_type,
+ capabilities=self.capabilities.copy())
+
+ @staticmethod
+ def fromConfig(config):
+ return WebInfo(
+ websocket_url=get_default(config, 'web', 'websocket_url', None),
+ stats_url=get_default(config, 'web', 'stats_url', None),
+ stats_prefix=get_default(config, 'statsd', 'prefix'),
+ stats_type=get_default(config, 'web', 'stats_type', 'graphite'),
+ )
+
+ def toDict(self):
+ d = dict()
+ d['websocket_url'] = self.websocket_url
+ stats = dict()
+ stats['url'] = self.stats_url
+ stats['prefix'] = self.stats_prefix
+ stats['type'] = self.stats_type
+ d['stats'] = stats
+ d['endpoint'] = self.endpoint
+ d['capabilities'] = self.capabilities.toDict()
+ if self.tenant:
+ d['tenant'] = self.tenant
+ return d
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 083cb12..606cd04 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -246,6 +246,7 @@
self.result_event_queue = queue.Queue()
self.management_event_queue = zuul.lib.queue.MergedQueue()
self.abide = model.Abide()
+ self.unparsed_abide = model.UnparsedAbideConfig()
if not testonly:
time_dir = self._get_time_database_dir()
@@ -550,8 +551,10 @@
self.log.info("Full reconfiguration beginning")
loader = configloader.ConfigLoader(
self.connections, self, self.merger)
+ self.unparsed_abide = loader.readConfig(
+ self.config.get('scheduler', 'tenant_config'))
abide = loader.loadConfig(
- self.config.get('scheduler', 'tenant_config'),
+ self.unparsed_abide,
self._get_project_key_dir())
for tenant in abide.tenants.values():
self._reconfigureTenant(tenant)
@@ -1149,6 +1152,8 @@
data['pipelines'] = pipelines
tenant = self.abide.tenants.get(tenant_name)
if not tenant:
+ if tenant_name not in self.unparsed_abide.known_tenants:
+ return json.dumps({"message": "Unknown tenant"})
self.log.warning("Tenant %s isn't loaded" % tenant_name)
return json.dumps(
{"message": "Tenant %s isn't ready" % tenant_name})
diff --git a/zuul/web/__init__.py b/zuul/web/__init__.py
index e962738..7a1af30 100755
--- a/zuul/web/__init__.py
+++ b/zuul/web/__init__.py
@@ -16,6 +16,7 @@
import asyncio
+import copy
import json
import logging
import os
@@ -25,6 +26,7 @@
import aiohttp
from aiohttp import web
+import zuul.model
import zuul.rpcclient
from zuul.web.handler import StaticHandler
@@ -158,41 +160,47 @@
'key_get': self.key_get,
}
- async def tenant_list(self, request):
+ async def tenant_list(self, request, result_filter=None):
job = self.rpc.submitJob('zuul:tenant_list', {})
return web.json_response(json.loads(job.data[0]))
- async def status_get(self, request):
+ async def status_get(self, request, result_filter=None):
tenant = request.match_info["tenant"]
if tenant not in self.cache or \
(time.time() - self.cache_time[tenant]) > self.cache_expiry:
job = self.rpc.submitJob('zuul:status_get', {'tenant': tenant})
self.cache[tenant] = json.loads(job.data[0])
self.cache_time[tenant] = time.time()
- resp = web.json_response(self.cache[tenant])
+ payload = self.cache[tenant]
+ if payload.get('message') == 'Unknown tenant':
+ return web.HTTPNotFound()
+ if result_filter:
+ payload = result_filter.filterPayload(payload)
+ resp = web.json_response(payload)
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers["Cache-Control"] = "public, max-age=%d" % \
self.cache_expiry
resp.last_modified = self.cache_time[tenant]
return resp
- async def job_list(self, request):
+ async def job_list(self, request, result_filter=None):
tenant = request.match_info["tenant"]
job = self.rpc.submitJob('zuul:job_list', {'tenant': tenant})
resp = web.json_response(json.loads(job.data[0]))
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
- async def key_get(self, request):
+ async def key_get(self, request, result_filter=None):
tenant = request.match_info["tenant"]
project = request.match_info["project"]
job = self.rpc.submitJob('zuul:key_get', {'tenant': tenant,
'project': project})
return web.Response(body=job.data[0])
- async def processRequest(self, request, action):
+ async def processRequest(self, request, action, result_filter=None):
+ resp = None
try:
- resp = await self.controllers[action](request)
+ resp = await self.controllers[action](request, result_filter)
except asyncio.CancelledError:
self.log.debug("request handling cancelled")
except Exception as e:
@@ -202,6 +210,24 @@
return resp
+class ChangeFilter(object):
+ def __init__(self, desired):
+ self.desired = desired
+
+ def filterPayload(self, payload):
+ status = []
+ for pipeline in payload['pipelines']:
+ for change_queue in pipeline['change_queues']:
+ for head in change_queue['heads']:
+ for change in head:
+ if self.wantChange(change):
+ status.append(copy.deepcopy(change))
+ return status
+
+ def wantChange(self, change):
+ return change['id'] == self.desired
+
+
class ZuulWeb(object):
log = logging.getLogger("zuul.web.ZuulWeb")
@@ -210,13 +236,16 @@
gear_server, gear_port,
ssl_key=None, ssl_cert=None, ssl_ca=None,
static_cache_expiry=3600,
- connections=None):
+ connections=None,
+ info=None):
+ self.start_time = time.time()
self.listen_address = listen_address
self.listen_port = listen_port
self.event_loop = None
self.term = None
self.server = None
self.static_cache_expiry = static_cache_expiry
+ self.info = info
# instanciate handlers
self.rpc = zuul.rpcclient.RPCClient(gear_server, gear_port,
ssl_key, ssl_cert, ssl_ca)
@@ -225,12 +254,37 @@
self._plugin_routes = [] # type: List[zuul.web.handler.BaseWebHandler]
connections = connections or []
for connection in connections:
- self._plugin_routes.extend(connection.getWebHandlers(self))
+ self._plugin_routes.extend(
+ connection.getWebHandlers(self, self.info))
async def _handleWebsocket(self, request):
return await self.log_streaming_handler.processRequest(
request)
+ async def _handleRootInfo(self, request):
+ info = self.info.copy()
+ info.endpoint = str(request.url.parent)
+ return self._handleInfo(info)
+
+ def _handleTenantInfo(self, request):
+ info = self.info.copy()
+ info.tenant = request.match_info["tenant"]
+ # yarl.URL.parent on a root url returns the root url, so this is
+ # both safe and accurate for white-labeled tenants like OpenStack,
+ # zuul-web running on / and zuul-web running on a sub-url like
+ # softwarefactory-project.io
+ info.endpoint = str(request.url.parent.parent.parent)
+ return self._handleInfo(info)
+
+ def _handleInfo(self, info):
+ resp = web.json_response({'info': info.toDict()}, status=200)
+ resp.headers['Access-Control-Allow-Origin'] = '*'
+ if self.static_cache_expiry:
+ resp.headers['Cache-Control'] = "public, max-age=%d" % \
+ self.static_cache_expiry
+ resp.last_modified = self.start_time
+ return resp
+
async def _handleTenantsRequest(self, request):
return await self.gearman_handler.processRequest(request,
'tenant_list')
@@ -238,6 +292,11 @@
async def _handleStatusRequest(self, request):
return await self.gearman_handler.processRequest(request, 'status_get')
+ async def _handleStatusChangeRequest(self, request):
+ change = request.match_info["change"]
+ return await self.gearman_handler.processRequest(
+ request, 'status_get', ChangeFilter(change))
+
async def _handleJobsRequest(self, request):
return await self.gearman_handler.processRequest(request, 'job_list')
@@ -256,9 +315,13 @@
is run within a separate (non-main) thread.
"""
routes = [
- ('GET', '/tenants.json', self._handleTenantsRequest),
- ('GET', '/{tenant}/status.json', self._handleStatusRequest),
- ('GET', '/{tenant}/jobs.json', self._handleJobsRequest),
+ ('GET', '/info', self._handleRootInfo),
+ ('GET', '/{tenant}/info', self._handleTenantInfo),
+ ('GET', '/tenants', self._handleTenantsRequest),
+ ('GET', '/{tenant}/status', self._handleStatusRequest),
+ ('GET', '/{tenant}/jobs', self._handleJobsRequest),
+ ('GET', '/{tenant}/status/change/{change}',
+ self._handleStatusChangeRequest),
('GET', '/{tenant}/console-stream', self._handleWebsocket),
('GET', '/{tenant}/{project:.*}.pub', self._handleKeyRequest),
]
diff --git a/zuul/web/static/javascripts/jquery.zuul.js b/zuul/web/static/javascripts/jquery.zuul.js
index 7e6788b..7da81dc 100644
--- a/zuul/web/static/javascripts/jquery.zuul.js
+++ b/zuul/web/static/javascripts/jquery.zuul.js
@@ -49,7 +49,7 @@
options = $.extend({
'enabled': true,
'graphite_url': '',
- 'source': 'status.json',
+ 'source': 'status',
'msg_id': '#zuul_msg',
'pipelines_id': '#zuul_pipelines',
'queue_events_num': '#zuul_queue_events_num',
diff --git a/zuul/web/static/javascripts/zuul.angular.js b/zuul/web/static/javascripts/zuul.angular.js
index 87cbbdd..49f2518 100644
--- a/zuul/web/static/javascripts/zuul.angular.js
+++ b/zuul/web/static/javascripts/zuul.angular.js
@@ -23,7 +23,7 @@
{
$scope.tenants = undefined;
$scope.tenants_fetch = function() {
- $http.get("tenants.json")
+ $http.get("tenants")
.then(function success(result) {
$scope.tenants = result.data;
});
@@ -36,7 +36,7 @@
{
$scope.jobs = undefined;
$scope.jobs_fetch = function() {
- $http.get("jobs.json")
+ $http.get("jobs")
.then(function success(result) {
$scope.jobs = result.data;
});
@@ -78,7 +78,7 @@
if ($scope.job_name) {query_string += "&job_name="+$scope.job_name;}
if ($scope.project) {query_string += "&project="+$scope.project;}
if (query_string != "") {query_string = "?" + query_string.substr(1);}
- $http.get("builds.json" + query_string)
+ $http.get("builds" + query_string)
.then(function success(result) {
for (build_pos = 0;
build_pos < result.data.length;
diff --git a/zuul/web/static/javascripts/zuul.app.js b/zuul/web/static/javascripts/zuul.app.js
index bf90a4d..6e35eb3 100644
--- a/zuul/web/static/javascripts/zuul.app.js
+++ b/zuul/web/static/javascripts/zuul.app.js
@@ -55,7 +55,7 @@
var demo = location.search.match(/[?&]demo=([^?&]*)/),
source_url = location.search.match(/[?&]source_url=([^?&]*)/),
source = demo ? './status-' + (demo[1] || 'basic') + '.json-sample' :
- 'status.json';
+ 'status';
source = source_url ? source_url[1] : source;
var zuul = $.zuul({