Always show the file browser for allocations and tasks.

Before, we'd show a helpful error message when a task isn't running
instead of erroring in a generic way. Turns out when an alloc is
terminal but reachable, the filesystem is left behind so we were hiding
it.

Now it is always shown and in the event that something errors, it'll
either be generic, or--more commonly--a 404 of the allocation.
This commit is contained in:
Michael Lange
2020-10-25 22:17:41 -07:00
parent 58562234c5
commit 05f3bf2d09
5 changed files with 56 additions and 107 deletions

View File

@@ -7,12 +7,7 @@ export default class FsRoute extends Route {
const decodedPath = decodeURIComponent(path);
const allocation = this.modelFor('allocations.allocation');
if (!allocation.isRunning) {
return {
path: decodedPath,
allocation,
};
}
if (!allocation) return;
return RSVP.all([allocation.stat(decodedPath), allocation.get('node')])
.then(([statJson]) => {

View File

@@ -6,19 +6,14 @@ export default class FsRoute extends Route {
model({ path = '/' }) {
const decodedPath = decodeURIComponent(path);
const taskState = this.modelFor('allocations.allocation.task');
const allocation = taskState.allocation;
if (!taskState || !taskState.allocation) return;
const allocation = taskState.allocation;
const pathWithTaskName = `${taskState.name}${
decodedPath.startsWith('/') ? '' : '/'
}${decodedPath}`;
if (!taskState.isRunning) {
return {
path: decodedPath,
taskState,
};
}
return RSVP.all([allocation.stat(pathWithTaskName), taskState.get('allocation.node')])
.then(([statJson]) => {
if (statJson.IsDir) {

View File

@@ -1,47 +1,38 @@
<section class="section is-closer {{if this.isFile "is-full-width"}}">
{{#if this.model.isRunning}}
{{#if this.isFile}}
<Fs::File @allocation={{this.allocation}} @taskState={{this.taskState}} @file={{this.path}} @stat={{this.stat}} @class="fs-explorer">
<Fs::Breadcrumbs @allocation={{this.allocation}} @taskState={{this.taskState}} @path={{this.path}} />
</Fs::File>
{{else}}
<div class="fs-explorer boxed-section">
<div class="boxed-section-head">
<Fs::Breadcrumbs @allocation={{this.allocation}} @taskState={{this.taskState}} @path={{this.path}} />
</div>
{{#if this.directoryEntries}}
<ListTable
@source={{this.sortedDirectoryEntries}}
@sortProperty={{this.sortProperty}}
@sortDescending={{this.sortDescending}}
@class="boxed-section-body is-full-bleed is-compact" as |t|>
<t.head>
<t.sort-by @prop="Name" @class="is-two-thirds">Name</t.sort-by>
<t.sort-by @prop="Size" @class="has-text-right">File Size</t.sort-by>
<t.sort-by @prop="ModTime" @class="has-text-right">Last Modified</t.sort-by>
</t.head>
<t.body as |row|>
<Fs::DirectoryEntry @path={{this.path}} @allocation={{this.allocation}} @taskState={{this.taskState}} @entry={{row.model}} />
</t.body>
</ListTable>
{{else}}
<div class="boxed-section-body">
<div data-test-empty-directory class="empty-message">
<h3 data-test-empty-directory-headline class="empty-message-headline">No Files</h3>
<p data-test-empty-directory-body class="empty-message-body">
Directory is currently empty.
</p>
</div>
</div>
{{/if}}
</div>
{{/if}}
{{#if this.isFile}}
<Fs::File @allocation={{this.allocation}} @taskState={{this.taskState}} @file={{this.path}} @stat={{this.stat}} @class="fs-explorer">
<Fs::Breadcrumbs @allocation={{this.allocation}} @taskState={{this.taskState}} @path={{this.path}} />
</Fs::File>
{{else}}
<div data-test-not-running class="empty-message">
<h3 data-test-not-running-headline class="empty-message-headline">{{capitalize this.type}} is not Running</h3>
<p data-test-not-running-body class="empty-message-body">
Cannot access files of a{{if this.allocation 'n'}} {{this.type}} that is not running.
</p>
<div class="fs-explorer boxed-section">
<div class="boxed-section-head">
<Fs::Breadcrumbs @allocation={{this.allocation}} @taskState={{this.taskState}} @path={{this.path}} />
</div>
{{#if this.directoryEntries}}
<ListTable
@source={{this.sortedDirectoryEntries}}
@sortProperty={{this.sortProperty}}
@sortDescending={{this.sortDescending}}
@class="boxed-section-body is-full-bleed is-compact" as |t|>
<t.head>
<t.sort-by @prop="Name" @class="is-two-thirds">Name</t.sort-by>
<t.sort-by @prop="Size" @class="has-text-right">File Size</t.sort-by>
<t.sort-by @prop="ModTime" @class="has-text-right">Last Modified</t.sort-by>
</t.head>
<t.body as |row|>
<Fs::DirectoryEntry @path={{this.path}} @allocation={{this.allocation}} @taskState={{this.taskState}} @entry={{row.model}} />
</t.body>
</ListTable>
{{else}}
<div class="boxed-section-body">
<div data-test-empty-directory class="empty-message">
<h3 data-test-empty-directory-headline class="empty-message-headline">No Files</h3>
<p data-test-empty-directory-body class="empty-message-body">
Directory is currently empty.
</p>
</div>
</div>
{{/if}}
</div>
{{/if}}
</section>

View File

@@ -1,14 +1,11 @@
/* eslint-disable ember-a11y-testing/a11y-audit-called */ // Covered in behaviours/fs
import { module, test } from 'qunit';
import { module } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import Response from 'ember-cli-mirage/response';
import browseFilesystem from './behaviors/fs';
import FS from 'nomad-ui/tests/pages/allocations/fs';
let allocation;
let files;
@@ -48,24 +45,6 @@ module('Acceptance | allocation fs', function(hooks) {
this.nestedDirectory = files[1];
});
test('when the allocation is not running, an empty state is shown', async function(assert) {
// The API 500s on stat when not running
this.server.get('/client/fs/stat/:allocation_id', () => {
return new Response(500, {}, 'no such file or directory');
});
allocation.update({
clientStatus: 'complete',
});
await FS.visitAllocation({ id: allocation.id });
assert.ok(FS.hasEmptyState, 'Non-running allocation has no files');
assert.ok(
FS.emptyState.headline.includes('Allocation is not Running'),
'Empty state explains the condition'
);
});
browseFilesystem({
visitSegments: ({ allocation }) => ({ id: allocation.id }),
getExpectedPathBase: ({ allocation }) => `/allocations/${allocation.id}/fs/`,

View File

@@ -1,14 +1,11 @@
/* eslint-disable ember-a11y-testing/a11y-audit-called */ // Covered in behaviours/fs
import { module, test } from 'qunit';
import { module } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import Response from 'ember-cli-mirage/response';
import browseFilesystem from './behaviors/fs';
import FS from 'nomad-ui/tests/pages/allocations/fs';
let allocation;
let task;
let files, taskDirectory, directory, nestedDirectory;
@@ -37,10 +34,18 @@ module('Acceptance | task fs', function(hooks) {
files.push(taskDirectory);
// Nested files
directory = server.create('allocFile', { isDir: true, name: 'directory', parent: taskDirectory });
directory = server.create('allocFile', {
isDir: true,
name: 'directory',
parent: taskDirectory,
});
files.push(directory);
nestedDirectory = server.create('allocFile', { isDir: true, name: 'another', parent: directory });
nestedDirectory = server.create('allocFile', {
isDir: true,
name: 'another',
parent: directory,
});
files.push(nestedDirectory);
files.push(
@@ -51,7 +56,9 @@ module('Acceptance | task fs', function(hooks) {
})
);
files.push(server.create('allocFile', { isDir: true, name: 'empty-directory', parent: taskDirectory }));
files.push(
server.create('allocFile', { isDir: true, name: 'empty-directory', parent: taskDirectory })
);
files.push(server.create('allocFile', 'file', { fileType: 'txt', parent: taskDirectory }));
files.push(server.create('allocFile', 'file', { fileType: 'txt', parent: taskDirectory }));
@@ -60,29 +67,11 @@ module('Acceptance | task fs', function(hooks) {
this.nestedDirectory = nestedDirectory;
});
test('when the task is not running, an empty state is shown', async function(assert) {
// The API 500s on stat when not running
this.server.get('/client/fs/stat/:allocation_id', () => {
return new Response(500, {}, 'no such file or directory');
});
task.update({
finishedAt: new Date(),
});
await FS.visitTask({ id: allocation.id, name: task.name });
assert.ok(FS.hasEmptyState, 'Non-running task has no files');
assert.ok(
FS.emptyState.headline.includes('Task is not Running'),
'Empty state explains the condition'
);
});
browseFilesystem({
visitSegments: ({allocation,task}) => ({ id: allocation.id, name: task.name }),
getExpectedPathBase: ({allocation,task}) => `/allocations/${allocation.id}/${task.name}/fs/`,
getTitleComponent: ({task}) => `Task ${task.name} filesystem`,
getBreadcrumbComponent: ({task}) => task.name,
visitSegments: ({ allocation, task }) => ({ id: allocation.id, name: task.name }),
getExpectedPathBase: ({ allocation, task }) => `/allocations/${allocation.id}/${task.name}/fs/`,
getTitleComponent: ({ task }) => `Task ${task.name} filesystem`,
getBreadcrumbComponent: ({ task }) => task.name,
getFilesystemRoot: ({ task }) => task.name,
pageObjectVisitFunctionName: 'visitTask',
pageObjectVisitPathFunctionName: 'visitTaskPath',