Skip to content

Commit

Permalink
[ui] Time-based scheduling for tasks (#20597)
Browse files Browse the repository at this point in the history
* Fragment and init

* 4-state 2-button alert box

* Small cleanup
  • Loading branch information
philrenaud authored May 22, 2024
1 parent 6b43c8f commit 256fd99
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 1 deletion.
23 changes: 23 additions & 0 deletions ui/app/adapters/allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ export default class AllocationAdapter extends Watchable {
.then(handleFSResponse);
}

// note: TaskName vs Task as key for PUT data differs from restart above
forcePause(allocation, taskName) {
const prefix = `${this.host || '/'}${this.urlPrefix()}`;
const url = `${prefix}/client/allocation/${allocation.id}/pause`;
return this.ajax(url, 'PUT', {
data: taskName && { Task: taskName, ScheduleState: 'pause' },
});
}
forceRun(allocation, taskName) {
const prefix = `${this.host || '/'}${this.urlPrefix()}`;
const url = `${prefix}/client/allocation/${allocation.id}/pause`;
return this.ajax(url, 'PUT', {
data: taskName && { Task: taskName, ScheduleState: 'run' },
});
}
reEnableSchedule(allocation, taskName) {
const prefix = `${this.host || '/'}${this.urlPrefix()}`;
const url = `${prefix}/client/allocation/${allocation.id}/pause`;
return this.ajax(url, 'PUT', {
data: taskName && { Task: taskName, ScheduleState: 'scheduled' },
});
}

async check(model) {
const res = await this.token.authorizedRequest(
`/v1/client/allocation/${model.id}/checks`
Expand Down
48 changes: 48 additions & 0 deletions ui/app/controllers/allocations/allocation/task/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,52 @@ export default class IndexController extends Controller {
this.nomadActions.hasActionPermissions
);
}

@task(function* () {
try {
yield this.model.forcePause();
this.notifications.add({
title: 'Task Force Paused',
message: 'Task has been force paused',
color: 'success',
});
} catch (err) {
this.set('error', {
title: 'Could Not Force Pause Task',
});
}
})
forcePause;

@task(function* () {
try {
yield this.model.forceRun();
this.notifications.add({
title: 'Task Force Run',
message: 'Task has been force run',
color: 'success',
});
} catch (err) {
this.set('error', {
title: 'Could Not Force Run Task',
});
}
})
forceRun;

@task(function* () {
try {
yield this.model.reEnableSchedule();
this.notifications.add({
title: 'Task Put Back On Schedule',
message: 'Task has been put back on its configured schedule',
color: 'success',
});
} catch (err) {
this.set('error', {
title: 'Could Not put back on schedule',
});
}
})
reEnableSchedule;
}
10 changes: 10 additions & 0 deletions ui/app/models/allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,14 @@ export default class Allocation extends Model {
stat(path) {
return this.store.adapterFor('allocation').stat(this, path);
}

forcePause(taskName) {
return this.store.adapterFor('allocation').forcePause(this, taskName);
}
forceRun(taskName) {
return this.store.adapterFor('allocation').forceRun(this, taskName);
}
reEnableSchedule(taskName) {
return this.store.adapterFor('allocation').reEnableSchedule(this, taskName);
}
}
13 changes: 13 additions & 0 deletions ui/app/models/task-schedule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import Fragment from 'ember-data-model-fragments/fragment';
import { attr } from '@ember-data/model';
import { fragmentOwner } from 'ember-data-model-fragments/attributes';

export default class TaskScheduleModel extends Fragment {
@fragmentOwner('task') task;
@attr() cron;
}
13 changes: 13 additions & 0 deletions ui/app/models/task-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class TaskState extends Fragment {
@attr('date') startedAt;
@attr('date') finishedAt;
@attr('boolean') failed;
@attr() paused;

@and('isActive', 'allocation.isRunning') isRunning;

Expand Down Expand Up @@ -70,4 +71,16 @@ export default class TaskState extends Fragment {
restart() {
return this.allocation.restart(this.name);
}

forcePause() {
return this.allocation.forcePause(this.name);
}

forceRun() {
return this.allocation.forceRun(this.name);
}

reEnableSchedule() {
return this.allocation.reEnableSchedule(this.name);
}
}
2 changes: 2 additions & 0 deletions ui/app/models/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export default class Task extends Fragment {

@attr() meta;

@fragment('task-schedule') schedule;

@computed('taskGroup.mergedMeta', 'meta')
get mergedMeta() {
return {
Expand Down
33 changes: 33 additions & 0 deletions ui/app/styles/core/notifications.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,36 @@ section.notifications {
}
}
}

.time-based-alert {
.hds-alert__content {
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
gap: 1rem;
align-items: start;
}

.hds-alert__text {
grid-column: 1 / 2;
grid-row: 1 / 2;
}

.hds-alert__actions {
grid-column: 1 / 2;
grid-row: 2 / 3;
}

.hds-code-block {
grid-column: 2 / 3;
grid-row: 1 / 3;
justify-self: end;
align-self: start;
}

.hds-code-block {
pre {
background-color: unset;
}
}
}
31 changes: 30 additions & 1 deletion ui/app/templates/allocations/allocation/task/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,35 @@
{{/if}}
</PH.Actions>
</Hds::PageHeader>
{{#if this.model.task.schedule}}
<Hds::Alert @type="inline" @icon="delay" @color="highlight" class="time-based-alert" as |A|>
{{#if (eq this.model.paused '')}}
<A.Title>This task is currently running on schedule</A.Title>
<A.Description>This task is running as per the defined schedule.</A.Description>
<A.Button @text="Force Pause" @color="secondary" {{on "click" (perform this.forcePause)}} />
<A.Button @text="Remove from Schedule" @color="secondary" {{on "click" (perform this.forceRun)}} />
{{else if (eq this.model.paused 'scheduled_pause')}}
<A.Title>This task is currently paused on schedule</A.Title>
<A.Description>This task is paused and will resume on the next scheduled run.</A.Description>
<A.Button @text="Force Run" @color="secondary" {{on "click" (perform this.forceRun)}} />
<A.Button @text="Remove from Schedule" @color="secondary" {{on "click" (perform this.forcePause)}} />
{{else if (eq this.model.paused 'force_pause')}}
<A.Title>This task is manually paused</A.Title>
<A.Description>This task has been paused manually and is not following the schedule.</A.Description>
<A.Button @text="Force Run" @color="secondary" {{on "click" (perform this.forceRun)}} />
<A.Button @text="Put Back on Schedule" @color="secondary" {{on "click" (perform this.reEnableSchedule)}} />
{{else if (eq this.model.paused 'force_run')}}
<A.Title>This task is manually running</A.Title>
<A.Description>This task is running manually and is not following the schedule.</A.Description>
<A.Button @text="Force Pause" @color="secondary" {{on "click" (perform this.forcePause)}} />
<A.Button @text="Put Back on Schedule" @color="secondary" {{on "click" (perform this.reEnableSchedule)}} />
{{/if}}
<A.Generic>
<Hds::CodeBlock @value={{stringify-object this.model.task.schedule.cron}} @hasLineNumbers={{false}} @language="hcl" />
</A.Generic>
</Hds::Alert>
<Hds::Separator />
{{/if}}
<div class="boxed-section is-small">
<div class="boxed-section-body inline-definitions">
<span class="label">
Expand Down Expand Up @@ -258,4 +287,4 @@
</ListTable>
</div>
</div>
</section>
</section>

0 comments on commit 256fd99

Please sign in to comment.