Skip to content

Commit

Permalink
[ui] task logs in sidebar (#14612)
Browse files Browse the repository at this point in the history
* button styles

* Further styles including global toggle adjustment

* sidebar funcs and header

* Functioning task logs in high-level sidebars

* same-lineify the show tasks toggle

* Changelog

* Full-height sidebar calc in css, plz drop soon container queries

* Active status and query params for allocations page

* Reactive shouldShowLogs getter and added to client and task group pages

* Higher order func passing, thanks @DingoEatingFuzz

* Non-service job types get allocation params passed

* Keyframe animation for task log sidebar

* Acceptance test

* A few more sub-row tests

* Lintfix
  • Loading branch information
philrenaud committed Sep 22, 2022
1 parent d1e90a1 commit e4b763f
Show file tree
Hide file tree
Showing 33 changed files with 342 additions and 44 deletions.
3 changes: 3 additions & 0 deletions .changelog/14612.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: adds a sidebar to show in-page logs for a given task, accessible via job, client, or task group routes
```
2 changes: 1 addition & 1 deletion ui/app/components/allocation-service-sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class AllocationServiceSidebarComponent extends Component {
}
keyCommands = [
{
label: 'Close Evaluations Sidebar',
label: 'Close Service Sidebar',
pattern: ['Escape'],
action: () => this.args.fns.closeSidebar(),
},
Expand Down
9 changes: 7 additions & 2 deletions ui/app/components/streaming-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
isStreaming = true;
logger = null;
follow = true;
shouldFillHeight = true;

// Internal bookkeeping to avoid multiple scroll events on one frame
requestFrame = true;
Expand Down Expand Up @@ -89,7 +90,9 @@ export default class StreamingFile extends Component.extend(WindowResizable) {

didInsertElement() {
super.didInsertElement(...arguments);
this.fillAvailableHeight();
if (this.shouldFillHeight) {
this.fillAvailableHeight();
}

this.set('_scrollHandler', this.scrollHandler.bind(this));
this.element.addEventListener('scroll', this._scrollHandler);
Expand All @@ -105,7 +108,9 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
}

windowResizeHandler() {
once(this, this.fillAvailableHeight);
if (this.shouldFillHeight) {
once(this, this.fillAvailableHeight);
}
}

fillAvailableHeight() {
Expand Down
44 changes: 44 additions & 0 deletions ui/app/components/task-context-sidebar.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<Portal @target="log-sidebar-portal">
<div
class="sidebar task-context-sidebar has-subnav {{if this.isSideBarOpen "open"}}"
{{on-click-outside
@fns.closeSidebar
capture=true
}}
>
{{#if @task}}
{{keyboard-commands this.keyCommands}}
<header>
<h1 class="title">
{{@task.name}}
<span class="state {{@task.state}}">
{{@task.state}}
</span>
</h1>
<LinkTo
class="link"
title={{@task.name}}
@route="allocations.allocation.task"
@models={{array @task.allocation @task}}
>
Go to Task page
</LinkTo>
<button
class="button close is-borderless"
type="button"
{{on "click" @fns.closeSidebar}}
>
{{x-icon "cancel"}}
</button>
</header>

<TaskLog
@allocation={{@task.allocation}}
@task={{@task.name}}
@shouldFillHeight={{false}}
/>


{{/if}}
</div>
</Portal>
16 changes: 16 additions & 0 deletions ui/app/components/task-context-sidebar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @ts-check
import Component from '@glimmer/component';

export default class TaskContextSidebarComponent extends Component {
get isSideBarOpen() {
return !!this.args.task;
}

keyCommands = [
{
label: 'Close Task Logs Sidebar',
pattern: ['Escape'],
action: () => this.args.fns.closeSidebar(),
},
];
}
2 changes: 2 additions & 0 deletions ui/app/components/task-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export default class TaskLog extends Component {
isStreaming = true;
streamMode = 'streaming';

shouldFillHeight = true;

@alias('userSettings.logMode') mode;

@computed('allocation.{id,node.httpAddr}', 'useServer')
Expand Down
25 changes: 17 additions & 8 deletions ui/app/components/task-sub-row.hbs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<tr class="task-sub-row"
<tr class="task-sub-row {{if @active "is-active"}}"
{{keyboard-shortcut
enumerated=true
action=(action "gotoTask" this.task.allocation this.task)
}}
>
<td colspan={{@namespan}}>
/
<LinkTo @route="allocations.allocation.task" @models={{array this.task.allocation this.task}}>
{{this.task.name}}
</LinkTo>
{{!-- TODO: in-page logs --}}
{{!-- <FlightIcon @name="logs" /> --}}
<div class="name-grid">
<LinkTo title={{this.task.name}} class="task-name" @route="allocations.allocation.task" @models={{array this.task.allocation this.task}}>{{this.task.name}}</LinkTo>
<button type="button" class="logs-sidebar-trigger button is-borderless is-inline is-compact" onclick={{action "handleTaskLogsClick" this.task}}>
<FlightIcon @name="logs" />View Logs
</button>
</div>
</td>
<td data-test-cpu class="is-1 has-text-centered">
{{#if this.task.isRunning}}
Expand Down Expand Up @@ -76,4 +76,13 @@
</td>
</tr>

{{yield}}
{{yield}}

{{#if this.shouldShowLogs}}
<TaskContextSidebar
@task={{this.task}}
@fns={{hash
closeSidebar=this.closeSidebar
}}
/>
{{/if}}
18 changes: 18 additions & 0 deletions ui/app/components/task-sub-row.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,22 @@ export default class TaskSubRowComponent extends Component {
} while (this.enablePolling);
}).drop())
fetchStats;

//#region Logs Sidebar

@alias('args.active') shouldShowLogs;

@action handleTaskLogsClick(task) {
if (this.args.onSetActiveTask) {
this.args.onSetActiveTask(task);
}
}

@action closeSidebar() {
if (this.args.onSetActiveTask) {
this.args.onSetActiveTask(null);
}
}

//#endregion Logs Sidebar
}
11 changes: 11 additions & 0 deletions ui/app/controllers/clients/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default class ClientController extends Controller.extend(
{
qpStatus: 'status',
},
'activeTask',
];

// Set in the route
Expand All @@ -57,6 +58,7 @@ export default class ClientController extends Controller.extend(
qpStatus = '';
currentPage = 1;
pageSize = 8;
activeTask = null;

sortProperty = 'modifyIndex';
sortDescending = true;
Expand Down Expand Up @@ -266,4 +268,13 @@ export default class ClientController extends Controller.extend(
setFacetQueryParam(queryParam, selection) {
this.set(queryParam, serialize(selection));
}

@action
setActiveTaskQueryParam(task) {
if (task) {
this.set('activeTask', `${task.allocation.id}-${task.name}`);
} else {
this.set('activeTask', null);
}
}
}
11 changes: 11 additions & 0 deletions ui/app/controllers/jobs/job/allocations.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ export default class AllocationsController extends Controller.extend(
{
qpTaskGroup: 'taskGroup',
},
'activeTask',
];

qpStatus = '';
qpClient = '';
qpTaskGroup = '';
currentPage = 1;
pageSize = 25;
activeTask = null;

sortProperty = 'modifyIndex';
sortDescending = true;
Expand Down Expand Up @@ -159,4 +161,13 @@ export default class AllocationsController extends Controller.extend(
setFacetQueryParam(queryParam, selection) {
this.set(queryParam, serialize(selection));
}

@action
setActiveTaskQueryParam(task) {
if (task) {
this.set('activeTask', `${task.allocation.id}-${task.name}`);
} else {
this.set('activeTask', null);
}
}
}
13 changes: 12 additions & 1 deletion ui/app/controllers/jobs/job/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { alias } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
import classic from 'ember-classic-decorator';

import { action } from '@ember/object';
@classic
export default class IndexController extends Controller.extend(
WithNamespaceResetting
Expand All @@ -20,6 +20,7 @@ export default class IndexController extends Controller.extend(
{
sortDescending: 'desc',
},
'activeTask',
];

currentPage = 1;
Expand All @@ -28,4 +29,14 @@ export default class IndexController extends Controller.extend(

sortProperty = 'name';
sortDescending = false;
activeTask = null;

@action
setActiveTaskQueryParam(task) {
if (task) {
this.set('activeTask', `${task.allocation.id}-${task.name}`);
} else {
this.set('activeTask', null);
}
}
}
11 changes: 11 additions & 0 deletions ui/app/controllers/jobs/job/task-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default class TaskGroupController extends Controller.extend(
{
qpClient: 'client',
},
'activeTask',
];

currentPage = 1;
Expand All @@ -52,6 +53,7 @@ export default class TaskGroupController extends Controller.extend(
qpClient = '';
sortProperty = 'modifyIndex';
sortDescending = true;
activeTask = null;

@computed
get searchProps() {
Expand Down Expand Up @@ -186,4 +188,13 @@ export default class TaskGroupController extends Controller.extend(
args: ['jobs.job.task-group', job, name],
};
}

@action
setActiveTaskQueryParam(task) {
if (task) {
this.set('activeTask', `${task.allocation.id}-${task.name}`);
} else {
this.set('activeTask', null);
}
}
}
3 changes: 3 additions & 0 deletions ui/app/styles/components/boxed-section.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
.is-padded {
padding: 0em 0em 0em 1em;
}
.is-one-line {
white-space: nowrap;
}
}

.is-fixed-width {
Expand Down
75 changes: 75 additions & 0 deletions ui/app/styles/components/sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,78 @@ $subNavOffset: 49px;
padding: 10px;
width: 100px;
}

.task-context-sidebar {
animation-name: slideFromRight;
animation-duration: 150ms;
animation-fill-mode: both;

header {
display: grid;
justify-content: left;
grid-template-columns: 1fr auto auto;
gap: 2rem;
border-bottom: 1px solid $grey-blue;
padding-bottom: 1rem;
margin-bottom: 24px;
height: 50px;

.title {
margin-bottom: unset;
}

.link {
align-self: center;
}

.state {
font-size: 1rem;
font-weight: normal;
margin-left: 1rem;
text-transform: capitalize;

&:before {
content: '';
display: inline-block;
height: 1rem;
width: 1rem;
margin-right: 5px;
border-radius: 4px;
position: relative;
top: 2px;
}

&.running:before {
background-color: $green;
}
&.dead:before {
background-color: $red;
}
&.pending:before {
background-color: $grey-lighter;
}
}
}

// Instead of trying to calculate on the fly with JS, let's use vh and offset nav and headers above.
// We can make this a LOT more streamlined when CSS Container Queries are available.
$sidebarTopOffset: 161px;
$sidebarInnerPadding: 48px;
$sidebarHeaderOffset: 74px;
$cliHeaderOffset: 54.5px;
.cli-window {
height: calc(
100vh - $sidebarTopOffset - $sidebarInnerPadding - $sidebarHeaderOffset -
$cliHeaderOffset
);
}
}

@keyframes slideFromRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
Loading

0 comments on commit e4b763f

Please sign in to comment.