Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: add filesystem browsing for allocations #7951

Merged
merged 34 commits into from
Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9f86b22
Add hack to allow allocation filesystem browsing
backspace May 13, 2020
796883c
Fix title
backspace May 13, 2020
c4bf88c
Fix text of not-running message
backspace May 13, 2020
14f11b5
Add note about allocation breadcrumb root
backspace May 13, 2020
db0421a
Add preliminary acceptance tests
backspace May 19, 2020
4fd9f40
Change root marker to be allocation short id
backspace May 19, 2020
3b964c1
Fix path request for empty directory
backspace May 19, 2020
c565940
Remove stripping of leading task name in paths
backspace May 19, 2020
5137395
Fix path used for filesystem queries
backspace May 19, 2020
c2062e9
Fix allocation breadcrumbs assertions
backspace May 19, 2020
8ae2fd5
Remove logs link
backspace May 20, 2020
4781a59
Add initial extraction of shared browsing tests
backspace May 27, 2020
d876037
Extract more shared tests
backspace May 27, 2020
96da717
Extract all other tests
backspace May 27, 2020
cc0d491
Combine breadcrumbs components
backspace May 28, 2020
b20f0b9
Merge branch 'master' into f-ui/alloc-fs
backspace May 28, 2020
d3df8c5
Add note about task file error text
backspace May 28, 2020
4b77ffb
Change task-file interface to be more generic
backspace May 28, 2020
1e33b69
Change fs-directory… interface to be more generic
backspace May 28, 2020
8e4f352
Extract fs-browser component
backspace May 28, 2020
2ed5c43
Move fs components into their own directory
backspace May 28, 2020
891b929
Extract fs link component
backspace May 28, 2020
e9eb924
Change message for inaccessible files
backspace May 28, 2020
f4c5a8e
Extract model determination to one place
backspace May 28, 2020
411cbb0
Remove unused import
backspace May 28, 2020
91bf699
Change browser invocation to be multi-line
backspace May 28, 2020
6b0667f
Move file component into fs directory
backspace May 28, 2020
8d688b2
Remove task state adapter
backspace May 28, 2020
b3a620a
Move FS page object up a level
backspace May 28, 2020
3bbc718
Rename task page object visit functions
backspace May 28, 2020
018737c
Change order of imports
backspace May 28, 2020
d2ba531
Change order of visit functions
backspace May 28, 2020
e634f9c
Merge branch 'master' into f-ui/alloc-fs
backspace Jun 1, 2020
5f82c18
Update changelog
backspace Jun 1, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ui/app/adapters/allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,41 @@ export default Watchable.extend({
data: taskName && { TaskName: taskName },
});
},

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

