Skip to content

Commit

Permalink
UI: Add allocation directory rendering (#5873)
Browse files Browse the repository at this point in the history
This lets users navigate the allocation filesystem. It doesn’t
support viewing actual files yet.
  • Loading branch information
backspace authored Jul 2, 2019
1 parent 464aeae commit ee07bab
Show file tree
Hide file tree
Showing 31 changed files with 620 additions and 100 deletions.
37 changes: 37 additions & 0 deletions ui/app/adapters/task-state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import ApplicationAdapter from './application';
import { inject as service } from '@ember/service';

export default ApplicationAdapter.extend({
token: service(),

ls(model, path) {
return this.token
.authorizedRequest(`/v1/client/fs/ls/${model.allocation.id}?path=${path}`)
.then(handleFSResponse);
},

stat(model, path) {
return this.token
.authorizedRequest(`/v1/client/fs/stat/${model.allocation.id}?path=${path}`)
.then(handleFSResponse);
},
});

async function handleFSResponse(response) {
if (response.ok) {
return response.json();
} else {
const body = await response.text();

// TODO update this if/when endpoint returns 404 as expected
const statusIs500 = response.status === 500;
const bodyIncludes404Text = body.includes('no such file or directory');

const translatedCode = statusIs500 && bodyIncludes404Text ? 404 : response.status;

throw {
code: translatedCode,
toString: () => body,
};
}
}
18 changes: 18 additions & 0 deletions ui/app/components/fs-directory-entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { isEmpty } from '@ember/utils';

export default Component.extend({
tagName: '',

pathToEntry: computed('path', 'entry.Name', function() {
const pathWithNoLeadingSlash = this.get('path').replace(/^\//, '');
const name = this.get('entry.Name');

if (isEmpty(pathWithNoLeadingSlash)) {
return name;
} else {
return `${pathWithNoLeadingSlash}/${name}`;
}
}),
});
14 changes: 14 additions & 0 deletions ui/app/components/task-subnav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { equal, or } from '@ember/object/computed';

export default Component.extend({
router: service(),

tagName: '',

fsIsActive: equal('router.currentRouteName', 'allocations.allocation.task.fs'),
fsRootIsActive: equal('router.currentRouteName', 'allocations.allocation.task.fs-root'),

filesLinkActive: or('fsIsActive', 'fsRootIsActive'),
});
3 changes: 3 additions & 0 deletions ui/app/controllers/allocations/allocation/task/fs-root.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import FSController from './fs';

export default FSController.extend();
43 changes: 43 additions & 0 deletions ui/app/controllers/allocations/allocation/task/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { filterBy } from '@ember/object/computed';
import { isEmpty } from '@ember/utils';

export default Controller.extend({
path: null,
task: null,
directoryEntries: null,
isFile: null,

directories: filterBy('directoryEntries', 'IsDir'),
files: filterBy('directoryEntries', 'IsDir', false),

breadcrumbs: computed('path', function() {
const breadcrumbs = this.path
.split('/')
.reject(isEmpty)
.reduce((breadcrumbs, pathSegment, index) => {
let breadcrumbPath;

if (index > 0) {
const lastBreadcrumb = breadcrumbs[index - 1];
breadcrumbPath = `${lastBreadcrumb.path}/${pathSegment}`;
} else {
breadcrumbPath = pathSegment;
}

breadcrumbs.push({
name: pathSegment,
path: breadcrumbPath,
});

return breadcrumbs;
}, []);

if (breadcrumbs.length) {
breadcrumbs[breadcrumbs.length - 1].isLast = true;
}

return breadcrumbs;
}),
});
8 changes: 8 additions & 0 deletions ui/app/models/task-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,12 @@ export default Fragment.extend({
restart() {
return this.allocation.restart(this.name);
},

ls(path) {
return this.store.adapterFor('task-state').ls(this, path);
},

stat(path) {
return this.store.adapterFor('task-state').stat(this, path);
},
});
5 changes: 2 additions & 3 deletions ui/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ Router.map(function() {
this.route('allocation', { path: '/:allocation_id' }, function() {
this.route('task', { path: '/:name' }, function() {
this.route('logs');
this.route('fs', function() {
this.route('path', { path: '/*path' });
});
this.route('fs-root', { path: '/fs' });
this.route('fs', { path: '/fs/*path' });
});
});
});
Expand Down
5 changes: 5 additions & 0 deletions ui/app/routes/allocations/allocation/task/fs-root.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import FSRoute from './fs';

export default FSRoute.extend({
templateName: 'allocations/allocation/task/fs',
});
37 changes: 37 additions & 0 deletions ui/app/routes/allocations/allocation/task/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import notifyError from 'nomad-ui/utils/notify-error';

export default Route.extend({
model({ path = '/' }) {
const decodedPath = decodeURIComponent(path);
const task = this.modelFor('allocations.allocation.task');

const pathWithTaskName = `${task.name}${decodedPath.startsWith('/') ? '' : '/'}${decodedPath}`;

return task
.stat(pathWithTaskName)
.then(statJson => {
if (statJson.IsDir) {
return RSVP.hash({
path: decodedPath,
task,
directoryEntries: task.ls(pathWithTaskName).catch(notifyError(this)),
isFile: false,
});
} else {
return {
path: decodedPath,
task,
isFile: true,
};
}
})
.catch(notifyError(this));
},

setupController(controller, { path, task, directoryEntries, isFile } = {}) {
this._super(...arguments);
controller.setProperties({ path, task, directoryEntries, isFile });
},
});
15 changes: 0 additions & 15 deletions ui/app/routes/allocations/allocation/task/fs/path.js

This file was deleted.

1 change: 1 addition & 0 deletions ui/app/styles/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@import './components/ember-power-select';
@import './components/empty-message';
@import './components/error-container';
@import './components/fs-explorer';
@import './components/gutter';
@import './components/gutter-toggle';
@import './components/inline-definitions';
Expand Down
73 changes: 73 additions & 0 deletions ui/app/styles/components/fs-explorer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.fs-explorer {
width: 100%;

.table.boxed-section-body.is-full-bleed {
border: 1px solid $grey-blue;
}

tbody {
a {
text-decoration: none;
color: inherit;

&:hover {
.name {
text-decoration: underline;
}
}
}
}

a {
position: relative;

// This is adapted from Bulma’s .button.is-loading::after
&.ember-transitioning-in::after {
animation: spinAround 500ms infinite linear;
border: 2px solid $grey-light;
border-radius: 290486px;
border-right-color: transparent;
border-top-color: transparent;
content: '';
display: block;
height: 1em;
width: 1em;
position: absolute;
right: -1.5em;
top: calc(50% - (1em / 2));
}
}

.breadcrumb {
margin: 0;

li::before {
color: $grey-light;
}

a {
padding-top: 0;
padding-bottom: 0;
color: $blue;
opacity: 1;
font-weight: $weight-bold;
text-decoration: none;

&:hover {
text-decoration: underline;
}

&.ember-transitioning-in {
margin-right: 1.5em;

&::after {
right: -1em;
}
}
}

.is-active a {
color: $black;
}
}
}
4 changes: 4 additions & 0 deletions ui/app/styles/core/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@

+ * {
margin-top: 5em;

&.is-closer {
margin-top: calc(3.5em + 1px);
}
}

@media #{$mq-hidden-gutter} {
Expand Down
66 changes: 65 additions & 1 deletion ui/app/templates/allocations/allocation/task/fs.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,65 @@
{{outlet}}
{{task-subnav task=task}}
<section class="section is-closer">
{{#if task.isRunning}}
<div class="fs-explorer boxed-section">
<div class="boxed-section-head">
<nav class="breadcrumb" data-test-fs-breadcrumbs>
<ul>
<li class={{if breadcrumbs "" "is-active"}}>
{{#link-to "allocations.allocation.task.fs-root" task.allocation task activeClass="is-active"}}
{{task.name}}
{{/link-to}}
</li>
{{#each breadcrumbs as |breadcrumb|}}
<li class={{if breadcrumb.isLast "is-active"}}>
{{#link-to "allocations.allocation.task.fs" task.allocation task breadcrumb.path activeClass="is-active"}}
{{breadcrumb.name}}
{{/link-to}}
</li>
{{/each}}
</ul>
</nav>
</div>

{{#if isFile}}
<div class="boxed-section-body">
<div data-test-file-viewer>placeholder file viewer</div>
</div>
{{else}}
{{! template-lint-disable table-groups }}
<table class="table boxed-section-body is-full-bleed is-compact">
{{#if directoryEntries}}
<thead>
<tr>
<th class="is-two-thirds">Name</th>
<th class="has-text-right">File Size</th>
<th class="has-text-right">Last Modified</th>
</tr>
</thead>
<tbody>
{{#each (append directories files) as |entry|}}
{{fs-directory-entry path=path task=task entry=entry}}
{{/each}}
</tbody>
{{else}}
<tbody>
<tr data-test-entry>
<td colspan="3">
<span data-test-empty-icon>{{x-icon "alert-circle-outline"}}</span>
<span class="name" data-test-name>Directory is empty</span>
</td>
</tr>
</tbody>
{{/if}}
</table>
{{/if}}
</div>
{{else}}
<div data-test-not-running class="empty-message">
<h3 data-test-not-running-headline class="empty-message-headline">Task is not Running</h3>
<p data-test-not-running-body class="empty-message-body">
Cannot access files of a task that is not running.
</p>
</div>
{{/if}}
</section>
14 changes: 0 additions & 14 deletions ui/app/templates/allocations/allocation/task/fs/index.hbs

This file was deleted.

4 changes: 0 additions & 4 deletions ui/app/templates/allocations/allocation/task/fs/path.hbs

This file was deleted.

2 changes: 1 addition & 1 deletion ui/app/templates/allocations/allocation/task/index.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{partial "allocations/allocation/task/subnav"}}
{{task-subnav task=model}}
<section class="section">
{{#if error}}
<div data-test-inline-error class="notification is-danger">
Expand Down
Loading

0 comments on commit ee07bab

Please sign in to comment.