diff --git a/ui/app/controllers/servers/server/monitor.js b/ui/app/controllers/servers/server/monitor.js new file mode 100644 index 000000000..6199fc671 --- /dev/null +++ b/ui/app/controllers/servers/server/monitor.js @@ -0,0 +1,9 @@ +import Controller from '@ember/controller'; +import classic from 'ember-classic-decorator'; + +@classic +export default class ServerMonitorController extends Controller { + queryParams = [{ level: 'level' }]; + + level = 'info'; +} diff --git a/ui/app/templates/servers/server/monitor.hbs b/ui/app/templates/servers/server/monitor.hbs index 52b8b4ddd..66a09bc06 100644 --- a/ui/app/templates/servers/server/monitor.hbs +++ b/ui/app/templates/servers/server/monitor.hbs @@ -1,4 +1,12 @@ {{title "Server " model.name}}
+ {{#if (can "read agent")}} + + {{else}} + + {{/if}}
diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 2bdb64a17..8d5e30c9e 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -317,7 +317,7 @@ export default function() { if (serverId && clientId) return new Response(400, {}, 'specify a client or a server, not both'); - if (serverId && !agents.find(serverId)) + if (serverId && !agents.findBy({ name: serverId })) return new Response(400, {}, 'specified server does not exist'); if (clientId && !nodes.find(clientId)) return new Response(400, {}, 'specified client does not exist'); diff --git a/ui/tests/acceptance/server-monitor-test.js b/ui/tests/acceptance/server-monitor-test.js new file mode 100644 index 000000000..5c64fdd3f --- /dev/null +++ b/ui/tests/acceptance/server-monitor-test.js @@ -0,0 +1,66 @@ +import { currentURL } from '@ember/test-helpers'; +import { run } from '@ember/runloop'; +import { module, test } from 'qunit'; +import { setupApplicationTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import ServerMonitor from 'nomad-ui/tests/pages/servers/monitor'; + +let agent; +let managementToken; +let clientToken; + +module('Acceptance | server monitor', function(hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(function() { + agent = server.create('agent'); + + managementToken = server.create('token'); + clientToken = server.create('token'); + + window.localStorage.nomadTokenSecret = managementToken.secretId; + + run.later(run, run.cancelTimers, 500); + }); + + test('/servers/:id/monitor should have a breadcrumb trail linking back to servers', async function(assert) { + await ServerMonitor.visit({ name: agent.name }); + + assert.equal(ServerMonitor.breadcrumbFor('servers.index').text, 'Servers'); + assert.equal(ServerMonitor.breadcrumbFor('servers.server').text, agent.name); + + await ServerMonitor.breadcrumbFor('servers.index').visit(); + assert.equal(currentURL(), '/servers'); + }); + + test('the monitor page immediately streams agent monitor output at the info level', async function(assert) { + await ServerMonitor.visit({ name: agent.name }); + + const logRequest = server.pretender.handledRequests.find(req => + req.url.startsWith('/v1/agent/monitor') + ); + assert.ok(ServerMonitor.logsArePresent); + assert.ok(logRequest); + assert.ok(logRequest.url.includes('log_level=info')); + }); + + test('switching the log level persists the new log level as a query param', async function(assert) { + await ServerMonitor.visit({ name: agent.name }); + await ServerMonitor.selectLogLevel('Debug'); + assert.equal(currentURL(), `/servers/${agent.name}/monitor?level=debug`); + }); + + test('when the current access token does not include the agent:read rule, a descriptive error message is shown', async function(assert) { + window.localStorage.nomadTokenSecret = clientToken.secretId; + + await ServerMonitor.visit({ name: agent.name }); + assert.notOk(ServerMonitor.logsArePresent); + assert.ok(ServerMonitor.error.isShown); + assert.equal(ServerMonitor.error.title, 'Not Authorized'); + assert.ok(ServerMonitor.error.message.includes('agent:read')); + + await ServerMonitor.error.seekHelp(); + assert.equal(currentURL(), '/settings/tokens'); + }); +}); diff --git a/ui/tests/pages/servers/monitor.js b/ui/tests/pages/servers/monitor.js new file mode 100644 index 000000000..8b6eec929 --- /dev/null +++ b/ui/tests/pages/servers/monitor.js @@ -0,0 +1,40 @@ +import { + create, + attribute, + clickable, + collection, + isPresent, + text, + visitable, +} from 'ember-cli-page-object'; +import { run } from '@ember/runloop'; +import { selectOpen, selectOpenChoose } from '../../utils/ember-power-select-extensions'; + +export default create({ + visit: visitable('/servers/:name/monitor'), + + breadcrumbs: collection('[data-test-breadcrumb]', { + id: attribute('data-test-breadcrumb'), + text: text(), + visit: clickable(), + }), + + breadcrumbFor(id) { + return this.breadcrumbs.toArray().find(crumb => crumb.id === id); + }, + + logsArePresent: isPresent('[data-test-log-box]'), + + error: { + isShown: isPresent('[data-test-error]'), + title: text('[data-test-error-title]'), + message: text('[data-test-error-message]'), + seekHelp: clickable('[data-test-error-message] a'), + }, + + async selectLogLevel(level) { + const contentId = await selectOpen('[data-test-level-switcher]'); + run.later(run, run.cancelTimers, 500); + await selectOpenChoose(contentId, level); + }, +});