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

UI: Server-side reschedule tracking #4254

Merged
merged 16 commits into from
May 10, 2018
Merged
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
11 changes: 9 additions & 2 deletions ui/app/components/allocation-row.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,11 @@ export default Component.extend({
const allocation = this.get('allocation');

if (allocation) {
this.get('fetchStats').perform(allocation);
run.scheduleOnce('afterRender', this, qualifyAllocation);
} else {
this.get('fetchStats').cancelAll();
this.set('stats', null);
}
run.scheduleOnce('afterRender', this, qualifyJob);
},

fetchStats: task(function*(allocation) {
Expand All @@ -84,6 +83,14 @@ export default Component.extend({
}).drop(),
});

function qualifyAllocation() {
const allocation = this.get('allocation');
return allocation.reload().then(() => {
this.get('fetchStats').perform(allocation);
run.scheduleOnce('afterRender', this, qualifyJob);
});
}

function qualifyJob() {
const allocation = this.get('allocation');
if (allocation.get('originalJobId')) {
Expand Down
20 changes: 20 additions & 0 deletions ui/app/components/reschedule-event-row.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';

export default Component.extend({
store: service(),
tagName: '',

// When given a string, the component will fetch the allocation
allocationId: null,

// An allocation can also be provided directly
allocation: computed('allocationId', function() {
return this.get('store').findRecord('allocation', this.get('allocationId'));
}),

time: null,
linkToAllocation: true,
label: '',
});
25 changes: 25 additions & 0 deletions ui/app/models/allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export default Model.extend({
return STATUS_ORDER[this.get('clientStatus')] || 100;
}),

// When allocations are server-side rescheduled, a paper trail
// is left linking all reschedule attempts.
previousAllocation: belongsTo('allocation', { inverse: 'nextAllocation' }),
nextAllocation: belongsTo('allocation', { inverse: 'previousAllocation' }),

followUpEvaluation: belongsTo('evaluation'),

statusClass: computed('clientStatus', function() {
const classMap = {
pending: 'is-pending',
Expand Down Expand Up @@ -67,4 +74,22 @@ export default Model.extend({
},

states: fragmentArray('task-state'),
rescheduleEvents: fragmentArray('reschedule-event'),

hasRescheduleEvents: computed('rescheduleEvents.length', 'nextAllocation', function() {
return this.get('rescheduleEvents.length') > 0 || this.get('nextAllocation');
}),

hasStoppedRescheduling: computed(
'nextAllocation',
'clientStatus',
'followUpEvaluation',
function() {
return (
!this.get('nextAllocation.content') &&
!this.get('followUpEvaluation.content') &&
this.get('clientStatus') === 'failed'
);
}
),
});
2 changes: 2 additions & 0 deletions ui/app/models/evaluation.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ export default Model.extend({
job: belongsTo('job'),

modifyIndex: attr('number'),

waitUntil: attr('date'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want this as a string and then parse it out when you need to use it? Fine if the answer is "no", just wanted to call out that date parsing on models is (unless they changed that recently) eager (which could be totally fine here).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been trying to do the things the expected way until it becomes a problem. Since evaluations don't get loaded by the thousands, I think this is okay here.

});
2 changes: 1 addition & 1 deletion ui/app/models/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ export default Model.extend({
return this.get('httpAddr') == null;
}),

allocations: hasMany('allocations'),
allocations: hasMany('allocations', { inverse: 'node' }),
});
16 changes: 16 additions & 0 deletions ui/app/models/reschedule-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Fragment from 'ember-data-model-fragments/fragment';
import attr from 'ember-data/attr';
import { fragmentOwner } from 'ember-data-model-fragments/attributes';
import shortUUIDProperty from '../utils/properties/short-uuid';

export default Fragment.extend({
allocation: fragmentOwner(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh TIL about fragmentOwner


previousAllocationId: attr('string'),
previousNodeId: attr('string'),
time: attr('date'),
delay: attr('string'),

previousAllocationShortId: shortUUIDProperty('previousAllocationId'),
previousNodeShortId: shortUUIDProperty('previousNodeShortId'),
});
7 changes: 7 additions & 0 deletions ui/app/serializers/allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export default ApplicationSerializer.extend({
hash.ModifyTimeNanos = hash.ModifyTime % 1000000;
hash.ModifyTime = Math.floor(hash.ModifyTime / 1000000);

hash.RescheduleEvents = (hash.RescheduleTracker || {}).Events;

// API returns empty strings instead of null
hash.PreviousAllocationID = hash.PreviousAllocation ? hash.PreviousAllocation : null;
hash.NextAllocationID = hash.NextAllocation ? hash.NextAllocation : null;
hash.FollowUpEvaluationID = hash.FollowupEvalID ? hash.FollowupEvalID : null;

return this._super(typeHash, hash);
},
});
17 changes: 17 additions & 0 deletions ui/app/serializers/reschedule-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import ApplicationSerializer from './application';

export default ApplicationSerializer.extend({
normalize(typeHash, hash) {
// Time is in the form of nanoseconds since epoch, but JS dates
// only understand time to the millisecond precision. So store
// the time (precise to ms) as a date, and store the remaining ns
// as a number to deal with when it comes up.
hash.TimeNanos = hash.RescheduleTime % 1000000;
hash.Time = Math.floor(hash.RescheduleTime / 1000000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌 nice solution


hash.PreviousAllocationId = hash.PrevAllocID ? hash.PrevAllocID : null;
hash.PreviousNodeId = hash.PrevNodeID ? hash.PrevNodeID : null;

return this._super(typeHash, hash);
},
});
9 changes: 9 additions & 0 deletions ui/app/styles/components/boxed-section.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
background: $white;
}

&.is-hollow {
border-bottom: none;
background: transparent;

& + .boxed-section-body {
border-top: none;
}
}

& + .boxed-section-body {
padding: 1.5em;
border-top-left-radius: 0;
Expand Down
4 changes: 4 additions & 0 deletions ui/app/styles/core/icon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ $icon-dimensions-large: 2rem;
width: $icon-dimensions-large;
}

&.is-faded {
fill: $grey-light;
}

@each $name, $pair in $colors {
$color: nth($pair, 1);

Expand Down
16 changes: 10 additions & 6 deletions ui/app/styles/core/table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,23 @@
width: 25%;
}

&.is-truncatable {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

&.is-narrow {
padding: 1.25em 0 1.25em 0.5em;
}

// Only use px modifiers when text needs to be truncated.
// In this and only this scenario are percentages not effective.
&.is-200px {
width: 200px;
max-width: 200px;
}

&.is-truncatable {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

@for $i from 1 through 11 {
&.is-#{$i} {
width: 100% / 12 * $i;
Expand Down
11 changes: 11 additions & 0 deletions ui/app/templates/allocations/allocation/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,16 @@
{{/list-table}}
</div>
</div>

{{#if model.hasRescheduleEvents}}
<div class="boxed-section" data-test-reschedule-events>
<div class="boxed-section-head is-hollow">
Reschedule Events
</div>
<div class="boxed-section-body">
{{reschedule-event-timeline allocation=model}}
</div>
</div>
{{/if}}
</section>
{{/gutter-menu}}
1 change: 1 addition & 0 deletions ui/app/templates/clients/client.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
sortDescending=sortDescending
class="with-foot" as |t|}}
{{#t.head}}
<th class="is-narrow"></th>
{{#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}}
Expand Down
7 changes: 7 additions & 0 deletions ui/app/templates/components/allocation-row.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
<td data-test-indicators class="is-narrow">
{{#if allocation.nextAllocation}}
<span class="tooltip text-center" aria-label="Allocation was rescheduled">
{{x-icon "history" class="is-faded"}}
</span>
{{/if}}
</td>
<td data-test-short-id>
{{#link-to "allocations.allocation" allocation class="is-primary"}}
{{allocation.shortId}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
source=deployment.allocations
class="allocations" as |t|}}
{{#t.head}}
<th class="is-narrow"></th>
<th>ID</th>
<th>Modified</th>
<th>Name</th>
Expand Down
46 changes: 46 additions & 0 deletions ui/app/templates/components/reschedule-event-row.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<li class="timeline-note">
{{#if label}}
<strong data-test-reschedule-label>{{label}}</strong>
{{/if}}
{{moment-format time "MMMM D, YYYY HH:mm:ss"}}
</li>
<li class="timeline-object" data-test-allocation={{allocation.id}}>
<div class="boxed-section">
{{#unless linkToAllocation}}
<div class="boxed-section-head" data-test-row-heading>
This Allocation
</div>
{{/unless}}
<div class="boxed-section-body inline-definitions">
<div class="columns">
<div class="column is-centered is-minimum">
<span data-test-allocation-status class="tag {{allocation.statusClass}}">
{{allocation.clientStatus}}
</span>
</div>
<div class="column">
<div class="boxed-section-row">
<span class="pair">
<span class="term">Allocation</span>
{{#if linkToAllocation}}
{{#link-to "allocations.allocation" allocation.id}}
<code data-test-allocation-link>{{allocation.shortId}}</code>
{{/link-to}}
{{else}}
<code data-test-allocation-link>{{allocation.shortId}}</code>
{{/if}}
</span>
<span class="pair">
<span class="term">Client</span>
<span>
{{#link-to "clients.client" data-test-node-link allocation.node.id}}
<code>{{allocation.node.id}}</code>
{{/link-to}}
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</li>
29 changes: 29 additions & 0 deletions ui/app/templates/components/reschedule-event-timeline.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<ol class="timeline">
{{#if allocation.nextAllocation}}
{{reschedule-event-row
label="Next Allocation"
allocation=allocation.nextAllocation
time=allocation.nextAllocation.modifyTime}}
{{/if}}
{{#if allocation.hasStoppedRescheduling}}
<li class="timeline-note" data-test-stop-warning>
{{x-icon "warning" class="is-warning is-text"}} Nomad has stopped attempting to reschedule this allocation.
</li>
{{/if}}
{{#if (and allocation.followUpEvaluation.waitUntil (not allocation.nextAllocation))}}
<li class="timeline-note" data-test-attempt-notice>
{{x-icon "clock" class="is-info is-text"}} Nomad will attempt to reschedule
{{moment-from-now allocation.followUpEvaluation.waitUntil interval=1000}}
</li>
{{/if}}
{{reschedule-event-row
allocation=allocation
linkToAllocation=false
time=allocation.modifyTime}}

{{#each (reverse allocation.rescheduleEvents) as |event|}}
{{reschedule-event-row
allocationId=event.previousAllocationId
time=event.time}}
{{/each}}
</ol>
3 changes: 2 additions & 1 deletion ui/app/templates/jobs/job/task-group.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
sortDescending=sortDescending
class="with-foot" as |t|}}
{{#t.head}}
<th class="is-narrow"></th>
{{#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}}
Expand All @@ -83,7 +84,7 @@
<th>Memory</th>
{{/t.head}}
{{#t.body as |row|}}
{{allocation-row data-test-allocation allocation=row.model context="job" onClick=(action "gotoAllocation" row.model)}}
{{allocation-row data-test-allocation=row.model.id allocation=row.model context="job" onClick=(action "gotoAllocation" row.model)}}
{{/t.body}}
{{/list-table}}
<div class="table-foot">
Expand Down
Loading