blob: b4c82f82ec59486bd4d431315daa6091afaae60f [file] [log] [blame]
Timo Tijhof51516cd2013-04-09 01:32:29 +02001// Client script for Zuul status page
2//
3// Copyright 2012 OpenStack Foundation
4// Copyright 2013 Timo Tijhof
5// Copyright 2013 Wikimedia Foundation
6//
7// Licensed under the Apache License, Version 2.0 (the "License"); you may
8// not use this file except in compliance with the License. You may obtain
9// a copy of the License at
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16// License for the specific language governing permissions and limitations
17// under the License.
18
19(function ($) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +110020 var $container, $msg, $msgWrap, $indicator, $queueInfo, $queueEventsNum,
21 $queueResultsNum, $pipelines, $jq;
22 var xhr, prevHtml, zuul,
Timo Tijhof51516cd2013-04-09 01:32:29 +020023 demo = location.search.match(/[?&]demo=([^?&]*)/),
24 source = demo ?
25 './status-' + (demo[1] || 'basic') + '.json-sample' :
James E. Blair7c7ed7a2013-05-15 13:13:26 -070026 'status.json';
Timo Tijhof51516cd2013-04-09 01:32:29 +020027
28 zuul = {
29 enabled: true,
30
31 schedule: function () {
32 if (!zuul.enabled) {
33 setTimeout(zuul.schedule, 5000);
34 return;
35 }
36 zuul.update().complete(function () {
37 setTimeout(zuul.schedule, 5000);
38 });
39 },
40
41 /** @return {jQuery.Promise} */
42 update: function () {
43 // Cancel the previous update if it hasn't completed yet.
44 if (xhr) {
45 xhr.abort();
46 }
47
48 zuul.emit('update-start');
49
50 xhr = $.ajax({
51 url: source,
52 dataType: 'json',
53 cache: false
54 })
55 .done(function (data) {
56 var html = '';
57 data = data || {};
58
59 if ('message' in data) {
60 $msg.html(data.message);
61 $msgWrap.removeClass('zuul-msg-wrap-off');
62 } else {
63 $msg.empty();
64 $msgWrap.addClass('zuul-msg-wrap-off');
65 }
66
Sergey Lukjanovdf550c52014-01-11 00:05:16 +040067 if ('zuul_version' in data) {
68 $('#zuul-version-span').text(data['zuul_version']);
69 }
Sergey Lukjanov4df0a222014-01-11 00:12:47 +040070 if ('last_reconfigured' in data) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +110071 var last_reconfigured =
72 new Date(data['last_reconfigured']);
73 $('#last-reconfigured-span').text(
74 last_reconfigured.toString());
Sergey Lukjanov4df0a222014-01-11 00:12:47 +040075 }
Sergey Lukjanovdf550c52014-01-11 00:05:16 +040076
Timo Tijhof51516cd2013-04-09 01:32:29 +020077 $.each(data.pipelines, function (i, pipeline) {
78 html += zuul.format.pipeline(pipeline);
79 });
80
81 // Only re-parse the DOM if we have to
82 if (html !== prevHtml) {
83 prevHtml = html;
84 $pipelines.html(html);
85 }
86
87 $queueEventsNum.text(
Joshua Heskethcbdcca12014-03-20 16:06:25 +110088 data.trigger_event_queue ?
89 data.trigger_event_queue.length : '0'
Timo Tijhof51516cd2013-04-09 01:32:29 +020090 );
91 $queueResultsNum.text(
Joshua Heskethcbdcca12014-03-20 16:06:25 +110092 data.result_event_queue ?
93 data.result_event_queue.length : '0'
Timo Tijhof51516cd2013-04-09 01:32:29 +020094 );
95 })
96 .fail(function (err, jqXHR, errMsg) {
97 $msg.text(source + ': ' + errMsg).show();
98 $msgWrap.removeClass('zuul-msg-wrap-off');
99 })
100 .complete(function () {
101 xhr = undefined;
102 zuul.emit('update-end');
103 });
104
105 return xhr;
106 },
107
108 format: {
109 change: function (change) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100110 var html = '<div class="well well-small zuul-change">' +
111 '<ul class="nav nav-list">',
Timo Tijhof51516cd2013-04-09 01:32:29 +0200112 id = change.id,
113 url = change.url;
114
115 html += '<li class="nav-header">' + change.project;
116 if (id.length === 40) {
117 id = id.substr(0, 7);
118 }
119 html += ' <span class="zuul-change-id">';
120 if (url !== null) {
121 html += '<a href="' + url + '">';
122 }
123 html += id;
124 if (url !== null) {
125 html += '</a>';
126 }
127 html += '</span></li>';
128
129 $.each(change.jobs, function (i, job) {
130 var result = job.result ? job.result.toLowerCase() : null,
131 resultClass = 'zuul-result label';
132 if (result === null) {
133 result = job.url ? 'in progress' : 'queued';
134 }
135 switch (result) {
136 case 'success':
137 resultClass += ' label-success';
138 break;
139 case 'failure':
140 resultClass += ' label-important';
141 break;
142 case 'lost':
143 case 'unstable':
144 resultClass += ' label-warning';
145 break;
146 }
147 html += '<li class="zuul-change-job">';
148 html += job.url !== null ?
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100149 '<a href="' + job.url + '" ' +
150 'class="zuul-change-job-link">' :
Timo Tijhof51516cd2013-04-09 01:32:29 +0200151 '<span class="zuul-change-job-link">';
152 html += job.name;
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100153 html += ' <span class="' + resultClass + '">' + result +
154 '</span>';
Timo Tijhof51516cd2013-04-09 01:32:29 +0200155 if (job.voting === false) {
156 html += ' <span class="muted">(non-voting)</span>';
157 }
158 html += job.url !== null ? '</a>' : '</span>';
159 html += '</li>';
160 });
161
162 html += '</ul></div>';
163 return html;
164 },
165
166 pipeline: function (pipeline) {
167 var html = '<div class="zuul-pipeline span4"><h3>' +
168 pipeline.name + '</h3>';
169 if (typeof pipeline.description === 'string') {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100170 html += '<p><small>' + pipeline.description +
171 '</small></p>';
Timo Tijhof51516cd2013-04-09 01:32:29 +0200172 }
173
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100174 $.each(pipeline.change_queues,
175 function (queueNum, changeQueue) {
Timo Tijhof51516cd2013-04-09 01:32:29 +0200176 $.each(changeQueue.heads, function (headNum, changes) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100177 if (pipeline.change_queues.length > 1 &&
178 headNum === 0) {
Timo Tijhof51516cd2013-04-09 01:32:29 +0200179 var name = changeQueue.name;
180 html += '<p>Queue: <abbr title="' + name + '">';
181 if (name.length > 32) {
182 name = name.substr(0, 32) + '...';
183 }
184 html += name + '</abbr></p>';
185 }
186 $.each(changes, function (changeNum, change) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100187 // If there are multiple changes in the same head
188 // it means they're connected
Timo Tijhof51516cd2013-04-09 01:32:29 +0200189 if (changeNum > 0) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100190 html += '<div class="zuul-change-arrow">' +
191 '&uarr;</div>';
Timo Tijhof51516cd2013-04-09 01:32:29 +0200192 }
193 html += zuul.format.change(change);
194 });
195 });
196 });
197
198 html += '</div>';
199 return html;
200 }
201 },
202
203 emit: function () {
204 $jq.trigger.apply($jq, arguments);
205 return this;
206 },
207 on: function () {
208 $jq.on.apply($jq, arguments);
209 return this;
210 },
211 one: function () {
212 $jq.one.apply($jq, arguments);
213 return this;
214 }
215 };
216
217 $jq = $(zuul);
218
219 $jq.on('update-start', function () {
220 $container.addClass('zuul-container-loading');
221 $indicator.addClass('zuul-spinner-on');
222 });
223
224 $jq.on('update-end', function () {
225 $container.removeClass('zuul-container-loading');
226 setTimeout(function () {
227 $indicator.removeClass('zuul-spinner-on');
228 }, 550);
229 });
230
231 $jq.one('update-end', function () {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100232 // Do this asynchronous so that if the first update adds a message, it
233 // will not animate while we fade in the content. Instead it simply
234 // appears with the rest of the content.
Timo Tijhof51516cd2013-04-09 01:32:29 +0200235 setTimeout(function () {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100236 // Fade in the content
237 $container.addClass('zuul-container-ready');
Timo Tijhof51516cd2013-04-09 01:32:29 +0200238 });
239 });
240
241 $(function ($) {
242 $msg = $('<div class="zuul-msg alert alert-error"></div>');
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100243 $msgWrap = $msg.wrap('<div class="zuul-msg-wrap zuul-msg-wrap-off">' +
244 '</div>').parent();
245 $indicator = $('<span class="btn pull-right zuul-spinner">updating ' +
246 '<i class="icon-refresh"></i></span>');
247 $queueInfo = $('<p>Queue lengths: <span>0</span> events, ' +
248 '<span>0</span> results.</p>');
Timo Tijhof51516cd2013-04-09 01:32:29 +0200249 $queueEventsNum = $queueInfo.find('span').eq(0);
250 $queueResultsNum = $queueEventsNum.next();
251 $pipelines = $('<div class="row"></div>');
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100252 $zuulVersion = $('<p>Zuul version: <span id="zuul-version-span">' +
253 '</span></p>');
254 $lastReconf = $('<p>Last reconfigured: ' +
255 '<span id="last-reconfigured-span"></span></p>');
Timo Tijhof51516cd2013-04-09 01:32:29 +0200256
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100257 $container = $('#zuul-container').append($msgWrap, $indicator,
258 $queueInfo, $pipelines,
259 $zuulVersion, $lastReconf);
Timo Tijhof51516cd2013-04-09 01:32:29 +0200260
261 zuul.schedule();
262
263 $(document).on({
264 'show.visibility': function () {
265 zuul.enabled = true;
266 zuul.update();
267 },
268 'hide.visibility': function () {
269 zuul.enabled = false;
270 }
271 });
272 });
273}(jQuery));