ui: prettify js files
This commit is contained in:
parent
3cf01877a0
commit
3a9057a89c
|
@ -14,7 +14,11 @@ module.exports = {
|
|||
shippedProposals: true,
|
||||
useBuiltIns: 'usage',
|
||||
corejs: '3',
|
||||
targets: ['last 1 Chrome versions', 'last 1 Firefox versions', 'last 1 Safari versions'],
|
||||
targets: [
|
||||
'last 1 Chrome versions',
|
||||
'last 1 Firefox versions',
|
||||
'last 1 Safari versions',
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
|
@ -27,7 +31,10 @@ module.exports = {
|
|||
],
|
||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
['@babel/plugin-proposal-object-rest-spread', { loose: true, useBuiltIns: true }],
|
||||
[
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
{ loose: true, useBuiltIns: true },
|
||||
],
|
||||
'babel-plugin-macros',
|
||||
['emotion', { sourceMap: true, autoLabel: true }],
|
||||
[
|
||||
|
|
|
@ -26,17 +26,26 @@ export default class Abstract extends Ability {
|
|||
get rulesForNamespace() {
|
||||
let namespace = this._namespace;
|
||||
|
||||
return (this.get('token.selfTokenPolicies') || []).toArray().reduce((rules, policy) => {
|
||||
let policyNamespaces = get(policy, 'rulesJSON.Namespaces') || [];
|
||||
return (this.get('token.selfTokenPolicies') || [])
|
||||
.toArray()
|
||||
.reduce((rules, policy) => {
|
||||
let policyNamespaces = get(policy, 'rulesJSON.Namespaces') || [];
|
||||
|
||||
let matchingNamespace = this._findMatchingNamespace(policyNamespaces, namespace);
|
||||
let matchingNamespace = this._findMatchingNamespace(
|
||||
policyNamespaces,
|
||||
namespace
|
||||
);
|
||||
|
||||
if (matchingNamespace) {
|
||||
rules.push(policyNamespaces.find((namespace) => namespace.Name === matchingNamespace));
|
||||
}
|
||||
if (matchingNamespace) {
|
||||
rules.push(
|
||||
policyNamespaces.find(
|
||||
(namespace) => namespace.Name === matchingNamespace
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}, []);
|
||||
return rules;
|
||||
}, []);
|
||||
}
|
||||
|
||||
@computed('token.selfTokenPolicies.[]')
|
||||
|
@ -44,9 +53,11 @@ export default class Abstract extends Ability {
|
|||
return (this.get('token.selfTokenPolicies') || [])
|
||||
.toArray()
|
||||
.reduce((allCapabilities, policy) => {
|
||||
(get(policy, 'rulesJSON.Namespaces') || []).forEach(({ Capabilities }) => {
|
||||
allCapabilities = allCapabilities.concat(Capabilities);
|
||||
});
|
||||
(get(policy, 'rulesJSON.Namespaces') || []).forEach(
|
||||
({ Capabilities }) => {
|
||||
allCapabilities = allCapabilities.concat(Capabilities);
|
||||
}
|
||||
);
|
||||
return allCapabilities;
|
||||
}, []);
|
||||
}
|
||||
|
@ -76,12 +87,16 @@ export default class Abstract extends Ability {
|
|||
return namespace;
|
||||
}
|
||||
|
||||
let globNamespaceNames = namespaceNames.filter((namespaceName) => namespaceName.includes('*'));
|
||||
let globNamespaceNames = namespaceNames.filter((namespaceName) =>
|
||||
namespaceName.includes('*')
|
||||
);
|
||||
|
||||
let matchingNamespaceName = globNamespaceNames.reduce(
|
||||
(mostMatching, namespaceName) => {
|
||||
// Convert * wildcards to .* for regex matching
|
||||
let namespaceNameRegExp = new RegExp(namespaceName.replace(/\*/g, '.*'));
|
||||
let namespaceNameRegExp = new RegExp(
|
||||
namespaceName.replace(/\*/g, '.*')
|
||||
);
|
||||
let characterDifference = namespace.length - namespaceName.length;
|
||||
|
||||
if (
|
||||
|
@ -96,7 +111,10 @@ export default class Abstract extends Ability {
|
|||
return mostMatching;
|
||||
}
|
||||
},
|
||||
{ mostMatchingNamespaceName: null, mostMatchingCharacterDifference: Number.MAX_SAFE_INTEGER }
|
||||
{
|
||||
mostMatchingNamespaceName: null,
|
||||
mostMatchingCharacterDifference: Number.MAX_SAFE_INTEGER,
|
||||
}
|
||||
).mostMatchingNamespaceName;
|
||||
|
||||
if (matchingNamespaceName) {
|
||||
|
|
|
@ -3,7 +3,11 @@ import { computed, get } from '@ember/object';
|
|||
import { or } from '@ember/object/computed';
|
||||
|
||||
export default class Client extends AbstractAbility {
|
||||
@or('bypassAuthorization', 'selfTokenIsManagement', 'policiesIncludeAgentReadOrWrite')
|
||||
@or(
|
||||
'bypassAuthorization',
|
||||
'selfTokenIsManagement',
|
||||
'policiesIncludeAgentReadOrWrite'
|
||||
)
|
||||
canRead;
|
||||
|
||||
@computed('token.selfTokenPolicies.[]')
|
||||
|
|
|
@ -10,17 +10,26 @@ export default class Client extends AbstractAbility {
|
|||
@or('bypassAuthorization', 'selfTokenIsManagement', 'policiesIncludeNodeRead')
|
||||
canRead;
|
||||
|
||||
@or('bypassAuthorization', 'selfTokenIsManagement', 'policiesIncludeNodeWrite')
|
||||
@or(
|
||||
'bypassAuthorization',
|
||||
'selfTokenIsManagement',
|
||||
'policiesIncludeNodeWrite'
|
||||
)
|
||||
canWrite;
|
||||
|
||||
@computed('token.selfTokenPolicies.[]')
|
||||
get policiesIncludeNodeRead() {
|
||||
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), ['read', 'write']);
|
||||
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), [
|
||||
'read',
|
||||
'write'
|
||||
]);
|
||||
}
|
||||
|
||||
@computed('token.selfTokenPolicies.[]')
|
||||
get policiesIncludeNodeWrite() {
|
||||
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), ['write']);
|
||||
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), [
|
||||
'write'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,11 @@ export default class Job extends AbstractAbility {
|
|||
@or('bypassAuthorization', 'selfTokenIsManagement')
|
||||
canListAll;
|
||||
|
||||
@or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportDispatching')
|
||||
@or(
|
||||
'bypassAuthorization',
|
||||
'selfTokenIsManagement',
|
||||
'policiesSupportDispatching'
|
||||
)
|
||||
canDispatch;
|
||||
|
||||
@computed('rulesForNamespace.@each.capabilities')
|
||||
|
|
|
@ -6,7 +6,11 @@ export default class Recommendation extends AbstractAbility {
|
|||
@and('dynamicApplicationSizingIsPresent', 'hasPermissions')
|
||||
canAccept;
|
||||
|
||||
@or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportAcceptingOnAnyNamespace')
|
||||
@or(
|
||||
'bypassAuthorization',
|
||||
'selfTokenIsManagement',
|
||||
'policiesSupportAcceptingOnAnyNamespace'
|
||||
)
|
||||
hasPermissions;
|
||||
|
||||
@computed('capabilitiesForAllNamespaces.[]')
|
||||
|
|
|
@ -14,13 +14,17 @@ export default class AllocationAdapter extends Watchable {
|
|||
|
||||
ls(model, path) {
|
||||
return this.token
|
||||
.authorizedRequest(`/v1/client/fs/ls/${model.id}?path=${encodeURIComponent(path)}`)
|
||||
.authorizedRequest(
|
||||
`/v1/client/fs/ls/${model.id}?path=${encodeURIComponent(path)}`
|
||||
)
|
||||
.then(handleFSResponse);
|
||||
}
|
||||
|
||||
stat(model, path) {
|
||||
return this.token
|
||||
.authorizedRequest(`/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(path)}`)
|
||||
.authorizedRequest(
|
||||
`/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(path)}`
|
||||
)
|
||||
.then(handleFSResponse);
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +44,10 @@ async function handleFSResponse(response) {
|
|||
|
||||
function adapterAction(path, verb = 'POST') {
|
||||
return function (allocation) {
|
||||
const url = addToPath(this.urlForFindRecord(allocation.id, 'allocation'), path);
|
||||
const url = addToPath(
|
||||
this.urlForFindRecord(allocation.id, 'allocation'),
|
||||
path
|
||||
);
|
||||
return this.ajax(url, verb);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -125,5 +125,7 @@ export default class ApplicationAdapter extends RESTAdapter {
|
|||
}
|
||||
|
||||
function associateRegion(url, region) {
|
||||
return url.indexOf('?') !== -1 ? `${url}®ion=${region}` : `${url}?region=${region}`;
|
||||
return url.indexOf('?') !== -1
|
||||
? `${url}®ion=${region}`
|
||||
: `${url}?region=${region}`;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,10 @@ export default class DeploymentAdapter extends Watchable {
|
|||
|
||||
promote(deployment) {
|
||||
const id = deployment.get('id');
|
||||
const url = urlForAction(this.urlForFindRecord(id, 'deployment'), '/promote');
|
||||
const url = urlForAction(
|
||||
this.urlForFindRecord(id, 'deployment'),
|
||||
'/promote'
|
||||
);
|
||||
return this.ajax(url, 'POST', {
|
||||
data: {
|
||||
DeploymentId: id,
|
||||
|
|
|
@ -5,7 +5,10 @@ export default class JobVersionAdapter extends ApplicationAdapter {
|
|||
revertTo(jobVersion) {
|
||||
const jobAdapter = this.store.adapterFor('job');
|
||||
|
||||
const url = addToPath(jobAdapter.urlForFindRecord(jobVersion.get('job.id'), 'job'), '/revert');
|
||||
const url = addToPath(
|
||||
jobAdapter.urlForFindRecord(jobVersion.get('job.id'), 'job'),
|
||||
'/revert'
|
||||
);
|
||||
const [jobName] = JSON.parse(jobVersion.get('job.id'));
|
||||
|
||||
return this.ajax(url, 'POST', {
|
||||
|
|
|
@ -14,7 +14,10 @@ export default class JobAdapter extends WatchableNamespaceIDs {
|
|||
|
||||
forcePeriodic(job) {
|
||||
if (job.get('periodic')) {
|
||||
const url = addToPath(this.urlForFindRecord(job.get('id'), 'job'), '/periodic/force');
|
||||
const url = addToPath(
|
||||
this.urlForFindRecord(job.get('id'), 'job'),
|
||||
'/periodic/force'
|
||||
);
|
||||
return this.ajax(url, 'POST');
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +74,10 @@ export default class JobAdapter extends WatchableNamespaceIDs {
|
|||
}
|
||||
|
||||
scale(job, group, count, message) {
|
||||
const url = addToPath(this.urlForFindRecord(job.get('id'), 'job'), '/scale');
|
||||
const url = addToPath(
|
||||
this.urlForFindRecord(job.get('id'), 'job'),
|
||||
'/scale'
|
||||
);
|
||||
return this.ajax(url, 'POST', {
|
||||
data: {
|
||||
Count: count,
|
||||
|
@ -87,7 +93,10 @@ export default class JobAdapter extends WatchableNamespaceIDs {
|
|||
}
|
||||
|
||||
dispatch(job, meta, payload) {
|
||||
const url = addToPath(this.urlForFindRecord(job.get('id'), 'job'), '/dispatch');
|
||||
const url = addToPath(
|
||||
this.urlForFindRecord(job.get('id'), 'job'),
|
||||
'/dispatch'
|
||||
);
|
||||
return this.ajax(url, 'POST', {
|
||||
data: {
|
||||
Payload: base64EncodeString(payload),
|
||||
|
|
|
@ -11,7 +11,10 @@ export default class NodeAdapter extends Watchable {
|
|||
}
|
||||
|
||||
setEligibility(node, isEligible) {
|
||||
const url = addToPath(this.urlForFindRecord(node.id, 'node'), '/eligibility');
|
||||
const url = addToPath(
|
||||
this.urlForFindRecord(node.id, 'node'),
|
||||
'/eligibility'
|
||||
);
|
||||
return this.ajax(url, 'POST', {
|
||||
data: {
|
||||
NodeID: node.id,
|
||||
|
|
|
@ -9,13 +9,20 @@ export default class RecommendationSummaryAdapter extends ApplicationAdapter {
|
|||
}
|
||||
|
||||
updateRecord(store, type, snapshot) {
|
||||
const url = `${super.urlForCreateRecord('recommendations', snapshot)}/apply`;
|
||||
const url = `${super.urlForCreateRecord(
|
||||
'recommendations',
|
||||
snapshot
|
||||
)}/apply`;
|
||||
|
||||
const allRecommendationIds = snapshot.hasMany('recommendations').mapBy('id');
|
||||
const excludedRecommendationIds = (snapshot.hasMany('excludedRecommendations') || []).mapBy(
|
||||
'id'
|
||||
const allRecommendationIds = snapshot
|
||||
.hasMany('recommendations')
|
||||
.mapBy('id');
|
||||
const excludedRecommendationIds = (
|
||||
snapshot.hasMany('excludedRecommendations') || []
|
||||
).mapBy('id');
|
||||
const includedRecommendationIds = allRecommendationIds.removeObjects(
|
||||
excludedRecommendationIds
|
||||
);
|
||||
const includedRecommendationIds = allRecommendationIds.removeObjects(excludedRecommendationIds);
|
||||
|
||||
const data = {
|
||||
Apply: includedRecommendationIds,
|
||||
|
|
|
@ -30,7 +30,10 @@ export default class TokenAdapter extends ApplicationAdapter {
|
|||
tokens: [token],
|
||||
});
|
||||
|
||||
return store.peekRecord('token', store.normalize('token', token).data.id);
|
||||
return store.peekRecord(
|
||||
'token',
|
||||
store.normalize('token', token).data.id
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
throw new OTTExchangeError();
|
||||
|
|
|
@ -26,7 +26,8 @@ export default class WatchableNamespaceIDs extends Watchable {
|
|||
|
||||
findRecord(store, type, id, snapshot) {
|
||||
const [, namespace] = JSON.parse(id);
|
||||
const namespaceQuery = namespace && namespace !== 'default' ? { namespace } : {};
|
||||
const namespaceQuery =
|
||||
namespace && namespace !== 'default' ? { namespace } : {};
|
||||
|
||||
return super.findRecord(store, type, id, snapshot, namespaceQuery);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,10 @@ export default class Watchable extends ApplicationAdapter {
|
|||
params.index = this.watchList.getIndexFor(url);
|
||||
}
|
||||
|
||||
const signal = get(snapshotRecordArray || {}, 'adapterOptions.abortController.signal');
|
||||
const signal = get(
|
||||
snapshotRecordArray || {},
|
||||
'adapterOptions.abortController.signal'
|
||||
);
|
||||
return this.ajax(url, 'GET', {
|
||||
signal,
|
||||
data: params,
|
||||
|
@ -48,9 +51,18 @@ export default class Watchable extends ApplicationAdapter {
|
|||
}
|
||||
|
||||
findRecord(store, type, id, snapshot, additionalParams = {}) {
|
||||
const originalUrl = this.buildURL(type.modelName, id, snapshot, 'findRecord');
|
||||
const originalUrl = this.buildURL(
|
||||
type.modelName,
|
||||
id,
|
||||
snapshot,
|
||||
'findRecord'
|
||||
);
|
||||
let [url, params] = originalUrl.split('?');
|
||||
params = assign(queryString.parse(params) || {}, this.buildQuery(), additionalParams);
|
||||
params = assign(
|
||||
queryString.parse(params) || {},
|
||||
this.buildQuery(),
|
||||
additionalParams
|
||||
);
|
||||
|
||||
if (get(snapshot || {}, 'adapterOptions.watch')) {
|
||||
params.index = this.watchList.getIndexFor(originalUrl);
|
||||
|
@ -68,15 +80,29 @@ export default class Watchable extends ApplicationAdapter {
|
|||
});
|
||||
}
|
||||
|
||||
query(store, type, query, snapshotRecordArray, options, additionalParams = {}) {
|
||||
query(
|
||||
store,
|
||||
type,
|
||||
query,
|
||||
snapshotRecordArray,
|
||||
options,
|
||||
additionalParams = {}
|
||||
) {
|
||||
const url = this.buildURL(type.modelName, null, null, 'query', query);
|
||||
let [urlPath, params] = url.split('?');
|
||||
params = assign(queryString.parse(params) || {}, this.buildQuery(), additionalParams, query);
|
||||
params = assign(
|
||||
queryString.parse(params) || {},
|
||||
this.buildQuery(),
|
||||
additionalParams,
|
||||
query
|
||||
);
|
||||
|
||||
if (get(options, 'adapterOptions.watch')) {
|
||||
// The intended query without additional blocking query params is used
|
||||
// to track the appropriate query index.
|
||||
params.index = this.watchList.getIndexFor(`${urlPath}?${queryString.stringify(query)}`);
|
||||
params.index = this.watchList.getIndexFor(
|
||||
`${urlPath}?${queryString.stringify(query)}`
|
||||
);
|
||||
}
|
||||
|
||||
const signal = get(options, 'adapterOptions.abortController.signal');
|
||||
|
@ -88,7 +114,9 @@ export default class Watchable extends ApplicationAdapter {
|
|||
|
||||
// Query params may not necessarily map one-to-one to attribute names.
|
||||
// Adapters are responsible for declaring param mappings.
|
||||
const queryParamsToAttrs = Object.keys(adapter.queryParamsToAttrs || {}).map((key) => ({
|
||||
const queryParamsToAttrs = Object.keys(
|
||||
adapter.queryParamsToAttrs || {}
|
||||
).map((key) => ({
|
||||
queryParam: key,
|
||||
attr: adapter.queryParamsToAttrs[key],
|
||||
}));
|
||||
|
@ -110,7 +138,11 @@ export default class Watchable extends ApplicationAdapter {
|
|||
});
|
||||
}
|
||||
|
||||
reloadRelationship(model, relationshipName, options = { watch: false, abortController: null }) {
|
||||
reloadRelationship(
|
||||
model,
|
||||
relationshipName,
|
||||
options = { watch: false, abortController: null }
|
||||
) {
|
||||
const { watch, abortController } = options;
|
||||
const relationship = model.relationshipFor(relationshipName);
|
||||
if (relationship.kind !== 'belongsTo' && relationship.kind !== 'hasMany') {
|
||||
|
@ -146,7 +178,11 @@ export default class Watchable extends ApplicationAdapter {
|
|||
: 'normalizeFindHasManyResponse';
|
||||
const serializer = store.serializerFor(relationship.type);
|
||||
const modelClass = store.modelFor(relationship.type);
|
||||
const normalizedData = serializer[normalizeMethod](store, modelClass, json);
|
||||
const normalizedData = serializer[normalizeMethod](
|
||||
store,
|
||||
modelClass,
|
||||
json
|
||||
);
|
||||
store.push(normalizedData);
|
||||
},
|
||||
(error) => {
|
||||
|
|
|
@ -85,7 +85,9 @@ async function qualifyAllocation() {
|
|||
// Make sure the allocation is a complete record and not a partial so we
|
||||
// can show information such as preemptions and rescheduled allocation.
|
||||
if (allocation.isPartial) {
|
||||
await this.store.findRecord('allocation', allocation.id, { backgroundReload: false });
|
||||
await this.store.findRecord('allocation', allocation.id, {
|
||||
backgroundReload: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (allocation.get('job.isPending')) {
|
||||
|
|
|
@ -42,8 +42,10 @@ export default class AllocationStat extends Component {
|
|||
|
||||
@computed('metric', 'statsTracker.{reservedMemory,reservedCPU}')
|
||||
get formattedReserved() {
|
||||
if (this.metric === 'memory') return formatBytes(this.statsTracker.reservedMemory, 'MiB');
|
||||
if (this.metric === 'cpu') return formatHertz(this.statsTracker.reservedCPU, 'MHz');
|
||||
if (this.metric === 'memory')
|
||||
return formatBytes(this.statsTracker.reservedMemory, 'MiB');
|
||||
if (this.metric === 'cpu')
|
||||
return formatHertz(this.statsTracker.reservedCPU, 'MHz');
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ export default class ChartPrimitiveArea extends Component {
|
|||
get colorClass() {
|
||||
if (this.args.colorClass) return this.args.colorClass;
|
||||
if (this.args.colorScale && this.args.index != null)
|
||||
return `${this.args.colorScale} ${this.args.colorScale}-${this.args.index + 1}`;
|
||||
return `${this.args.colorScale} ${this.args.colorScale}-${
|
||||
this.args.index + 1
|
||||
}`;
|
||||
return 'is-primary';
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,22 @@ export default class ChildrenStatusBar extends DistributionBar {
|
|||
return [];
|
||||
}
|
||||
|
||||
const children = this.job.getProperties('pendingChildren', 'runningChildren', 'deadChildren');
|
||||
const children = this.job.getProperties(
|
||||
'pendingChildren',
|
||||
'runningChildren',
|
||||
'deadChildren'
|
||||
);
|
||||
return [
|
||||
{ label: 'Pending', value: children.pendingChildren, className: 'queued' },
|
||||
{ label: 'Running', value: children.runningChildren, className: 'running' },
|
||||
{
|
||||
label: 'Pending',
|
||||
value: children.pendingChildren,
|
||||
className: 'queued',
|
||||
},
|
||||
{
|
||||
label: 'Running',
|
||||
value: children.runningChildren,
|
||||
className: 'running',
|
||||
},
|
||||
{ label: 'Dead', value: children.deadChildren, className: 'complete' },
|
||||
];
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import classic from 'ember-classic-decorator';
|
|||
@classic
|
||||
@tagName('tr')
|
||||
@classNames('client-node-row', 'is-interactive')
|
||||
export default class ClientNodeRow extends Component.extend(WithVisibilityDetection) {
|
||||
export default class ClientNodeRow extends Component.extend(
|
||||
WithVisibilityDetection
|
||||
) {
|
||||
@service store;
|
||||
|
||||
node = null;
|
||||
|
|
|
@ -4,7 +4,8 @@ import { tracked } from '@glimmer/tracking';
|
|||
import { action } from '@ember/object';
|
||||
|
||||
export default class DasDismissedComponent extends Component {
|
||||
@localStorageProperty('nomadRecommendationDismssalUnderstood', false) explanationUnderstood;
|
||||
@localStorageProperty('nomadRecommendationDismssalUnderstood', false)
|
||||
explanationUnderstood;
|
||||
|
||||
@tracked dismissInTheFuture = false;
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ export default class DasRecommendationAccordionComponent extends Component {
|
|||
|
||||
@(task(function* () {
|
||||
this.closing = true;
|
||||
this.animationContainerStyle = htmlSafe(`height: ${this.accordionElement.clientHeight}px`);
|
||||
this.animationContainerStyle = htmlSafe(
|
||||
`height: ${this.accordionElement.clientHeight}px`
|
||||
);
|
||||
|
||||
yield timeout(10);
|
||||
|
||||
|
|
|
@ -93,35 +93,39 @@ export default class DasRecommendationCardComponent extends Component {
|
|||
get taskToggleRows() {
|
||||
const taskNameToTaskToggles = {};
|
||||
|
||||
return this.args.summary.recommendations.reduce((taskToggleRows, recommendation) => {
|
||||
let taskToggleRow = taskNameToTaskToggles[recommendation.task.name];
|
||||
return this.args.summary.recommendations.reduce(
|
||||
(taskToggleRows, recommendation) => {
|
||||
let taskToggleRow = taskNameToTaskToggles[recommendation.task.name];
|
||||
|
||||
if (!taskToggleRow) {
|
||||
taskToggleRow = {
|
||||
recommendations: [],
|
||||
task: recommendation.task,
|
||||
if (!taskToggleRow) {
|
||||
taskToggleRow = {
|
||||
recommendations: [],
|
||||
task: recommendation.task,
|
||||
};
|
||||
|
||||
taskNameToTaskToggles[recommendation.task.name] = taskToggleRow;
|
||||
taskToggleRows.push(taskToggleRow);
|
||||
}
|
||||
|
||||
const isCpu = recommendation.resource === 'CPU';
|
||||
const rowResourceProperty = isCpu ? 'cpu' : 'memory';
|
||||
|
||||
taskToggleRow[rowResourceProperty] = {
|
||||
recommendation,
|
||||
isActive:
|
||||
!this.args.summary.excludedRecommendations.includes(recommendation),
|
||||
};
|
||||
|
||||
taskNameToTaskToggles[recommendation.task.name] = taskToggleRow;
|
||||
taskToggleRows.push(taskToggleRow);
|
||||
}
|
||||
if (isCpu) {
|
||||
taskToggleRow.recommendations.unshift(recommendation);
|
||||
} else {
|
||||
taskToggleRow.recommendations.push(recommendation);
|
||||
}
|
||||
|
||||
const isCpu = recommendation.resource === 'CPU';
|
||||
const rowResourceProperty = isCpu ? 'cpu' : 'memory';
|
||||
|
||||
taskToggleRow[rowResourceProperty] = {
|
||||
recommendation,
|
||||
isActive: !this.args.summary.excludedRecommendations.includes(recommendation),
|
||||
};
|
||||
|
||||
if (isCpu) {
|
||||
taskToggleRow.recommendations.unshift(recommendation);
|
||||
} else {
|
||||
taskToggleRow.recommendations.push(recommendation);
|
||||
}
|
||||
|
||||
return taskToggleRows;
|
||||
}, []);
|
||||
return taskToggleRows;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
get showToggleAllToggles() {
|
||||
|
@ -129,23 +133,30 @@ export default class DasRecommendationCardComponent extends Component {
|
|||
}
|
||||
|
||||
get allCpuToggleDisabled() {
|
||||
return !this.args.summary.recommendations.filterBy('resource', 'CPU').length;
|
||||
return !this.args.summary.recommendations.filterBy('resource', 'CPU')
|
||||
.length;
|
||||
}
|
||||
|
||||
get allMemoryToggleDisabled() {
|
||||
return !this.args.summary.recommendations.filterBy('resource', 'MemoryMB').length;
|
||||
return !this.args.summary.recommendations.filterBy('resource', 'MemoryMB')
|
||||
.length;
|
||||
}
|
||||
|
||||
get cannotAccept() {
|
||||
return (
|
||||
this.args.summary.excludedRecommendations.length == this.args.summary.recommendations.length
|
||||
this.args.summary.excludedRecommendations.length ==
|
||||
this.args.summary.recommendations.length
|
||||
);
|
||||
}
|
||||
|
||||
get copyButtonLink() {
|
||||
const path = this.router.urlFor('optimize.summary', this.args.summary.slug, {
|
||||
queryParams: { namespace: this.args.summary.jobNamespace },
|
||||
});
|
||||
const path = this.router.urlFor(
|
||||
'optimize.summary',
|
||||
this.args.summary.slug,
|
||||
{
|
||||
queryParams: { namespace: this.args.summary.jobNamespace },
|
||||
}
|
||||
);
|
||||
const { origin } = window.location;
|
||||
|
||||
return `${origin}${path}`;
|
||||
|
@ -185,7 +196,9 @@ export default class DasRecommendationCardComponent extends Component {
|
|||
@action
|
||||
dismiss() {
|
||||
this.storeCardHeight();
|
||||
this.args.summary.excludedRecommendations.pushObjects(this.args.summary.recommendations);
|
||||
this.args.summary.excludedRecommendations.pushObjects(
|
||||
this.args.summary.recommendations
|
||||
);
|
||||
this.args.summary
|
||||
.save()
|
||||
.then(
|
||||
|
|
|
@ -64,10 +64,12 @@ export default class RecommendationChartComponent extends Component {
|
|||
edgeTickHeight = 23;
|
||||
centerTickOffset = 6;
|
||||
|
||||
centerY = this.tickTextHeight + this.centerTickOffset + this.edgeTickHeight / 2;
|
||||
centerY =
|
||||
this.tickTextHeight + this.centerTickOffset + this.edgeTickHeight / 2;
|
||||
|
||||
edgeTickY1 = this.tickTextHeight + this.centerTickOffset;
|
||||
edgeTickY2 = this.tickTextHeight + this.edgeTickHeight + this.centerTickOffset;
|
||||
edgeTickY2 =
|
||||
this.tickTextHeight + this.edgeTickHeight + this.centerTickOffset;
|
||||
|
||||
deltaTextY = this.edgeTickY2;
|
||||
|
||||
|
@ -129,7 +131,10 @@ export default class RecommendationChartComponent extends Component {
|
|||
},
|
||||
rect: {
|
||||
x: this.gutterWidthLeft,
|
||||
y: (this.edgeTickHeight - rectHeight) / 2 + this.centerTickOffset + this.tickTextHeight,
|
||||
y:
|
||||
(this.edgeTickHeight - rectHeight) / 2 +
|
||||
this.centerTickOffset +
|
||||
this.tickTextHeight,
|
||||
width: rectWidth,
|
||||
height: rectHeight,
|
||||
},
|
||||
|
@ -145,7 +150,10 @@ export default class RecommendationChartComponent extends Component {
|
|||
}
|
||||
|
||||
get maximumX() {
|
||||
return Math.max(this.higherValue, get(this.args.stats, 'max') || Number.MIN_SAFE_INTEGER);
|
||||
return Math.max(
|
||||
this.higherValue,
|
||||
get(this.args.stats, 'max') || Number.MIN_SAFE_INTEGER
|
||||
);
|
||||
}
|
||||
|
||||
get lowerValue() {
|
||||
|
@ -153,7 +161,9 @@ export default class RecommendationChartComponent extends Component {
|
|||
}
|
||||
|
||||
get xScale() {
|
||||
return scaleLinear().domain([0, this.maximumX]).rangeRound([0, this.barWidth]);
|
||||
return scaleLinear()
|
||||
.domain([0, this.maximumX])
|
||||
.rangeRound([0, this.barWidth]);
|
||||
}
|
||||
|
||||
get lowerValueWidth() {
|
||||
|
@ -210,9 +220,13 @@ export default class RecommendationChartComponent extends Component {
|
|||
let translateX;
|
||||
|
||||
if (this.shown) {
|
||||
translateX = this.isIncrease ? this.higherValueWidth : this.lowerValueWidth;
|
||||
translateX = this.isIncrease
|
||||
? this.higherValueWidth
|
||||
: this.lowerValueWidth;
|
||||
} else {
|
||||
translateX = this.isIncrease ? this.lowerValueWidth : this.higherValueWidth;
|
||||
translateX = this.isIncrease
|
||||
? this.lowerValueWidth
|
||||
: this.higherValueWidth;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -220,7 +234,9 @@ export default class RecommendationChartComponent extends Component {
|
|||
points: `
|
||||
0,${this.center.y1}
|
||||
0,${this.center.y1 - this.deltaTriangleHeight / 2}
|
||||
${(directionXMultiplier * this.deltaTriangleHeight) / 2},${this.center.y1}
|
||||
${(directionXMultiplier * this.deltaTriangleHeight) / 2},${
|
||||
this.center.y1
|
||||
}
|
||||
0,${this.center.y1 + this.deltaTriangleHeight / 2}
|
||||
`,
|
||||
};
|
||||
|
@ -234,7 +250,9 @@ export default class RecommendationChartComponent extends Component {
|
|||
},
|
||||
delta: {
|
||||
style: htmlSafe(
|
||||
`transform: translateX(${this.shown ? this.higherValueWidth : this.lowerValueWidth}px)`
|
||||
`transform: translateX(${
|
||||
this.shown ? this.higherValueWidth : this.lowerValueWidth
|
||||
}px)`
|
||||
),
|
||||
},
|
||||
};
|
||||
|
@ -245,7 +263,9 @@ export default class RecommendationChartComponent extends Component {
|
|||
},
|
||||
delta: {
|
||||
style: htmlSafe(
|
||||
`transform: translateX(${this.shown ? this.lowerValueWidth : this.higherValueWidth}px)`
|
||||
`transform: translateX(${
|
||||
this.shown ? this.lowerValueWidth : this.higherValueWidth
|
||||
}px)`
|
||||
),
|
||||
},
|
||||
};
|
||||
|
@ -269,7 +289,8 @@ export default class RecommendationChartComponent extends Component {
|
|||
};
|
||||
|
||||
const percentText = formatPercent(
|
||||
(this.args.recommendedValue - this.args.currentValue) / this.args.currentValue
|
||||
(this.args.recommendedValue - this.args.currentValue) /
|
||||
this.args.currentValue
|
||||
);
|
||||
|
||||
const percent = {
|
||||
|
@ -314,7 +335,10 @@ export default class RecommendationChartComponent extends Component {
|
|||
};
|
||||
|
||||
return Object.keys(statsWithCurrentAndRecommended)
|
||||
.map((key) => ({ label: statsKeyToLabel[key], value: statsWithCurrentAndRecommended[key] }))
|
||||
.map((key) => ({
|
||||
label: statsKeyToLabel[key],
|
||||
value: statsWithCurrentAndRecommended[key],
|
||||
}))
|
||||
.sortBy('value');
|
||||
} else {
|
||||
return [];
|
||||
|
|
|
@ -34,17 +34,20 @@ export default class DistributionBar extends Component.extend(WindowResizable) {
|
|||
const data = copy(this.data, true);
|
||||
const sum = data.mapBy('value').reduce(sumAggregate, 0);
|
||||
|
||||
return data.map(({ label, value, className, layers, legendLink, help }, index) => ({
|
||||
label,
|
||||
value,
|
||||
className,
|
||||
layers,
|
||||
legendLink,
|
||||
help,
|
||||
index,
|
||||
percent: value / sum,
|
||||
offset: data.slice(0, index).mapBy('value').reduce(sumAggregate, 0) / sum,
|
||||
}));
|
||||
return data.map(
|
||||
({ label, value, className, layers, legendLink, help }, index) => ({
|
||||
label,
|
||||
value,
|
||||
className,
|
||||
layers,
|
||||
legendLink,
|
||||
help,
|
||||
index,
|
||||
percent: value / sum,
|
||||
offset:
|
||||
data.slice(0, index).mapBy('value').reduce(sumAggregate, 0) / sum,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
|
@ -60,7 +63,10 @@ export default class DistributionBar extends Component.extend(WindowResizable) {
|
|||
run(() => {
|
||||
this.set('isActive', false);
|
||||
this.set('activeDatum', null);
|
||||
chart.selectAll('g').classed('active', false).classed('inactive', false);
|
||||
chart
|
||||
.selectAll('g')
|
||||
.classed('active', false)
|
||||
.classed('inactive', false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -31,7 +31,9 @@ export default class TaskGroupParent extends Component {
|
|||
|
||||
@computed('taskGroup.allocations.@each.clientStatus')
|
||||
get hasPendingAllocations() {
|
||||
return this.taskGroup.allocations.any((allocation) => allocation.clientStatus === 'pending');
|
||||
return this.taskGroup.allocations.any(
|
||||
(allocation) => allocation.clientStatus === 'pending'
|
||||
);
|
||||
}
|
||||
|
||||
@mapBy('taskGroup.allocations', 'states') allocationTaskStatesRecordArrays;
|
||||
|
@ -39,7 +41,10 @@ export default class TaskGroupParent extends Component {
|
|||
get allocationTaskStates() {
|
||||
const flattenRecordArrays = (accumulator, recordArray) =>
|
||||
accumulator.concat(recordArray.toArray());
|
||||
return this.allocationTaskStatesRecordArrays.reduce(flattenRecordArrays, []);
|
||||
return this.allocationTaskStatesRecordArrays.reduce(
|
||||
flattenRecordArrays,
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
@filterBy('allocationTaskStates', 'isActive') activeTaskStates;
|
||||
|
@ -56,11 +61,16 @@ export default class TaskGroupParent extends Component {
|
|||
get tasksWithRunningStates() {
|
||||
const activeTaskStateNames = this.activeTaskStates
|
||||
.filter((taskState) => {
|
||||
return taskState.task && taskState.task.taskGroup.name === this.taskGroup.name;
|
||||
return (
|
||||
taskState.task &&
|
||||
taskState.task.taskGroup.name === this.taskGroup.name
|
||||
);
|
||||
})
|
||||
.mapBy('name');
|
||||
|
||||
return this.taskGroup.tasks.filter((task) => activeTaskStateNames.includes(task.name));
|
||||
return this.taskGroup.tasks.filter((task) =>
|
||||
activeTaskStateNames.includes(task.name)
|
||||
);
|
||||
}
|
||||
|
||||
taskSorting = ['name'];
|
||||
|
|
|
@ -18,7 +18,9 @@ export default class FlexMasonry extends Component {
|
|||
// There's nothing to do if there is no element
|
||||
if (!this.element) return;
|
||||
|
||||
const items = this.element.querySelectorAll(':scope > .flex-masonry-item');
|
||||
const items = this.element.querySelectorAll(
|
||||
':scope > .flex-masonry-item'
|
||||
);
|
||||
|
||||
// Clear out specified order and flex-basis values in case this was once a multi-column layout
|
||||
if (this.args.columns === 1 || !this.args.columns) {
|
||||
|
@ -62,10 +64,12 @@ export default class FlexMasonry extends Component {
|
|||
// beteen the height of the column and the previous column, then flexbox will naturally place the first
|
||||
// item at the end of the previous column).
|
||||
columns.forEach((column, index) => {
|
||||
const nextHeight = index < columns.length - 1 ? columns[index + 1].height : 0;
|
||||
const nextHeight =
|
||||
index < columns.length - 1 ? columns[index + 1].height : 0;
|
||||
const item = column.elements.lastObject;
|
||||
if (item) {
|
||||
item.style.flexBasis = item.clientHeight + Math.max(0, nextHeight - column.height) + 'px';
|
||||
item.style.flexBasis =
|
||||
item.clientHeight + Math.max(0, nextHeight - column.height) + 'px';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -39,11 +39,18 @@ export default class Browser extends Component {
|
|||
@filterBy('directoryEntries', 'IsDir') directories;
|
||||
@filterBy('directoryEntries', 'IsDir', false) files;
|
||||
|
||||
@computed('directories', 'directoryEntries.[]', 'files', 'sortDescending', 'sortProperty')
|
||||
@computed(
|
||||
'directories',
|
||||
'directoryEntries.[]',
|
||||
'files',
|
||||
'sortDescending',
|
||||
'sortProperty'
|
||||
)
|
||||
get sortedDirectoryEntries() {
|
||||
const sortProperty = this.sortProperty;
|
||||
|
||||
const directorySortProperty = sortProperty === 'Size' ? 'Name' : sortProperty;
|
||||
const directorySortProperty =
|
||||
sortProperty === 'Size' ? 'Name' : sortProperty;
|
||||
|
||||
const sortedDirectories = this.directories.sortBy(directorySortProperty);
|
||||
const sortedFiles = this.files.sortBy(sortProperty);
|
||||
|
|
|
@ -39,7 +39,10 @@ export default class File extends Component {
|
|||
|
||||
if (contentType.startsWith('image/')) {
|
||||
return 'image';
|
||||
} else if (contentType.startsWith('text/') || contentType.startsWith('application/json')) {
|
||||
} else if (
|
||||
contentType.startsWith('text/') ||
|
||||
contentType.startsWith('application/json')
|
||||
) {
|
||||
return 'stream';
|
||||
} else {
|
||||
return 'unknown';
|
||||
|
@ -108,7 +111,14 @@ export default class File extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
@computed('clientTimeout', 'fileParams', 'fileUrl', 'mode', 'serverTimeout', 'useServer')
|
||||
@computed(
|
||||
'clientTimeout',
|
||||
'fileParams',
|
||||
'fileUrl',
|
||||
'mode',
|
||||
'serverTimeout',
|
||||
'useServer'
|
||||
)
|
||||
get logger() {
|
||||
// The cat and readat APIs are in plainText while the stream API is always encoded.
|
||||
const plainText = this.mode === 'head' || this.mode === 'tail';
|
||||
|
|
|
@ -43,14 +43,17 @@ export default class GlobalSearchControl extends Component {
|
|||
}
|
||||
|
||||
@task(function* (string) {
|
||||
const searchResponse = yield this.token.authorizedRequest('/v1/search/fuzzy', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
Text: string,
|
||||
Context: 'all',
|
||||
Namespace: '*',
|
||||
}),
|
||||
});
|
||||
const searchResponse = yield this.token.authorizedRequest(
|
||||
'/v1/search/fuzzy',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
Text: string,
|
||||
Context: 'all',
|
||||
Namespace: '*',
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const results = yield searchResponse.json();
|
||||
|
||||
|
@ -95,11 +98,13 @@ export default class GlobalSearchControl extends Component {
|
|||
label: `${namespace} > ${jobId} > ${id}`,
|
||||
}));
|
||||
|
||||
const csiPluginResults = allCSIPluginResults.slice(0, MAXIMUM_RESULTS).map(({ ID: id }) => ({
|
||||
type: 'plugin',
|
||||
id,
|
||||
label: id,
|
||||
}));
|
||||
const csiPluginResults = allCSIPluginResults
|
||||
.slice(0, MAXIMUM_RESULTS)
|
||||
.map(({ ID: id }) => ({
|
||||
type: 'plugin',
|
||||
id,
|
||||
label: id,
|
||||
}));
|
||||
|
||||
const {
|
||||
jobs: jobsTruncated,
|
||||
|
@ -111,11 +116,21 @@ export default class GlobalSearchControl extends Component {
|
|||
|
||||
return [
|
||||
{
|
||||
groupName: resultsGroupLabel('Jobs', jobResults, allJobResults, jobsTruncated),
|
||||
groupName: resultsGroupLabel(
|
||||
'Jobs',
|
||||
jobResults,
|
||||
allJobResults,
|
||||
jobsTruncated
|
||||
),
|
||||
options: jobResults,
|
||||
},
|
||||
{
|
||||
groupName: resultsGroupLabel('Clients', nodeResults, allNodeResults, nodesTruncated),
|
||||
groupName: resultsGroupLabel(
|
||||
'Clients',
|
||||
nodeResults,
|
||||
allNodeResults,
|
||||
nodesTruncated
|
||||
),
|
||||
options: nodeResults,
|
||||
},
|
||||
{
|
||||
|
@ -191,10 +206,14 @@ export default class GlobalSearchControl extends Component {
|
|||
openOnClickOrTab(select, { target }) {
|
||||
// Bypass having to press enter to access search after clicking/tabbing
|
||||
const targetClassList = target.classList;
|
||||
const targetIsTrigger = targetClassList.contains('ember-power-select-trigger');
|
||||
const targetIsTrigger = targetClassList.contains(
|
||||
'ember-power-select-trigger'
|
||||
);
|
||||
|
||||
// Allow tabbing out of search
|
||||
const triggerIsNotActive = !targetClassList.contains('ember-power-select-trigger--active');
|
||||
const triggerIsNotActive = !targetClassList.contains(
|
||||
'ember-power-select-trigger--active'
|
||||
);
|
||||
|
||||
if (targetIsTrigger && triggerIsNotActive) {
|
||||
debounce(this, this.open, 150);
|
||||
|
|
|
@ -44,7 +44,9 @@ export default class GutterMenu extends Component {
|
|||
// an intent to reset context, but where to reset to depends on where the namespace
|
||||
// is being switched from. Jobs take precedence, but if the namespace is switched from
|
||||
// a storage-related page, context should be reset to volumes.
|
||||
const destination = this.router.currentRouteName.startsWith('csi.') ? 'csi.volumes' : 'jobs';
|
||||
const destination = this.router.currentRouteName.startsWith('csi.')
|
||||
? 'csi.volumes'
|
||||
: 'jobs';
|
||||
|
||||
this.router.transitionTo(destination, {
|
||||
queryParams: { namespace: namespace.get('id') },
|
||||
|
|
|
@ -12,8 +12,16 @@ export default class JobClientStatusBar extends DistributionBar {
|
|||
|
||||
@computed('job.namespace', 'jobClientStatus.byStatus')
|
||||
get data() {
|
||||
const { queued, starting, running, complete, degraded, failed, lost, notScheduled } =
|
||||
this.jobClientStatus.byStatus;
|
||||
const {
|
||||
queued,
|
||||
starting,
|
||||
running,
|
||||
complete,
|
||||
degraded,
|
||||
failed,
|
||||
lost,
|
||||
notScheduled,
|
||||
} = this.jobClientStatus.byStatus;
|
||||
|
||||
return [
|
||||
{
|
||||
|
|
|
@ -5,7 +5,11 @@ import classic from 'ember-classic-decorator';
|
|||
|
||||
@classic
|
||||
@classNames('job-diff')
|
||||
@classNameBindings('isEdited:is-edited', 'isAdded:is-added', 'isDeleted:is-deleted')
|
||||
@classNameBindings(
|
||||
'isEdited:is-edited',
|
||||
'isAdded:is-added',
|
||||
'isDeleted:is-deleted'
|
||||
)
|
||||
export default class JobDiff extends Component {
|
||||
diff = null;
|
||||
|
||||
|
|
|
@ -59,8 +59,14 @@ export default class JobDispatch extends Component {
|
|||
);
|
||||
|
||||
// Fetch the different types of parameters.
|
||||
const required = mapper(this.args.job.parameterizedDetails.MetaRequired || [], true);
|
||||
const optional = mapper(this.args.job.parameterizedDetails.MetaOptional || [], false);
|
||||
const required = mapper(
|
||||
this.args.job.parameterizedDetails.MetaRequired || [],
|
||||
true
|
||||
);
|
||||
const optional = mapper(
|
||||
this.args.job.parameterizedDetails.MetaOptional || [],
|
||||
false
|
||||
);
|
||||
|
||||
// Merge them, required before optional.
|
||||
this.metaFields = required.concat(optional);
|
||||
|
|
|
@ -25,7 +25,10 @@ export default class JobEditor extends Component {
|
|||
set context(value) {
|
||||
const allowedValues = ['new', 'edit'];
|
||||
|
||||
assert(`context must be one of: ${allowedValues.join(', ')}`, allowedValues.includes(value));
|
||||
assert(
|
||||
`context must be one of: ${allowedValues.join(', ')}`,
|
||||
allowedValues.includes(value)
|
||||
);
|
||||
|
||||
this.set('_context', value);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ export default class JobVersion extends Component {
|
|||
return (
|
||||
fieldChanges(diff) +
|
||||
taskGroups.reduce(arrayOfFieldChanges, 0) +
|
||||
(taskGroups.mapBy('Tasks') || []).reduce(flatten, []).reduce(arrayOfFieldChanges, 0)
|
||||
(taskGroups.mapBy('Tasks') || [])
|
||||
.reduce(flatten, [])
|
||||
.reduce(arrayOfFieldChanges, 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -58,7 +60,8 @@ export default class JobVersion extends Component {
|
|||
this.handleError({
|
||||
level: 'warn',
|
||||
title: 'Reversion Had No Effect',
|
||||
description: 'Reverting to an identical older version doesn’t produce a new version',
|
||||
description:
|
||||
'Reverting to an identical older version doesn’t produce a new version',
|
||||
});
|
||||
} else {
|
||||
const job = this.get('version.job');
|
||||
|
@ -79,7 +82,8 @@ export default class JobVersion extends Component {
|
|||
}
|
||||
|
||||
const flatten = (accumulator, array) => accumulator.concat(array);
|
||||
const countChanges = (total, field) => (changeTypes.includes(field.Type) ? total + 1 : total);
|
||||
const countChanges = (total, field) =>
|
||||
changeTypes.includes(field.Type) ? total + 1 : total;
|
||||
|
||||
function fieldChanges(diff) {
|
||||
return (
|
||||
|
|
|
@ -24,7 +24,9 @@ export default class JobVersionsStream extends Component {
|
|||
meta.showDate = true;
|
||||
} else {
|
||||
const previousVersion = versions.objectAt(index - 1);
|
||||
const previousStart = moment(previousVersion.get('submitTime')).startOf('day');
|
||||
const previousStart = moment(previousVersion.get('submitTime')).startOf(
|
||||
'day'
|
||||
);
|
||||
const currentStart = moment(version.get('submitTime')).startOf('day');
|
||||
if (previousStart.diff(currentStart, 'days') > 0) {
|
||||
meta.showDate = true;
|
||||
|
|
|
@ -92,7 +92,9 @@ export default class LineChart extends Component {
|
|||
@action
|
||||
xFormat(timeseries) {
|
||||
if (this.args.xFormat) return this.args.xFormat;
|
||||
return timeseries ? d3TimeFormat.timeFormat('%b %d, %H:%M') : d3Format.format(',');
|
||||
return timeseries
|
||||
? d3TimeFormat.timeFormat('%b %d, %H:%M')
|
||||
: d3Format.format(',');
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -153,7 +155,11 @@ export default class LineChart extends Component {
|
|||
get xAxis() {
|
||||
const formatter = this.xFormat(this.args.timeseries);
|
||||
|
||||
return d3Axis.axisBottom().scale(this.xScale).ticks(5).tickFormat(formatter);
|
||||
return d3Axis
|
||||
.axisBottom()
|
||||
.scale(this.xScale)
|
||||
.ticks(5)
|
||||
.tickFormat(formatter);
|
||||
}
|
||||
|
||||
get yTicks() {
|
||||
|
@ -167,7 +173,11 @@ export default class LineChart extends Component {
|
|||
get yAxis() {
|
||||
const formatter = this.yFormat();
|
||||
|
||||
return d3Axis.axisRight().scale(this.yScale).tickValues(this.yTicks).tickFormat(formatter);
|
||||
return d3Axis
|
||||
.axisRight()
|
||||
.scale(this.yScale)
|
||||
.tickValues(this.yTicks)
|
||||
.tickFormat(formatter);
|
||||
}
|
||||
|
||||
get yGridlines() {
|
||||
|
@ -297,7 +307,11 @@ export default class LineChart extends Component {
|
|||
// Of the selected data, determine which is closest
|
||||
const closestDatum = activeData
|
||||
.slice()
|
||||
.sort((a, b) => Math.abs(a.datum.datum[xProp] - x) - Math.abs(b.datum.datum[xProp] - x))[0];
|
||||
.sort(
|
||||
(a, b) =>
|
||||
Math.abs(a.datum.datum[xProp] - x) -
|
||||
Math.abs(b.datum.datum[xProp] - x)
|
||||
)[0];
|
||||
|
||||
// If any other selected data are beyond a distance threshold, drop them from the list
|
||||
// xScale is used here to measure distance in screen-space rather than data-space.
|
||||
|
@ -340,7 +354,9 @@ export default class LineChart extends Component {
|
|||
if (!this.isDestroyed && !this.isDestroying) {
|
||||
d3.select(this.element.querySelector('.x-axis')).call(this.xAxis);
|
||||
d3.select(this.element.querySelector('.y-axis')).call(this.yAxis);
|
||||
d3.select(this.element.querySelector('.y-gridlines')).call(this.yGridlines);
|
||||
d3.select(this.element.querySelector('.y-gridlines')).call(
|
||||
this.yGridlines
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,8 +60,12 @@ export default class MultiSelectDropdown extends Component {
|
|||
dropdown.actions.open(e);
|
||||
e.preventDefault();
|
||||
} else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) {
|
||||
const optionsId = this.element.querySelector('.dropdown-trigger').getAttribute('aria-owns');
|
||||
const firstElement = document.querySelector(`#${optionsId} .dropdown-option`);
|
||||
const optionsId = this.element
|
||||
.querySelector('.dropdown-trigger')
|
||||
.getAttribute('aria-owns');
|
||||
const firstElement = document.querySelector(
|
||||
`#${optionsId} .dropdown-option`
|
||||
);
|
||||
|
||||
if (firstElement) {
|
||||
firstElement.focus();
|
||||
|
|
|
@ -49,7 +49,9 @@ export default class PopoverMenu extends Component {
|
|||
dropdown.actions.open(e);
|
||||
e.preventDefault();
|
||||
} else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) {
|
||||
const optionsId = this.element.querySelector('.popover-trigger').getAttribute('aria-owns');
|
||||
const optionsId = this.element
|
||||
.querySelector('.popover-trigger')
|
||||
.getAttribute('aria-owns');
|
||||
const popoverContentEl = document.querySelector(`#${optionsId}`);
|
||||
const firstFocusableElement = popoverContentEl.querySelector(FOCUSABLE);
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ import { task, timeout } from 'ember-concurrency';
|
|||
import { assert } from '@ember/debug';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action, get } from '@ember/object';
|
||||
import { formatScheduledBytes, formatScheduledHertz } from 'nomad-ui/utils/units';
|
||||
import {
|
||||
formatScheduledBytes,
|
||||
formatScheduledHertz,
|
||||
} from 'nomad-ui/utils/units';
|
||||
|
||||
export default class NodePrimaryMetric extends Component {
|
||||
@service('stats-trackers-registry') statsTrackersRegistry;
|
||||
|
|
|
@ -53,7 +53,9 @@ export default class TaskPrimaryMetric extends Component {
|
|||
@action
|
||||
start() {
|
||||
this.taskState = this.args.taskState;
|
||||
this.tracker = this.statsTrackersRegistry.getTracker(this.args.taskState.allocation);
|
||||
this.tracker = this.statsTrackersRegistry.getTracker(
|
||||
this.args.taskState.allocation
|
||||
);
|
||||
this.poller.perform();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,10 @@ export default class ScaleEventsChart extends Component {
|
|||
}
|
||||
|
||||
toggleEvent(ev) {
|
||||
if (this.activeEvent && get(this.activeEvent, 'event.uid') === get(ev, 'event.uid')) {
|
||||
if (
|
||||
this.activeEvent &&
|
||||
get(this.activeEvent, 'event.uid') === get(ev, 'event.uid')
|
||||
) {
|
||||
this.closeEventDetails();
|
||||
} else {
|
||||
this.activeEvent = ev;
|
||||
|
|
|
@ -3,7 +3,11 @@ import { alias } from '@ember/object/computed';
|
|||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
import { lazyClick } from '../helpers/lazy-click';
|
||||
import { classNames, classNameBindings, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
classNameBindings,
|
||||
tagName,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
@ -38,7 +42,8 @@ export default class ServerAgentRow extends Component {
|
|||
}
|
||||
|
||||
click() {
|
||||
const transition = () => this.router.transitionTo('servers.server', this.agent);
|
||||
const transition = () =>
|
||||
this.router.transitionTo('servers.server', this.agent);
|
||||
lazyClick([transition, event]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,14 +39,18 @@ export default class StatsTimeSeries extends Component {
|
|||
const [low, high] = d3Array.extent(data, (d) => d.timestamp);
|
||||
const minLow = moment(high).subtract(5, 'minutes').toDate();
|
||||
|
||||
const extent = data.length ? [Math.min(low, minLow), high] : [minLow, new Date()];
|
||||
const extent = data.length
|
||||
? [Math.min(low, minLow), high]
|
||||
: [minLow, new Date()];
|
||||
scale.rangeRound([10, yAxisOffset]).domain(extent);
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
yScale(data, xAxisOffset) {
|
||||
const yValues = (data || []).mapBy(this.args.dataProp ? 'percentStack' : 'percent');
|
||||
const yValues = (data || []).mapBy(
|
||||
this.args.dataProp ? 'percentStack' : 'percent'
|
||||
);
|
||||
|
||||
let [low, high] = [0, 1];
|
||||
if (yValues.compact().length) {
|
||||
|
|
|
@ -9,7 +9,12 @@ const ESC = 27;
|
|||
|
||||
@classic
|
||||
@classNames('stepper-input')
|
||||
@classNameBindings('class', 'disabled:is-disabled', 'disabled:tooltip', 'disabled:multiline')
|
||||
@classNameBindings(
|
||||
'class',
|
||||
'disabled:is-disabled',
|
||||
'disabled:tooltip',
|
||||
'disabled:multiline'
|
||||
)
|
||||
export default class StepperInput extends Component {
|
||||
min = 0;
|
||||
max = 10;
|
||||
|
@ -41,7 +46,9 @@ export default class StepperInput extends Component {
|
|||
@action
|
||||
setValue(e) {
|
||||
if (e.target.value !== '') {
|
||||
const newValue = Math.floor(Math.min(this.max, Math.max(this.min, e.target.value)));
|
||||
const newValue = Math.floor(
|
||||
Math.min(this.max, Math.max(this.min, e.target.value))
|
||||
);
|
||||
this.set('internalValue', newValue);
|
||||
this.update(this.internalValue);
|
||||
} else {
|
||||
|
|
|
@ -59,7 +59,10 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
|
|||
if (this.requestFrame) {
|
||||
window.requestAnimationFrame(() => {
|
||||
// If the scroll position is close enough to the bottom, autoscroll to the bottom
|
||||
this.set('follow', cli.scrollHeight - cli.scrollTop - cli.clientHeight < 20);
|
||||
this.set(
|
||||
'follow',
|
||||
cli.scrollHeight - cli.scrollTop - cli.clientHeight < 20
|
||||
);
|
||||
this.requestFrame = true;
|
||||
});
|
||||
}
|
||||
|
@ -105,7 +108,9 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
|
|||
// of having the log window fill available height is worth the hack.
|
||||
const margins = 30; // Account for padding and margin on either side of the CLI
|
||||
const cliWindow = this.element;
|
||||
cliWindow.style.height = `${window.innerHeight - cliWindow.offsetTop - margins}px`;
|
||||
cliWindow.style.height = `${
|
||||
window.innerHeight - cliWindow.offsetTop - margins
|
||||
}px`;
|
||||
}
|
||||
|
||||
@task(function* () {
|
||||
|
|
|
@ -27,7 +27,8 @@ export default class TaskGroupRow extends Component {
|
|||
get tooltipText() {
|
||||
if (this.can.cannot('scale job', null, { namespace: this.namespace }))
|
||||
return "You aren't allowed to scale task groups";
|
||||
if (this.runningDeployment) return 'You cannot scale task groups during a deployment';
|
||||
if (this.runningDeployment)
|
||||
return 'You cannot scale task groups during a deployment';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,9 @@ export default class TaskLog extends Component {
|
|||
// If the log request can't settle in one second, the client
|
||||
// must be unavailable and the server should be used instead
|
||||
|
||||
const aborter = window.AbortController ? new AbortController() : new MockAbortController();
|
||||
const aborter = window.AbortController
|
||||
? new AbortController()
|
||||
: new MockAbortController();
|
||||
const timing = this.useServer ? this.serverTimeout : this.clientTimeout;
|
||||
|
||||
// Capture the state of useServer at logger create time to avoid a race
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import Component from '@ember/component';
|
||||
import { classNames, classNameBindings, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
classNameBindings,
|
||||
tagName,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
|
|
@ -8,7 +8,9 @@ export default class Tooltip extends Component {
|
|||
}
|
||||
|
||||
const prefix = inputText.substr(0, 15).trim();
|
||||
const suffix = inputText.substr(inputText.length - 10, inputText.length).trim();
|
||||
const suffix = inputText
|
||||
.substr(inputText.length - 10, inputText.length)
|
||||
.trim();
|
||||
return `${prefix}...${suffix}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,14 @@ export default class TopoViz extends Component {
|
|||
@styleStringProperty('tooltipProps') tooltipStyle;
|
||||
|
||||
get isSingleColumn() {
|
||||
if (this.topology.datacenters.length <= 1 || this.viewportColumns === 1) return true;
|
||||
if (this.topology.datacenters.length <= 1 || this.viewportColumns === 1)
|
||||
return true;
|
||||
|
||||
// Compute the coefficient of variance to determine if it would be
|
||||
// better to stack datacenters or place them in columns
|
||||
const nodeCounts = this.topology.datacenters.map((datacenter) => datacenter.nodes.length);
|
||||
const nodeCounts = this.topology.datacenters.map(
|
||||
(datacenter) => datacenter.nodes.length
|
||||
);
|
||||
const variationCoefficient = deviation(nodeCounts) / mean(nodeCounts);
|
||||
|
||||
// The point at which the varation is too extreme for a two column layout
|
||||
|
@ -43,7 +46,10 @@ export default class TopoViz extends Component {
|
|||
// If there are enough nodes, use two columns of nodes within
|
||||
// a single column layout of datacenters to increase density.
|
||||
if (this.viewportColumns === 1) return true;
|
||||
return !this.isSingleColumn || (this.isSingleColumn && this.args.nodes.length <= 20);
|
||||
return (
|
||||
!this.isSingleColumn ||
|
||||
(this.isSingleColumn && this.args.nodes.length <= 20)
|
||||
);
|
||||
}
|
||||
|
||||
// Once a cluster is large enough, the exact details of a node are
|
||||
|
@ -110,7 +116,10 @@ export default class TopoViz extends Component {
|
|||
// Ignore orphaned allocations and allocations on nodes with an old Nomad agent version.
|
||||
if (!nodeContainer) return;
|
||||
|
||||
const allocationContainer = this.dataForAllocation(allocation, nodeContainer);
|
||||
const allocationContainer = this.dataForAllocation(
|
||||
allocation,
|
||||
nodeContainer
|
||||
);
|
||||
nodeContainer.allocations.push(allocationContainer);
|
||||
|
||||
const key = allocationContainer.groupKey;
|
||||
|
@ -119,11 +128,15 @@ export default class TopoViz extends Component {
|
|||
});
|
||||
|
||||
// Group nodes into datacenters
|
||||
const datacentersMap = nodeContainers.reduce((datacenters, nodeContainer) => {
|
||||
if (!datacenters[nodeContainer.datacenter]) datacenters[nodeContainer.datacenter] = [];
|
||||
datacenters[nodeContainer.datacenter].push(nodeContainer);
|
||||
return datacenters;
|
||||
}, {});
|
||||
const datacentersMap = nodeContainers.reduce(
|
||||
(datacenters, nodeContainer) => {
|
||||
if (!datacenters[nodeContainer.datacenter])
|
||||
datacenters[nodeContainer.datacenter] = [];
|
||||
datacenters[nodeContainer.datacenter].push(nodeContainer);
|
||||
return datacenters;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// Turn hash of datacenters into a sorted array
|
||||
const datacenters = Object.keys(datacentersMap)
|
||||
|
@ -191,7 +204,8 @@ export default class TopoViz extends Component {
|
|||
this.activeEdges = [];
|
||||
|
||||
if (this.topology.selectedKey) {
|
||||
const selectedAllocations = this.topology.allocationIndex[this.topology.selectedKey];
|
||||
const selectedAllocations =
|
||||
this.topology.allocationIndex[this.topology.selectedKey];
|
||||
if (selectedAllocations) {
|
||||
selectedAllocations.forEach((allocation) => {
|
||||
set(allocation, 'isSelected', false);
|
||||
|
@ -205,7 +219,8 @@ export default class TopoViz extends Component {
|
|||
}
|
||||
this.activeNode = null;
|
||||
this.activeAllocation = allocation;
|
||||
const selectedAllocations = this.topology.allocationIndex[this.topology.selectedKey];
|
||||
const selectedAllocations =
|
||||
this.topology.allocationIndex[this.topology.selectedKey];
|
||||
if (selectedAllocations) {
|
||||
selectedAllocations.forEach((allocation) => {
|
||||
set(allocation, 'isSelected', false);
|
||||
|
@ -213,7 +228,8 @@ export default class TopoViz extends Component {
|
|||
}
|
||||
|
||||
set(this.topology, 'selectedKey', allocation.groupKey);
|
||||
const newAllocations = this.topology.allocationIndex[this.topology.selectedKey];
|
||||
const newAllocations =
|
||||
this.topology.allocationIndex[this.topology.selectedKey];
|
||||
if (newAllocations) {
|
||||
newAllocations.forEach((allocation) => {
|
||||
set(allocation, 'isSelected', true);
|
||||
|
@ -221,14 +237,19 @@ export default class TopoViz extends Component {
|
|||
}
|
||||
|
||||
// Only show the lines if the selected allocations are sparse (low count relative to the client count or low count generally).
|
||||
if (newAllocations.length < 10 || newAllocations.length < this.args.nodes.length * 0.75) {
|
||||
if (
|
||||
newAllocations.length < 10 ||
|
||||
newAllocations.length < this.args.nodes.length * 0.75
|
||||
) {
|
||||
this.computedActiveEdges();
|
||||
} else {
|
||||
this.activeEdges = [];
|
||||
}
|
||||
}
|
||||
if (this.args.onAllocationSelect)
|
||||
this.args.onAllocationSelect(this.activeAllocation && this.activeAllocation.allocation);
|
||||
this.args.onAllocationSelect(
|
||||
this.activeAllocation && this.activeAllocation.allocation
|
||||
);
|
||||
if (this.args.onNodeSelect) this.args.onNodeSelect(this.activeNode);
|
||||
}
|
||||
|
||||
|
@ -251,11 +272,15 @@ export default class TopoViz extends Component {
|
|||
const path = line().curve(curveBasis);
|
||||
// 1. Get the active element
|
||||
const allocation = this.activeAllocation.allocation;
|
||||
const activeEl = this.element.querySelector(`[data-allocation-id="${allocation.id}"]`);
|
||||
const activeEl = this.element.querySelector(
|
||||
`[data-allocation-id="${allocation.id}"]`
|
||||
);
|
||||
const activePoint = centerOfBBox(activeEl.getBoundingClientRect());
|
||||
|
||||
// 2. Collect the mem and cpu pairs for all selected allocs
|
||||
const selectedMem = Array.from(this.element.querySelectorAll('.memory .bar.is-selected'));
|
||||
const selectedMem = Array.from(
|
||||
this.element.querySelectorAll('.memory .bar.is-selected')
|
||||
);
|
||||
const selectedPairs = selectedMem.map((mem) => {
|
||||
const id = mem.closest('[data-allocation-id]').dataset.allocationId;
|
||||
const cpu = mem
|
||||
|
@ -283,8 +308,14 @@ export default class TopoViz extends Component {
|
|||
const stepsSecondary = [0.8, 1.0];
|
||||
selectedPoints.forEach((points) => {
|
||||
curves.push(
|
||||
curveFromPoints(...pointsAlongPath(activePoint, points[2], stepsMain), points[0]),
|
||||
curveFromPoints(...pointsAlongPath(activePoint, points[2], stepsSecondary), points[1])
|
||||
curveFromPoints(
|
||||
...pointsAlongPath(activePoint, points[2], stepsMain),
|
||||
points[0]
|
||||
),
|
||||
curveFromPoints(
|
||||
...pointsAlongPath(activePoint, points[2], stepsSecondary),
|
||||
points[1]
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ import Component from '@glimmer/component';
|
|||
export default class TopoVizDatacenter extends Component {
|
||||
get scheduledAllocations() {
|
||||
return this.args.datacenter.nodes.reduce(
|
||||
(all, node) => all.concat(node.allocations.filterBy('allocation.isScheduled')),
|
||||
(all, node) =>
|
||||
all.concat(node.allocations.filterBy('allocation.isScheduled')),
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ export default class TopoVizNode extends Component {
|
|||
@tracked activeAllocation = null;
|
||||
|
||||
get height() {
|
||||
return this.args.heightScale ? this.args.heightScale(this.args.node.memory) : 15;
|
||||
return this.args.heightScale
|
||||
? this.args.heightScale(this.args.node.memory)
|
||||
: 15;
|
||||
}
|
||||
|
||||
get labelHeight() {
|
||||
|
@ -57,11 +59,13 @@ export default class TopoVizNode extends Component {
|
|||
get allocations() {
|
||||
// Sort by the delta between memory and cpu percent. This creates the least amount of
|
||||
// drift between the positional alignment of an alloc's cpu and memory representations.
|
||||
return this.args.node.allocations.filterBy('allocation.isScheduled').sort((a, b) => {
|
||||
const deltaA = Math.abs(a.memoryPercent - a.cpuPercent);
|
||||
const deltaB = Math.abs(b.memoryPercent - b.cpuPercent);
|
||||
return deltaA - deltaB;
|
||||
});
|
||||
return this.args.node.allocations
|
||||
.filterBy('allocation.isScheduled')
|
||||
.sort((a, b) => {
|
||||
const deltaA = Math.abs(a.memoryPercent - a.cpuPercent);
|
||||
const deltaB = Math.abs(b.memoryPercent - b.cpuPercent);
|
||||
return deltaA - deltaB;
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -91,7 +95,8 @@ export default class TopoVizNode extends Component {
|
|||
@action
|
||||
highlightAllocation(allocation, { target }) {
|
||||
this.activeAllocation = allocation;
|
||||
this.args.onAllocationFocus && this.args.onAllocationFocus(allocation, target);
|
||||
this.args.onAllocationFocus &&
|
||||
this.args.onAllocationFocus(allocation, target);
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -9,7 +9,10 @@ import classic from 'ember-classic-decorator';
|
|||
|
||||
@classic
|
||||
@classNames('two-step-button')
|
||||
@classNameBindings('inlineText:has-inline-text', 'fadingBackground:has-fading-background')
|
||||
@classNameBindings(
|
||||
'inlineText:has-inline-text',
|
||||
'fadingBackground:has-fading-background'
|
||||
)
|
||||
export default class TwoStepButton extends Component {
|
||||
idleText = '';
|
||||
cancelText = '';
|
||||
|
|
|
@ -10,11 +10,17 @@ import intersection from 'lodash.intersection';
|
|||
import Sortable from 'nomad-ui/mixins/sortable';
|
||||
import Searchable from 'nomad-ui/mixins/searchable';
|
||||
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class ClientController extends Controller.extend(Sortable, Searchable) {
|
||||
export default class ClientController extends Controller.extend(
|
||||
Sortable,
|
||||
Searchable
|
||||
) {
|
||||
queryParams = [
|
||||
{
|
||||
currentPage: 'page',
|
||||
|
@ -66,18 +72,32 @@ export default class ClientController extends Controller.extend(Sortable, Search
|
|||
return this.onlyPreemptions ? this.preemptions : this.model.allocations;
|
||||
}
|
||||
|
||||
@computed('visibleAllocations.[]', 'selectionNamespace', 'selectionJob', 'selectionStatus')
|
||||
@computed(
|
||||
'visibleAllocations.[]',
|
||||
'selectionNamespace',
|
||||
'selectionJob',
|
||||
'selectionStatus'
|
||||
)
|
||||
get filteredAllocations() {
|
||||
const { selectionNamespace, selectionJob, selectionStatus } = this;
|
||||
|
||||
return this.visibleAllocations.filter((alloc) => {
|
||||
if (selectionNamespace.length && !selectionNamespace.includes(alloc.get('namespace'))) {
|
||||
if (
|
||||
selectionNamespace.length &&
|
||||
!selectionNamespace.includes(alloc.get('namespace'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (selectionJob.length && !selectionJob.includes(alloc.get('plainJobId'))) {
|
||||
if (
|
||||
selectionJob.length &&
|
||||
!selectionJob.includes(alloc.get('plainJobId'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (selectionStatus.length && !selectionStatus.includes(alloc.clientStatus)) {
|
||||
if (
|
||||
selectionStatus.length &&
|
||||
!selectionStatus.includes(alloc.clientStatus)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -217,12 +237,17 @@ export default class ClientController extends Controller.extend(Sortable, Search
|
|||
|
||||
@computed('model.allocations.[]', 'selectionNamespace')
|
||||
get optionsNamespace() {
|
||||
const ns = Array.from(new Set(this.model.allocations.mapBy('namespace'))).compact();
|
||||
const ns = Array.from(
|
||||
new Set(this.model.allocations.mapBy('namespace'))
|
||||
).compact();
|
||||
|
||||
// Update query param when the list of namespaces changes.
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpNamespace', serialize(intersection(ns, this.selectionNamespace)));
|
||||
this.set(
|
||||
'qpNamespace',
|
||||
serialize(intersection(ns, this.selectionNamespace))
|
||||
);
|
||||
});
|
||||
|
||||
return ns.sort().map((n) => ({ key: n, label: n }));
|
||||
|
|
|
@ -7,7 +7,10 @@ import { scheduleOnce } from '@ember/runloop';
|
|||
import intersection from 'lodash.intersection';
|
||||
import SortableFactory from 'nomad-ui/mixins/sortable-factory';
|
||||
import Searchable from 'nomad-ui/mixins/searchable';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
@ -83,7 +86,10 @@ export default class IndexController extends Controller.extend(
|
|||
// Remove any invalid node classes from the query param/selection
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpClass', serialize(intersection(classes, this.selectionClass)));
|
||||
this.set(
|
||||
'qpClass',
|
||||
serialize(intersection(classes, this.selectionClass))
|
||||
);
|
||||
});
|
||||
|
||||
return classes.sort().map((dc) => ({ key: dc, label: dc }));
|
||||
|
@ -102,12 +108,17 @@ export default class IndexController extends Controller.extend(
|
|||
|
||||
@computed('nodes.[]', 'selectionDatacenter')
|
||||
get optionsDatacenter() {
|
||||
const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact();
|
||||
const datacenters = Array.from(
|
||||
new Set(this.nodes.mapBy('datacenter'))
|
||||
).compact();
|
||||
|
||||
// Remove any invalid datacenters from the query param/selection
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter)));
|
||||
this.set(
|
||||
'qpDatacenter',
|
||||
serialize(intersection(datacenters, this.selectionDatacenter))
|
||||
);
|
||||
});
|
||||
|
||||
return datacenters.sort().map((dc) => ({ key: dc, label: dc }));
|
||||
|
@ -120,7 +131,10 @@ export default class IndexController extends Controller.extend(
|
|||
// Remove any invalid versions from the query param/selection
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpVersion', serialize(intersection(versions, this.selectionVersion)));
|
||||
this.set(
|
||||
'qpVersion',
|
||||
serialize(intersection(versions, this.selectionVersion))
|
||||
);
|
||||
});
|
||||
|
||||
return versions.sort().map((v) => ({ key: v, label: v }));
|
||||
|
@ -135,7 +149,10 @@ export default class IndexController extends Controller.extend(
|
|||
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpVolume', serialize(intersection(volumes, this.selectionVolume)));
|
||||
this.set(
|
||||
'qpVolume',
|
||||
serialize(intersection(volumes, this.selectionVolume))
|
||||
);
|
||||
});
|
||||
|
||||
return volumes.sort().map((volume) => ({ key: volume, label: volume }));
|
||||
|
@ -165,11 +182,18 @@ export default class IndexController extends Controller.extend(
|
|||
const statuses = states.without('ineligible').without('draining');
|
||||
|
||||
return this.nodes.filter((node) => {
|
||||
if (classes.length && !classes.includes(node.get('nodeClass'))) return false;
|
||||
if (statuses.length && !statuses.includes(node.get('status'))) return false;
|
||||
if (datacenters.length && !datacenters.includes(node.get('datacenter'))) return false;
|
||||
if (versions.length && !versions.includes(node.get('version'))) return false;
|
||||
if (volumes.length && !node.hostVolumes.find((volume) => volumes.includes(volume.name)))
|
||||
if (classes.length && !classes.includes(node.get('nodeClass')))
|
||||
return false;
|
||||
if (statuses.length && !statuses.includes(node.get('status')))
|
||||
return false;
|
||||
if (datacenters.length && !datacenters.includes(node.get('datacenter')))
|
||||
return false;
|
||||
if (versions.length && !versions.includes(node.get('version')))
|
||||
return false;
|
||||
if (
|
||||
volumes.length &&
|
||||
!node.hostVolumes.find((volume) => volumes.includes(volume.name))
|
||||
)
|
||||
return false;
|
||||
|
||||
if (onlyIneligible && node.get('isEligible')) return false;
|
||||
|
|
|
@ -59,6 +59,9 @@ export default class IndexController extends Controller.extend(
|
|||
|
||||
@action
|
||||
gotoPlugin(plugin, event) {
|
||||
lazyClick([() => this.transitionToRoute('csi.plugins.plugin', plugin.plainId), event]);
|
||||
lazyClick([
|
||||
() => this.transitionToRoute('csi.plugins.plugin', plugin.plainId),
|
||||
event,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ import { action, computed } from '@ember/object';
|
|||
import { alias, readOnly } from '@ember/object/computed';
|
||||
import SortableFactory from 'nomad-ui/mixins/sortable-factory';
|
||||
import { lazyClick } from 'nomad-ui/helpers/lazy-click';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
@ -82,7 +85,8 @@ export default class AllocationsController extends Controller.extend(
|
|||
listToFilter = this.model.nodes;
|
||||
}
|
||||
|
||||
if (healths.length === 1 && healths[0] === 'true') return listToFilter.filterBy('healthy');
|
||||
if (healths.length === 1 && healths[0] === 'true')
|
||||
return listToFilter.filterBy('healthy');
|
||||
if (healths.length === 1 && healths[0] === 'false')
|
||||
return listToFilter.filterBy('healthy', false);
|
||||
return listToFilter;
|
||||
|
@ -103,6 +107,9 @@ export default class AllocationsController extends Controller.extend(
|
|||
|
||||
@action
|
||||
gotoAllocation(allocation, event) {
|
||||
lazyClick([() => this.transitionToRoute('allocations.allocation', allocation), event]);
|
||||
lazyClick([
|
||||
() => this.transitionToRoute('allocations.allocation', allocation),
|
||||
event,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,25 @@ export default class ExecController extends Controller {
|
|||
@computed('model.allocations.@each.clientStatus')
|
||||
get pendingAndRunningAllocations() {
|
||||
return this.model.allocations.filter(
|
||||
(allocation) => allocation.clientStatus === 'pending' || allocation.clientStatus === 'running'
|
||||
(allocation) =>
|
||||
allocation.clientStatus === 'pending' ||
|
||||
allocation.clientStatus === 'running'
|
||||
);
|
||||
}
|
||||
|
||||
@mapBy('pendingAndRunningAllocations', 'taskGroup') pendingAndRunningTaskGroups;
|
||||
@mapBy('pendingAndRunningAllocations', 'taskGroup')
|
||||
pendingAndRunningTaskGroups;
|
||||
@uniq('pendingAndRunningTaskGroups') uniquePendingAndRunningTaskGroups;
|
||||
|
||||
taskGroupSorting = ['name'];
|
||||
@sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting') sortedTaskGroups;
|
||||
@sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting')
|
||||
sortedTaskGroups;
|
||||
|
||||
setUpTerminal(Terminal) {
|
||||
this.terminal = new Terminal({ fontFamily: 'monospace', fontWeight: '400' });
|
||||
this.terminal = new Terminal({
|
||||
fontFamily: 'monospace',
|
||||
fontWeight: '400',
|
||||
});
|
||||
window.execTerminal = this.terminal; // Issue to improve: https://github.com/hashicorp/nomad/issues/7457
|
||||
|
||||
this.terminal.write(ANSI_UI_GRAY_400);
|
||||
|
@ -64,7 +71,10 @@ export default class ExecController extends Controller {
|
|||
allocation = this.allocations.findBy('shortId', this.allocationShortId);
|
||||
} else {
|
||||
allocation = this.allocations.find((allocation) =>
|
||||
allocation.states.filterBy('isActive').mapBy('name').includes(this.taskName)
|
||||
allocation.states
|
||||
.filterBy('isActive')
|
||||
.mapBy('name')
|
||||
.includes(this.taskName)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -94,7 +104,9 @@ export default class ExecController extends Controller {
|
|||
this.terminal.writeln('');
|
||||
}
|
||||
|
||||
this.terminal.writeln('Customize your command, then hit ‘return’ to run.');
|
||||
this.terminal.writeln(
|
||||
'Customize your command, then hit ‘return’ to run.'
|
||||
);
|
||||
this.terminal.writeln('');
|
||||
this.terminal.write(
|
||||
`$ nomad alloc exec -i -t -task ${escapeTaskName(taskName)} ${
|
||||
|
@ -126,7 +138,9 @@ export default class ExecController extends Controller {
|
|||
|
||||
new ExecSocketXtermAdapter(this.terminal, this.socket, this.token.secret);
|
||||
} else {
|
||||
this.terminal.writeln(`Failed to open a socket because task ${this.taskName} is not active.`);
|
||||
this.terminal.writeln(
|
||||
`Failed to open a socket because task ${this.taskName} is not active.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,17 @@ import { scheduleOnce } from '@ember/runloop';
|
|||
import intersection from 'lodash.intersection';
|
||||
import Sortable from 'nomad-ui/mixins/sortable';
|
||||
import Searchable from 'nomad-ui/mixins/searchable';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class IndexController extends Controller.extend(Sortable, Searchable) {
|
||||
export default class IndexController extends Controller.extend(
|
||||
Sortable,
|
||||
Searchable
|
||||
) {
|
||||
@service system;
|
||||
@service userSettings;
|
||||
|
||||
|
@ -100,7 +106,9 @@ export default class IndexController extends Controller.extend(Sortable, Searcha
|
|||
@computed('selectionDatacenter', 'visibleJobs.[]')
|
||||
get optionsDatacenter() {
|
||||
const flatten = (acc, val) => acc.concat(val);
|
||||
const allDatacenters = new Set(this.visibleJobs.mapBy('datacenters').reduce(flatten, []));
|
||||
const allDatacenters = new Set(
|
||||
this.visibleJobs.mapBy('datacenters').reduce(flatten, [])
|
||||
);
|
||||
|
||||
// Remove any invalid datacenters from the query param/selection
|
||||
const availableDatacenters = Array.from(allDatacenters).compact();
|
||||
|
@ -144,7 +152,10 @@ export default class IndexController extends Controller.extend(Sortable, Searcha
|
|||
const availablePrefixes = prefixes.mapBy('prefix');
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpPrefix', serialize(intersection(availablePrefixes, this.selectionPrefix)));
|
||||
this.set(
|
||||
'qpPrefix',
|
||||
serialize(intersection(availablePrefixes, this.selectionPrefix))
|
||||
);
|
||||
});
|
||||
|
||||
// Sort, format, and include the count in the label
|
||||
|
@ -216,12 +227,18 @@ export default class IndexController extends Controller.extend(Sortable, Searcha
|
|||
return false;
|
||||
}
|
||||
|
||||
if (datacenters.length && !job.get('datacenters').find((dc) => datacenters.includes(dc))) {
|
||||
if (
|
||||
datacenters.length &&
|
||||
!job.get('datacenters').find((dc) => datacenters.includes(dc))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const name = job.get('name');
|
||||
if (prefixes.length && !prefixes.find((prefix) => name.startsWith(prefix))) {
|
||||
if (
|
||||
prefixes.length &&
|
||||
!prefixes.find((prefix) => name.startsWith(prefix))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@ import intersection from 'lodash.intersection';
|
|||
import Sortable from 'nomad-ui/mixins/sortable';
|
||||
import Searchable from 'nomad-ui/mixins/searchable';
|
||||
import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
@ -61,18 +64,32 @@ export default class AllocationsController extends Controller.extend(
|
|||
return this.get('model.allocations') || [];
|
||||
}
|
||||
|
||||
@computed('allocations.[]', 'selectionStatus', 'selectionClient', 'selectionTaskGroup')
|
||||
@computed(
|
||||
'allocations.[]',
|
||||
'selectionStatus',
|
||||
'selectionClient',
|
||||
'selectionTaskGroup'
|
||||
)
|
||||
get filteredAllocations() {
|
||||
const { selectionStatus, selectionClient, selectionTaskGroup } = this;
|
||||
|
||||
return this.allocations.filter((alloc) => {
|
||||
if (selectionStatus.length && !selectionStatus.includes(alloc.clientStatus)) {
|
||||
if (
|
||||
selectionStatus.length &&
|
||||
!selectionStatus.includes(alloc.clientStatus)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (selectionClient.length && !selectionClient.includes(alloc.get('node.shortId'))) {
|
||||
if (
|
||||
selectionClient.length &&
|
||||
!selectionClient.includes(alloc.get('node.shortId'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (selectionTaskGroup.length && !selectionTaskGroup.includes(alloc.taskGroupName)) {
|
||||
if (
|
||||
selectionTaskGroup.length &&
|
||||
!selectionTaskGroup.includes(alloc.taskGroupName)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -104,12 +121,17 @@ export default class AllocationsController extends Controller.extend(
|
|||
|
||||
@computed('model.allocations.[]', 'selectionClient')
|
||||
get optionsClients() {
|
||||
const clients = Array.from(new Set(this.model.allocations.mapBy('node.shortId'))).compact();
|
||||
const clients = Array.from(
|
||||
new Set(this.model.allocations.mapBy('node.shortId'))
|
||||
).compact();
|
||||
|
||||
// Update query param when the list of clients changes.
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpClient', serialize(intersection(clients, this.selectionClient)));
|
||||
this.set(
|
||||
'qpClient',
|
||||
serialize(intersection(clients, this.selectionClient))
|
||||
);
|
||||
});
|
||||
|
||||
return clients.sort().map((c) => ({ key: c, label: c }));
|
||||
|
@ -117,12 +139,17 @@ export default class AllocationsController extends Controller.extend(
|
|||
|
||||
@computed('model.allocations.[]', 'selectionTaskGroup')
|
||||
get optionsTaskGroups() {
|
||||
const taskGroups = Array.from(new Set(this.model.allocations.mapBy('taskGroupName'))).compact();
|
||||
const taskGroups = Array.from(
|
||||
new Set(this.model.allocations.mapBy('taskGroupName'))
|
||||
).compact();
|
||||
|
||||
// Update query param when the list of task groups changes.
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpTaskGroup', serialize(intersection(taskGroups, this.selectionTaskGroup)));
|
||||
this.set(
|
||||
'qpTaskGroup',
|
||||
serialize(intersection(taskGroups, this.selectionTaskGroup))
|
||||
);
|
||||
});
|
||||
|
||||
return taskGroups.sort().map((tg) => ({ key: tg, label: tg }));
|
||||
|
|
|
@ -8,7 +8,10 @@ import SortableFactory 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';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
@ -96,7 +99,10 @@ export default class ClientsController extends Controller.extend(
|
|||
|
||||
return this.nodes
|
||||
.filter((node) => {
|
||||
if (statuses.length && !statuses.includes(this.jobClientStatus.byNode[node.id])) {
|
||||
if (
|
||||
statuses.length &&
|
||||
!statuses.includes(this.jobClientStatus.byNode[node.id])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (datacenters.length && !datacenters.includes(node.datacenter)) {
|
||||
|
@ -109,7 +115,9 @@ export default class ClientsController extends Controller.extend(
|
|||
return true;
|
||||
})
|
||||
.map((node) => {
|
||||
const allocations = this.job.allocations.filter((alloc) => alloc.get('node.id') == node.id);
|
||||
const allocations = this.job.allocations.filter(
|
||||
(alloc) => alloc.get('node.id') == node.id
|
||||
);
|
||||
|
||||
return {
|
||||
node,
|
||||
|
@ -137,12 +145,17 @@ export default class ClientsController extends Controller.extend(
|
|||
|
||||
@computed('selectionDatacenter', 'nodes')
|
||||
get optionsDatacenter() {
|
||||
const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact();
|
||||
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('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter)));
|
||||
this.set(
|
||||
'qpDatacenter',
|
||||
serialize(intersection(datacenters, this.selectionDatacenter))
|
||||
);
|
||||
});
|
||||
|
||||
return datacenters.sort().map((dc) => ({ key: dc, label: dc }));
|
||||
|
@ -150,15 +163,22 @@ export default class ClientsController extends Controller.extend(
|
|||
|
||||
@computed('selectionClientClass', 'nodes')
|
||||
get optionsClientClass() {
|
||||
const clientClasses = Array.from(new Set(this.nodes.mapBy('nodeClass'))).compact();
|
||||
const clientClasses = 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('qpClientClass', serialize(intersection(clientClasses, this.selectionClientClass)));
|
||||
this.set(
|
||||
'qpClientClass',
|
||||
serialize(intersection(clientClasses, this.selectionClientClass))
|
||||
);
|
||||
});
|
||||
|
||||
return clientClasses.sort().map((clientClass) => ({ key: clientClass, label: clientClass }));
|
||||
return clientClasses
|
||||
.sort()
|
||||
.map((clientClass) => ({ key: clientClass, label: clientClass }));
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -4,7 +4,9 @@ import { alias } from '@ember/object/computed';
|
|||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class DefinitionController extends Controller.extend(WithNamespaceResetting) {
|
||||
export default class DefinitionController extends Controller.extend(
|
||||
WithNamespaceResetting
|
||||
) {
|
||||
@alias('model.job') job;
|
||||
@alias('model.definition') definition;
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import { alias } from '@ember/object/computed';
|
|||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class DeploymentsController extends Controller.extend(WithNamespaceResetting) {
|
||||
export default class DeploymentsController extends Controller.extend(
|
||||
WithNamespaceResetting
|
||||
) {
|
||||
@alias('model') job;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ import { action } from '@ember/object';
|
|||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class IndexController extends Controller.extend(WithNamespaceResetting) {
|
||||
export default class IndexController extends Controller.extend(
|
||||
WithNamespaceResetting
|
||||
) {
|
||||
@service system;
|
||||
|
||||
queryParams = [
|
||||
|
@ -38,7 +40,11 @@ export default class IndexController extends Controller.extend(WithNamespaceRese
|
|||
|
||||
@action
|
||||
gotoTaskGroup(taskGroup) {
|
||||
this.transitionToRoute('jobs.job.task-group', taskGroup.get('job'), taskGroup);
|
||||
this.transitionToRoute(
|
||||
'jobs.job.task-group',
|
||||
taskGroup.get('job'),
|
||||
taskGroup
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -9,7 +9,10 @@ import { qpBuilder } from 'nomad-ui/utils/classes/query-params';
|
|||
import Sortable from 'nomad-ui/mixins/sortable';
|
||||
import Searchable from 'nomad-ui/mixins/searchable';
|
||||
import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
@ -65,10 +68,16 @@ export default class TaskGroupController extends Controller.extend(
|
|||
const { selectionStatus, selectionClient } = this;
|
||||
|
||||
return this.allocations.filter((alloc) => {
|
||||
if (selectionStatus.length && !selectionStatus.includes(alloc.clientStatus)) {
|
||||
if (
|
||||
selectionStatus.length &&
|
||||
!selectionStatus.includes(alloc.clientStatus)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (selectionClient.length && !selectionClient.includes(alloc.get('node.shortId'))) {
|
||||
if (
|
||||
selectionClient.length &&
|
||||
!selectionClient.includes(alloc.get('node.shortId'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -94,15 +103,23 @@ export default class TaskGroupController extends Controller.extend(
|
|||
|
||||
@computed('sortedScaleEvents.@each.hasCount', function () {
|
||||
const countEventsCount = this.sortedScaleEvents.filterBy('hasCount').length;
|
||||
return countEventsCount > 1 && countEventsCount >= this.sortedScaleEvents.length / 2;
|
||||
return (
|
||||
countEventsCount > 1 &&
|
||||
countEventsCount >= this.sortedScaleEvents.length / 2
|
||||
);
|
||||
})
|
||||
shouldShowScaleEventTimeline;
|
||||
|
||||
@computed('model.job.{namespace,runningDeployment}')
|
||||
get tooltipText() {
|
||||
if (this.can.cannot('scale job', null, { namespace: this.model.job.namespace.get('name') }))
|
||||
if (
|
||||
this.can.cannot('scale job', null, {
|
||||
namespace: this.model.job.namespace.get('name'),
|
||||
})
|
||||
)
|
||||
return "You aren't allowed to scale task groups";
|
||||
if (this.model.job.runningDeployment) return 'You cannot scale task groups during a deployment';
|
||||
if (this.model.job.runningDeployment)
|
||||
return 'You cannot scale task groups during a deployment';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -128,12 +145,17 @@ export default class TaskGroupController extends Controller.extend(
|
|||
|
||||
@computed('model.allocations.[]', 'selectionClient')
|
||||
get optionsClients() {
|
||||
const clients = Array.from(new Set(this.model.allocations.mapBy('node.shortId'))).compact();
|
||||
const clients = Array.from(
|
||||
new Set(this.model.allocations.mapBy('node.shortId'))
|
||||
).compact();
|
||||
|
||||
// Update query param when the list of clients changes.
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('qpClient', serialize(intersection(clients, this.selectionClient)));
|
||||
this.set(
|
||||
'qpClient',
|
||||
serialize(intersection(clients, this.selectionClient))
|
||||
);
|
||||
});
|
||||
|
||||
return clients.sort().map((dc) => ({ key: dc, label: dc }));
|
||||
|
|
|
@ -12,14 +12,18 @@ const errorLevelToAlertClass = {
|
|||
};
|
||||
|
||||
@classic
|
||||
export default class VersionsController extends Controller.extend(WithNamespaceResetting) {
|
||||
export default class VersionsController extends Controller.extend(
|
||||
WithNamespaceResetting
|
||||
) {
|
||||
error = null;
|
||||
|
||||
@alias('model') job;
|
||||
|
||||
@computed('error.level')
|
||||
get errorLevelClass() {
|
||||
return errorLevelToAlertClass[this.get('error.level')] || alertClassFallback;
|
||||
return (
|
||||
errorLevelToAlertClass[this.get('error.level')] || alertClassFallback
|
||||
);
|
||||
}
|
||||
|
||||
onDismiss() {
|
||||
|
|
|
@ -7,7 +7,10 @@ import { inject as service } from '@ember/service';
|
|||
import { scheduleOnce } from '@ember/runloop';
|
||||
import { task } from 'ember-concurrency';
|
||||
import intersection from 'lodash.intersection';
|
||||
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
||||
import {
|
||||
serialize,
|
||||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
|
||||
import EmberObject, { computed } from '@ember/object';
|
||||
import { alias } from '@ember/object/computed';
|
||||
|
@ -104,13 +107,17 @@ export default class OptimizeController extends Controller {
|
|||
|
||||
get optionsDatacenter() {
|
||||
const flatten = (acc, val) => acc.concat(val);
|
||||
const allDatacenters = new Set(this.summaries.mapBy('job.datacenters').reduce(flatten, []));
|
||||
const allDatacenters = new Set(
|
||||
this.summaries.mapBy('job.datacenters').reduce(flatten, [])
|
||||
);
|
||||
|
||||
// Remove any invalid datacenters from the query param/selection
|
||||
const availableDatacenters = Array.from(allDatacenters).compact();
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.qpDatacenter = serialize(intersection(availableDatacenters, this.selectionDatacenter));
|
||||
this.qpDatacenter = serialize(
|
||||
intersection(availableDatacenters, this.selectionDatacenter)
|
||||
);
|
||||
});
|
||||
|
||||
return availableDatacenters.sort().map((dc) => ({ key: dc, label: dc }));
|
||||
|
@ -144,7 +151,9 @@ export default class OptimizeController extends Controller {
|
|||
const availablePrefixes = prefixes.mapBy('prefix');
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.qpPrefix = serialize(intersection(availablePrefixes, this.selectionPrefix));
|
||||
this.qpPrefix = serialize(
|
||||
intersection(availablePrefixes, this.selectionPrefix)
|
||||
);
|
||||
});
|
||||
|
||||
// Sort, format, and include the count in the label
|
||||
|
@ -171,7 +180,10 @@ export default class OptimizeController extends Controller {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (this.qpNamespace !== '*' && job.get('namespace.name') !== this.qpNamespace) {
|
||||
if (
|
||||
this.qpNamespace !== '*' &&
|
||||
job.get('namespace.name') !== this.qpNamespace
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -183,12 +195,18 @@ export default class OptimizeController extends Controller {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (datacenters.length && !job.get('datacenters').find((dc) => datacenters.includes(dc))) {
|
||||
if (
|
||||
datacenters.length &&
|
||||
!job.get('datacenters').find((dc) => datacenters.includes(dc))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const name = job.get('name');
|
||||
if (prefixes.length && !prefixes.find((prefix) => name.startsWith(prefix))) {
|
||||
if (
|
||||
prefixes.length &&
|
||||
!prefixes.find((prefix) => name.startsWith(prefix))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -207,8 +225,12 @@ export default class OptimizeController extends Controller {
|
|||
// This is a task because the accordion uses timeouts for animation
|
||||
// eslint-disable-next-line require-yield
|
||||
@(task(function* () {
|
||||
const currentSummaryIndex = this.filteredSummaries.indexOf(this.activeRecommendationSummary);
|
||||
const nextSummary = this.filteredSummaries.objectAt(currentSummaryIndex + 1);
|
||||
const currentSummaryIndex = this.filteredSummaries.indexOf(
|
||||
this.activeRecommendationSummary
|
||||
);
|
||||
const nextSummary = this.filteredSummaries.objectAt(
|
||||
currentSummaryIndex + 1
|
||||
);
|
||||
|
||||
if (nextSummary) {
|
||||
this.transitionToSummary(nextSummary);
|
||||
|
|
|
@ -31,13 +31,17 @@ export default class TopologyControllers extends Controller {
|
|||
|
||||
@computed('model.nodes.@each.resources')
|
||||
get totalMemory() {
|
||||
const mibs = this.model.nodes.mapBy('resources.memory').reduce(sumAggregator, 0);
|
||||
const mibs = this.model.nodes
|
||||
.mapBy('resources.memory')
|
||||
.reduce(sumAggregator, 0);
|
||||
return mibs * 1024 * 1024;
|
||||
}
|
||||
|
||||
@computed('model.nodes.@each.resources')
|
||||
get totalCPU() {
|
||||
return this.model.nodes.mapBy('resources.cpu').reduce((sum, cpu) => sum + (cpu || 0), 0);
|
||||
return this.model.nodes
|
||||
.mapBy('resources.cpu')
|
||||
.reduce((sum, cpu) => sum + (cpu || 0), 0);
|
||||
}
|
||||
|
||||
@computed('totalMemory')
|
||||
|
@ -70,7 +74,9 @@ export default class TopologyControllers extends Controller {
|
|||
|
||||
@computed('scheduledAllocations.@each.allocatedResources')
|
||||
get totalReservedCPU() {
|
||||
return this.scheduledAllocations.mapBy('allocatedResources.cpu').reduce(sumAggregator, 0);
|
||||
return this.scheduledAllocations
|
||||
.mapBy('allocatedResources.cpu')
|
||||
.reduce(sumAggregator, 0);
|
||||
}
|
||||
|
||||
@computed('totalMemory', 'totalReservedMemory')
|
||||
|
@ -85,23 +91,35 @@ export default class TopologyControllers extends Controller {
|
|||
return this.totalReservedCPU / this.totalCPU;
|
||||
}
|
||||
|
||||
@computed('activeAllocation.taskGroupName', 'scheduledAllocations.@each.{job,taskGroupName}')
|
||||
@computed(
|
||||
'activeAllocation.taskGroupName',
|
||||
'scheduledAllocations.@each.{job,taskGroupName}'
|
||||
)
|
||||
get siblingAllocations() {
|
||||
if (!this.activeAllocation) return [];
|
||||
const taskGroup = this.activeAllocation.taskGroupName;
|
||||
const jobId = this.activeAllocation.belongsTo('job').id();
|
||||
|
||||
return this.scheduledAllocations.filter((allocation) => {
|
||||
return allocation.taskGroupName === taskGroup && allocation.belongsTo('job').id() === jobId;
|
||||
return (
|
||||
allocation.taskGroupName === taskGroup &&
|
||||
allocation.belongsTo('job').id() === jobId
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@computed('activeNode')
|
||||
get nodeUtilization() {
|
||||
const node = this.activeNode;
|
||||
const [formattedMemory, memoryUnits] = reduceBytes(node.memory * 1024 * 1024);
|
||||
const totalReservedMemory = node.allocations.mapBy('memory').reduce(sumAggregator, 0);
|
||||
const totalReservedCPU = node.allocations.mapBy('cpu').reduce(sumAggregator, 0);
|
||||
const [formattedMemory, memoryUnits] = reduceBytes(
|
||||
node.memory * 1024 * 1024
|
||||
);
|
||||
const totalReservedMemory = node.allocations
|
||||
.mapBy('memory')
|
||||
.reduce(sumAggregator, 0);
|
||||
const totalReservedCPU = node.allocations
|
||||
.mapBy('cpu')
|
||||
.reduce(sumAggregator, 0);
|
||||
|
||||
return {
|
||||
totalMemoryFormatted: formattedMemory.toFixed(2),
|
||||
|
|
|
@ -9,7 +9,10 @@ import { assert } from '@ember/debug';
|
|||
* Returns a version of a function bound to the template target (e.g., component or controller)
|
||||
*/
|
||||
export function bind([func, target]) {
|
||||
assert('A function is required as the first argument', typeof func === 'function');
|
||||
assert(
|
||||
'A function is required as the first argument',
|
||||
typeof func === 'function'
|
||||
);
|
||||
assert('A context is required as the second argument', target);
|
||||
return func.bind(target);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import Helper from '@ember/component/helper';
|
||||
|
||||
export function isObject([value]) {
|
||||
const isObject = !Array.isArray(value) && value !== null && typeof value === 'object';
|
||||
const isObject =
|
||||
!Array.isArray(value) && value !== null && typeof value === 'object';
|
||||
return isObject;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ import SVGs from '../svgs';
|
|||
*/
|
||||
export function xIcon(params, options) {
|
||||
const name = params[0];
|
||||
const classes = [options.class, 'icon', `icon-is-${name}`].compact().join(' ');
|
||||
const classes = [options.class, 'icon', `icon-is-${name}`]
|
||||
.compact()
|
||||
.join(' ');
|
||||
|
||||
return inlineSvg(SVGs, name, { class: classes });
|
||||
}
|
||||
|
|
|
@ -95,7 +95,11 @@ export default Mixin.create({
|
|||
|
||||
if (this.exactMatchEnabled) {
|
||||
results.push(
|
||||
...exactMatchSearch(searchTerm, this.listToSearch, this.exactMatchSearchProps)
|
||||
...exactMatchSearch(
|
||||
searchTerm,
|
||||
this.listToSearch,
|
||||
this.exactMatchSearchProps
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -114,7 +118,9 @@ export default Mixin.create({
|
|||
}
|
||||
|
||||
if (this.regexEnabled) {
|
||||
results.push(...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps));
|
||||
results.push(
|
||||
...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps)
|
||||
);
|
||||
}
|
||||
|
||||
return results.uniq();
|
||||
|
@ -134,7 +140,9 @@ function regexSearch(term, list, keys) {
|
|||
const regex = new RegExp(term, 'i');
|
||||
// Test the value of each key for each object against the regex
|
||||
// All that match are returned.
|
||||
return list.filter((item) => keys.some((key) => regex.test(get(item, key))));
|
||||
return list.filter((item) =>
|
||||
keys.some((key) => regex.test(get(item, key)))
|
||||
);
|
||||
} catch (e) {
|
||||
// Swallow the error; most likely due to an eager search of an incomplete regex
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ import { warn } from '@ember/debug';
|
|||
- listSorted: a copy of listToSort that has been sorted
|
||||
*/
|
||||
export default function sortableFactory(properties, fromSortableMixin) {
|
||||
const eachProperties = properties.map((property) => `listToSort.@each.${property}`);
|
||||
const eachProperties = properties.map(
|
||||
(property) => `listToSort.@each.${property}`
|
||||
);
|
||||
|
||||
// eslint-disable-next-line ember/no-new-mixins
|
||||
return Mixin.create({
|
||||
|
@ -44,10 +46,13 @@ export default function sortableFactory(properties, fromSortableMixin) {
|
|||
'Using SortableFactory without property keys means the list will only sort when the members change, not when any of their properties change.';
|
||||
|
||||
if (fromSortableMixin) {
|
||||
message += ' The Sortable mixin is deprecated in favor of SortableFactory.';
|
||||
message +=
|
||||
' The Sortable mixin is deprecated in favor of SortableFactory.';
|
||||
}
|
||||
|
||||
warn(message, properties.length > 0, { id: 'nomad.no-sortable-properties' });
|
||||
warn(message, properties.length > 0, {
|
||||
id: 'nomad.no-sortable-properties',
|
||||
});
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set('_sortableFactoryWarningPrinted', true);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ import { on } from '@ember/object/evented';
|
|||
// eslint-disable-next-line ember/no-new-mixins
|
||||
export default Mixin.create({
|
||||
windowResizeHandler() {
|
||||
assert('windowResizeHandler needs to be overridden in the Component', false);
|
||||
assert(
|
||||
'windowResizeHandler needs to be overridden in the Component',
|
||||
false
|
||||
);
|
||||
},
|
||||
|
||||
setupWindowResize: on('didInsertElement', function () {
|
||||
|
|
|
@ -36,7 +36,10 @@ export default Mixin.create(WithVisibilityDetection, {
|
|||
actions: {
|
||||
willTransition(transition) {
|
||||
// Don't cancel watchers if transitioning into a sub-route
|
||||
if (!transition.intent.name || !transition.intent.name.startsWith(this.routeName)) {
|
||||
if (
|
||||
!transition.intent.name ||
|
||||
!transition.intent.name.startsWith(this.routeName)
|
||||
) {
|
||||
this.cancelAllWatchers();
|
||||
}
|
||||
|
||||
|
|
|
@ -77,8 +77,10 @@ export default class Allocation extends Model {
|
|||
@belongsTo('allocation', { inverse: 'nextAllocation' }) previousAllocation;
|
||||
@belongsTo('allocation', { inverse: 'previousAllocation' }) nextAllocation;
|
||||
|
||||
@hasMany('allocation', { inverse: 'preemptedByAllocation' }) preemptedAllocations;
|
||||
@belongsTo('allocation', { inverse: 'preemptedAllocations' }) preemptedByAllocation;
|
||||
@hasMany('allocation', { inverse: 'preemptedByAllocation' })
|
||||
preemptedAllocations;
|
||||
@belongsTo('allocation', { inverse: 'preemptedAllocations' })
|
||||
preemptedByAllocation;
|
||||
@attr('boolean') wasPreempted;
|
||||
|
||||
@belongsTo('evaluation') followUpEvaluation;
|
||||
|
@ -135,7 +137,11 @@ export default class Allocation extends Model {
|
|||
return this.get('rescheduleEvents.length') > 0 || this.nextAllocation;
|
||||
}
|
||||
|
||||
@computed('clientStatus', 'followUpEvaluation.content', 'nextAllocation.content')
|
||||
@computed(
|
||||
'clientStatus',
|
||||
'followUpEvaluation.content',
|
||||
'nextAllocation.content'
|
||||
)
|
||||
get hasStoppedRescheduling() {
|
||||
return (
|
||||
!this.get('nextAllocation.content') &&
|
||||
|
|
|
@ -24,7 +24,10 @@ export default class Deployment extends Model {
|
|||
this.status === 'running' &&
|
||||
this.taskGroupSummaries
|
||||
.toArray()
|
||||
.some((summary) => summary.get('requiresPromotion') && !summary.get('promoted'))
|
||||
.some(
|
||||
(summary) =>
|
||||
summary.get('requiresPromotion') && !summary.get('promoted')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -38,7 +41,10 @@ export default class Deployment extends Model {
|
|||
|
||||
@computed('versionNumber', 'job.versions.content.@each.number')
|
||||
get version() {
|
||||
return (this.get('job.versions') || []).findBy('number', this.versionNumber);
|
||||
return (this.get('job.versions') || []).findBy(
|
||||
'number',
|
||||
this.versionNumber
|
||||
);
|
||||
}
|
||||
|
||||
// Dependent keys can only go one level past an @each so an alias is needed
|
||||
|
@ -65,7 +71,10 @@ export default class Deployment extends Model {
|
|||
}
|
||||
|
||||
promote() {
|
||||
assert('A deployment needs to requirePromotion to be promoted', this.requiresPromotion);
|
||||
assert(
|
||||
'A deployment needs to requirePromotion to be promoted',
|
||||
this.requiresPromotion
|
||||
);
|
||||
return this.store.adapterFor('deployment').promote(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ export default class Evaluation extends Model {
|
|||
@attr('string') triggeredBy;
|
||||
@attr('string') status;
|
||||
@attr('string') statusDescription;
|
||||
@fragmentArray('placement-failure', { defaultValue: () => [] }) failedTGAllocs;
|
||||
@fragmentArray('placement-failure', { defaultValue: () => [] })
|
||||
failedTGAllocs;
|
||||
|
||||
@bool('failedTGAllocs.length') hasPlacementFailures;
|
||||
@equal('status', 'blocked') isBlocked;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { hasMany } from '@ember-data/model';
|
|||
|
||||
export default class JobPlan extends Model {
|
||||
@attr() diff;
|
||||
@fragmentArray('placement-failure', { defaultValue: () => [] }) failedTGAllocs;
|
||||
@fragmentArray('placement-failure', { defaultValue: () => [] })
|
||||
failedTGAllocs;
|
||||
@hasMany('allocation') preemptions;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,9 @@ export default class Job extends Model {
|
|||
// The parent job name is prepended to child launch job names
|
||||
@computed('name', 'parent.content')
|
||||
get trimmedName() {
|
||||
return this.get('parent.content') ? this.name.replace(/.+?\//, '') : this.name;
|
||||
return this.get('parent.content')
|
||||
? this.name.replace(/.+?\//, '')
|
||||
: this.name;
|
||||
}
|
||||
|
||||
// A composite of type and other job attributes to determine
|
||||
|
@ -69,7 +71,12 @@ export default class Job extends Model {
|
|||
|
||||
// A composite of type and other job attributes to determine
|
||||
// type for templating rather than scheduling
|
||||
@computed('type', 'periodic', 'parameterized', 'parent.{periodic,parameterized}')
|
||||
@computed(
|
||||
'type',
|
||||
'periodic',
|
||||
'parameterized',
|
||||
'parent.{periodic,parameterized}'
|
||||
)
|
||||
get templateType() {
|
||||
const type = this.type;
|
||||
|
||||
|
@ -158,7 +165,9 @@ export default class Job extends Model {
|
|||
|
||||
@computed('evaluations.@each.isBlocked')
|
||||
get hasBlockedEvaluation() {
|
||||
return this.evaluations.toArray().some((evaluation) => evaluation.get('isBlocked'));
|
||||
return this.evaluations
|
||||
.toArray()
|
||||
.some((evaluation) => evaluation.get('isBlocked'));
|
||||
}
|
||||
|
||||
@and('latestFailureEvaluation', 'hasBlockedEvaluation') hasPlacementFailures;
|
||||
|
@ -256,7 +265,8 @@ export default class Job extends Model {
|
|||
}
|
||||
|
||||
scale(group, count, message) {
|
||||
if (message == null) message = `Manually scaled to ${count} from the Nomad UI`;
|
||||
if (message == null)
|
||||
message = `Manually scaled to ${count} from the Nomad UI`;
|
||||
return this.store.adapterFor('job').scale(this, group, count, message);
|
||||
}
|
||||
|
||||
|
@ -278,7 +288,10 @@ export default class Job extends Model {
|
|||
}
|
||||
|
||||
resetId() {
|
||||
this.set('id', JSON.stringify([this.plainId, this.get('namespace.name') || 'default']));
|
||||
this.set(
|
||||
'id',
|
||||
JSON.stringify([this.plainId, this.get('namespace.name') || 'default'])
|
||||
);
|
||||
}
|
||||
|
||||
@computed('status')
|
||||
|
|
|
@ -63,7 +63,9 @@ export default class Node extends Model {
|
|||
|
||||
@computed('allocations.@each.{isMigrating,isRunning}')
|
||||
get migratingAllocations() {
|
||||
return this.allocations.filter((alloc) => alloc.isRunning && alloc.isMigrating);
|
||||
return this.allocations.filter(
|
||||
(alloc) => alloc.isRunning && alloc.isMigrating
|
||||
);
|
||||
}
|
||||
|
||||
@computed('allocations.@each.{isMigrating,isRunning,modifyTime}')
|
||||
|
|
|
@ -5,7 +5,8 @@ import { action } from '@ember/object';
|
|||
|
||||
export default class RecommendationSummary extends Model {
|
||||
@hasMany('recommendation') recommendations;
|
||||
@hasMany('recommendation', { defaultValue: () => [] }) excludedRecommendations;
|
||||
@hasMany('recommendation', { defaultValue: () => [] })
|
||||
excludedRecommendations;
|
||||
|
||||
@belongsTo('job') job;
|
||||
@attr('string') jobId;
|
||||
|
@ -30,7 +31,8 @@ export default class RecommendationSummary extends Model {
|
|||
@action
|
||||
toggleRecommendation(recommendation) {
|
||||
if (this.excludedRecommendations.includes(recommendation)) {
|
||||
this.excludedRecommendations = this.excludedRecommendations.removeObject(recommendation);
|
||||
this.excludedRecommendations =
|
||||
this.excludedRecommendations.removeObject(recommendation);
|
||||
} else {
|
||||
this.excludedRecommendations.pushObject(recommendation);
|
||||
}
|
||||
|
@ -39,9 +41,14 @@ export default class RecommendationSummary extends Model {
|
|||
@action
|
||||
toggleAllRecommendationsForResource(resource, enabled) {
|
||||
if (enabled) {
|
||||
this.excludedRecommendations = this.excludedRecommendations.rejectBy('resource', resource);
|
||||
this.excludedRecommendations = this.excludedRecommendations.rejectBy(
|
||||
'resource',
|
||||
resource
|
||||
);
|
||||
} else {
|
||||
this.excludedRecommendations.pushObjects(this.recommendations.filterBy('resource', resource));
|
||||
this.excludedRecommendations.pushObjects(
|
||||
this.recommendations.filterBy('resource', resource)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ import { get } from '@ember/object';
|
|||
|
||||
export default class Recommendation extends Model {
|
||||
@belongsTo('job') job;
|
||||
@belongsTo('recommendation-summary', { inverse: 'recommendations' }) recommendationSummary;
|
||||
@belongsTo('recommendation-summary', { inverse: 'recommendations' })
|
||||
recommendationSummary;
|
||||
|
||||
@attr('date') submitTime;
|
||||
|
||||
|
@ -22,7 +23,8 @@ export default class Recommendation extends Model {
|
|||
@attr('number') value;
|
||||
|
||||
get currentValue() {
|
||||
const resourceProperty = this.resource === 'CPU' ? 'reservedCPU' : 'reservedMemory';
|
||||
const resourceProperty =
|
||||
this.resource === 'CPU' ? 'reservedCPU' : 'reservedMemory';
|
||||
return get(this, `task.${resourceProperty}`);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { computed } from '@ember/object';
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragmentOwner, fragmentArray } from 'ember-data-model-fragments/attributes';
|
||||
import {
|
||||
fragmentOwner,
|
||||
fragmentArray,
|
||||
} from 'ember-data-model-fragments/attributes';
|
||||
|
||||
export default class TaskGroupScale extends Fragment {
|
||||
@fragmentOwner() jobScale;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { computed } from '@ember/object';
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragmentOwner, fragmentArray, fragment } from 'ember-data-model-fragments/attributes';
|
||||
import {
|
||||
fragmentOwner,
|
||||
fragmentArray,
|
||||
fragment,
|
||||
} from 'ember-data-model-fragments/attributes';
|
||||
import sumAggregation from '../utils/properties/sum-aggregation';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
|
@ -39,7 +43,10 @@ export default class TaskGroup extends Fragment {
|
|||
|
||||
@computed('job.allocations.@each.taskGroup', 'name')
|
||||
get allocations() {
|
||||
return maybe(this.get('job.allocations')).filterBy('taskGroupName', this.name);
|
||||
return maybe(this.get('job.allocations')).filterBy(
|
||||
'taskGroupName',
|
||||
this.name
|
||||
);
|
||||
}
|
||||
|
||||
@sumAggregation('tasks', 'reservedCPU') reservedCPU;
|
||||
|
@ -57,13 +64,17 @@ export default class TaskGroup extends Fragment {
|
|||
|
||||
@computed('job.latestFailureEvaluation.failedTGAllocs.[]', 'name')
|
||||
get placementFailures() {
|
||||
const placementFailures = this.get('job.latestFailureEvaluation.failedTGAllocs');
|
||||
const placementFailures = this.get(
|
||||
'job.latestFailureEvaluation.failedTGAllocs'
|
||||
);
|
||||
return placementFailures && placementFailures.findBy('name', this.name);
|
||||
}
|
||||
|
||||
@computed('summary.{queuedAllocs,startingAllocs}')
|
||||
get queuedOrStartingAllocs() {
|
||||
return this.get('summary.queuedAllocs') + this.get('summary.startingAllocs');
|
||||
return (
|
||||
this.get('summary.queuedAllocs') + this.get('summary.startingAllocs')
|
||||
);
|
||||
}
|
||||
|
||||
@computed('job.taskGroupSummaries.[]', 'name')
|
||||
|
@ -73,7 +84,10 @@ export default class TaskGroup extends Fragment {
|
|||
|
||||
@computed('job.scaleState.taskGroupScales.[]', 'name')
|
||||
get scaleState() {
|
||||
return maybe(this.get('job.scaleState.taskGroupScales')).findBy('name', this.name);
|
||||
return maybe(this.get('job.scaleState.taskGroupScales')).findBy(
|
||||
'name',
|
||||
this.name
|
||||
);
|
||||
}
|
||||
|
||||
scale(count, message) {
|
||||
|
|
|
@ -2,7 +2,11 @@ import { computed } from '@ember/object';
|
|||
import { alias, none, and } from '@ember/object/computed';
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragment, fragmentOwner, fragmentArray } from 'ember-data-model-fragments/attributes';
|
||||
import {
|
||||
fragment,
|
||||
fragmentOwner,
|
||||
fragmentArray,
|
||||
} from 'ember-data-model-fragments/attributes';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { attr } from '@ember-data/model';
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { fragment, fragmentArray, fragmentOwner } from 'ember-data-model-fragments/attributes';
|
||||
import {
|
||||
fragment,
|
||||
fragmentArray,
|
||||
fragmentOwner,
|
||||
} from 'ember-data-model-fragments/attributes';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export default class Task extends Fragment {
|
||||
|
|
|
@ -14,7 +14,10 @@ export default class Volume extends Model {
|
|||
|
||||
@computed('writeAllocations.[]', 'readAllocations.[]')
|
||||
get allocations() {
|
||||
return [...this.writeAllocations.toArray(), ...this.readAllocations.toArray()];
|
||||
return [
|
||||
...this.writeAllocations.toArray(),
|
||||
...this.readAllocations.toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
@attr('number') currentWriters;
|
||||
|
|
|
@ -15,7 +15,9 @@ export default class FsRoute extends Route {
|
|||
return RSVP.hash({
|
||||
path: decodedPath,
|
||||
allocation,
|
||||
directoryEntries: allocation.ls(decodedPath).catch(notifyError(this)),
|
||||
directoryEntries: allocation
|
||||
.ls(decodedPath)
|
||||
.catch(notifyError(this)),
|
||||
isFile: false,
|
||||
});
|
||||
} else {
|
||||
|
@ -30,8 +32,17 @@ export default class FsRoute extends Route {
|
|||
.catch(notifyError(this));
|
||||
}
|
||||
|
||||
setupController(controller, { path, allocation, directoryEntries, isFile, stat } = {}) {
|
||||
setupController(
|
||||
controller,
|
||||
{ path, allocation, directoryEntries, isFile, stat } = {}
|
||||
) {
|
||||
super.setupController(...arguments);
|
||||
controller.setProperties({ path, allocation, directoryEntries, isFile, stat });
|
||||
controller.setProperties({
|
||||
path,
|
||||
allocation,
|
||||
directoryEntries,
|
||||
isFile,
|
||||
stat,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ export default class IndexRoute extends Route {
|
|||
setupController(controller, model) {
|
||||
// Suppress the preemptedByAllocation fetch error in the event it's a 404
|
||||
if (model) {
|
||||
const setPreempter = () => controller.set('preempter', model.preemptedByAllocation);
|
||||
const setPreempter = () =>
|
||||
controller.set('preempter', model.preemptedByAllocation);
|
||||
model.preemptedByAllocation.then(setPreempter, setPreempter);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ export default class TaskRoute extends Route {
|
|||
const task = allocation.get('states').findBy('name', name);
|
||||
|
||||
if (!task) {
|
||||
const err = new EmberError(`Task ${name} not found for allocation ${allocation.get('id')}`);
|
||||
const err = new EmberError(
|
||||
`Task ${name} not found for allocation ${allocation.get('id')}`
|
||||
);
|
||||
err.code = '404';
|
||||
this.controllerFor('application').set('error', err);
|
||||
}
|
||||
|
|
|
@ -14,13 +14,18 @@ export default class FsRoute extends Route {
|
|||
decodedPath.startsWith('/') ? '' : '/'
|
||||
}${decodedPath}`;
|
||||
|
||||
return RSVP.all([allocation.stat(pathWithTaskName), taskState.get('allocation.node')])
|
||||
return RSVP.all([
|
||||
allocation.stat(pathWithTaskName),
|
||||
taskState.get('allocation.node'),
|
||||
])
|
||||
.then(([statJson]) => {
|
||||
if (statJson.IsDir) {
|
||||
return RSVP.hash({
|
||||
path: decodedPath,
|
||||
taskState,
|
||||
directoryEntries: allocation.ls(pathWithTaskName).catch(notifyError(this)),
|
||||
directoryEntries: allocation
|
||||
.ls(pathWithTaskName)
|
||||
.catch(notifyError(this)),
|
||||
isFile: false,
|
||||
});
|
||||
} else {
|
||||
|
@ -35,8 +40,17 @@ export default class FsRoute extends Route {
|
|||
.catch(notifyError(this));
|
||||
}
|
||||
|
||||
setupController(controller, { path, taskState, directoryEntries, isFile, stat } = {}) {
|
||||
setupController(
|
||||
controller,
|
||||
{ path, taskState, directoryEntries, isFile, stat } = {}
|
||||
) {
|
||||
super.setupController(...arguments);
|
||||
controller.setProperties({ path, taskState, directoryEntries, isFile, stat });
|
||||
controller.setProperties({
|
||||
path,
|
||||
taskState,
|
||||
directoryEntries,
|
||||
isFile,
|
||||
stat,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,13 +50,17 @@ export default class ApplicationRoute extends Route {
|
|||
this.controllerFor('application').set('error', e);
|
||||
}
|
||||
|
||||
const fetchSelfTokenAndPolicies = this.get('token.fetchSelfTokenAndPolicies')
|
||||
const fetchSelfTokenAndPolicies = this.get(
|
||||
'token.fetchSelfTokenAndPolicies'
|
||||
)
|
||||
.perform()
|
||||
.catch();
|
||||
|
||||
const fetchLicense = this.get('system.fetchLicense').perform().catch();
|
||||
|
||||
const checkFuzzySearchPresence = this.get('system.checkFuzzySearchPresence')
|
||||
const checkFuzzySearchPresence = this.get(
|
||||
'system.checkFuzzySearchPresence'
|
||||
)
|
||||
.perform()
|
||||
.catch();
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import Route from '@ember/routing/route';
|
||||
import { collect } from '@ember/object/computed';
|
||||
import { watchRecord, watchRelationship } from 'nomad-ui/utils/properties/watch';
|
||||
import {
|
||||
watchRecord,
|
||||
watchRelationship,
|
||||
} from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
|
||||
export default class ClientRoute extends Route.extend(WithWatchers) {
|
||||
|
|
|
@ -7,6 +7,8 @@ export default class PluginsRoute extends Route.extend(WithForbiddenState) {
|
|||
@service store;
|
||||
|
||||
model() {
|
||||
return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this));
|
||||
return this.store
|
||||
.query('plugin', { type: 'csi' })
|
||||
.catch(notifyForbidden(this));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ export default class PluginRoute extends Route {
|
|||
}
|
||||
|
||||
model(params) {
|
||||
return this.store.findRecord('plugin', `csi/${params.plugin_name}`).catch(notifyError(this));
|
||||
return this.store
|
||||
.findRecord('plugin', `csi/${params.plugin_name}`)
|
||||
.catch(notifyError(this));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,10 @@ import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
|||
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
|
||||
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
|
||||
|
||||
export default class IndexRoute extends Route.extend(WithWatchers, WithForbiddenState) {
|
||||
export default class IndexRoute extends Route.extend(
|
||||
WithWatchers,
|
||||
WithForbiddenState
|
||||
) {
|
||||
@service store;
|
||||
|
||||
queryParams = {
|
||||
|
@ -29,7 +32,10 @@ export default class IndexRoute extends Route.extend(WithWatchers, WithForbidden
|
|||
controller.set('namespacesWatch', this.watchNamespaces.perform());
|
||||
controller.set(
|
||||
'modelWatch',
|
||||
this.watchVolumes.perform({ type: 'csi', namespace: controller.qpNamespace })
|
||||
this.watchVolumes.perform({
|
||||
type: 'csi',
|
||||
namespace: controller.qpNamespace,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue