Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
小 白蛋
Nomad
Commits
cf7675a3
Unverified
Commit
cf7675a3
authored
3 years ago
by
Luiz Aoqui
Committed by
GitHub
3 years ago
Browse files
Options
Download
Email Patches
Plain Diff
ui: set the job namespace when redirecting after the job is dispatched (#11141)
parent
d891a716
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
.changelog/11141.txt
+3
-0
.changelog/11141.txt
ui/app/components/job-dispatch.js
+3
-1
ui/app/components/job-dispatch.js
ui/app/templates/components/job-page/parts/title.hbs
+1
-1
ui/app/templates/components/job-page/parts/title.hbs
ui/tests/acceptance/job-dispatch-test.js
+173
-153
ui/tests/acceptance/job-dispatch-test.js
ui/tests/integration/components/job-page/periodic-test.js
+1
-1
ui/tests/integration/components/job-page/periodic-test.js
ui/tests/pages/jobs/detail.js
+2
-0
ui/tests/pages/jobs/detail.js
with
183 additions
and
156 deletions
+183
-156
.changelog/11141.txt
0 → 100644
+
3
-
0
View file @
cf7675a3
```release-note:bug
ui: Fixed an issue when dispatching jobs from a non-default namespace
```
This diff is collapsed.
Click to expand it.
ui/app/components/job-dispatch.js
+
3
-
1
View file @
cf7675a3
...
...
@@ -98,7 +98,9 @@ export default class JobDispatch extends Component {
const
dispatch
=
yield
this
.
args
.
job
.
dispatch
(
paramValues
,
this
.
payload
);
// Navigate to the newly created instance.
this
.
router
.
transitionTo
(
'
jobs.job
'
,
dispatch
.
DispatchedJobID
);
this
.
router
.
transitionTo
(
'
jobs.job
'
,
dispatch
.
DispatchedJobID
,
{
queryParams
:
{
namespace
:
this
.
args
.
job
.
get
(
'
namespace.name
'
)
},
});
}
catch
(
err
)
{
const
error
=
messageFromAdapterError
(
err
)
||
'
Could not dispatch job
'
;
this
.
errors
.
pushObject
(
error
);
...
...
This diff is collapsed.
Click to expand it.
ui/app/templates/components/job-page/parts/title.hbs
+
1
-
1
View file @
cf7675a3
<h1
class=
"title with-flex"
>
<div>
<div
data-test-job-name
>
{{
or
this
.
title
this
.
job
.
name
}}
<span
class=
"bumper-left tag
{{
this
.
job
.
statusClass
}}
"
data-test-job-status
>
{{
this
.
job
.
status
}}
</span>
{{
yield
}}
...
...
This diff is collapsed.
Click to expand it.
ui/tests/acceptance/job-dispatch-test.js
+
173
-
153
View file @
cf7675a3
/* eslint-disable ember/no-test-module-for */
import
{
module
,
test
}
from
'
qunit
'
;
import
{
setupApplicationTest
}
from
'
ember-qunit
'
;
import
{
setupMirage
}
from
'
ember-cli-mirage/test-support
'
;
...
...
@@ -9,197 +10,216 @@ import { currentURL } from '@ember/test-helpers';
const
REQUIRED_INDICATOR
=
'
*
'
;
let
job
,
namespace
,
managementToken
,
clientToken
;
moduleForJobDispatch
(
'
Acceptance | job dispatch
'
,
()
=>
{
server
.
createList
(
'
namespace
'
,
2
);
const
namespace
=
server
.
db
.
namespaces
[
0
];
module
(
'
Acceptance | job dispatch
'
,
function
(
hooks
)
{
setupApplicationTest
(
hooks
);
setupCodeMirror
(
hooks
);
setupMirage
(
hooks
);
hooks
.
beforeEach
(
function
()
{
// Required for placing allocations (a result of dispatching jobs)
server
.
create
(
'
node
'
);
server
.
createList
(
'
namespace
'
,
2
);
namespace
=
server
.
db
.
namespaces
[
0
];
job
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
});
managementToken
=
server
.
create
(
'
token
'
);
clientToken
=
server
.
create
(
'
token
'
);
window
.
localStorage
.
nomadTokenSecret
=
managementToken
.
secretId
;
return
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
});
});
test
(
'
it passes an accessibility audit
'
,
async
function
(
assert
)
{
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
a11yAudit
(
assert
);
});
moduleForJobDispatch
(
'
Acceptance | job dispatch (with namespace)
'
,
()
=>
{
server
.
createList
(
'
namespace
'
,
2
);
const
namespace
=
server
.
db
.
namespaces
[
1
];
test
(
'
the dispatch button is displayed with management token
'
,
async
function
(
assert
)
{
await
JobDetail
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
notOk
(
JobDetail
.
dispatchButton
.
isDisabled
);
return
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
});
});
test
(
'
the dispatch button is displayed when allowed
'
,
async
function
(
assert
)
{
window
.
localStorage
.
nomadTokenSecret
=
clientToken
.
secretId
;
const
policy
=
server
.
create
(
'
policy
'
,
{
id
:
'
dispatch
'
,
name
:
'
dispatch
'
,
rulesJSON
:
{
Namespaces
:
[
{
Name
:
namespace
.
name
,
Capabilities
:
[
'
list-jobs
'
,
'
dispatch-job
'
],
},
],
},
});
clientToken
.
policyIds
=
[
policy
.
id
];
clientToken
.
save
();
await
JobDetail
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
notOk
(
JobDetail
.
dispatchButton
.
isDisabled
);
function
moduleForJobDispatch
(
title
,
jobFactory
)
{
let
job
,
namespace
,
managementToken
,
clientToken
;
// Reset clientToken policies.
clientToken
.
policyIds
=
[]
;
clientToken
.
save
(
);
}
);
module
(
title
,
function
(
hooks
)
{
setupApplicationTest
(
hooks
)
;
setupCodeMirror
(
hooks
);
setupMirage
(
hooks
);
test
(
'
the dispatch button is disabled when not allowed
'
,
async
function
(
assert
)
{
window
.
localStorage
.
nomadTokenSecret
=
clientToken
.
secretId
;
hooks
.
beforeEach
(
function
()
{
// Required for placing allocations (a result of dispatching jobs)
server
.
create
(
'
node
'
);
await
JobDetail
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
ok
(
JobDetail
.
dispatchButton
.
isDisabled
);
});
job
=
jobFactory
();
namespace
=
server
.
db
.
namespaces
.
find
(
job
.
namespaceId
);
test
(
'
all meta fields are displayed
'
,
async
function
(
assert
)
{
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
equal
(
JobDispatch
.
metaFields
.
length
,
job
.
parameterizedJob
.
MetaOptional
.
length
+
job
.
parameterizedJob
.
MetaRequired
.
length
);
});
managementToken
=
server
.
create
(
'
token
'
);
clientToken
=
server
.
create
(
'
token
'
);
test
(
'
required meta fields are properly indicated
'
,
async
function
(
assert
)
{
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
window
.
localStorage
.
nomadTokenSecret
=
managementToken
.
secretId
;
});
JobDispatch
.
metaFields
.
forEach
(
f
=>
{
const
hasIndicator
=
f
.
label
.
includes
(
REQUIRED_INDICATOR
);
const
isRequired
=
job
.
parameterizedJob
.
MetaRequired
.
includes
(
f
.
field
.
id
);
test
(
'
it passes an accessibility audit
'
,
async
function
(
assert
)
{
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
a11yAudit
(
assert
);
});
if
(
isRequired
)
{
assert
.
ok
(
hasIndicator
,
`
${
f
.
label
}
contains required indicator.`
);
}
else
{
assert
.
notOk
(
hasIndicator
,
`
${
f
.
label
}
doesn't contain required indicator.`
);
}
test
(
'
the dispatch button is displayed with management token
'
,
async
function
(
assert
)
{
await
JobDetail
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
notOk
(
JobDetail
.
dispatchButton
.
isDisabled
);
});
});
test
(
'
job without meta fields
'
,
async
function
(
assert
)
{
const
jobWithoutMeta
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
parameterizedJob
:
{
MetaRequired
:
null
,
MetaOptional
:
null
,
},
test
(
'
the dispatch button is displayed when allowed
'
,
async
function
(
assert
)
{
window
.
localStorage
.
nomadTokenSecret
=
clientToken
.
secretId
;
const
policy
=
server
.
create
(
'
policy
'
,
{
id
:
'
dispatch
'
,
name
:
'
dispatch
'
,
rulesJSON
:
{
Namespaces
:
[
{
Name
:
namespace
.
name
,
Capabilities
:
[
'
list-jobs
'
,
'
dispatch-job
'
],
},
],
},
});
clientToken
.
policyIds
=
[
policy
.
id
];
clientToken
.
save
();
await
JobDetail
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
notOk
(
JobDetail
.
dispatchButton
.
isDisabled
);
// Reset clientToken policies.
clientToken
.
policyIds
=
[];
clientToken
.
save
();
});
await
JobDispatch
.
visit
({
id
:
jobWithoutMeta
.
id
,
namespace
:
namespace
.
name
});
assert
.
ok
(
JobDispatch
.
dispatchButton
.
isPresent
);
});
test
(
'
the dispatch button is disabled when not allowed
'
,
async
function
(
assert
)
{
window
.
localStorage
.
nomadTokenSecret
=
clientToken
.
secretId
;
test
(
'
payload text area is hidden when forbidden
'
,
async
function
(
assert
)
{
job
.
parameterizedJob
.
Payload
=
'
forbidden
'
;
job
.
save
(
);
await
JobDetail
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
ok
(
JobDetail
.
dispatchButton
.
isDisabled
)
;
}
);
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
test
(
'
all meta fields are displayed
'
,
async
function
(
assert
)
{
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
assert
.
equal
(
JobDispatch
.
metaFields
.
length
,
job
.
parameterizedJob
.
MetaOptional
.
length
+
job
.
parameterizedJob
.
MetaRequired
.
length
);
});
assert
.
ok
(
JobDispatch
.
payload
.
emptyMessage
.
isPresent
);
assert
.
notOk
(
JobDispatch
.
payload
.
editor
.
isPresent
);
});
test
(
'
required meta fields are properly indicated
'
,
async
function
(
assert
)
{
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
test
(
'
payload is indicated as required
'
,
async
function
(
assert
)
{
const
jobPayloadRequired
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
parameterizedJob
:
{
Payload
:
'
required
'
,
},
JobDispatch
.
metaFields
.
forEach
(
f
=>
{
const
hasIndicator
=
f
.
label
.
includes
(
REQUIRED_INDICATOR
);
const
isRequired
=
job
.
parameterizedJob
.
MetaRequired
.
includes
(
f
.
field
.
id
);
if
(
isRequired
)
{
assert
.
ok
(
hasIndicator
,
`
${
f
.
label
}
contains required indicator.`
);
}
else
{
assert
.
notOk
(
hasIndicator
,
`
${
f
.
label
}
doesn't contain required indicator.`
);
}
});
});
const
jobPayloadOptional
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
parameterizedJob
:
{
Payload
:
'
optional
'
,
},
test
(
'
job without meta fields
'
,
async
function
(
assert
)
{
const
jobWithoutMeta
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
parameterizedJob
:
{
MetaRequired
:
null
,
MetaOptional
:
null
,
},
});
await
JobDispatch
.
visit
({
id
:
jobWithoutMeta
.
id
,
namespace
:
namespace
.
name
});
assert
.
ok
(
JobDispatch
.
dispatchButton
.
isPresent
);
});
await
JobDispatch
.
visit
({
id
:
jobPayloadRequired
.
id
,
namespace
:
namespace
.
name
});
test
(
'
payload text area is hidden when forbidden
'
,
async
function
(
assert
)
{
job
.
parameterizedJob
.
Payload
=
'
forbidden
'
;
job
.
save
();
let
payloadTitle
=
JobDispatch
.
payload
.
title
;
assert
.
ok
(
payloadTitle
.
includes
(
REQUIRED_INDICATOR
),
`
${
payloadTitle
}
contains required indicator.`
);
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
JobDispatch
.
visit
({
id
:
jobPayloadOptional
.
id
,
namespace
:
namespace
.
name
});
assert
.
ok
(
JobDispatch
.
payload
.
emptyMessage
.
isPresent
);
assert
.
notOk
(
JobDispatch
.
payload
.
editor
.
isPresent
);
});
payloadTitle
=
JobDispatch
.
payload
.
title
;
assert
.
notOk
(
payloadTitle
.
includes
(
REQUIRED_INDICATOR
),
`
${
payloadTitle
}
doesn't contain required indicator.`
);
});
test
(
'
payload is indicated as required
'
,
async
function
(
assert
)
{
const
jobPayloadRequired
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
parameterizedJob
:
{
Payload
:
'
required
'
,
},
});
const
jobPayloadOptional
=
server
.
create
(
'
job
'
,
'
parameterized
'
,
{
status
:
'
running
'
,
namespaceId
:
namespace
.
name
,
parameterizedJob
:
{
Payload
:
'
optional
'
,
},
});
await
JobDispatch
.
visit
({
id
:
jobPayloadRequired
.
id
,
namespace
:
namespace
.
name
});
let
payloadTitle
=
JobDispatch
.
payload
.
title
;
assert
.
ok
(
payloadTitle
.
includes
(
REQUIRED_INDICATOR
),
`
${
payloadTitle
}
contains required indicator.`
);
await
JobDispatch
.
visit
({
id
:
jobPayloadOptional
.
id
,
namespace
:
namespace
.
name
});
payloadTitle
=
JobDispatch
.
payload
.
title
;
assert
.
notOk
(
payloadTitle
.
includes
(
REQUIRED_INDICATOR
),
`
${
payloadTitle
}
doesn't contain required indicator.`
);
});
test
(
'
dispatch a job
'
,
async
function
(
assert
)
{
function
countDispatchChildren
()
{
return
server
.
db
.
jobs
.
where
(
j
=>
j
.
id
.
startsWith
(
`
${
job
.
id
}
/`
)).
length
;
}
test
(
'
dispatch a job
'
,
async
function
(
assert
)
{
function
countDispatchChildren
()
{
return
server
.
db
.
jobs
.
where
(
j
=>
j
.
id
.
startsWith
(
`
${
job
.
id
}
/`
)).
length
;
}
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
// Fill form.
JobDispatch
.
metaFields
.
map
(
f
=>
f
.
field
.
input
(
'
meta value
'
));
JobDispatch
.
payload
.
editor
.
fillIn
(
'
payload
'
);
// Fill form.
JobDispatch
.
metaFields
.
map
(
f
=>
f
.
field
.
input
(
'
meta value
'
));
JobDispatch
.
payload
.
editor
.
fillIn
(
'
payload
'
);
const
childrenCountBefore
=
countDispatchChildren
();
await
JobDispatch
.
dispatchButton
.
click
();
const
childrenCountAfter
=
countDispatchChildren
();
const
childrenCountBefore
=
countDispatchChildren
();
await
JobDispatch
.
dispatchButton
.
click
();
const
childrenCountAfter
=
countDispatchChildren
();
assert
.
equal
(
childrenCountAfter
,
childrenCountBefore
+
1
);
assert
.
ok
(
currentURL
().
startsWith
(
`/jobs/
${
encodeURIComponent
(
`
${
job
.
id
}
/`
)}
`
));
});
assert
.
equal
(
childrenCountAfter
,
childrenCountBefore
+
1
);
assert
.
ok
(
currentURL
().
startsWith
(
`/jobs/
${
encodeURIComponent
(
`
${
job
.
id
}
/`
)}
`
));
assert
.
ok
(
JobDetail
.
jobName
);
});
test
(
'
fail when required meta field is empty
'
,
async
function
(
assert
)
{
// Make sure we have a required meta param.
job
.
parameterizedJob
.
MetaRequired
=
[
'
required
'
];
job
.
parameterizedJob
.
Payload
=
'
forbidden
'
;
job
.
save
();
test
(
'
fail when required meta field is empty
'
,
async
function
(
assert
)
{
// Make sure we have a required meta param.
job
.
parameterizedJob
.
MetaRequired
=
[
'
required
'
];
job
.
parameterizedJob
.
Payload
=
'
forbidden
'
;
job
.
save
();
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
// Fill only optional meta params.
JobDispatch
.
optionalMetaFields
.
map
(
f
=>
f
.
field
.
input
(
'
meta value
'
));
// Fill only optional meta params.
JobDispatch
.
optionalMetaFields
.
map
(
f
=>
f
.
field
.
input
(
'
meta value
'
));
await
JobDispatch
.
dispatchButton
.
click
();
await
JobDispatch
.
dispatchButton
.
click
();
assert
.
ok
(
JobDispatch
.
hasError
,
'
Dispatch error message is shown
'
);
});
assert
.
ok
(
JobDispatch
.
hasError
,
'
Dispatch error message is shown
'
);
});
test
(
'
fail when required payload is empty
'
,
async
function
(
assert
)
{
job
.
parameterizedJob
.
MetaRequired
=
[];
job
.
parameterizedJob
.
Payload
=
'
required
'
;
job
.
save
();
test
(
'
fail when required payload is empty
'
,
async
function
(
assert
)
{
job
.
parameterizedJob
.
MetaRequired
=
[];
job
.
parameterizedJob
.
Payload
=
'
required
'
;
job
.
save
();
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
JobDispatch
.
dispatchButton
.
click
();
await
JobDispatch
.
visit
({
id
:
job
.
id
,
namespace
:
namespace
.
name
});
await
JobDispatch
.
dispatchButton
.
click
();
assert
.
ok
(
JobDispatch
.
hasError
,
'
Dispatch error message is shown
'
);
assert
.
ok
(
JobDispatch
.
hasError
,
'
Dispatch error message is shown
'
);
});
});
}
);
}
This diff is collapsed.
Click to expand it.
ui/tests/integration/components/job-page/periodic-test.js
+
1
-
1
View file @
cf7675a3
...
...
@@ -75,7 +75,7 @@ module('Integration | Component | job-page/periodic', function(hooks) {
const
currentJobCount
=
server
.
db
.
jobs
.
length
;
assert
.
equal
(
findAll
(
'
[data-test-job-name]
'
).
length
,
findAll
(
'
[data-test-job-row]
[data-test-job-name]
'
).
length
,
childrenCount
,
'
The new periodic job launch is in the children list
'
);
...
...
This diff is collapsed.
Click to expand it.
ui/tests/pages/jobs/detail.js
+
2
-
0
View file @
cf7675a3
...
...
@@ -17,6 +17,8 @@ import recommendationAccordion from 'nomad-ui/tests/pages/components/recommendat
export
default
create
({
visit
:
visitable
(
'
/jobs/:id
'
),
jobName
:
text
(
'
[data-test-job-name]
'
),
tabs
:
collection
(
'
[data-test-tab]
'
,
{
id
:
attribute
(
'
data-test-tab
'
),
visit
:
clickable
(
'
a
'
),
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment