Merge pull request #14495 from hashicorp/ui-services-checks-uniq-by-alloc

[ui] Service health checks unique by allocation
This commit is contained in:
Phil Renaud
2022-09-08 17:00:04 -04:00
committed by GitHub
7 changed files with 60 additions and 28 deletions

View File

@@ -41,6 +41,11 @@ export default class AllocationAdapter extends Watchable {
`/v1/client/allocation/${model.id}/checks`
);
const data = await res.json();
// Append allocation ID to each check
Object.values(data).forEach((check) => {
check.Alloc = model.id;
check.Timestamp = check.Timestamp * 1000; // Convert to milliseconds
});
return data;
}
}

View File

@@ -86,8 +86,8 @@
</div>
</div>
</div>
{{#if @service.mostRecentChecks.length}}
<ListTable class="health-checks" @source={{@service.mostRecentChecks}} as |t|>
{{#if this.checks.length}}
<ListTable class="health-checks" @source={{this.checks}} as |t|>
<t.head>
<th>
Name

View File

@@ -32,10 +32,22 @@ export default class AllocationServiceSidebarComponent extends Component {
}
get aggregateStatus() {
return this.args.service?.mostRecentChecks?.any(
(check) => check.Status === 'failure'
)
return this.checks.any((check) => check.Status === 'failure')
? 'Unhealthy'
: 'Healthy';
}
get checks() {
if (!this.args.service || !this.args.allocation) return [];
let allocID = this.args.allocation.id;
// Our UI checks run every 2 seconds; but a check itself may only update every, say, minute.
// Therefore, we'll have duplicate checks in a service's healthChecks array.
// Only get the most recent check for each check.
return (this.args.service.healthChecks || [])
.filterBy('Alloc', allocID)
.sortBy('Timestamp')
.reverse()
.uniqBy('Check')
.sortBy('Check');
}
}

View File

@@ -71,7 +71,7 @@ export default class IndexController extends Controller.extend(Sortable) {
@union('taskServices', 'groupServices') services;
@computed('model.healthChecks.{}', 'services')
@computed('model.{healthChecks,id}', 'services')
get servicesWithHealthChecks() {
return this.services.map((service) => {
if (this.model.healthChecks) {
@@ -83,19 +83,15 @@ export default class IndexController extends Controller.extend(Sortable) {
return currentServiceName === service.refID;
}
);
// Only append those healthchecks whose timestamps are not already found in service.healthChecks
healthChecks.forEach((check) => {
if (
!service.healthChecks.find(
(sc) =>
sc.Check === check.Check && sc.Timestamp === check.Timestamp
)
) {
service.healthChecks.pushObject(check);
service.healthChecks = [...service.healthChecks.slice(-10)];
}
service.healthChecks.pushObject(check);
});
}
// Contextualize healthchecks for the allocation we're in
service.healthChecks = service.healthChecks.filterBy(
'Alloc',
this.model.id
);
return service;
});
}

View File

@@ -3,9 +3,5 @@ import classic from 'ember-classic-decorator';
@classic
export default class ServiceFragmentSerializer extends ApplicationSerializer {
attrs = {
connect: 'Connect',
};
arrayNullOverrides = ['Tags'];
}

View File

@@ -627,7 +627,7 @@ module('Acceptance | allocation detail (services)', function (hooks) {
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
server.createList('node', 5);
server.createList('job', 1, { createRecommendations: true });
server.create('job', {
const job = server.create('job', {
withGroupServices: true,
withTaskServices: true,
name: 'Service-haver',
@@ -635,12 +635,16 @@ module('Acceptance | allocation detail (services)', function (hooks) {
namespaceId: 'default',
});
const currentAlloc = server.db.allocations.findBy({ jobId: job.id });
const otherAlloc = server.db.allocations.reject((j) => j.jobId !== job.id);
server.db.serviceFragments.update({
healthChecks: [
{
Status: 'success',
Check: 'check1',
Timestamp: 99,
Alloc: currentAlloc.id,
},
{
Status: 'failure',
@@ -649,18 +653,30 @@ module('Acceptance | allocation detail (services)', function (hooks) {
propThatDoesntMatter:
'this object will be ignored, since it shared a Check name with a later one.',
Timestamp: 98,
Alloc: currentAlloc.id,
},
{
Status: 'success',
Check: 'check2',
Output: 'Two',
Timestamp: 99,
Alloc: currentAlloc.id,
},
{
Status: 'failure',
Check: 'check3',
Output: 'Oh no!',
Timestamp: 99,
Alloc: currentAlloc.id,
},
{
Status: 'success',
Check: 'check3',
Output: 'Wont be seen',
propThatDoesntMatter:
'this object will be ignored, in spite of its later timestamp, since it exists on a different alloc',
Timestamp: 100,
Alloc: otherAlloc.id,
},
],
});
@@ -669,7 +685,6 @@ module('Acceptance | allocation detail (services)', function (hooks) {
test('Allocation has a list of services with active checks', async function (assert) {
await visit('jobs/service-haver@default');
await click('.allocation-row');
assert.dom('[data-test-service]').exists();
assert.dom('.service-sidebar').exists();
assert.dom('.service-sidebar').doesNotHaveClass('open');

View File

@@ -9,8 +9,7 @@ module('Unit | Controller | allocations/allocation/index', function (hooks) {
let controller = this.owner.lookup(
'controller:allocations/allocation/index'
);
controller.set('model', Allocation);
controller.set('model', JSON.parse(JSON.stringify(Allocation)));
const groupFakePy = {
refID: 'fakepy-group-fake-py',
@@ -95,12 +94,11 @@ module('Unit | Controller | allocations/allocation/index', function (hooks) {
);
});
test('it handles duplicate names', function (assert) {
test('it handles duplicate names', async function (assert) {
let controller = this.owner.lookup(
'controller:allocations/allocation/index'
);
controller.set('model', Allocation);
controller.set('model', JSON.parse(JSON.stringify(Allocation)));
const groupDupe = {
refID: 'fakepy-duper',
@@ -229,6 +227,7 @@ var Allocation = {
healthChecks: {
c97fda942e772b43a5a537e5b0c8544c: {
Check: 'service: "task-fake-py" check',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: 'c97fda942e772b43a5a537e5b0c8544c',
Mode: 'healthiness',
@@ -241,6 +240,7 @@ var Allocation = {
},
'2e1bfc8ecc485ee86b972ae08e890152': {
Check: 'task-happy',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: '2e1bfc8ecc485ee86b972ae08e890152',
Mode: 'healthiness',
@@ -253,6 +253,7 @@ var Allocation = {
},
'6162723ab20b268c25eda69b400dc9c6': {
Check: 'task-sad',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: '6162723ab20b268c25eda69b400dc9c6',
Mode: 'healthiness',
@@ -266,6 +267,7 @@ var Allocation = {
},
a4a7050175a2b236edcf613cb3563753: {
Check: 'task-sad2',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: 'a4a7050175a2b236edcf613cb3563753',
Mode: 'healthiness',
@@ -279,6 +281,7 @@ var Allocation = {
},
'2dfe58eb841bdfa704f0ae9ef5b5af5e': {
Check: 'tcp_probe',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: '2dfe58eb841bdfa704f0ae9ef5b5af5e',
Mode: 'readiness',
@@ -290,6 +293,7 @@ var Allocation = {
},
'69021054964f4c461b3c4c4f456e16a8': {
Check: 'happy',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: '69021054964f4c461b3c4c4f456e16a8',
Mode: 'healthiness',
@@ -301,6 +305,7 @@ var Allocation = {
},
'913f5b725ceecdd5ff48a9a51ddf8513': {
Check: 'sad',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: '913f5b725ceecdd5ff48a9a51ddf8513',
Mode: 'healthiness',
@@ -313,6 +318,7 @@ var Allocation = {
},
bloop: {
Check: 'is-alive',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: 'bloop',
Mode: 'healthiness',
@@ -323,6 +329,7 @@ var Allocation = {
},
'group-dupe': {
Check: 'is-alive',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: 'group-dupe',
Mode: 'healthiness',
@@ -333,6 +340,7 @@ var Allocation = {
},
'task-dupe': {
Check: 'is-alive',
Alloc: 'my-alloc',
Group: 'trying-multi-dupes.fakepy[1]',
ID: 'task-dupe',
Mode: 'healthiness',
@@ -342,7 +350,7 @@ var Allocation = {
Timestamp: 1662131947,
},
},
id: 'my-alloc',
states: [
{
Name: 'http.server',