blob: f8e6af282eca38fc48e80c2e129e0dc82575bb10 [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
Joshua Hesketh6b1a2182014-03-21 14:40:04 +11006// Copyright 2014 Rackspace Australia
Timo Tijhof51516cd2013-04-09 01:32:29 +02007//
8// Licensed under the Apache License, Version 2.0 (the "License"); you may
9// not use this file except in compliance with the License. You may obtain
10// a copy of the License at
11//
12// http://www.apache.org/licenses/LICENSE-2.0
13//
14// Unless required by applicable law or agreed to in writing, software
15// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17// License for the specific language governing permissions and limitations
18// under the License.
19
20(function ($) {
Joshua Hesketh4863b602014-03-21 14:19:06 +110021 var $container, $msg, $indicator, $queueInfo, $queueEventsNum,
Joshua Heskethcbdcca12014-03-20 16:06:25 +110022 $queueResultsNum, $pipelines, $jq;
Joshua Hesketh298c4912014-03-20 16:06:25 +110023 var xhr, zuul,
Timo Tijhof51516cd2013-04-09 01:32:29 +020024 demo = location.search.match(/[?&]demo=([^?&]*)/),
Joshua Hesketh668700b2014-03-21 14:25:53 +110025 source_url = location.search.match(/[?&]source_url=([^?&]*)/),
Timo Tijhof51516cd2013-04-09 01:32:29 +020026 source = demo ?
27 './status-' + (demo[1] || 'basic') + '.json-sample' :
James E. Blair7c7ed7a2013-05-15 13:13:26 -070028 'status.json';
Joshua Hesketh668700b2014-03-21 14:25:53 +110029 source = source_url ? source_url[1] : source;
Timo Tijhof51516cd2013-04-09 01:32:29 +020030
31 zuul = {
32 enabled: true,
33
34 schedule: function () {
35 if (!zuul.enabled) {
36 setTimeout(zuul.schedule, 5000);
37 return;
38 }
39 zuul.update().complete(function () {
40 setTimeout(zuul.schedule, 5000);
41 });
42 },
43
44 /** @return {jQuery.Promise} */
45 update: function () {
46 // Cancel the previous update if it hasn't completed yet.
47 if (xhr) {
48 xhr.abort();
49 }
50
51 zuul.emit('update-start');
52
Joshua Hesketh298c4912014-03-20 16:06:25 +110053 xhr = $.getJSON(source)
54 .done(function (data) {
55 if ('message' in data) {
Joshua Hesketh4863b602014-03-21 14:19:06 +110056 $msg.removeClass('alert-danger').addClass('alert-info');
Joshua Hesketh298c4912014-03-20 16:06:25 +110057 $msg.text(data.message);
Joshua Hesketh4863b602014-03-21 14:19:06 +110058 $msg.show();
Joshua Hesketh298c4912014-03-20 16:06:25 +110059 } else {
60 $msg.empty();
Joshua Hesketh4863b602014-03-21 14:19:06 +110061 $msg.hide();
Joshua Hesketh298c4912014-03-20 16:06:25 +110062 }
Timo Tijhof51516cd2013-04-09 01:32:29 +020063
Joshua Hesketh298c4912014-03-20 16:06:25 +110064 if ('zuul_version' in data) {
65 $('#zuul-version-span').text(data['zuul_version']);
66 }
67 if ('last_reconfigured' in data) {
68 var last_reconfigured =
69 new Date(data['last_reconfigured']);
70 $('#last-reconfigured-span').text(
71 last_reconfigured.toString());
72 }
73
74 $pipelines.html('');
75 $.each(data.pipelines, function (i, pipeline) {
76 $pipelines.append(zuul.format.pipeline(pipeline));
77 });
78
79 $queueEventsNum.text(
80 data.trigger_event_queue ?
81 data.trigger_event_queue.length : '0'
82 );
83 $queueResultsNum.text(
84 data.result_event_queue ?
85 data.result_event_queue.length : '0'
86 );
87 })
88 .fail(function (err, jqXHR, errMsg) {
89 $msg.text(source + ': ' + errMsg).show();
Timo Tijhof51516cd2013-04-09 01:32:29 +020090 $msgWrap.removeClass('zuul-msg-wrap-off');
Joshua Hesketh298c4912014-03-20 16:06:25 +110091 })
92 .complete(function () {
93 xhr = undefined;
94 zuul.emit('update-end');
Timo Tijhof51516cd2013-04-09 01:32:29 +020095 });
96
Timo Tijhof51516cd2013-04-09 01:32:29 +020097 return xhr;
98 },
99
100 format: {
Joshua Hesketh6b1a2182014-03-21 14:40:04 +1100101 job: function(job) {
102 if (job.url !== null) {
103 $job_line = $('<a href="' + job.url + '" />');
104 }
105 else{
106 $job_line = $('<span />');
107 }
108 $job_line.text(job.name)
109 .append(zuul.format.job_status(job));
110
111 if (job.voting === false) {
112 $job_line.append(
113 $(' <small />').text(' (non-voting)')
114 );
115 }
116 return $job_line;
117 },
118
119 job_status: function(job) {
120 var result = job.result ? job.result.toLowerCase() : null;
121 if (result === null) {
122 result = job.url ? 'in progress' : 'queued';
123 }
124
125 if (result == 'in progress') {
126 return zuul.format.progress_bar(job.elapsed_time,
127 job.remaining_time);
128 }
129 else {
130 return zuul.format.status_label(result);
131 }
132 },
133
134 status_label: function(result) {
135 $status = $('<span />');
136 $status.addClass('zuul-result label');
137
138 switch (result) {
139 case 'success':
140 $status.addClass('label-success');
141 break;
142 case 'failure':
143 $status.addClass('label-danger');
144 break;
145 case 'unstable':
146 $status.addClass('label-warning');
147 break;
148 case 'in progress':
149 case 'queued':
150 case 'lost':
151 $status.addClass('label-default');
152 break;
153 }
154 $status.text(result);
155 return $status;
156 },
157
158 progress_bar: function(elapsed_time, remaining_time) {
159 var progress_percent = 100 * (elapsed_time / (elapsed_time +
160 remaining_time));
161 var $bar_inner = $('<div />')
162 .addClass('progress-bar')
163 .attr('role', 'progressbar')
164 .attr('aria-valuenow', 'progressbar')
165 .attr('aria-valuemin', progress_percent)
166 .attr('aria-valuemin', '0')
167 .attr('aria-valuemax', '100')
168 .css('width', progress_percent + '%');
169
170 var $bar_outter = $('<div />')
171 .addClass('progress zuul-result')
172 .append($bar_inner);
173
174 return $bar_outter;
175 },
176
177 time: function(ms, words) {
178 if (typeof(words) === 'undefined') words = false;
179 var seconds = (+ms)/1000;
180 var minutes = Math.floor(seconds/60);
181 var hours = Math.floor(minutes/60);
182 seconds = Math.floor(seconds % 60);
183 minutes = Math.floor(minutes % 60);
184 r = '';
185 if (words) {
186 if (hours) {
187 r += hours;
188 r += ' hr ';
189 }
190 r += minutes + ' min';
191 } else {
192 if (hours < 10) r += '0';
193 r += hours + ':';
194 if (minutes < 10) r += '0';
195 r += minutes + ':';
196 if (seconds < 10) r += '0';
197 r += seconds;
198 }
199 return r;
200 },
201
Timo Tijhof51516cd2013-04-09 01:32:29 +0200202 change: function (change) {
Joshua Hesketh298c4912014-03-20 16:06:25 +1100203 if (change.id.length === 40) {
204 change.id = change.id.substr(0, 7);
205 }
Timo Tijhof51516cd2013-04-09 01:32:29 +0200206
Joshua Hesketh298c4912014-03-20 16:06:25 +1100207 var $html = $('<div />')
Joshua Hesketh4863b602014-03-21 14:19:06 +1100208 .addClass('panel panel-default zuul-change')
209
210 var $change_header = $('<div />').text(change.project);
211 $change_header.addClass('panel-heading');
Joshua Hesketh298c4912014-03-20 16:06:25 +1100212
213 if (change.url !== null) {
214 var $id_span = $('<span />').append(
215 $("<a />").attr("href", change.url).text(change.id)
216 );
Timo Tijhof51516cd2013-04-09 01:32:29 +0200217 }
Joshua Hesketh298c4912014-03-20 16:06:25 +1100218 else {
219 var $id_span = $('<span />').text(change.id);
Timo Tijhof51516cd2013-04-09 01:32:29 +0200220 }
Joshua Hesketh4863b602014-03-21 14:19:06 +1100221 $change_header.append($id_span.addClass('zuul-change-id'));
222 $html.append($change_header);
Timo Tijhof51516cd2013-04-09 01:32:29 +0200223
Joshua Hesketh6b1a2182014-03-21 14:40:04 +1100224 var $list = $('<ul />')
225 .addClass('list-group');
226
Timo Tijhof51516cd2013-04-09 01:32:29 +0200227 $.each(change.jobs, function (i, job) {
Joshua Hesketh6b1a2182014-03-21 14:40:04 +1100228 var $item = $('<li />')
229 .addClass('list-group-item')
230 .addClass('zuul-change-job')
231 .append(zuul.format.job(job));
Joshua Hesketh298c4912014-03-20 16:06:25 +1100232 $list.append($item);
Timo Tijhof51516cd2013-04-09 01:32:29 +0200233 });
234
Joshua Hesketh298c4912014-03-20 16:06:25 +1100235 $html.append($list);
236 return $html;
Timo Tijhof51516cd2013-04-09 01:32:29 +0200237 },
238
239 pipeline: function (pipeline) {
Joshua Hesketh298c4912014-03-20 16:06:25 +1100240 var $html = $('<div />')
Joshua Hesketh4863b602014-03-21 14:19:06 +1100241 .addClass('zuul-pipeline col-md-4')
Joshua Hesketh298c4912014-03-20 16:06:25 +1100242 .append($('<h3 />').text(pipeline.name));
243
Timo Tijhof51516cd2013-04-09 01:32:29 +0200244 if (typeof pipeline.description === 'string') {
Joshua Hesketh298c4912014-03-20 16:06:25 +1100245 $html.append(
246 $('<p />').append(
247 $('<small />').text(pipeline.description)
248 )
249 );
Timo Tijhof51516cd2013-04-09 01:32:29 +0200250 }
251
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100252 $.each(pipeline.change_queues,
253 function (queueNum, changeQueue) {
Timo Tijhof51516cd2013-04-09 01:32:29 +0200254 $.each(changeQueue.heads, function (headNum, changes) {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100255 if (pipeline.change_queues.length > 1 &&
256 headNum === 0) {
Timo Tijhof51516cd2013-04-09 01:32:29 +0200257 var name = changeQueue.name;
Joshua Hesketh6b1a2182014-03-21 14:40:04 +1100258 var short_name = name;
259 if (short_name.length > 32) {
260 short_name = short_name.substr(0, 32) + '...';
Timo Tijhof51516cd2013-04-09 01:32:29 +0200261 }
Joshua Hesketh298c4912014-03-20 16:06:25 +1100262 $html.append(
263 $('<p />')
264 .text('Queue: ')
265 .append(
266 $('<abbr />')
267 .attr('title', name)
268 .text(short_name)
269 )
270 );
Timo Tijhof51516cd2013-04-09 01:32:29 +0200271 }
272 $.each(changes, function (changeNum, change) {
Joshua Hesketh298c4912014-03-20 16:06:25 +1100273 $html.append(zuul.format.change(change))
Timo Tijhof51516cd2013-04-09 01:32:29 +0200274 });
275 });
276 });
Joshua Hesketh298c4912014-03-20 16:06:25 +1100277 return $html;
Timo Tijhof51516cd2013-04-09 01:32:29 +0200278 }
279 },
280
281 emit: function () {
282 $jq.trigger.apply($jq, arguments);
283 return this;
284 },
285 on: function () {
286 $jq.on.apply($jq, arguments);
287 return this;
288 },
289 one: function () {
290 $jq.one.apply($jq, arguments);
291 return this;
292 }
293 };
294
295 $jq = $(zuul);
296
297 $jq.on('update-start', function () {
298 $container.addClass('zuul-container-loading');
299 $indicator.addClass('zuul-spinner-on');
300 });
301
302 $jq.on('update-end', function () {
303 $container.removeClass('zuul-container-loading');
304 setTimeout(function () {
305 $indicator.removeClass('zuul-spinner-on');
306 }, 550);
307 });
308
309 $jq.one('update-end', function () {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100310 // Do this asynchronous so that if the first update adds a message, it
311 // will not animate while we fade in the content. Instead it simply
312 // appears with the rest of the content.
Timo Tijhof51516cd2013-04-09 01:32:29 +0200313 setTimeout(function () {
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100314 // Fade in the content
315 $container.addClass('zuul-container-ready');
Timo Tijhof51516cd2013-04-09 01:32:29 +0200316 });
317 });
318
319 $(function ($) {
Joshua Hesketh4863b602014-03-21 14:19:06 +1100320 $msg = $('<div />').addClass('alert').hide();
321 $indicator = $('<button class="btn pull-right zuul-spinner">updating '
322 + '<span class="glyphicon glyphicon-refresh"></span>'
323 + '</button>');
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100324 $queueInfo = $('<p>Queue lengths: <span>0</span> events, ' +
325 '<span>0</span> results.</p>');
Joshua Hesketh298c4912014-03-20 16:06:25 +1100326 $queueEventsNum = $queueInfo.find('span').eq(0);
327 $queueResultsNum = $queueEventsNum.next();
Timo Tijhof51516cd2013-04-09 01:32:29 +0200328 $pipelines = $('<div class="row"></div>');
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100329 $zuulVersion = $('<p>Zuul version: <span id="zuul-version-span">' +
330 '</span></p>');
331 $lastReconf = $('<p>Last reconfigured: ' +
332 '<span id="last-reconfigured-span"></span></p>');
Timo Tijhof51516cd2013-04-09 01:32:29 +0200333
Joshua Hesketh4863b602014-03-21 14:19:06 +1100334 $container = $('#zuul-container').append($msg, $indicator,
Joshua Heskethcbdcca12014-03-20 16:06:25 +1100335 $queueInfo, $pipelines,
336 $zuulVersion, $lastReconf);
Timo Tijhof51516cd2013-04-09 01:32:29 +0200337
338 zuul.schedule();
339
340 $(document).on({
341 'show.visibility': function () {
342 zuul.enabled = true;
343 zuul.update();
344 },
345 'hide.visibility': function () {
346 zuul.enabled = false;
347 }
348 });
349 });
350}(jQuery));