Server monitor page

This commit is contained in:
Michael Lange
2020-06-15 22:47:05 -07:00
parent 53027ba6ac
commit a0c6cc2dfd
5 changed files with 124 additions and 1 deletions

View File

@@ -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';
}

View File

@@ -1,4 +1,12 @@
{{title "Server " model.name}}
<ServerSubnav @server={{model}} />
<section class="section">
{{#if (can "read agent")}}
<AgentMonitor
@level={{level}}
@server={{model}}
@onLevelChange={{action (mut level)}} />
{{else}}
<ForbiddenMessage @permission="agent:read" />
{{/if}}
</section>

View File

@@ -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');

View File

@@ -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');
});
});

View File

@@ -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);
},
});