diff --git a/ui/app/adapters/allocation.js b/ui/app/adapters/allocation.js index 66ba70940..cbba94096 100644 --- a/ui/app/adapters/allocation.js +++ b/ui/app/adapters/allocation.js @@ -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; } } diff --git a/ui/app/components/allocation-service-sidebar.hbs b/ui/app/components/allocation-service-sidebar.hbs index ada404772..73a556065 100644 --- a/ui/app/components/allocation-service-sidebar.hbs +++ b/ui/app/components/allocation-service-sidebar.hbs @@ -86,8 +86,8 @@ - {{#if @service.mostRecentChecks.length}} - + {{#if this.checks.length}} + Name diff --git a/ui/app/components/allocation-service-sidebar.js b/ui/app/components/allocation-service-sidebar.js index a66d3ff62..eae004e83 100644 --- a/ui/app/components/allocation-service-sidebar.js +++ b/ui/app/components/allocation-service-sidebar.js @@ -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'); + } } diff --git a/ui/app/controllers/allocations/allocation/index.js b/ui/app/controllers/allocations/allocation/index.js index d3e4d5a9e..a81f866b1 100644 --- a/ui/app/controllers/allocations/allocation/index.js +++ b/ui/app/controllers/allocations/allocation/index.js @@ -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; }); } diff --git a/ui/app/serializers/service-fragment.js b/ui/app/serializers/service-fragment.js index bd74d87e5..d4f7df6fe 100644 --- a/ui/app/serializers/service-fragment.js +++ b/ui/app/serializers/service-fragment.js @@ -3,9 +3,5 @@ import classic from 'ember-classic-decorator'; @classic export default class ServiceFragmentSerializer extends ApplicationSerializer { - attrs = { - connect: 'Connect', - }; - arrayNullOverrides = ['Tags']; } diff --git a/ui/tests/acceptance/allocation-detail-test.js b/ui/tests/acceptance/allocation-detail-test.js index a85fb13ce..14807f176 100644 --- a/ui/tests/acceptance/allocation-detail-test.js +++ b/ui/tests/acceptance/allocation-detail-test.js @@ -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'); diff --git a/ui/tests/unit/controllers/allocations/allocation/index-test.js b/ui/tests/unit/controllers/allocations/allocation/index-test.js index c68f70f39..451864ac7 100644 --- a/ui/tests/unit/controllers/allocations/allocation/index-test.js +++ b/ui/tests/unit/controllers/allocations/allocation/index-test.js @@ -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',