diff --git a/CHANGELOG.md b/CHANGELOG.md
index 707e57a7540..05fe254334e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ IMPROVEMENTS:
* networking: Added support for interpolating host network names with node attributes. [[GH-10196](https://github.com/hashicorp/nomad/issues/10196)]
* nomad/structs: Removed deprecated Node.Drain field, added API extensions to restore it [[GH-10202](https://github.com/hashicorp/nomad/issues/10202)]
* ui: Added a job reversion button [[GH-10336](https://github.com/hashicorp/nomad/pull/10336)]
+ * ui: Added memory maximum to task group ribbon [[GH-10459](https://github.com/hashicorp/nomad/pull/10459)]
* ui: Updated global search to use fuzzy search API [[GH-10412](https://github.com/hashicorp/nomad/pull/10412)]
BUG FIXES:
diff --git a/ui/app/models/resources.js b/ui/app/models/resources.js
index 1ac7bfad9bd..5e3fbce00d0 100644
--- a/ui/app/models/resources.js
+++ b/ui/app/models/resources.js
@@ -5,6 +5,7 @@ import { fragmentArray } from 'ember-data-model-fragments/attributes';
export default class Resources extends Fragment {
@attr('number') cpu;
@attr('number') memory;
+ @attr('number') memoryMax;
@attr('number') disk;
@attr('number') iops;
@fragmentArray('network', { defaultValue: () => [] }) networks;
diff --git a/ui/app/models/task-group.js b/ui/app/models/task-group.js
index 49d53a23154..14475e7315e 100644
--- a/ui/app/models/task-group.js
+++ b/ui/app/models/task-group.js
@@ -36,6 +36,13 @@ export default class TaskGroup extends Fragment {
@sumAggregation('tasks', 'reservedMemory') reservedMemory;
@sumAggregation('tasks', 'reservedDisk') reservedDisk;
+ @computed('tasks.@each.{reservedMemory,reservedMemoryMax}')
+ get reservedMemoryMax() {
+ return this.get('tasks')
+ .map(t => t.get('reservedMemoryMax') || t.get('reservedMemory'))
+ .reduce((sum, count) => sum + count, 0);
+ }
+
@attr('number') reservedEphemeralDisk;
@computed('job.latestFailureEvaluation.failedTGAllocs.[]', 'name')
diff --git a/ui/app/models/task.js b/ui/app/models/task.js
index b5f637da577..3dbb818f79e 100644
--- a/ui/app/models/task.js
+++ b/ui/app/models/task.js
@@ -30,6 +30,7 @@ export default class Task extends Fragment {
}
@attr('number') reservedMemory;
+ @attr('number') reservedMemoryMax;
@attr('number') reservedCPU;
@attr('number') reservedDisk;
@attr('number') reservedEphemeralDisk;
diff --git a/ui/app/serializers/task.js b/ui/app/serializers/task.js
index ea09c5dcf62..2ed5dbe9bae 100644
--- a/ui/app/serializers/task.js
+++ b/ui/app/serializers/task.js
@@ -6,6 +6,7 @@ export default class Task extends ApplicationSerializer {
const resources = hash.Resources;
if (resources) {
hash.ReservedMemory = resources.MemoryMB;
+ hash.ReservedMemoryMax = resources.MemoryMaxMB;
hash.ReservedCPU = resources.CPU;
hash.ReservedDisk = resources.DiskMB;
hash.ReservedEphemeralDisk = hash.EphemeralDisk.SizeMB;
diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs
index 934368824bb..e1f7fd5c069 100644
--- a/ui/app/templates/jobs/job/task-group.hbs
+++ b/ui/app/templates/jobs/job/task-group.hbs
@@ -31,7 +31,13 @@
# Tasks {{this.model.tasks.length}}
Reserved CPU {{format-scheduled-hertz this.model.reservedCPU}}
- Reserved Memory {{format-scheduled-bytes this.model.reservedMemory start="MiB"}}
+
+ Reserved Memory
+ {{format-scheduled-bytes this.model.reservedMemory start="MiB"}}
+ {{#if (gt this.model.reservedMemoryMax this.model.reservedMemory)}}
+ ({{format-scheduled-bytes this.model.reservedMemoryMax start="MiB"}} Max)
+ {{/if}}
+
Reserved Disk {{format-scheduled-bytes this.model.reservedEphemeralDisk start="MiB"}}
{{#if this.model.scaling}}
Count Range
diff --git a/ui/mirage/common.js b/ui/mirage/common.js
index cac53cdb0ac..9646a60f691 100644
--- a/ui/mirage/common.js
+++ b/ui/mirage/common.js
@@ -24,7 +24,7 @@ export const HOSTS = provide(100, () => {
export const STORAGE_PROVIDERS = ['ebs', 'zfs', 'nfs', 'cow', 'moo'];
export function generateResources(options = {}) {
- return {
+ const resources = {
Cpu: {
CpuShares: options.CPU || faker.helpers.randomize(CPU_RESERVATIONS),
},
@@ -37,6 +37,15 @@ export function generateResources(options = {}) {
Networks: generateNetworks(options.networks),
Ports: generatePorts(options.networks),
};
+
+ if (faker.random.boolean()) {
+ const higherMemoryReservations = MEMORY_RESERVATIONS.filter(mb => mb > resources.Memory.MemoryMB);
+ resources.Memory.MemoryMaxMB = faker.helpers.randomize(higherMemoryReservations) || resources.Memory.MemoryMB + 1;
+ } else {
+ resources.Memory.MemoryMaxMB = 0;
+ }
+
+ return resources;
}
export function generateNetworks(options = {}) {
diff --git a/ui/mirage/factories/task.js b/ui/mirage/factories/task.js
index 88c5b23cff4..4b79820e3c1 100644
--- a/ui/mirage/factories/task.js
+++ b/ui/mirage/factories/task.js
@@ -26,6 +26,7 @@ export default Factory.extend({
return {
CPU: resources.Cpu.CpuShares,
MemoryMB: resources.Memory.MemoryMB,
+ MemoryMaxMB: resources.Memory.MemoryMaxMB,
DiskMB: resources.Disk.DiskMB,
};
},
diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js
index e097b27a9d3..a56c8874710 100644
--- a/ui/tests/acceptance/task-group-detail-test.js
+++ b/ui/tests/acceptance/task-group-detail-test.js
@@ -82,6 +82,7 @@ module('Acceptance | task group detail', function(hooks) {
test('/jobs/:id/:task-group should list high-level metrics for the allocation', async function(assert) {
const totalCPU = tasks.mapBy('resources.CPU').reduce(sum, 0);
const totalMemory = tasks.mapBy('resources.MemoryMB').reduce(sum, 0);
+ const totalMemoryMax = tasks.map(t => t.resources.MemoryMaxMB || t.resources.MemoryMB).reduce(sum, 0);
const totalDisk = taskGroup.ephemeralDisk.SizeMB;
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
@@ -92,9 +93,16 @@ module('Acceptance | task group detail', function(hooks) {
`Reserved CPU ${formatScheduledHertz(totalCPU, 'MHz')}`,
'Aggregated CPU reservation for all tasks'
);
+
+ let totalMemoryMaxAddendum = '';
+
+ if (totalMemoryMax > totalMemory) {
+ totalMemoryMaxAddendum = ` (${formatScheduledBytes(totalMemoryMax, 'MiB')} Max)`;
+ }
+
assert.equal(
TaskGroup.mem,
- `Reserved Memory ${formatScheduledBytes(totalMemory, 'MiB')}`,
+ `Reserved Memory ${formatScheduledBytes(totalMemory, 'MiB')}${totalMemoryMaxAddendum}`,
'Aggregated Memory reservation for all tasks'
);
assert.equal(