diff --git a/ui/app/components/job-editor.js b/ui/app/components/job-editor.js new file mode 100644 index 000000000..7464742cf --- /dev/null +++ b/ui/app/components/job-editor.js @@ -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); + }, +}); diff --git a/ui/app/controllers/jobs/run.js b/ui/app/controllers/jobs/run.js index 79525e460..baaf84183 100644 --- a/ui/app/controllers/jobs/run.js +++ b/ui/app/controllers/jobs/run.js @@ -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 }, + }); }, }); diff --git a/ui/app/templates/components/job-editor.hbs b/ui/app/templates/components/job-editor.hbs new file mode 100644 index 000000000..250934bc7 --- /dev/null +++ b/ui/app/templates/components/job-editor.hbs @@ -0,0 +1,92 @@ +{{#if parseError}} +
+

Parse Error

+

{{parseError}}

+
+{{/if}} +{{#if planError}} +
+

Plan Error

+

{{planError}}

+
+{{/if}} +{{#if runError}} +
+

Run Error

+

{{runError}}

+
+{{/if}} + +{{#if (eq stage "editor")}} + {{#if showEditorMessage}} +
+
+
+

Run a Job

+

Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.

+
+
+ +
+
+
+ {{/if}} +
+
+ Job Definition +
+
+ {{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 + )}} +
+
+
+ +
+{{/if}} + +{{#if (eq stage "plan")}} + {{#if showPlanMessage}} +
+
+
+

Job Plan

+

This is the impact running this job will have on your cluster.

+
+
+ +
+
+
+ {{/if}} +
+
Job Plan
+
+ {{job-diff data-test-plan-output diff=planOutput.diff verbose=false}} +
+
+
+
Scheduler dry-run
+
+ {{#if planOutput.failedTGAllocs}} + {{#each planOutput.failedTGAllocs as |placementFailure|}} + {{placement-failure failedTGAlloc=placementFailure}} + {{/each}} + {{else}} + All tasks successfully allocated. + {{/if}} +
+
+
+ + +
+{{/if}} diff --git a/ui/app/templates/jobs/run.hbs b/ui/app/templates/jobs/run.hbs index 9039cae7f..52400b413 100644 --- a/ui/app/templates/jobs/run.hbs +++ b/ui/app/templates/jobs/run.hbs @@ -1,94 +1,3 @@
- {{#if parseError}} -
-

Parse Error

-

{{parseError}}

-
- {{/if}} - {{#if planError}} -
-

Plan Error

-

{{planError}}

-
- {{/if}} - {{#if runError}} -
-

Run Error

-

{{runError}}

-
- {{/if}} - - {{#if (eq stage "editor")}} - {{#if showEditorMessage}} -
-
-
-

Run a Job

-

Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.

-
-
- -
-
-
- {{/if}} -
-
- Job Definition -
-
- {{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 - )}} -
-
-
- -
- {{/if}} - - {{#if (eq stage "plan")}} - {{#if showPlanMessage}} -
-
-
-

Job Plan

-

This is the impact running this job will have on your cluster.

-
-
- -
-
-
- {{/if}} -
-
Job Plan
-
- {{job-diff data-test-plan-output diff=planOutput.diff verbose=false}} -
-
-
-
Scheduler dry-run
-
- {{#if planOutput.failedTGAllocs}} - {{#each planOutput.failedTGAllocs as |placementFailure|}} - {{placement-failure failedTGAlloc=placementFailure}} - {{/each}} - {{else}} - All tasks successfully allocated. - {{/if}} -
-
-
- - -
- {{/if}} + {{job-editor job=model onSubmit=(action onSubmit)}}