Move the bulk of the new job page into a new job editor component
This commit is contained in:
parent
55fca36bea
commit
b3c2538c68
70
ui/app/components/job-editor.js
Normal file
70
ui/app/components/job-editor.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import { task } from 'ember-concurrency';
|
||||
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
|
||||
import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
|
||||
|
||||
export default Component.extend({
|
||||
store: service(),
|
||||
|
||||
job: null,
|
||||
onSubmit() {},
|
||||
|
||||
parseError: null,
|
||||
planError: null,
|
||||
runError: null,
|
||||
|
||||
planOutput: null,
|
||||
|
||||
showPlanMessage: localStorageProperty('nomadMessageJobPlan', true),
|
||||
showEditorMessage: localStorageProperty('nomadMessageJobEditor', true),
|
||||
|
||||
stage: computed('planOutput', function() {
|
||||
return this.get('planOutput') ? 'plan' : 'editor';
|
||||
}),
|
||||
|
||||
plan: task(function*() {
|
||||
this.reset();
|
||||
|
||||
try {
|
||||
yield this.get('job').parse();
|
||||
} catch (err) {
|
||||
const error = messageFromAdapterError(err) || 'Could not parse input';
|
||||
this.set('parseError', error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.get('job').plan();
|
||||
const plan = this.get('store').peekRecord('job-plan', this.get('job.id'));
|
||||
this.set('planOutput', plan);
|
||||
} catch (err) {
|
||||
const error = messageFromAdapterError(err) || 'Could not plan job';
|
||||
this.set('planError', error);
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
submit: task(function*() {
|
||||
try {
|
||||
yield this.get('job').run();
|
||||
|
||||
const id = this.get('job.plainId');
|
||||
const namespace = this.get('job.namespace.name') || 'default';
|
||||
|
||||
this.reset();
|
||||
|
||||
// Treat the job as ephemeral and only provide ID parts.
|
||||
this.get('onSubmit')(id, namespace);
|
||||
} catch (err) {
|
||||
const error = messageFromAdapterError(err) || 'Could not submit job';
|
||||
this.set('runError', error);
|
||||
}
|
||||
}),
|
||||
|
||||
reset() {
|
||||
this.set('planOutput', null);
|
||||
this.set('planError', null);
|
||||
this.set('parseError', null);
|
||||
},
|
||||
});
|
|
@ -1,69 +1,9 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import { task } from 'ember-concurrency';
|
||||
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
|
||||
import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
|
||||
|
||||
export default Controller.extend({
|
||||
store: service(),
|
||||
|
||||
parseError: null,
|
||||
planError: null,
|
||||
runError: null,
|
||||
|
||||
planOutput: null,
|
||||
|
||||
showPlanMessage: localStorageProperty('nomadMessageJobPlan', true),
|
||||
showEditorMessage: localStorageProperty('nomadMessageJobEditor', true),
|
||||
|
||||
stage: computed('planOutput', function() {
|
||||
return this.get('planOutput') ? 'plan' : 'editor';
|
||||
}),
|
||||
|
||||
plan: task(function*() {
|
||||
this.reset();
|
||||
|
||||
try {
|
||||
yield this.get('model').parse();
|
||||
} catch (err) {
|
||||
const error = messageFromAdapterError(err) || 'Could not parse input';
|
||||
this.set('parseError', error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.get('model').plan();
|
||||
const plan = this.get('store').peekRecord('job-plan', this.get('model.id'));
|
||||
this.set('planOutput', plan);
|
||||
} catch (err) {
|
||||
const error = messageFromAdapterError(err) || 'Could not plan job';
|
||||
this.set('planError', error);
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
submit: task(function*() {
|
||||
try {
|
||||
yield this.get('model').run();
|
||||
|
||||
const id = this.get('model.plainId');
|
||||
const namespace = this.get('model.namespace.name') || 'default';
|
||||
|
||||
this.reset();
|
||||
|
||||
// navigate to the new job page
|
||||
this.transitionToRoute('jobs.job', id, {
|
||||
queryParams: { jobNamespace: namespace },
|
||||
});
|
||||
} catch (err) {
|
||||
const error = messageFromAdapterError(err) || 'Could not submit job';
|
||||
this.set('runError', error);
|
||||
}
|
||||
}),
|
||||
|
||||
reset() {
|
||||
this.set('planOutput', null);
|
||||
this.set('planError', null);
|
||||
this.set('parseError', null);
|
||||
onSubmit(id, namespace) {
|
||||
this.transitionToRoute('jobs.job', id, {
|
||||
queryParams: { jobNamespace: namespace },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
92
ui/app/templates/components/job-editor.hbs
Normal file
92
ui/app/templates/components/job-editor.hbs
Normal file
|
@ -0,0 +1,92 @@
|
|||
{{#if parseError}}
|
||||
<div data-test-parse-error class="notification is-danger">
|
||||
<h3 class="title is-4" data-test-parse-error-title>Parse Error</h3>
|
||||
<p data-test-parse-error-message>{{parseError}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if planError}}
|
||||
<div data-test-plan-error class="notification is-danger">
|
||||
<h3 class="title is-4" data-test-plan-error-title>Plan Error</h3>
|
||||
<p data-test-plan-error-message>{{planError}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if runError}}
|
||||
<div data-test-run-error class="notification is-danger">
|
||||
<h3 class="title is-4" data-test-run-error-title>Run Error</h3>
|
||||
<p data-test-run-error-message>{{runError}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq stage "editor")}}
|
||||
{{#if showEditorMessage}}
|
||||
<div class="notification is-info">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4" data-test-editor-help-title>Run a Job</h3>
|
||||
<p data-test-editor-help-message>Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-info" onclick={{toggle-action "showEditorMessage" this}} data-test-editor-help-dismiss>Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Job Definition
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{ivy-codemirror
|
||||
data-test-editor
|
||||
value=(or job._newDefinition jobSpec)
|
||||
valueUpdated=(action (mut job._newDefinition))
|
||||
options=(hash
|
||||
mode="javascript"
|
||||
theme="hashi"
|
||||
tabSize=2
|
||||
lineNumbers=true
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content is-associative">
|
||||
<button class="button is-primary {{if plan.isRunning "is-loading"}}" type="button" onclick={{perform plan}} disabled={{or plan.isRunning (not job._newDefinition)}} data-test-plan>Plan</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq stage "plan")}}
|
||||
{{#if showPlanMessage}}
|
||||
<div class="notification is-info">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4" data-test-plan-help-title>Job Plan</h3>
|
||||
<p data-test-plan-help-message>This is the impact running this job will have on your cluster.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-info" onclick={{toggle-action "showPlanMessage" this}} data-test-plan-help-dismiss>Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">Job Plan</div>
|
||||
<div class="boxed-section-body is-dark">
|
||||
{{job-diff data-test-plan-output diff=planOutput.diff verbose=false}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section {{if planOutput.failedTGAllocs "is-warning" "is-primary"}}" data-test-dry-run-message>
|
||||
<div class="boxed-section-head" data-test-dry-run-title>Scheduler dry-run</div>
|
||||
<div class="boxed-section-body" data-test-dry-run-body>
|
||||
{{#if planOutput.failedTGAllocs}}
|
||||
{{#each planOutput.failedTGAllocs as |placementFailure|}}
|
||||
{{placement-failure failedTGAlloc=placementFailure}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
All tasks successfully allocated.
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content is-associative">
|
||||
<button class="button is-primary {{if submit.isRunning "is-loading"}}" type="button" onclick={{perform submit}} disabled={{submit.isRunning}} data-test-run>Run</button>
|
||||
<button class="button is-light" type="button" onclick={{action reset}} data-test-cancel>Cancel</button>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,94 +1,3 @@
|
|||
<section class="section">
|
||||
{{#if parseError}}
|
||||
<div data-test-parse-error class="notification is-danger">
|
||||
<h3 class="title is-4" data-test-parse-error-title>Parse Error</h3>
|
||||
<p data-test-parse-error-message>{{parseError}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if planError}}
|
||||
<div data-test-plan-error class="notification is-danger">
|
||||
<h3 class="title is-4" data-test-plan-error-title>Plan Error</h3>
|
||||
<p data-test-plan-error-message>{{planError}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if runError}}
|
||||
<div data-test-run-error class="notification is-danger">
|
||||
<h3 class="title is-4" data-test-run-error-title>Run Error</h3>
|
||||
<p data-test-run-error-message>{{runError}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq stage "editor")}}
|
||||
{{#if showEditorMessage}}
|
||||
<div class="notification is-info">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4" data-test-editor-help-title>Run a Job</h3>
|
||||
<p data-test-editor-help-message>Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-info" onclick={{toggle-action "showEditorMessage" this}} data-test-editor-help-dismiss>Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Job Definition
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{ivy-codemirror
|
||||
data-test-editor
|
||||
value=(or model._newDefinition jobSpec)
|
||||
valueUpdated=(action (mut model._newDefinition))
|
||||
options=(hash
|
||||
mode="javascript"
|
||||
theme="hashi"
|
||||
tabSize=2
|
||||
lineNumbers=true
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content is-associative">
|
||||
<button class="button is-primary {{if plan.isRunning "is-loading"}}" type="button" onclick={{perform plan}} disabled={{or plan.isRunning (not model._newDefinition)}} data-test-plan>Plan</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq stage "plan")}}
|
||||
{{#if showPlanMessage}}
|
||||
<div class="notification is-info">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4" data-test-plan-help-title>Job Plan</h3>
|
||||
<p data-test-plan-help-message>This is the impact running this job will have on your cluster.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-info" onclick={{toggle-action "showPlanMessage" this}} data-test-plan-help-dismiss>Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">Job Plan</div>
|
||||
<div class="boxed-section-body is-dark">
|
||||
{{job-diff data-test-plan-output diff=planOutput.diff verbose=false}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section {{if planOutput.failedTGAllocs "is-warning" "is-primary"}}" data-test-dry-run-message>
|
||||
<div class="boxed-section-head" data-test-dry-run-title>Scheduler dry-run</div>
|
||||
<div class="boxed-section-body" data-test-dry-run-body>
|
||||
{{#if planOutput.failedTGAllocs}}
|
||||
{{#each planOutput.failedTGAllocs as |placementFailure|}}
|
||||
{{placement-failure failedTGAlloc=placementFailure}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
All tasks successfully allocated.
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content is-associative">
|
||||
<button class="button is-primary {{if submit.isRunning "is-loading"}}" type="button" onclick={{perform submit}} disabled={{submit.isRunning}} data-test-run>Run</button>
|
||||
<button class="button is-light" type="button" onclick={{action reset}} data-test-cancel>Cancel</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{job-editor job=model onSubmit=(action onSubmit)}}
|
||||
</section>
|
||||
|
|
Loading…
Reference in a new issue