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
d89d071b
Unverified
Commit
d89d071b
authored
3 years ago
by
Luiz Aoqui
Browse files
Options
Download
Email Patches
Plain Diff
ui: policy system and sysbatch job details clients tab
parent
2a2902d3
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
ui/app/components/allocation-row.js
+3
-1
ui/app/components/allocation-row.js
ui/app/components/client-row.hbs
+20
-15
ui/app/components/client-row.hbs
ui/app/components/client-row.js
+55
-59
ui/app/components/client-row.js
ui/app/components/task-row.js
+3
-1
ui/app/components/task-row.js
ui/app/controllers/jobs/job/clients.js
+102
-56
ui/app/controllers/jobs/job/clients.js
ui/app/routes/jobs/job/clients.js
+27
-2
ui/app/routes/jobs/job/clients.js
ui/app/styles/components.scss
+1
-0
ui/app/styles/components.scss
ui/app/styles/components/job-client-status-row.scss
+9
-0
ui/app/styles/components/job-client-status-row.scss
ui/app/templates/jobs/job/clients.hbs
+8
-12
ui/app/templates/jobs/job/clients.hbs
ui/mirage/scenarios/sysbatch.js
+6
-0
ui/mirage/scenarios/sysbatch.js
with
234 additions
and
146 deletions
+234
-146
ui/app/components/allocation-row.js
+
3
-
1
View file @
d89d071b
...
...
@@ -65,7 +65,9 @@ export default class AllocationRow extends Component {
do
{
if
(
this
.
stats
)
{
try
{
yield
this
.
get
(
'
stats.poll
'
).
perform
();
yield
this
.
get
(
'
stats.poll
'
)
.
linked
()
.
perform
();
this
.
set
(
'
statsError
'
,
false
);
}
catch
(
error
)
{
this
.
set
(
'
statsError
'
,
true
);
...
...
This diff is collapsed.
Click to expand it.
ui/app/components/client-row.hbs
+
20
-
15
View file @
d89d071b
<tr>
<th
class=
"is-narrow"
></th>
<tr
class=
"job-client-status-row is-interactive"
{{
on
"click"
(
fn
@onClick
this
.
row
.
node
)
}}
>
<td
data-test-short-id
>
<LinkTo
@
route=
"clients.client"
@
model=
{{
this
.
node
.
id
}}
class=
"is-primary"
>
{{
this
.
node
.
shortId
}}
</LinkTo>
<strong>
{{
this
.
row
.
node
.
shortId
}}
</strong>
</td>
<td
data-test-short-id
>
{{
this
.
node
.
name
}}
<td
data-test-short-id
class=
"is-200px is-truncatable"
>
{{
this
.
row
.
node
.
name
}}
</td>
<td
data-test-create-time
>
<span
class=
"tooltip"
aria-label=
"
{{
format-month-ts
this
.
eldestCreateTime
}}
"
>
{{
moment-from-now
this
.
eldestCreateTime
}}
{{#if
this
.
row
.
createTime
}}
<span
class=
"tooltip"
aria-label=
"
{{
format-month-ts
this
.
row
.
createTime
}}
"
>
{{
moment-from-now
this
.
row
.
createTime
}}
</span>
{{else}}
-
{{/if}}
</td>
<td
data-test-modify-time
>
<span
class=
"tooltip"
aria-label=
"
{{
format-month-ts
this
.
node
.
modifyTime
}}
"
>
{{
moment-from-now
this
.
allocation
.
modifyTime
}}
{{#if
this
.
row
.
modifyTime
}}
<span
class=
"tooltip"
aria-label=
"
{{
format-month-ts
this
.
row
.
modifyTime
}}
"
>
{{
moment-from-now
this
.
row
.
modifyTime
}}
</span>
{{else}}
-
{{/if}}
</td>
<td
data-test-client-status
class=
"is-one-line"
>
<span
class=
"color-swatch
{{
this
.
statu
s
}}
"
></span>
{{
this
.
s
tatus
}}
<span
class=
"color-swatch
{{
this
.
jobStatusClas
s
}}
"
></span>
{{
this
.
humanizedJobS
tatus
}}
</td>
<td
data-test-client-allocations
class=
"is-one-line"
>
<td
data-test-client-allocations
class=
"
allocation-summary
is-one-line"
>
{{#if
this
.
shouldDisplayAllocationSummary
}}
<div
class=
"inline-chart"
>
<AllocationStatusBar
@
allocationContainer=
{{
this
.
allocationContainer
}}
@
isNarrow=
{{
true
}}
/>
</div>
{{else}}
Not Scheduled
<div
class=
"is-empty"
>
{{
this
.
allocationSummaryPlaceholder
}}
</div>
{{/if}}
</td>
</tr>
\ No newline at end of file
This diff is collapsed.
Click to expand it.
ui/app/components/client-row.js
+
55
-
59
View file @
d89d071b
import
{
classNames
,
tagName
}
from
'
@ember-decorators/component
'
;
import
EmberObject
from
'
@ember/object
'
;
import
Component
from
'
@glimmer/component
'
;
@
tagName
(
'
tr
'
)
@
classNames
(
'
client-row
'
,
'
is-interactive
'
)
export
default
class
ClientRowComponent
extends
Component
{
get
shouldDisplayAllocationSummary
()
{
return
this
.
status
!==
'
notScheduled
'
;
export
default
class
ClientRow
extends
Component
{
// Attribute set in the template as @onClick.
onClick
()
{}
get
row
()
{
return
this
.
args
.
row
.
model
;
}
get
node
()
{
return
this
.
args
.
node
.
model
;
get
shouldDisplayAllocationSummary
()
{
return
this
.
args
.
row
.
model
.
jobStatus
!==
'
notScheduled
'
;
}
get
eldestCreateTime
()
{
let
eldest
=
null
;
for
(
const
allocation
of
this
.
node
.
id
)
{
if
(
!
eldest
||
allocation
.
createTime
<
eldest
)
{
eldest
=
allocation
.
createTime
;
}
get
allocationSummaryPlaceholder
()
{
switch
(
this
.
args
.
row
.
model
.
jobStatus
)
{
case
'
notScheduled
'
:
return
'
Not Scheduled
'
;
default
:
return
''
;
}
return
eldest
;
}
get
mostRecentModifyTime
()
{
let
mostRecent
=
null
;
for
(
const
allocation
of
this
.
node
.
id
)
{
if
(
!
mostRecent
||
allocation
.
modifyTime
>
mostRecent
)
{
mostRecent
=
allocation
.
createTime
;
}
get
humanizedJobStatus
()
{
switch
(
this
.
args
.
row
.
model
.
jobStatus
)
{
case
'
notScheduled
'
:
return
'
not scheduled
'
;
default
:
return
this
.
args
.
row
.
model
.
jobStatus
;
}
return
mostRecent
;
}
get
status
()
{
return
this
.
args
.
jobClientStatus
.
byNode
[
this
.
node
.
id
];
get
jobStatusClass
()
{
switch
(
this
.
args
.
row
.
model
.
jobStatus
)
{
case
'
notScheduled
'
:
return
'
not-scheduled
'
;
default
:
return
this
.
args
.
row
.
model
.
jobStatus
;
}
}
get
allocationContainer
()
{
...
...
@@ -45,46 +49,38 @@ export default class ClientRowComponent extends Component {
startingAllocs
:
0
,
lostAllocs
:
0
,
};
// query by allocations for job then group by node use the mapBy method
if
(
this
.
status
===
'
notScheduled
'
)
return
EmberObject
.
create
(...
statusSummary
);
const
allocsByNodeID
=
{};
this
.
args
.
allocations
.
forEach
(
a
=>
{
const
nodeId
=
a
.
node
.
get
(
'
id
'
);
if
(
!
allocsByNodeID
[
nodeId
])
{
allocsByNodeID
[
nodeId
]
=
[];
}
allocsByNodeID
[
nodeId
].
push
(
a
);
});
for
(
const
allocation
of
allocsByNodeID
[
this
.
node
.
id
])
{
if
(
this
.
status
===
'
queued
'
)
{
statusSummary
.
queuedAllocs
=
allocsByNodeID
[
this
.
node
.
id
].
length
;
switch
(
this
.
args
.
row
.
model
.
jobStatus
)
{
case
'
notSchedule
'
:
break
;
}
else
if
(
this
.
status
===
'
starting
'
)
{
statusSummary
.
starting
Allocs
=
allocsByNodeID
[
this
.
node
.
id
]
.
length
;
case
'
queued
'
:
statusSummary
.
queued
Allocs
=
this
.
args
.
row
.
model
.
allocations
.
length
;
break
;
}
else
if
(
this
.
status
===
'
notScheduled
'
)
{
case
'
starting
'
:
statusSummary
.
startingAllocs
=
this
.
args
.
row
.
model
.
allocations
.
length
;
break
;
}
const
{
clientStatus
}
=
allocation
;
switch
(
clientStatus
)
{
case
'
running
'
:
statusSummary
.
runningAllocs
++
;
break
;
case
'
lost
'
:
statusSummary
.
lostAllocs
++
;
break
;
case
'
failed
'
:
statusSummary
.
failedAllocs
++
;
break
;
case
'
complete
'
:
statusSummary
.
completeAllocs
++
;
break
;
case
'
starting
'
:
statusSummary
.
startingAllocs
++
;
break
;
}
default
:
for
(
const
alloc
of
this
.
args
.
row
.
model
.
allocations
)
{
switch
(
alloc
.
clientStatus
)
{
case
'
running
'
:
statusSummary
.
runningAllocs
++
;
break
;
case
'
lost
'
:
statusSummary
.
lostAllocs
++
;
break
;
case
'
failed
'
:
statusSummary
.
failedAllocs
++
;
break
;
case
'
complete
'
:
statusSummary
.
completeAllocs
++
;
break
;
case
'
starting
'
:
statusSummary
.
startingAllocs
++
;
break
;
}
}
}
const
Allocations
=
EmberObject
.
extend
({
...
statusSummary
,
});
...
...
This diff is collapsed.
Click to expand it.
ui/app/components/task-row.js
+
3
-
1
View file @
d89d071b
...
...
@@ -54,7 +54,9 @@ export default class TaskRow extends Component {
do
{
if
(
this
.
stats
)
{
try
{
yield
this
.
get
(
'
stats.poll
'
).
perform
();
yield
this
.
get
(
'
stats.poll
'
)
.
linked
()
.
perform
();
this
.
set
(
'
statsError
'
,
false
);
}
catch
(
error
)
{
this
.
set
(
'
statsError
'
,
true
);
...
...
This diff is collapsed.
Click to expand it.
ui/app/controllers/jobs/job/clients.js
+
102
-
56
View file @
d89d071b
/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */
import
Controller
from
'
@ember/controller
'
;
import
{
action
,
computed
}
from
'
@ember/object
'
;
import
{
scheduleOnce
}
from
'
@ember/runloop
'
;
import
intersection
from
'
lodash.intersection
'
;
import
{
alias
}
from
'
@ember/object/computed
'
;
import
Sortable
from
'
nomad-ui/mixins/sortable
'
;
import
Sortable
Factory
from
'
nomad-ui/mixins/sortable
-factory
'
;
import
Searchable
from
'
nomad-ui/mixins/searchable
'
;
import
WithNamespaceResetting
from
'
nomad-ui/mixins/with-namespace-resetting
'
;
import
jobClientStatus
from
'
nomad-ui/utils/properties/job-client-status
'
;
...
...
@@ -12,7 +13,7 @@ import classic from 'ember-classic-decorator';
@
classic
export
default
class
ClientsController
extends
Controller
.
extend
(
Sortable
,
Sortable
Factory
([
'
id
'
,
'
name
'
,
'
jobStatus
'
])
,
Searchable
,
WithNamespaceResetting
)
{
...
...
@@ -40,51 +41,50 @@ export default class ClientsController extends Controller.extend(
},
];
currentPage
=
1
;
pageSize
=
25
;
sortProperty
=
'
modifyIndex
'
;
sortDescending
=
true
;
@
computed
get
searchProps
()
{
return
[
'
id
'
,
'
name
'
,
'
taskGroupName
'
];
}
qpStatus
=
''
;
qpDatacenter
=
''
;
qpNodeClass
=
''
;
currentPage
=
1
;
pageSize
=
25
;
sortProperty
=
'
jobStatus
'
;
sortDescending
=
false
;
@
selection
(
'
qpStatus
'
)
selectionStatus
;
@
selection
(
'
qpDatacenter
'
)
selectionDatacenter
;
@
selection
(
'
qpNodeClass
'
)
selectionNodeClass
;
@
computed
get
optionsStatus
()
{
return
[
{
key
:
'
queued
'
,
label
:
'
Queued
'
},
{
key
:
'
notScheduled
'
,
label
:
'
Not Scheduled
'
},
{
key
:
'
starting
'
,
label
:
'
Starting
'
},
{
key
:
'
running
'
,
label
:
'
Running
'
},
{
key
:
'
complete
'
,
label
:
'
Complete
'
},
{
key
:
'
degraded
'
,
label
:
'
Degraded
'
},
{
key
:
'
failed
'
,
label
:
'
Failed
'
},
{
key
:
'
lost
'
,
label
:
'
Lost
'
},
];
}
@
alias
(
'
model
'
)
job
;
@
jobClientStatus
(
'
n
odes
'
,
'
job
'
)
jobClientStatus
;
@
jobClientStatus
(
'
allN
odes
'
,
'
job
'
)
jobClientStatus
;
@
alias
(
'
filteredNodes
'
)
listToSort
;
@
alias
(
'
listSorted
'
)
listToSearch
;
@
alias
(
'
listSearched
'
)
sortedClients
;
get
nodes
()
{
@
computed
get
allNodes
()
{
return
this
.
store
.
peekAll
(
'
node
'
);
}
@
computed
(
'
nodes
'
,
'
selectionStatus
'
,
'
selectionDatacenter
'
,
'
selectionNodeClass
'
)
@
computed
(
'
allNodes
'
)
get
nodes
()
{
return
this
.
allNodes
.
filter
(
node
=>
this
.
jobClientStatus
.
byNode
[
node
.
id
]);
}
@
computed
get
searchProps
()
{
return
[
'
node.id
'
,
'
node.name
'
];
}
@
computed
(
'
nodes
'
,
'
job.allocations
'
,
'
jobClientStatus.byNode
'
,
'
selectionStatus
'
,
'
selectionDatacenter
'
,
'
selectionNodeClass
'
)
get
filteredNodes
()
{
const
{
selectionStatus
:
statuses
,
...
...
@@ -92,43 +92,69 @@ export default class ClientsController extends Controller.extend(
selectionNodeClass
:
nodeclasses
,
}
=
this
;
return
this
.
nodes
.
filter
(
node
=>
{
if
(
statuses
.
length
&&
!
statuses
.
includes
(
this
.
jobClientStatus
.
byNode
[
node
.
id
]))
{
return
false
;
}
if
(
datacenters
.
length
&&
!
datacenters
.
includes
(
node
.
datacenter
))
{
return
false
;
}
if
(
nodeclasses
.
length
&&
!
nodeclasses
.
includes
(
node
.
nodeClass
))
{
return
false
;
}
return
true
;
});
return
this
.
nodes
.
filter
(
node
=>
{
if
(
statuses
.
length
&&
!
statuses
.
includes
(
this
.
jobClientStatus
.
byNode
[
node
.
id
]))
{
return
false
;
}
if
(
datacenters
.
length
&&
!
datacenters
.
includes
(
node
.
datacenter
))
{
return
false
;
}
if
(
nodeclasses
.
length
&&
!
nodeclasses
.
includes
(
node
.
nodeClass
))
{
return
false
;
}
return
true
;
})
.
map
(
node
=>
{
const
allocations
=
this
.
job
.
allocations
.
filter
(
alloc
=>
alloc
.
get
(
'
node.id
'
)
==
node
.
id
);
return
{
node
,
jobStatus
:
this
.
jobClientStatus
.
byNode
[
node
.
id
],
allocations
,
createTime
:
eldestCreateTime
(
allocations
),
modifyTime
:
mostRecentModifyTime
(
allocations
),
};
});
}
@
computed
(
'
selectionDatacenter
'
,
'
job.datacenters
'
)
@
computed
get
optionsJobStatus
()
{
return
[
{
key
:
'
queued
'
,
label
:
'
Queued
'
},
{
key
:
'
notScheduled
'
,
label
:
'
Not Scheduled
'
},
{
key
:
'
starting
'
,
label
:
'
Starting
'
},
{
key
:
'
running
'
,
label
:
'
Running
'
},
{
key
:
'
complete
'
,
label
:
'
Complete
'
},
{
key
:
'
degraded
'
,
label
:
'
Degraded
'
},
{
key
:
'
failed
'
,
label
:
'
Failed
'
},
{
key
:
'
lost
'
,
label
:
'
Lost
'
},
];
}
@
computed
(
'
selectionDatacenter
'
,
'
nodes
'
)
get
optionsDatacenter
()
{
// eslint-disable-next-line ember/no-incorrect-calls-with-inline-anonymous-functions
const
datacenters
=
Array
.
from
(
new
Set
(
this
.
nodes
.
mapBy
(
'
datacenter
'
))).
compact
();
// Update query param when the list of datacenters changes.
scheduleOnce
(
'
actions
'
,
()
=>
{
// eslint-disable-next-line ember/no-side-effects
this
.
set
(
this
.
qpDatacenter
,
serialize
(
intersection
(
this
.
job
.
datacenters
,
this
.
selectionDatacenter
))
);
this
.
set
(
'
qpDatacenter
'
,
serialize
(
intersection
(
datacenters
,
this
.
selectionDatacenter
)));
});
return
this
.
job
.
datacenters
.
sort
().
map
(
dc
=>
({
key
:
dc
,
label
:
dc
}));
return
datacenters
.
sort
().
map
(
dc
=>
({
key
:
dc
,
label
:
dc
}));
}
@
computed
(
'
selectionNodeClass
'
,
'
nodes
'
)
get
optionsNodeClass
()
{
const
nodeClasses
=
Array
.
from
(
new
Set
(
this
.
nodes
.
mapBy
(
'
nodeClass
'
)));
// // eslint-disable-next-line ember/no-incorrect-calls-with-inline-anonymous-functions
// scheduleOnce('actions', () => {
// // eslint-disable-next-line ember/no-side-effects
// this.set(this.qpNodeClass, serialize(intersection(nodeClasses, this.selectionNodeClass)));
// });
const
nodeClasses
=
Array
.
from
(
new
Set
(
this
.
nodes
.
mapBy
(
'
nodeClass
'
))).
compact
();
// Update query param when the list of datacenters changes.
scheduleOnce
(
'
actions
'
,
()
=>
{
// eslint-disable-next-line ember/no-side-effects
this
.
set
(
'
qpNodeClass
'
,
serialize
(
intersection
(
nodeClasses
,
this
.
selectionNodeClass
)));
});
return
nodeClasses
.
sort
().
map
(
nodeClass
=>
({
key
:
nodeClass
,
label
:
nodeClass
}));
}
...
...
@@ -142,3 +168,23 @@ export default class ClientsController extends Controller.extend(
this
.
set
(
queryParam
,
serialize
(
selection
));
}
}
function
eldestCreateTime
(
allocations
)
{
let
eldest
=
null
;
for
(
const
alloc
of
allocations
)
{
if
(
!
eldest
||
alloc
.
createTime
<
eldest
)
{
eldest
=
alloc
.
createTime
;
}
}
return
eldest
;
}
function
mostRecentModifyTime
(
allocations
)
{
let
mostRecent
=
null
;
for
(
const
alloc
of
allocations
)
{
if
(
!
mostRecent
||
alloc
.
modifyTime
>
mostRecent
)
{
mostRecent
=
alloc
.
modifyTime
;
}
}
return
mostRecent
;
}
This diff is collapsed.
Click to expand it.
ui/app/routes/jobs/job/clients.js
+
27
-
2
View file @
d89d071b
import
Route
from
'
@ember/routing/route
'
;
import
WithWatchers
from
'
nomad-ui/mixins/with-watchers
'
;
import
{
watchRecord
,
watchRelationship
,
watchAll
}
from
'
nomad-ui/utils/properties/watch
'
;
import
{
collect
}
from
'
@ember/object/computed
'
;
export
default
class
ClientsRoute
extends
Route
{
// TODO: add watcher for nodes.
export
default
class
ClientsRoute
extends
Route
.
extend
(
WithWatchers
)
{
async
model
()
{
await
this
.
store
.
findAll
(
'
node
'
);
return
this
.
modelFor
(
'
jobs.job
'
);
}
startWatchers
(
controller
,
model
)
{
if
(
!
model
)
{
return
;
}
controller
.
set
(
'
watchers
'
,
{
model
:
this
.
watch
.
perform
(
model
),
allocations
:
this
.
watchAllocations
.
perform
(
model
),
nodes
:
this
.
watchNodes
.
perform
(),
});
}
@
watchRecord
(
'
job
'
)
watch
;
@
watchAll
(
'
node
'
)
watchNodes
;
@
watchRelationship
(
'
allocations
'
)
watchAllocations
;
@
collect
(
'
watch
'
,
'
watchNodes
'
,
'
watchAllocations
'
)
watchers
;
}
This diff is collapsed.
Click to expand it.
ui/app/styles/components.scss
+
1
-
0
View file @
d89d071b
...
...
@@ -21,6 +21,7 @@
@import
'./components/gutter-toggle'
;
@import
'./components/image-file.scss'
;
@import
'./components/inline-definitions'
;
@import
'./components/job-client-status-row'
;
@import
'./components/job-diff'
;
@import
'./components/json-viewer'
;
@import
'./components/legend'
;
...
...
This diff is collapsed.
Click to expand it.
ui/app/styles/components/job-client-status-row.scss
0 → 100644
+
9
-
0
View file @
d89d071b
.job-client-status-row
{
.allocation-summary
{
.is-empty
{
color
:
darken
(
$grey-blue
,
20%
);
text-align
:
center
;
font-style
:
italic
;
}
}
}
This diff is collapsed.
Click to expand it.
ui/app/templates/jobs/job/clients.hbs
+
8
-
12
View file @
d89d071b
...
...
@@ -15,8 +15,8 @@
<div
class=
"button-bar"
>
<MultiSelectDropdown
data-test-status-facet
@
label=
"Status"
@
options=
{{
this
.
optionsStatus
}}
@
label=
"
Job
Status"
@
options=
{{
this
.
options
Job
Status
}}
@
selection=
{{
this
.
selectionStatus
}}
@
onSelect=
{{
action
this
.
setFacetQueryParam
"qpStatus"
}}
/>
...
...
@@ -51,20 +51,19 @@
@
class=
"with-foot"
as
|
t
|
>
<t.head>
<th
class=
"is-narrow"
></th>
<t.sort-by
@
prop=
"id"
>
Client ID
</t.sort-by>
<t.sort-by
@
prop=
"name"
>
<t.sort-by
@
prop=
"name"
class=
"is-200px is-truncatable"
>
Client Name
</t.sort-by>
<t.sort-by
@
prop=
"create
Index
"
@
title=
"Create
Index
"
>
<t.sort-by
@
prop=
"create
Time
"
@
title=
"Create
Time
"
>
Created
</t.sort-by>
<t.sort-by
@
prop=
"modify
Index
"
@
title=
"Modify
Index
"
>
<t.sort-by
@
prop=
"modify
Time
"
@
title=
"Modify
Time
"
>
Modified
</t.sort-by>
<t.sort-by
@
prop=
"
s
tatus
Index
"
>
<t.sort-by
@
prop=
"
jobS
tatus"
>
Job Status
</t.sort-by>
<th
class=
"is-3"
>
...
...
@@ -74,11 +73,8 @@
<t.body
as
|
row
|
>
<ClientRow
@
data-test-client=
{{
"sdf"
}}
@
jobClientStatus=
{{
this
.
jobClientStatus
}}
@
allocations=
{{
this
.
job
.
allocations
}}
@
node=
{{
row
}}
@
context=
"job"
@
onClick=
{{
action
"gotoClient"
row
}}
@
row=
{{
row
}}
@
onClick=
{{
this
.
gotoClient
}}
/>
</t.body>
</ListTable>
...
...
This diff is collapsed.
Click to expand it.
ui/mirage/scenarios/sysbatch.js
+
6
-
0
View file @
d89d071b
...
...
@@ -5,6 +5,12 @@ export function sysbatchSmall(server) {
status
:
'
ready
'
,
});
// Create some clients not targeted by the sysbatch job.
server
.
createList
(
'
node
'
,
3
,
{
datacenter
:
'
dc3
'
,
status
:
'
ready
'
,
});
// Job with 1 task group.
const
job1
=
server
.
create
(
'
job
'
,
{
status
:
'
running
'
,
...
...
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