mirror of https://github.com/rancher/ui.git
359 lines
12 KiB
JavaScript
359 lines
12 KiB
JavaScript
import { reject, Promise as EmberPromise } from 'rsvp';
|
|
import { computed, get } from '@ember/object';
|
|
import { equal, alias } from '@ember/object/computed';
|
|
import { inject as service } from '@ember/service';
|
|
import Mixin from '@ember/object/mixin';
|
|
import { ucFirst, sortableNumericSuffix } from 'ui/utils/util';
|
|
import C from 'ui/utils/constants';
|
|
|
|
const defaultStateMap = {
|
|
'activating': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'active': {icon: 'icon icon-circle-o', color: 'text-success'},
|
|
'backedup': {icon: 'icon icon-backup', color: 'text-success'},
|
|
'created': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'creating': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'deactivating': {icon: 'icon icon-adjust', color: 'text-info' },
|
|
'degraded': {icon: 'icon icon-alert', color: 'text-warning'},
|
|
'disconnected': {icon: 'icon icon-alert', color: 'text-warning'},
|
|
'error': {icon: 'icon icon-alert', color: 'text-error' },
|
|
'erroring': {icon: 'icon icon-alert', color: 'text-error' },
|
|
'expired': {icon: 'icon icon-alert', color: 'text-warning'},
|
|
'healthy': {icon: 'icon icon-circle-o', color: 'text-success'},
|
|
'inactive': {icon: 'icon icon-circle', color: 'text-error' },
|
|
'initializing': {icon: 'icon icon-alert', color: 'text-warning'},
|
|
'migrating': {icon: 'icon icon-info', color: 'text-info' },
|
|
'provisioning': {icon: 'icon icon-circle', color: 'text-info' },
|
|
'pending': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'purged': {icon: 'icon icon-purged', color: 'text-error' },
|
|
'purging': {icon: 'icon icon-purged', color: 'text-info' },
|
|
'reconnecting': {icon: 'icon icon-alert', color: 'text-error' },
|
|
'registering': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'reinitializing': {icon: 'icon icon-alert', color: 'text-warning'},
|
|
'removed': {icon: 'icon icon-trash', color: 'text-error' },
|
|
'removing': {icon: 'icon icon-trash', color: 'text-info' },
|
|
'requested': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'restarting': {icon: 'icon icon-adjust', color: 'text-info' },
|
|
'restoring': {icon: 'icon icon-medicalcross', color: 'text-info' },
|
|
'running': {icon: 'icon icon-circle-o', color: 'text-success'},
|
|
'starting': {icon: 'icon icon-adjust', color: 'text-info' },
|
|
'stopped': {icon: 'icon icon-circle', color: 'text-error' },
|
|
'stopping': {icon: 'icon icon-adjust', color: 'text-info' },
|
|
'unavailable': {icon: 'icon icon-alert', color: 'text-error' },
|
|
'unhealthy': {icon: 'icon icon-alert', color: 'text-error' },
|
|
'unknown': {icon: 'icon icon-help', color: 'text-warning'},
|
|
'updating': {icon: 'icon icon-tag', color: 'text-info' },
|
|
'waiting': {icon: 'icon icon-tag', color: 'text-info' },
|
|
};
|
|
|
|
const stateColorSortMap = {
|
|
'error': 1,
|
|
'warning': 2,
|
|
'info': 3,
|
|
'success': 4,
|
|
'other': 5,
|
|
};
|
|
|
|
export default Mixin.create({
|
|
endpointSvc: service('endpoint'), // Some machine drivers have a property called 'endpoint'
|
|
cookies: service(),
|
|
growl: service(),
|
|
intl: service(),
|
|
|
|
modalService: service('modal'),
|
|
reservedKeys: ['waitInterval','waitTimeout'],
|
|
|
|
state: null,
|
|
transitioning: null,
|
|
transitioningMessage: null,
|
|
transitioningProgress: null,
|
|
|
|
availableActions: function() {
|
|
/*
|
|
Override me and return [
|
|
{
|
|
enabled: true/false, // Whether it's enabled or greyed out
|
|
detail: true/false, // If true, this action will only be shown on detailed screens
|
|
label: 'Delete', // Label shown on hover or in menu
|
|
icon: 'icon icon-trash',// Icon shown on screen
|
|
action: 'promptDelete', // Action to call on the controller when clicked
|
|
altAction: 'delete' // Action to call on the controller when alt+clicked
|
|
divider: true, // Just this will make a divider
|
|
},
|
|
...
|
|
]
|
|
*/
|
|
return [];
|
|
}.property(),
|
|
|
|
actions: {
|
|
promptDelete: function() {
|
|
this.get('modalService').toggleModal('confirm-delete', {resources: [this]});
|
|
},
|
|
|
|
delete: function() {
|
|
return this.delete();
|
|
},
|
|
|
|
restore: function() {
|
|
return this.doAction('restore');
|
|
},
|
|
|
|
purge: function() {
|
|
return this.doAction('purge');
|
|
},
|
|
|
|
goToApi: function() {
|
|
let url = this.get('links.self'); // http://a.b.c.d/v1/things/id, a.b.c.d is where the UI is running
|
|
// let endpoint = this.get('endpointSvc.absolute'); // http://e.f.g.h/ , does not include version. e.f.g.h is where the API actually is.
|
|
// url = url.replace(/https?:\/\/[^\/]+\/?/,endpoint);
|
|
|
|
window.open(url, '_blank');
|
|
},
|
|
},
|
|
|
|
displayName: function() {
|
|
return this.get('name') || '('+this.get('id')+')';
|
|
}.property('name','id'),
|
|
|
|
sortName: function() {
|
|
return sortableNumericSuffix(this.get('displayName').toLowerCase());
|
|
}.property('displayName'),
|
|
|
|
isTransitioning: equal('transitioning','yes'),
|
|
isError: equal('transitioning','error'),
|
|
isRemoved: computed('state', () => { return !C.REMOVEDISH_STATES.includes(this.state); }),
|
|
isPurged: equal('state','purged'),
|
|
isActive: equal('state','active'),
|
|
|
|
relevantState: function() {
|
|
return this.get('combinedState') || this.get('state') || 'unknown';
|
|
}.property('combinedState','state'),
|
|
|
|
displayState: alias('_displayState'),
|
|
_displayState: function() {
|
|
var state = this.get('relevantState')||'';
|
|
return state.split(/-/).map((word) => {
|
|
return ucFirst(word);
|
|
}).join('-');
|
|
}.property('relevantState'),
|
|
|
|
showTransitioningMessage: function() {
|
|
var trans = this.get('transitioning');
|
|
if (trans === 'yes' || trans === 'error') {
|
|
let message = (this.get('transitioningMessage')||'');
|
|
if ( message.length && message.toLowerCase() !== this.get('displayState').toLowerCase() ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}.property('transitioning','transitioningMessage','displayState'),
|
|
|
|
stateIcon: function() {
|
|
var trans = this.get('transitioning');
|
|
var icon = '';
|
|
if ( trans === 'yes' )
|
|
{
|
|
icon = 'icon icon-spinner icon-spin';
|
|
}
|
|
else if ( trans === 'error' )
|
|
{
|
|
icon = 'icon icon-alert';
|
|
}
|
|
else
|
|
{
|
|
var map = this.constructor.stateMap;
|
|
var key = (this.get('relevantState')||'').toLowerCase();
|
|
if ( map && map[key] && map[key].icon !== undefined)
|
|
{
|
|
if ( typeof map[key].icon === 'function' )
|
|
{
|
|
icon = map[key].icon(this);
|
|
}
|
|
else
|
|
{
|
|
icon = map[key].icon;
|
|
}
|
|
}
|
|
|
|
if ( !icon && defaultStateMap[key] && defaultStateMap[key].icon )
|
|
{
|
|
icon = defaultStateMap[key].icon;
|
|
}
|
|
|
|
if ( !icon )
|
|
{
|
|
icon = this.constructor.defaultStateIcon;
|
|
}
|
|
|
|
if ( icon.indexOf('icon ') === -1 )
|
|
{
|
|
icon = 'icon ' + icon;
|
|
}
|
|
}
|
|
|
|
return icon;
|
|
}.property('relevantState','transitioning'),
|
|
|
|
stateColor: function() {
|
|
if ( this.get('isError') ) {
|
|
return 'text-error';
|
|
}
|
|
|
|
var map = this.constructor.stateMap;
|
|
var key = (this.get('relevantState')||'').toLowerCase();
|
|
if ( map && map[key] && map[key].color !== undefined )
|
|
{
|
|
if ( typeof map[key].color === 'function' )
|
|
{
|
|
return map[key].color(this);
|
|
}
|
|
else
|
|
{
|
|
return map[key].color;
|
|
}
|
|
}
|
|
|
|
if ( defaultStateMap[key] && defaultStateMap[key].color )
|
|
{
|
|
return defaultStateMap[key].color;
|
|
}
|
|
|
|
return this.constructor.defaultStateColor;
|
|
}.property('relevantState','isError'),
|
|
|
|
sortState: function() {
|
|
var color = this.get('stateColor').replace('text-','');
|
|
return (stateColorSortMap[color] || stateColorSortMap['other']) + ' ' + this.get('relevantState');
|
|
}.property('stateColor','relevantState'),
|
|
|
|
stateBackground: function() {
|
|
return this.get('stateColor').replace("text-","bg-");
|
|
}.property('stateColor'),
|
|
|
|
cloneForNew: function() {
|
|
var copy = this.clone();
|
|
delete copy.id;
|
|
delete copy.actionLinks;
|
|
delete copy.links;
|
|
delete copy.uuid;
|
|
return copy;
|
|
},
|
|
|
|
serializeForNew: function() {
|
|
var copy = this.serialize();
|
|
delete copy.id;
|
|
delete copy.actionLinks;
|
|
delete copy.links;
|
|
delete copy.uuid;
|
|
return copy;
|
|
},
|
|
|
|
// Show growls for errors on actions
|
|
delete: function(/*arguments*/) {
|
|
var promise = this._super.apply(this, arguments);
|
|
return promise.catch((err) => {
|
|
this.get('growl').fromError('Error deleting',err);
|
|
});
|
|
},
|
|
|
|
doAction: function(name, data, opt) {
|
|
var promise = this._super.apply(this, arguments);
|
|
|
|
if ( !opt || opt.catchGrowl !== false )
|
|
{
|
|
return promise.catch((err) => {
|
|
this.get('growl').fromError(ucFirst(name) + ' Error', err);
|
|
return reject(err);
|
|
});
|
|
}
|
|
|
|
return promise;
|
|
},
|
|
|
|
// You really shouldn't have to use any of these.
|
|
// Needing these is a sign that the API is bad and should feel bad.
|
|
// Yet here they are, nonetheless.
|
|
waitInterval: 1000,
|
|
waitTimeout: 30000,
|
|
_waitForTestFn: function(testFn, msg) {
|
|
return new EmberPromise((resolve, reject) => {
|
|
// Do a first check immediately
|
|
if ( testFn.apply(this) ) {
|
|
resolve(this);
|
|
return;
|
|
}
|
|
|
|
var timeout = setTimeout(() => {
|
|
clearInterval(interval);
|
|
clearTimeout(timeout);
|
|
reject(this);
|
|
}, this.get('waitTimeout'));
|
|
|
|
var interval = setInterval(() => {
|
|
if ( testFn.apply(this) )
|
|
{
|
|
clearInterval(interval);
|
|
clearTimeout(timeout);
|
|
resolve(this);
|
|
}
|
|
}, this.get('waitInterval'));
|
|
}, msg||'Wait for it...');
|
|
},
|
|
|
|
waitForState: function(state) {
|
|
return this._waitForTestFn(function() {
|
|
return this.get('state') === state;
|
|
}, 'Wait for state='+state);
|
|
},
|
|
|
|
waitForTransition: function() {
|
|
return this._waitForTestFn(function() {
|
|
return this.get('transitioning') !== 'yes';
|
|
}, 'Wait for transition');
|
|
},
|
|
|
|
waitForAction: function(name) {
|
|
return this._waitForTestFn(function() {
|
|
//console.log('waitForAction('+name+'):', this.hasAction(name));
|
|
return this.hasAction(name);
|
|
}, 'Wait for action='+name);
|
|
},
|
|
|
|
waitForCondition: function(condition, status='True') {
|
|
return this._waitForTestFn(function() {
|
|
let cMap = (this.get('conditions')||[]).findBy('type', condition);
|
|
|
|
if (cMap) {
|
|
return ( get(cMap,'status')||'').toLowerCase() === (status+'').toLowerCase();
|
|
}
|
|
|
|
return false;
|
|
}, `Wait for Condition: ${condition}: ${status}`);
|
|
},
|
|
|
|
displayUserLabelStrings: function() {
|
|
let out = [];
|
|
let labels = this.get('labels')||{};
|
|
Object.keys(labels).forEach(function(key) {
|
|
if ( key.indexOf(C.LABEL.AFFINITY_PREFIX) === 0 ||
|
|
key.indexOf(C.LABEL.SYSTEM_PREFIX) === 0 ||
|
|
C.LABELS_TO_IGNORE.indexOf(key) >= 0
|
|
) {
|
|
// Skip ignored labels
|
|
return;
|
|
} else {
|
|
for ( let i = 0 ; i < C.LABEL_PREFIX_TO_IGNORE.length ; i++ ) {
|
|
if ( key.startsWith(C.LABEL_PREFIX_TO_IGNORE[i]) ) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
out.push(key + (labels[key] ? '='+labels[key] : ''));
|
|
});
|
|
|
|
return out;
|
|
}.property('labels'),
|
|
});
|