stat(model, path) {
return this.token
.authorizedRequest(
`/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(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,
};
}
}

function adapterAction(path, verb = 'POST') {
return function(allocation) {
const url = addToPath(this.urlForFindRecord(allocation.id, 'allocation'), path);
Expand Down
39 changes: 0 additions & 39 deletions ui/app/adapters/task-state.js

This file was deleted.

14 changes: 14 additions & 0 deletions ui/app/components/allocation-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.fs'),
fsRootIsActive: equal('router.currentRouteName', 'allocations.allocation.fs-root'),

filesLinkActive: or('fsIsActive', 'fsRootIsActive'),
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default Component.extend({

'data-test-fs-breadcrumbs': true,

allocation: null,
task: null,
path: null,

Expand Down
57 changes: 57 additions & 0 deletions ui/app/components/fs/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { filterBy } from '@ember/object/computed';

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

model: null,

allocation: computed('model', function() {
if (this.model.allocation) {
return this.model.allocation;
} else {
return this.model;
}
}),

task: computed('model', function() {
if (this.model.allocation) {
return this.model;
}
}),

type: computed('task', function() {
if (this.task) {
return 'task';
} else {
return 'allocation';
}
}),

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

sortedDirectoryEntries: computed(
'directoryEntries.[]',
'sortProperty',
'sortDescending',
function() {
const sortProperty = this.sortProperty;

const directorySortProperty = sortProperty === 'Size' ? 'Name' : sortProperty;

const sortedDirectories = this.directories.sortBy(directorySortProperty);
const sortedFiles = this.files.sortBy(sortProperty);

const sortedDirectoryEntries = sortedDirectories.concat(sortedFiles);

if (this.sortDescending) {
return sortedDirectoryEntries.reverse();
} else {
return sortedDirectoryEntries;
}
}
),

});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { isEmpty } from '@ember/utils';
export default Component.extend({
tagName: '',

allocation: null,
task: null,

pathToEntry: computed('path', 'entry.Name', function() {
const pathWithNoLeadingSlash = this.get('path').replace(/^\//, '');
const name = encodeURIComponent(this.get('entry.Name'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export default Component.extend({
isStreaming: false,

catUrl: computed('allocation.id', 'task.name', 'file', function() {
const encodedPath = encodeURIComponent(`${this.task.name}/${this.file}`);
const taskUrlPrefix = this.task ? `${this.task.name}/` : '';
const encodedPath = encodeURIComponent(`${taskUrlPrefix}${this.file}`);
return `/v1/client/fs/cat/${this.allocation.id}?path=${encodedPath}`;
}),

Expand Down Expand Up @@ -79,7 +80,8 @@ export default Component.extend({

fileParams: computed('task.name', 'file', 'mode', function() {
// The Log class handles encoding query params
const path = `${this.task.name}/${this.file}`;
const taskUrlPrefix = this.task ? `${this.task.name}/` : '';
const path = `${taskUrlPrefix}${this.file}`;

switch (this.mode) {
case 'head':
Expand Down
8 changes: 8 additions & 0 deletions ui/app/components/fs/link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Component from '@ember/component';

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

allocation: null,
task: null,
});
3 changes: 3 additions & 0 deletions ui/app/controllers/allocations/allocation/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();
28 changes: 28 additions & 0 deletions ui/app/controllers/allocations/allocation/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';

export default Controller.extend({
queryParams: {
sortProperty: 'sort',
sortDescending: 'desc',
},

sortProperty: 'Name',
sortDescending: false,

path: null,
allocation: null,
directoryEntries: null,
isFile: null,
stat: null,

pathWithLeadingSlash: computed('path', function() {
const path = this.path;

if (path.startsWith('/')) {
return path;
} else {
return `/${path}`;
}
}),
});
26 changes: 0 additions & 26 deletions ui/app/controllers/allocations/allocation/task/fs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { filterBy } from '@ember/object/computed';

export default Controller.extend({
queryParams: {
Expand All @@ -17,9 +16,6 @@ export default Controller.extend({
isFile: null,
stat: null,

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

pathWithLeadingSlash: computed('path', function() {
const path = this.path;

Expand All @@ -29,26 +25,4 @@ export default Controller.extend({
return `/${path}`;
}
}),

sortedDirectoryEntries: computed(
'directoryEntries.[]',
'sortProperty',
'sortDescending',
function() {
const sortProperty = this.sortProperty;

const directorySortProperty = sortProperty === 'Size' ? 'Name' : sortProperty;

const sortedDirectories = this.directories.sortBy(directorySortProperty);
const sortedFiles = this.files.sortBy(sortProperty);

const sortedDirectoryEntries = sortedDirectories.concat(sortedFiles);

if (this.sortDescending) {
return sortedDirectoryEntries.reverse();
} else {
return sortedDirectoryEntries;
}
}
),
});
8 changes: 8 additions & 0 deletions ui/app/models/allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,12 @@ export default Model.extend({
restart(taskName) {
return this.store.adapterFor('allocation').restart(this, taskName);
},

ls(path) {
return this.store.adapterFor('allocation').ls(this, path);
},

stat(path) {
return this.store.adapterFor('allocation').stat(this, path);
},
});
8 changes: 0 additions & 8 deletions ui/app/models/task-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,4 @@ 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);
},
});
3 changes: 3 additions & 0 deletions ui/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ Router.map(function() {

this.route('allocations', function() {
this.route('allocation', { path: '/:allocation_id' }, function() {
this.route('fs-root', { path: '/fs' });
this.route('fs', { path: '/fs/*path' });

this.route('task', { path: '/:name' }, function() {
this.route('logs');
this.route('fs-root', { path: '/fs' });
Expand Down
5 changes: 5 additions & 0 deletions ui/app/routes/allocations/allocation/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/fs',
});
42 changes: 42 additions & 0 deletions ui/app/routes/allocations/allocation/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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 allocation = this.modelFor('allocations.allocation');

if (!allocation.isRunning) {
return {
path: decodedPath,
allocation,
};
}

return RSVP.all([allocation.stat(decodedPath), allocation.get('node')])
.then(([statJson]) => {
if (statJson.IsDir) {
return RSVP.hash({
path: decodedPath,
allocation,
directoryEntries: allocation.ls(decodedPath).catch(notifyError(this)),
isFile: false,
});
} else {
return {
path: decodedPath,
allocation,
isFile: true,
stat: statJson,
};
}
})
.catch(notifyError(this));
},

setupController(controller, { path, allocation, directoryEntries, isFile, stat } = {}) {
this._super(...arguments);
controller.setProperties({ path, allocation, directoryEntries, isFile, stat });
},
});
5 changes: 3 additions & 2 deletions ui/app/routes/allocations/allocation/task/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default Route.extend({
model({ path = '/' }) {
const decodedPath = decodeURIComponent(path);
const task = this.modelFor('allocations.allocation.task');
const allocation = task.allocation;

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

Expand All @@ -16,13 +17,13 @@ export default Route.extend({
};
}

return RSVP.all([task.stat(pathWithTaskName), task.get('allocation.node')])
return RSVP.all([allocation.stat(pathWithTaskName), task.get('allocation.node')])
.then(([statJson]) => {
if (statJson.IsDir) {
return RSVP.hash({
path: decodedPath,
task,
directoryEntries: task.ls(pathWithTaskName).catch(notifyError(this)),
directoryEntries: allocation.ls(pathWithTaskName).catch(notifyError(this)),
isFile: false,
});
} else {
Expand Down
10 changes: 10 additions & 0 deletions ui/app/templates/allocations/allocation/fs.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{{title pathWithLeadingSlash " - Allocation " allocation.shortId " filesystem"}}
{{allocation-subnav allocation=allocation}}
{{fs/browser
model=allocation
path=path
stat=stat
isFile=isFile
directoryEntries=directoryEntries
sortProperty=sortProperty
sortDescending=sortDescending}}
Loading