Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: appengine/swarming/elements/res/imp/taskpage/task-page.html

Issue 2408743002: Move elements/ to ui/ (Closed)
Patch Set: rebase again Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!--
2 Copyright 2016 The LUCI Authors. All rights reserved.
3 Use of this source code is governed under the Apache License, Version 2.0
4 that can be found in the LICENSE file.
5
6 This in an HTML Import-able file that contains the definition
7 of the following elements:
8
9 <task-page>
10
11 task-page shows the request, results, stats, and standard output of a task.
12
13 This is a top-level element.
14
15 Properties:
16 task_id: String, Used in testing to specify a task_id
17 client_id: String, Oauth 2.0 client id. It will be set by server-side
18 template evaluation.
19
20 Methods:
21 None.
22
23 Events:
24 None.
25 -->
26
27 <link rel="import" href="/res/imp/bower_components/iron-icon/iron-icon.html">
28 <link rel="import" href="/res/imp/bower_components/iron-icons/iron-icons.html">
29 <link rel="import" href="/res/imp/bower_components/paper-button/paper-button.htm l">
30 <link rel="import" href="/res/imp/bower_components/paper-dialog/paper-dialog.htm l">
31 <link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html" >
32 <link rel="import" href="/res/imp/bower_components/paper-tabs/paper-tabs.html">
33 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html">
34
35 <link rel="import" href="/res/imp/common/common-behavior.html">
36 <link rel="import" href="/res/imp/common/error-toast.html">
37 <link rel="import" href="/res/imp/common/interval-timer.html">
38 <link rel="import" href="/res/imp/common/single-page-style.html">
39 <link rel="import" href="/res/imp/common/swarming-app.html">
40 <link rel="import" href="/res/imp/common/task-behavior.html">
41 <link rel="import" href="/res/imp/common/url-param.html">
42
43 <link rel="import" href="task-page-data.html">
44
45 <dom-module id="task-page">
46 <template>
47 <style include="iron-flex iron-flex-alignment swarming-app-style single-page -style task-style">
48 .milo {
49 width: calc(100% - 11px);
50 /** We don't control the milo site and it's on a different domain than
51 us, so there's no good way to avoid scrolling other than tell the iframe
52 it is really tall.*/
53 height: 2000px;
54 }
55
56 .left {
57 min-width: 550px;
58 }
59 .right {
60 min-width: 500px;
61 margin-top: 8px;
62 }
63
64 .expand {
65 min-width: 3em;
66 vertical-align: middle;
67 padding: .5em;
68 }
69
70 .code {
71 font-family: monospace;
72 }
73
74 .stdout {
75 white-space: pre-line;
76 padding: 2px;
77 }
78
79 .refresh_input {
80 padding: 0 5px;
81 }
82
83 .tabbed {
84 border: 3px solid #1F78B4;
85 margin-left: 5px;
86 min-height: 80vh;
87 }
88 </style>
89
90 <url-param name="id"
91 value="{{task_id}}">
92 </url-param>
93 <url-param name="request_detail"
94 value="{{_request_detail}}">
95 </url-param>
96 <url-param name="show_raw"
97 value="{{_show_raw}}">
98 </url-param>
99 <url-param name="refresh"
100 value="{{_refresh_interval}}"
101 default_value="10">
102 </url-param>
103
104 <swarming-app
105 client_id="[[client_id]]"
106 auth_headers="{{_auth_headers}}"
107 permissions="{{_permissions}}"
108 profile="{{_profile}}"
109 server_details="{{_server_details}}"
110 signed_in="{{_signed_in}}"
111
112 busy="[[_busy]]"
113 name="Swarming Task Page">
114
115 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
116
117 <div hidden$="[[_not(_signed_in)]]">
118
119 <task-page-data
120 id="data"
121 auth_headers="[[_auth_headers]]"
122 task_id="[[task_id]]"
123
124 busy="{{_busy}}"
125 request="{{_request}}"
126 result="{{_result}}"
127 stdout="{{_stdout}}">
128 </task-page-data>
129
130 <div class="horizontal layout wrap">
131 <div class="left flex">
132 <div class="horizontal layout">
133 <paper-input class="id_input" label="Task id" value="{{task_id}}"> </paper-input>
134 <button on-click="_refresh">
135 <iron-icon class="refresh" icon="icons:refresh"></iron-icon>
136 </button>
137 <button on-click="_promptRetry">Retry</button>
138 <template is="dom-if" if="[[_canCancelTask(_result,_permissions)]] ">
139 <button on-click="_promptCancel">Cancel</button>
140 </template>
141 </div>
142 <table>
143 <tr>
144 <td>Name</td>
145 <td>[[_request.name]]</td>
146 </tr>
147 <tr>
148 <td>State</td>
149 <td class$="[[_stateClass(_result)]]">[[_state(_result)]]</td>
150 </tr>
151 <tr>
152 <td>Created</td>
153 <td title$="[[_request.created_ts]]">[[_request.human_created_ts ]]</td>
154 </tr>
155 <template is="dom-if" if="[[_wasPickedUp(_result)]]">
156 <tr>
157 <td>Started</td>
158 <td title$="[[_result.started_ts]]">[[_result.human_started_ts ]]</td>
159 </tr>
160 </template>
161 <template is="dom-if" if="[[_wasNotPickedUp(_result)]]">
162 <tr>
163 <td>Expires</td>
164 <td>[[_expires(_request)]]</td>
165 </tr>
166 </template>
167 <template is="dom-if" if="[[_result.human_completed_ts]]">
168 <tr>
169 <td>Completed</td>
170 <td title$="[[_result.completed_ts]]">[[_result.human_complete d_ts]]</td>
171 </tr>
172 </template>
173 <template is="dom-if" if="[[_result.human_abandoned_ts]]">
174 <tr>
175 <td>Abandoned</td>
176 <td title$="[[_result.abandoned_ts]]">[[_result.human_abandone d_ts]]</td>
177 </tr>
178 </template>
179 <tr>
180 <td>Last Updated</td>
181 <td title$="[[_result.modified_ts]]">[[_result.human_modified_ts ]]</td>
182 </tr>
183 <template is="dom-if" if="[[_result.deduped_from]]">
184 <tr>
185 <td><b>Deduped from</b></td>
186 <td>
187 <a href$="[[_taskLink(_result.deduped_from)]]">
188 [[_result.deduped_from]]
189 </a>
190 </td>
191 </tr>
192 </template>
193 <tr>
194 <td>Pending Time</td>
195 <td>[[_pending(_result)]]</td>
196 </tr>
197 <tr>
198 <td>Duration</td>
199 <td>[[_result.human_duration]]</td>
200 </tr>
201 <tr>
202 <td>Priority</td>
203 <td>[[_request.priority]]</td>
204 </tr>
205 <tr>
206 <td>User</td>
207 <td>[[_request.user]]</td>
208 </tr>
209 <tr>
210 <td>Authenticated</td>
211 <td>[[_request.authenticated]]</td>
212 </tr>
213 <template is="dom-if" if="[[_request.service_account]]">
214 <tr>
215 <td>Service Account</td>
216 <td>[[_request.service_account]]</td>
217 </tr>
218 </template>
219 <template is="dom-if" if="[[_request.properties.secret_bytes]]">
220 <tr>
221 <td>Secret Bytes</td>
222 <td>[[_request.properties.secret_bytes]]</td>
223 </tr>
224 </template>
225 <template is="dom-if" if="[[_request.parent_task_id]]">
226 <tr>
227 <td>Parent Task</td>
228 <td>
229 <a href$="[[_taskLink(_request.parent_task_id)]]">[[_request .parent_task_id]]</a>
230 </td>
231 </tr>
232 </template>
233 <tr>
234 <td rowspan$="[[_rowspan(_request.properties.dimensions)]]">
235 <a
236 title="The list of bots that matches the list of dimension s"
237 href$="[[_botListLink(_request.properties.dimensions)]]">
238 Requested Dimensions
239 </a>
240 </td>
241 </tr>
242 <template is="dom-repeat" items="{{_request.properties.dimensions} }" as="dimension">
243 <tr>
244 <td><b>[[dimension.key]]:</b> [[_alias(dimension)]]</td>
245 </tr>
246 </template>
247 <tr>
248 <td>Isolated Inputs</td>
249 <td>
250 <a href$="[[_isolateLink(_request.properties.inputs_ref)]]">
251 [[_request.properties.inputs_ref.isolated]]
252 </a>
253 </td>
254 </tr>
255 <template is="dom-if" if="[[_not(_request_detail)]]">
256 <tr>
257 <td>More Details</td>
258 <td>
259 <button on-click="_toggleDetails">
260 <iron-icon icon="icons:add-circle-outline"></iron-icon>
261 </button>
262 </td>
263 </tr>
264 </template>
265 <template is="dom-if" if="[[_request_detail]]">
266 <tr>
267 <td>Hide Details</td>
268 <td>
269 <button on-click="_toggleDetails">
270 <iron-icon icon="icons:remove-circle-outline"></iron-icon>
271 </button>
272 </td>
273 </tr>
274 </template>
275 <template is="dom-if" if="[[_request_detail]]">
276 <tr>
277 <td>Extra Args</td>
278 <td class="code">[[_extraArgs(_request)]]</td>
279 </tr>
280 <tr>
281 <td rowspan$="[[_rowspan(_request.tags)]]">Tags</td>
282 </tr>
283 <template is="dom-repeat" items="{{_request.tags}}" as="tag">
284 <tr>
285 <td>[[tag]]</td>
286 </tr>
287 </template>
288
289 <tr>
290 <td>Execution timeout</td>
291 <td>[[_humanDuration(_request.properties.execution_timeout_sec s)]]</td>
292 </tr>
293 <tr>
294 <td>I/O timeout</td>
295 <td>[[_humanDuration(_request.properties.io_timeout_secs)]]</t d>
296 </tr>
297 <tr>
298 <td>Grace period</td>
299 <td>[[_humanDuration(_request.properties.grace_period_secs)]]< /td>
300 </tr>
301
302 <tr>
303 <td>CIPD server</td>
304 <td>
305 <a href$="[[_request.properties.cipd_input.server]]">
306 [[_request.properties.cipd_input.server]]
307 </a>
308 </td>
309 </tr>
310 <tr>
311 <td>CIPD version</td>
312 <td>[[_request.properties.cipd_input.client_package.version]]< /td>
313 </tr>
314 <template is="dom-if" if="[[_wasPickedUp(_result)]]">
315 <tr>
316 <td>CIPD package name</td>
317 <td>[[_result.cipd_pins.client_package.package_name]]</td>
318 </tr>
319 </template>
320
321 <tr hidden$="[[_not(_request.properties.cipd_input)]]">
322 <td rowspan$="[[_cipdRowspan(_request,_result)]]">CIPD package s</td>
323 </tr>
324 <template is="dom-repeat" items="[[_cipdPackages(_request,_resul t)]]" as="cipd">
325 <tr>
326 <td>[[cipd.path]]/</td>
327 </tr>
328 <tr>
329 <td><b>Requested:</b>[[cipd.requested]]</td>
330 </tr>
331 <tr hidden$="[[_wasNotPickedUp(_result)]]">
332 <td><b>Actual:</b>[[cipd.actual]]</td>
333 </tr>
334 </template>
335 </template>
336 </table>
337
338 <div class="title">Task Execution</div>
339 <template is="dom-if" if="[[_wasPickedUp(_result)]]">
340 <table>
341 <tr>
342 <td>Bot assigned to task</td>
343 <td><a href$="[[_botLink(_result.bot_id)]]">[[_result.bot_id]] </td>
344 </tr>
345 <tr>
346 <td rowspan$="[[_rowspan(_result.bot_dimensions)]]">
347 <a>Bot Dimensions</a>
348 </td>
349 </tr>
350 <template is="dom-repeat" items="[[_result.bot_dimensions]]" as= "dimension">
351 <tr>
352 <td><b>[[dimension.key]]:</b> [[_alias(dimension)]]</td>
353 </tr>
354 </template>
355
356 <tr>
357 <td>Exit code</td>
358 <td>[[_result.exit_code]]</td>
359 </tr>
360 <tr>
361 <td>Try number</td>
362 <td>[[_result.try_number]]</td>
363 </tr>
364 <tr>
365 <td>Failure</td>
366 <td class$="[[_failureClass(_result.failure)]]">[[_result.fail ure]]</td>
367 </tr>
368 <tr>
369 <td>Internal Failure</td>
370 <td class$="[[_internalClass(_result.internal_failure)]]">[[_r esult.internal_failure]]</td>
371 </tr>
372 <tr>
373 <td>Isolated Outputs</td>
374 <td>
375 <a href$="[[_isolateLink(_result.outputs_ref)]]">
376 [[_result.outputs_ref.isolated]]
377 </a>
378 </td>
379 </tr>
380 <tr>
381 <td>Bot version</td>
382 <td>[[_result.bot_version]]</td>
383 </tr>
384 <tr>
385 <td>Server version</td>
386 <td>[[_result.server_versions]]</td>
387 </tr>
388 </table>
389 </template>
390 <template is="dom-if" if="[[_wasNotPickedUp(_result)]]">
391 This space left blank until a bot is assigned to the task.
392 </template>
393
394 <template is="dom-if" if="[[_result.performance_stats]]">
395 <div class="title">Performance Stats</div>
396 <table>
397 <tr>
398 <td title="This includes time taken to download inputs, isolat e outputs, and setup CIPD">Total Overhead</td>
399 <td>[[_humanDuration(_result.performance_stats.bot_overhead)]] </td>
400 </tr>
401 <tr>
402 <td>Downloading Inputs From Isolate</td>
403 <td>[[_humanDuration(_result.performance_stats.isolated_downlo ad.duration)]]</td>
404 </tr>
405 <tr>
406 <td>Uploading Outputs To Isolate</td>
407 <td>[[_humanDuration(_result.performance_stats.isolated_upload .duration)]]</td>
408 </tr>
409 <tr>
410 <td>Initial bot cache</td>
411 <td>[[_result.performance_stats.isolated_download.initial_numb er_items]] items;
412 [[_bytes(_result.performance_stats.isolated_download.initial_s ize)]]</td>
413 </tr>
414 </table>
415 </template>
416 </div>
417
418 <div class="flex right">
419 <div class="horizontal layout">
420 <div class="tabs">
421 <paper-tabs selected="{{_show_raw}}" no-bar>
422 <paper-tab disabled$="[[_noMilo(_request)]]">Milo Output</pape r-tab>
423 <paper-tab>Raw Output</paper-tab>
424 </paper-tabs>
425 </div>
426
427 <paper-input
428 class="refresh_input"
429 label="Refresh Interval (seconds)"
430 value="{{_refresh_interval}}"
431 title="How often to refresh all information about the task"
432 auto-validate
433 min="1"
434 max="1000"
435 pattern="[0-9]+">
436 </paper-input>
437 </div>
438
439 <template is="dom-if" if="[[_supportsMilo(_request,_show_raw)]]">
440 <iframe id="miloFrame" class="milo tabbed" src$="[[_getDisplayServ erLink(_server_details.display_server_url_template,task_id)]]"></iframe>
441 </template>
442 <template is="dom-if" if="[[_show_raw]]">
443 <div class="code stdout tabbed">[[_stdout]]</div>
444 </template>
445 </div>
446 </div>
447 </div>
448 </swarming-app>
449
450 <paper-dialog id="prompt" modal on-iron-overlay-closed="_promptClosed">
451 <h2>Are you sure?</h2>
452 <div>Are you sure you want to [[_dialogPrompt]]?</div>
453 <div class="buttons">
454 <paper-button dialog-dismiss autofocus>No</paper-button>
455 <paper-button dialog-confirm>Yes</paper-button>
456 </div>
457 </paper-dialog>
458
459 <error-toast></error-toast>
460
461 <interval-timer period="[[_refresh_interval]]" on-trigger="_softRefresh">
462 </interval-timer>
463
464 </template>
465 <script>
466 (function(){
467 Polymer({
468 is: 'task-page',
469
470 behaviors: [
471 SwarmingBehaviors.CommonBehavior,
472 SwarmingBehaviors.TaskBehavior,
473 ],
474
475 properties: {
476 task_id: {
477 type: String,
478 },
479 client_id: {
480 type: String,
481 },
482
483 _dialogPrompt: {
484 type: String,
485 value: "",
486 },
487 _refresh_interval: {
488 type: Number,
489 },
490 _request: {
491 type: Object,
492 observer: "_requestUpdated"
493 },
494 _request_detail: {
495 type: Boolean,
496 },
497 _result: {
498 type: Object,
499 },
500 _server_details: {
501 type: Object,
502 },
503 _stdout: {
504 type: String,
505 }
506 },
507
508 _alias: function(dim) {
509 var values = dim.value;
510 if (!Array.isArray(values)) {
511 values = [values];
512 }
513 if (swarming.alias.has(dim.key)) {
514 values.forEach(function(v, i){
515 values[i] = swarming.alias.apply(v, dim.key);
516 });
517 }
518 return values.join(" | ");
519 },
520
521 _bytes: function(sizeInBytes) {
522 return sk.human.bytes(sizeInBytes);
523 },
524
525 _canCancelTask: function(result, permissions) {
526 return result && result.state === "PENDING" && permissions.cancel_task;
527 },
528
529 _cancelTask: function() {
530 var url = "/_ah/api/swarming/v1/task/" + this.task_id +"/cancel";
531 swarming.postWithToast(url, "Canceling task " + this.task_id, this._auth _headers);
532 },
533
534 _cipdRowspan: function(request, result) {
535 if (!request || !request.properties || !request.properties.cipd_input) {
536 return 0;
537 }
538 // We always need to at least double the number of packages because we
539 // show the path and then the requested. If the actual package info
540 // is available, we triple the number of packages to account for that.
541 var rowSpan = (request.properties.cipd_input.packages || []).length;
542 if (result && result.cipd_pins && result.cipd_pins.packages) {
543 rowSpan *= 3;
544 } else {
545 rowSpan *= 2;
546 }
547 // Add one because rowSpan counts from 1.
548 return rowSpan + 1;
549 },
550
551 _cipdPackages: function(request, result) {
552 if (!request || !request.properties || !request.properties.cipd_input) {
553 return [];
554 }
555 var packages = request.properties.cipd_input.packages || [];
556 var actual = (result && result.cipd_pins && result.cipd_pins.packages) | | [];
557 packages.forEach(function(p) {
558 p.requested = p.package_name + ":" + p.version;
559 actual.forEach(function(c) {
560 if (c.path === p.path) {
561 p.actual = c.package_name + ":" + c.version;
562 }
563 });
564 });
565 return packages;
566 },
567
568 _expires: function(request) {
569 var delta = parseInt(request.expiration_secs);
570 if (delta) {
571 return sk.human.localeTime(new Date(request.created_ts.getTime() + del ta * 1000));
572 }
573 // Fall back to something
574 return request.expiration_secs + " seconds from created time";
575 },
576
577 _extraArgs: function(request) {
578 if (!request || !request.properties) {
579 return "";
580 }
581 var args = request.properties.extra_args || [];
582 return args.join(" ");
583 },
584
585 _failureClass: function(failure) {
586 if (failure) {
587 return "failed_task";
588 }
589 return "";
590 },
591
592 _getDisplayServerLink: function(template,id) {
593 if (!template) {
594 return undefined;
595 }
596 return template.replace("%s", id);
597 },
598
599 _internalClass: function(failure) {
600 if (failure) {
601 return "exception";
602 }
603 return "";
604 },
605
606 _isolateLink: function(ref) {
607 if (!ref || !ref.isolatedserver) {
608 return undefined;
609 }
610 return ref.isolatedserver + "/browse?namespace="+ref.namespace +
611 "&hash=" + ref.isolated;
612 },
613
614 _noMilo: function(result) {
615 return !this._tag(result, "allow_milo");
616 },
617
618 _pending: function(result) {
619 if (!result.created_ts) {
620 return "";
621 }
622 var end = result.started_ts || result.abandoned_ts || new Date();
623 // In the case of deduplicated tasks, started_ts comes before the task.
624 if (end <= result.created_ts) {
625 return "0s";
626 }
627 return this._timeDiffExact(result.created_ts, end);
628 },
629
630 _promptClosed: function(e) {
631 if (e.detail.confirmed) {
632 if (this._dialogPrompt.startsWith("cancel")) {
633 this._cancelTask();
634 } else {
635 this._retryTask();
636 }
637 }
638 },
639
640 _promptCancel: function() {
641 this.set("_dialogPrompt", "cancel task "+this.task_id);
642 this.$.prompt.open();
643 },
644
645 _promptRetry: function() {
646 this.set("_dialogPrompt", "retry task "+this.task_id);
647 this.$.prompt.open();
648 },
649
650 _refresh: function() {
651 this.$.data.reload();
652 },
653
654 _requestUpdated: function(request) {
655 if (this._noMilo(request)) {
656 this.set("_show_raw", 1);
657 }
658 },
659
660 _softRefresh: function() {
661 if (this._result && this._result.state !== "RUNNING" &&
662 this._result.state !== "PENDING") {
663 return;
664 }
665 this.$.data.reload();
666 var miloFrame = this.$$("iframe")
667 if (miloFrame) {
668 miloFrame.src = this._getDisplayServerLink(this._server_details.displa y_server_url_template,this.task_id);
669 }
670 },
671
672 _retryTask: function() {
673 if (!this._request) {
674 sk.errorMessage("Task not yet loaded", 3000);
675 return;
676 }
677 var newTask = {
678 expiration_secs: this._request.expiration_secs,
679 name: this._request.name +" (retry)",
680 parent_task_id: this._request.parent_task_id,
681 priority: this._request.priority,
682 properties:this._request.properties,
683 tags: this._request.tags,
684 user: this._profile.email,
685 service_account: this._request.service_account,
686 }
687 swarming.postWithToast("/_ah/api/swarming/v1/tasks/new", "Retrying task " + this.task_id,
688 this._auth_headers, newTask)
689 .then(function(response) {
690 response = JSON.parse(response);
691 if (response && response.task_id) {
692 this.set("task_id", response.task_id);
693 }
694 }.bind(this), function(e) {
695 console.log("Task could not be retried", e);
696 }
697 );
698 },
699
700 _rowspan: function(dims) {
701 dims = dims || [];
702 return dims.length + 1;
703 },
704
705 _supportsMilo: function(request, showRaw) {
706 return !showRaw && request && this._tag(request, "allow_milo");
707 },
708
709 _state: function(result) {
710 if (!result) {
711 return "";
712 }
713 if (result.state === this.COMPLETED) {
714 if (result.failure) {
715 return this.COMPLETED_FAILURE;
716 }
717 if (result.try_number === "0") {
718 return this.COMPLETED_DEDUPED;
719 }
720 return this.COMPLETED_SUCCESS;
721 }
722 return result.state;
723 },
724
725 _stateClass: function(result) {
726 return this.stateClass(this._state(result));
727 },
728
729 _toggleDetails: function() {
730 this.set("_request_detail", !this._request_detail);
731 },
732
733 _tag: function(result, col) {
734 if (!result || !result.tagMap) {
735 return undefined;
736 }
737 return result.tagMap[col];
738 },
739
740 _wasPickedUp: function(result) {
741 return result && result.state !== this.PENDING && result.state !== this. CANCELED && result.state != this.EXPIRED;
742 },
743
744 _wasNotPickedUp: function(result) {
745 return result && !this._wasPickedUp(result);
746 },
747 });
748 })();
749 </script>
750 </dom-module>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698