diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
index 5d19a7da5..d4f4d42a0 100644
--- a/ui/.eslintrc.js
+++ b/ui/.eslintrc.js
@@ -10,6 +10,9 @@ module.exports = {
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module',
+ ecmaFeatures: {
+ experimentalObjectRestSpread: true,
+ },
},
rules: {
indent: ['error', 2, { SwitchCase: 1 }],
diff --git a/ui/app/components/job-page/parts/recent-allocations.js b/ui/app/components/job-page/parts/recent-allocations.js
new file mode 100644
index 000000000..c50ff7f02
--- /dev/null
+++ b/ui/app/components/job-page/parts/recent-allocations.js
@@ -0,0 +1,24 @@
+import Component from '@ember/component';
+import { computed } from '@ember/object';
+import PromiseArray from 'nomad-ui/utils/classes/promise-array';
+
+export default Component.extend({
+ sortProperty: 'modifyIndex',
+ sortDescending: true,
+ sortedAllocations: computed('job.allocations.@each.modifyIndex', function() {
+ return new PromiseArray({
+ promise: this.get('job.allocations').then(allocations =>
+ allocations
+ .sortBy('modifyIndex')
+ .reverse()
+ .slice(0, 5)
+ ),
+ });
+ }),
+
+ actions: {
+ gotoAllocation(allocation) {
+ this.transitionToRoute('allocations.allocation', allocation);
+ },
+ },
+});
diff --git a/ui/app/controllers/jobs/job/allocations.js b/ui/app/controllers/jobs/job/allocations.js
new file mode 100644
index 000000000..1ec7a40a2
--- /dev/null
+++ b/ui/app/controllers/jobs/job/allocations.js
@@ -0,0 +1,39 @@
+import { alias } from '@ember/object/computed';
+import Controller from '@ember/controller';
+import { computed } from '@ember/object';
+import Sortable from 'nomad-ui/mixins/sortable';
+import Searchable from 'nomad-ui/mixins/searchable';
+import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
+
+export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, {
+ queryParams: {
+ currentPage: 'page',
+ searchTerm: 'search',
+ sortProperty: 'sort',
+ sortDescending: 'desc',
+ },
+
+ currentPage: 1,
+ pageSize: 25,
+
+ sortProperty: 'modifyIndex',
+ sortDescending: true,
+
+ job: alias('model'),
+
+ searchProps: computed(() => ['shortId', 'name', 'taskGroupName']),
+
+ allocations: computed('model.allocations.[]', function() {
+ return this.get('model.allocations') || [];
+ }),
+
+ listToSort: alias('allocations'),
+ listToSearch: alias('listSorted'),
+ sortedAllocations: alias('listSearched'),
+
+ actions: {
+ gotoAllocation(allocation) {
+ this.transitionToRoute('allocations.allocation', allocation);
+ },
+ },
+});
diff --git a/ui/app/models/allocation.js b/ui/app/models/allocation.js
index b65232ce2..f98e3fc22 100644
--- a/ui/app/models/allocation.js
+++ b/ui/app/models/allocation.js
@@ -25,9 +25,13 @@ export default Model.extend({
name: attr('string'),
taskGroupName: attr('string'),
resources: fragment('resources'),
+ jobVersion: attr('number'),
+
modifyIndex: attr('number'),
modifyTime: attr('date'),
- jobVersion: attr('number'),
+
+ createIndex: attr('number'),
+ createTime: attr('date'),
clientStatus: attr('string'),
desiredStatus: attr('string'),
diff --git a/ui/app/router.js b/ui/app/router.js
index ce0a1bc74..39446ffa9 100644
--- a/ui/app/router.js
+++ b/ui/app/router.js
@@ -14,6 +14,7 @@ Router.map(function() {
this.route('versions');
this.route('deployments');
this.route('evaluations');
+ this.route('allocations');
});
});
diff --git a/ui/app/routes/jobs/job/allocations.js b/ui/app/routes/jobs/job/allocations.js
new file mode 100644
index 000000000..da59c95a4
--- /dev/null
+++ b/ui/app/routes/jobs/job/allocations.js
@@ -0,0 +1,19 @@
+import Route from '@ember/routing/route';
+import { collect } from '@ember/object/computed';
+import { watchRelationship } from 'nomad-ui/utils/properties/watch';
+import WithWatchers from 'nomad-ui/mixins/with-watchers';
+
+export default Route.extend(WithWatchers, {
+ model() {
+ const job = this.modelFor('jobs.job');
+ return job.get('allocations').then(() => job);
+ },
+
+ startWatchers(controller, model) {
+ controller.set('watchAllocations', this.get('watchAllocations').perform(model));
+ },
+
+ watchAllocations: watchRelationship('allocations'),
+
+ watchers: collect('watchAllocations'),
+});
diff --git a/ui/app/routes/jobs/job/index.js b/ui/app/routes/jobs/job/index.js
index 58bf30beb..30423ce9d 100644
--- a/ui/app/routes/jobs/job/index.js
+++ b/ui/app/routes/jobs/job/index.js
@@ -11,6 +11,7 @@ export default Route.extend(WithWatchers, {
controller.set('watchers', {
model: this.get('watch').perform(model),
summary: this.get('watchSummary').perform(model.get('summary')),
+ allocations: this.get('watchAllocations').perform(model),
evaluations: this.get('watchEvaluations').perform(model),
latestDeployment:
model.get('supportsDeployments') && this.get('watchLatestDeployment').perform(model),
@@ -21,6 +22,7 @@ export default Route.extend(WithWatchers, {
watch: watchRecord('job'),
watchAll: watchAll('job'),
watchSummary: watchRecord('job-summary'),
+ watchAllocations: watchRelationship('allocations'),
watchEvaluations: watchRelationship('evaluations'),
watchLatestDeployment: watchRelationship('latestDeployment'),
@@ -28,6 +30,7 @@ export default Route.extend(WithWatchers, {
'watch',
'watchAll',
'watchSummary',
+ 'watchAllocations',
'watchEvaluations',
'watchLatestDeployment'
),
diff --git a/ui/app/serializers/allocation.js b/ui/app/serializers/allocation.js
index 7cbd76a18..2aa91d39f 100644
--- a/ui/app/serializers/allocation.js
+++ b/ui/app/serializers/allocation.js
@@ -34,6 +34,9 @@ export default ApplicationSerializer.extend({
hash.ModifyTimeNanos = hash.ModifyTime % 1000000;
hash.ModifyTime = Math.floor(hash.ModifyTime / 1000000);
+ hash.CreateTimeNanos = hash.CreateTime % 1000000;
+ hash.CreateTime = Math.floor(hash.CreateTime / 1000000);
+
hash.RescheduleEvents = (hash.RescheduleTracker || {}).Events;
// API returns empty strings instead of null
diff --git a/ui/app/styles/charts.scss b/ui/app/styles/charts.scss
index e70738650..3db77a93b 100644
--- a/ui/app/styles/charts.scss
+++ b/ui/app/styles/charts.scss
@@ -1,11 +1,15 @@
-@import "./charts/distribution-bar";
-@import "./charts/tooltip";
-@import "./charts/colors";
+@import './charts/distribution-bar';
+@import './charts/tooltip';
+@import './charts/colors';
.inline-chart {
height: 1.5rem;
display: flex;
align-items: center;
+
+ &.is-small {
+ width: 50px;
+ }
}
// Patterns are templates referenced by other SVG fill properties.
diff --git a/ui/app/templates/clients/client.hbs b/ui/app/templates/clients/client.hbs
index b6e2bcf9f..3b84f643e 100644
--- a/ui/app/templates/clients/client.hbs
+++ b/ui/app/templates/clients/client.hbs
@@ -100,7 +100,7 @@
|
{{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
{{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
- {{#t.sort-by prop="name"}}Name{{/t.sort-by}}
+ {{#t.sort-by prop="createIndex" title="Create Index"}}Created{{/t.sort-by}}
{{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
{{#t.sort-by prop="job.name"}}Job{{/t.sort-by}}
{{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
diff --git a/ui/app/templates/components/allocation-row.hbs b/ui/app/templates/components/allocation-row.hbs
index 1da565ff5..fd16e5bc9 100644
--- a/ui/app/templates/components/allocation-row.hbs
+++ b/ui/app/templates/components/allocation-row.hbs
@@ -15,12 +15,19 @@
{{allocation.shortId}}
{{/link-to}}
-{{moment-format allocation.modifyTime "MM/DD HH:mm:ss"}} |
-{{allocation.name}} |
+{{#if (eq context "job")}}
+
+ {{#link-to "jobs.job.task-group" allocation.job allocation.taskGroupName (query-params jobNamespace=allocation.job.namespace.id)}}
+ {{allocation.taskGroupName}}
+ {{/link-to}}
+ |
+{{/if}}
+{{moment-format allocation.createTime "MM/DD HH:mm:ss"}} |
+{{moment-from-now allocation.modifyTime}} |
{{allocation.clientStatus}}
|
-{{#if (eq context "job")}}
+{{#if (or (eq context "taskGroup") (eq context "job"))}}
{{allocation.jobVersion}} |
{{#link-to "clients.client" allocation.node}}{{allocation.node.shortId}}{{/link-to}} |
{{else if (eq context "node")}}
@@ -32,9 +39,9 @@
/ {{allocation.taskGroup.name}}
{{/if}}
- {{allocation.jobVersion}} |
+ {{allocation.jobVersion}} |
{{/if}}
-
+ |
{{#if (and (not stats) fetchStats.isRunning)}}
...
{{else if (not allocation)}}
@@ -44,7 +51,7 @@
{{x-icon "warning" class="is-warning"}}
{{else}}
- |
-
+ |
{{#if (and (not stats) fetchStats.isRunning)}}
...
{{else if (not allocation)}}
{{! nothing when there's no allocation}}
{{else if statsError}}
-
+
{{x-icon "warning" class="is-warning"}}
{{else}}
diff --git a/ui/app/templates/components/job-deployment/deployment-allocations.hbs b/ui/app/templates/components/job-deployment/deployment-allocations.hbs
index 242427238..3f7dbf895 100644
--- a/ui/app/templates/components/job-deployment/deployment-allocations.hbs
+++ b/ui/app/templates/components/job-deployment/deployment-allocations.hbs
@@ -9,8 +9,9 @@
{{#t.head}}
| |
ID |
+ Task Group |
+ Created |
Modified |
- Name |
Status |
Version |
Node |
diff --git a/ui/app/templates/components/job-page/batch.hbs b/ui/app/templates/components/job-page/batch.hbs
index f11a77849..dcd1ed7be 100644
--- a/ui/app/templates/components/job-page/batch.hbs
+++ b/ui/app/templates/components/job-page/batch.hbs
@@ -22,4 +22,6 @@
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
+
+ {{job-page/parts/recent-allocations job=job}}
{{/job-page/parts/body}}
diff --git a/ui/app/templates/components/job-page/parameterized-child.hbs b/ui/app/templates/components/job-page/parameterized-child.hbs
index a5cbfb846..b20362263 100644
--- a/ui/app/templates/components/job-page/parameterized-child.hbs
+++ b/ui/app/templates/components/job-page/parameterized-child.hbs
@@ -29,6 +29,8 @@
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
+ {{job-page/parts/recent-allocations job=job}}
+
Payload
diff --git a/ui/app/templates/components/job-page/parts/recent-allocations.hbs b/ui/app/templates/components/job-page/parts/recent-allocations.hbs
new file mode 100644
index 000000000..4a0c22965
--- /dev/null
+++ b/ui/app/templates/components/job-page/parts/recent-allocations.hbs
@@ -0,0 +1,46 @@
+
+
+ Recent Allocations
+
+
+ {{#if job.allocations.length}}
+ {{#list-table
+ source=sortedAllocations
+ sortProperty=sortProperty
+ sortDescending=sortDescending
+ class="with-foot" as |t|}}
+ {{#t.head}}
+
|
+
ID |
+
Task Group |
+
Created |
+
Modified |
+
Status |
+
Version |
+
Client |
+
CPU |
+
Memory |
+ {{/t.head}}
+ {{#t.body as |row|}}
+ {{allocation-row
+ data-test-allocation=row.model.id
+ allocation=row.model
+ context="job"
+ onClick=(action "gotoAllocation" row.model)}}
+ {{/t.body}}
+ {{/list-table}}
+ {{else}}
+
+
No Allocations
+
No allocations have been placed.
+
+ {{/if}}
+
+ {{#if job.allocations.length}}
+
+ {{/if}}
+
diff --git a/ui/app/templates/components/job-page/periodic-child.hbs b/ui/app/templates/components/job-page/periodic-child.hbs
index 2accceb84..698397503 100644
--- a/ui/app/templates/components/job-page/periodic-child.hbs
+++ b/ui/app/templates/components/job-page/periodic-child.hbs
@@ -28,4 +28,6 @@
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
+
+ {{job-page/parts/recent-allocations job=job}}
{{/job-page/parts/body}}
diff --git a/ui/app/templates/components/job-page/service.hbs b/ui/app/templates/components/job-page/service.hbs
index da7af5d5d..6a921beb8 100644
--- a/ui/app/templates/components/job-page/service.hbs
+++ b/ui/app/templates/components/job-page/service.hbs
@@ -24,4 +24,6 @@
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
+
+ {{job-page/parts/recent-allocations job=job}}
{{/job-page/parts/body}}
diff --git a/ui/app/templates/components/job-page/system.hbs b/ui/app/templates/components/job-page/system.hbs
index f11a77849..dcd1ed7be 100644
--- a/ui/app/templates/components/job-page/system.hbs
+++ b/ui/app/templates/components/job-page/system.hbs
@@ -22,4 +22,6 @@
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
+
+ {{job-page/parts/recent-allocations job=job}}
{{/job-page/parts/body}}
diff --git a/ui/app/templates/jobs/job/allocations.hbs b/ui/app/templates/jobs/job/allocations.hbs
new file mode 100644
index 000000000..080919b6a
--- /dev/null
+++ b/ui/app/templates/jobs/job/allocations.hbs
@@ -0,0 +1,71 @@
+{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
+ {{partial "jobs/job/subnav"}}
+
+ {{#if allocations.length}}
+
+
+ {{search-box
+ data-test-allocations-search
+ searchTerm=(mut searchTerm)
+ placeholder="Search allocations..."}}
+
+
+ {{#list-pagination
+ source=sortedAllocations
+ size=pageSize
+ page=currentPage
+ class="allocations" as |p|}}
+ {{#list-table
+ source=p.list
+ sortProperty=sortProperty
+ sortDescending=sortDescending
+ class="with-foot" as |t|}}
+ {{#t.head}}
+ |
+ {{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
+ {{#t.sort-by prop="taskGroupName"}}Task Group{{/t.sort-by}}
+ {{#t.sort-by prop="createIndex" title="Create Index"}}Created{{/t.sort-by}}
+ {{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
+ {{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
+ {{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
+ {{#t.sort-by prop="node.shortId"}}Client{{/t.sort-by}}
+ CPU |
+ Memory |
+ {{/t.head}}
+ {{#t.body as |row|}}
+ {{allocation-row
+ data-test-allocation=row.model.id
+ allocation=row.model
+ context="job"
+ onClick=(action "gotoAllocation" row.model)}}
+ {{/t.body}}
+ {{/list-table}}
+
+ {{else}}
+
+
+
No Matches
+
No allocations match the term {{searchTerm}}
+
+
+ {{/list-pagination}}
+ {{else}}
+
+
+
No Allocations
+
No allocations have been placed.
+
+
+ {{/if}}
+
+{{/gutter-menu}}
+
diff --git a/ui/app/templates/jobs/job/evaluations.hbs b/ui/app/templates/jobs/job/evaluations.hbs
index 212b6ea42..0c6e18290 100644
--- a/ui/app/templates/jobs/job/evaluations.hbs
+++ b/ui/app/templates/jobs/job/evaluations.hbs
@@ -1,46 +1,39 @@
{{partial "jobs/job/subnav"}}
-
-
- Evaluations
+ {{#if sortedEvaluations.length}}
+ {{#list-table
+ source=sortedEvaluations
+ sortProperty=sortProperty
+ sortDescending=sortDescending as |t|}}
+ {{#t.head}}
+
ID |
+ {{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
+ {{#t.sort-by prop="triggeredBy"}}Triggered By{{/t.sort-by}}
+ {{#t.sort-by prop="status"}}Status{{/t.sort-by}}
+ {{#t.sort-by prop="hasPlacementFailures"}}Placement Failures{{/t.sort-by}}
+ {{/t.head}}
+ {{#t.body as |row|}}
+
+ {{row.model.shortId}} |
+ {{row.model.priority}} |
+ {{row.model.triggeredBy}} |
+ {{row.model.status}} |
+
+ {{#if (eq row.model.status "blocked")}}
+ N/A - In Progress
+ {{else if row.model.hasPlacementFailures}}
+ True
+ {{else}}
+ False
+ {{/if}}
+ |
+
+ {{/t.body}}
+ {{/list-table}}
+ {{else}}
+
+
No Evaluations
+
This is most likely due to garbage collection.
-
- {{#if sortedEvaluations.length}}
- {{#list-table
- source=sortedEvaluations
- sortProperty=sortProperty
- sortDescending=sortDescending as |t|}}
- {{#t.head}}
-
ID |
- {{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
- {{#t.sort-by prop="triggeredBy"}}Triggered By{{/t.sort-by}}
- {{#t.sort-by prop="status"}}Status{{/t.sort-by}}
- {{#t.sort-by prop="hasPlacementFailures"}}Placement Failures{{/t.sort-by}}
- {{/t.head}}
- {{#t.body as |row|}}
-
- {{row.model.shortId}} |
- {{row.model.priority}} |
- {{row.model.triggeredBy}} |
- {{row.model.status}} |
-
- {{#if (eq row.model.status "blocked")}}
- N/A - In Progress
- {{else if row.model.hasPlacementFailures}}
- True
- {{else}}
- False
- {{/if}}
- |
-
- {{/t.body}}
- {{/list-table}}
- {{else}}
-
-
No Evaluations
-
This is most likely due to garbage collection.
-
- {{/if}}
-
-
+ {{/if}}
diff --git a/ui/app/templates/jobs/job/subnav.hbs b/ui/app/templates/jobs/job/subnav.hbs
index 257072649..fa3738205 100644
--- a/ui/app/templates/jobs/job/subnav.hbs
+++ b/ui/app/templates/jobs/job/subnav.hbs
@@ -6,6 +6,7 @@
{{#if job.supportsDeployments}}
{{#link-to "jobs.job.deployments" job activeClass="is-active"}}Deployments{{/link-to}}
{{/if}}
+
{{#link-to "jobs.job.allocations" job activeClass="is-active"}}Allocations{{/link-to}}
{{#link-to "jobs.job.evaluations" job activeClass="is-active"}}Evaluations{{/link-to}}
diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs
index 0bf01cb84..1c20a03e7 100644
--- a/ui/app/templates/jobs/job/task-group.hbs
+++ b/ui/app/templates/jobs/job/task-group.hbs
@@ -63,8 +63,8 @@
{{#t.head}}
|
{{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
+ {{#t.sort-by prop="createIndex" title="Create Index"}}Created{{/t.sort-by}}
{{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
- {{#t.sort-by prop="name"}}Name{{/t.sort-by}}
{{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
{{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
{{#t.sort-by prop="node.shortId"}}Client{{/t.sort-by}}
@@ -72,7 +72,7 @@
Memory |
{{/t.head}}
{{#t.body as |row|}}
- {{allocation-row data-test-allocation=row.model.id allocation=row.model context="job" onClick=(action "gotoAllocation" row.model)}}
+ {{allocation-row data-test-allocation=row.model.id allocation=row.model context="taskGroup" onClick=(action "gotoAllocation" row.model)}}
{{/t.body}}
{{/list-table}}