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

Backport of [ui] Show ALL regions' leaders when viewing servers route into release/1.9.x #24799

Open
wants to merge 1 commit into
base: release/1.9.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .changelog/24723.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: add leadership status for servers in other regions
```
13 changes: 8 additions & 5 deletions ui/app/models/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import Model from '@ember-data/model';
import { attr } from '@ember-data/model';
import classic from 'ember-classic-decorator';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import formatHost from 'nomad-ui/utils/format-host';

@classic
export default class Agent extends Model {
@service system;

Expand All @@ -29,9 +29,12 @@ export default class Agent extends Model {
return formatHost(address, rpcPort);
}

@computed('rpcAddr', 'system.leader.rpcAddr')
get isLeader() {
return this.get('system.leader.rpcAddr') === this.rpcAddr;
@tracked isLeader = false;

@action async checkForLeadership() {
const leaders = await this.system.leaders;
this.isLeader = leaders.includes(this.rpcAddr);
return this.isLeader;
}

@computed('tags.build')
Expand Down
5 changes: 0 additions & 5 deletions ui/app/routes/clients.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ import classic from 'ember-classic-decorator';
@classic
export default class ClientsRoute extends Route.extend(WithForbiddenState) {
@service store;
@service system;

beforeModel() {
return this.get('system.leader');
}

model() {
return RSVP.hash({
Expand Down
10 changes: 6 additions & 4 deletions ui/app/routes/servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ export default class ServersRoute extends Route.extend(WithForbiddenState) {
@service store;
@service system;

beforeModel() {
return this.get('system.leader');
async beforeModel() {
await this.system.leaders;
}

model() {
async model() {
const agents = await this.store.findAll('agent');
await Promise.all(agents.map((agent) => agent.checkForLeadership()));
return RSVP.hash({
nodes: this.store.findAll('node'),
agents: this.store.findAll('agent'),
agents,
}).catch(notifyForbidden(this));
}
}
28 changes: 12 additions & 16 deletions ui/app/services/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,23 @@ import { namespace } from '../adapters/application';
import jsonWithDefault from '../utils/json-with-default';
import classic from 'ember-classic-decorator';
import { task } from 'ember-concurrency';

@classic
export default class SystemService extends Service {
@service token;
@service store;

@computed('activeRegion')
get leader() {
const token = this.token;

return PromiseObject.create({
promise: token
.authorizedRequest(`/${namespace}/status/leader`)
.then((res) => res.json())
.then((rpcAddr) => ({ rpcAddr }))
.then((leader) => {
// Dirty self so leader can be used as a dependent key
this.notifyPropertyChange('leader.rpcAddr');
return leader;
}),
});
/**
* Iterates over all regions and returns a list of leaders' rpcAddrs
*/
@computed('regions.[]')
get leaders() {
return Promise.all(
this.regions.map((region) => {
return this.token
.authorizedRequest(`/${namespace}/status/leader?region=${region}`)
.then((res) => res.json());
})
);
}

@computed
Expand Down
3 changes: 2 additions & 1 deletion ui/app/styles/core/table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@
white-space: nowrap;
}

&.node-status-badges {
&.node-status-badges,
&.server-status-badges {
.hds-badge__text {
white-space: nowrap;
}
Expand Down
26 changes: 17 additions & 9 deletions ui/app/templates/components/server-agent-row.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
~}}

<td data-test-server-name
{{keyboard-shortcut
{{keyboard-shortcut
enumerated=true
action=(action this.goToAgent)
}}
Expand All @@ -16,14 +16,22 @@
@size="large"
/>
</span></td>
<td data-test-server-is-leader>

<Hds::Badge
@text={{if this.agent.isLeader "True" "False"}}
@icon={{if this.agent.isLeader "check-circle" ""}}
@color={{if this.agent.isLeader "success" "neutral"}}
@size="large"
/>
<td data-test-server-is-leader class="server-status-badges">
<Hds::Badge
@text={{if
this.agent.isLeader
(if
this.agent.system.shouldShowRegions
(concat "True" " (" this.agent.region ")")
"True"
)
"False"
}}
@icon={{if this.agent.isLeader "check-circle" ""}}
@color={{if this.agent.isLeader "success" "neutral"}}
@size="large"
class="no-wrap"
/>
</td>
<td data-test-server-address class="is-200px is-truncatable">{{this.agent.address}}</td>
<td data-test-server-port>{{this.agent.serfPort}}</td>
Expand Down
15 changes: 11 additions & 4 deletions ui/mirage/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ import { copy } from 'ember-copy';
import formatHost from 'nomad-ui/utils/format-host';
import faker from 'nomad-ui/mirage/faker';

export function findLeader(schema) {
const agent = schema.agents.first();
export function findLeader(schema, region = null) {
let agent;
let agents = schema.agents.all().models;
if (region) {
agent = agents.find((agent) => agent.member?.Tags?.region === region);
} else {
agent = agents[0];
}
return formatHost(agent.member.Address, agent.member.Tags.port);
}

Expand Down Expand Up @@ -741,8 +747,9 @@ export default function () {
return logEncode(logFrames, logFrames.length - 1);
});

this.get('/status/leader', function (schema) {
return JSON.stringify(findLeader(schema));
this.get('/status/leader', function (schema, { queryParams: { region } }) {
let leader = JSON.stringify(findLeader(schema, region));
return leader;
});

this.get('/acl/tokens', function ({ tokens }, req) {
Expand Down
1 change: 1 addition & 0 deletions ui/mirage/factories/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,6 @@ function generateTags(serfPort) {
rpcPortCandidate === serfPort ? rpcPortCandidate + 1 : rpcPortCandidate,
dc: faker.helpers.randomize(DATACENTERS),
build: faker.helpers.randomize(AGENT_BUILDS),
region: 'global',
};
}
3 changes: 3 additions & 0 deletions ui/mirage/scenarios/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ function smallCluster(server) {
server.create('feature', { name: 'Dynamic Application Sizing' });
server.create('feature', { name: 'Sentinel Policies' });
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
if (withRegions) {
server.db.agents[0].member.Tags.region = server.db.regions[0].id;
}
server.createList('node-pool', 2);
server.createList('node', 5);
server.create(
Expand Down
32 changes: 31 additions & 1 deletion ui/tests/acceptance/servers-list-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ module('Acceptance | servers list', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);

hooks.beforeEach(function () {
server.create('region', { id: 'global' });
});

test('it passes an accessibility audit', async function (assert) {
minimumSetup();
await ServersList.visit();
Expand All @@ -51,7 +55,6 @@ module('Acceptance | servers list', function (hooks) {
const sortedAgents = server.db.agents.sort(agentSort(leader)).reverse();

await ServersList.visit();

await percySnapshot(assert);

assert.equal(
Expand Down Expand Up @@ -116,4 +119,31 @@ module('Acceptance | servers list', function (hooks) {
await ServersList.error.seekHelp();
assert.equal(currentURL(), '/settings/tokens');
});

test('multiple regions should each show leadership values', async function (assert) {
server.createList('node-pool', 1);
server.createList('node', 1);
server.create('region', { id: 'global' });
server.create('region', { id: 'galactic' });
server.createList('agent', 3);
server.db.agents[0].member.Tags.region = 'global';
server.db.agents[1].member.Tags.region = 'galactic';
server.db.agents[2].member.Tags.region = 'galactic';
await ServersList.visit();
assert.equal(
ServersList.servers.objectAt(0).leader,
'True (galactic)',
'Leadership is shown for the galactic region'
);
assert.equal(
ServersList.servers.objectAt(1).leader,
'True (global)',
'Leadership is shown for the global region'
);
assert.equal(
ServersList.servers.objectAt(2).leader,
'False',
'Non-leader servers are shown'
);
});
});
Loading