mirror of https://github.com/rancher/ui.git
377 lines
8.7 KiB
JavaScript
377 lines
8.7 KiB
JavaScript
import { isArray } from '@ember/array';
|
|
import EmberObject, { set, setProperties, computed } from '@ember/object';
|
|
import Mixin from '@ember/object/mixin';
|
|
import C from 'ui/utils/constants';
|
|
import { debouncedObserver } from 'ui/utils/debounce';
|
|
import { isEmpty } from '@ember/utils';
|
|
|
|
const USER = 'user';
|
|
const SYSTEM = 'system';
|
|
const AFFINITY = 'affinity';
|
|
|
|
export const TYPE = {
|
|
USER,
|
|
SYSTEM,
|
|
AFFINITY
|
|
}
|
|
|
|
export const K3S_LABELS_TO_IGNORE = [
|
|
C.LABEL.K3S_NODE_ARGS,
|
|
C.LABEL.K3S_NODE_CONFIG_HASH,
|
|
C.LABEL.K3S_NODE_ENV
|
|
];
|
|
|
|
export function flattenLabelArrays(...lists) {
|
|
let out = {};
|
|
|
|
function flatten(row) {
|
|
if ( row.value === undefined ) {
|
|
delete out[row.key];
|
|
} else {
|
|
out[row.key] = row.value;
|
|
}
|
|
}
|
|
|
|
for ( let i = 0 ; i < lists.length ; i++ ) {
|
|
(lists[i] || []).forEach(flatten);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function isSoftUser(type, key) {
|
|
// Include actual user labels
|
|
if ( type === USER ) {
|
|
return true;
|
|
}
|
|
|
|
// Don't include any affinity labels
|
|
if ( type === AFFINITY ) {
|
|
return false;
|
|
}
|
|
|
|
// Don't include any system labels that are blacklisted (because they have their own controls, like global)
|
|
if ( C.SYSTEM_LABELS_WITH_CONTROL.indexOf(key) >= 0 ) {
|
|
return false;
|
|
}
|
|
|
|
// Include anything else
|
|
return true;
|
|
}
|
|
|
|
export default Mixin.create({
|
|
labelArray: null,
|
|
|
|
k3sLabelsToIgnore: K3S_LABELS_TO_IGNORE,
|
|
|
|
actions: {
|
|
addUserLabel() {
|
|
this.get('labelArray').pushObject(EmberObject.create({
|
|
type: USER,
|
|
key: '',
|
|
value: '',
|
|
}));
|
|
},
|
|
|
|
addSystemLabel() {
|
|
this.get('labelArray').pushObject(EmberObject.create({
|
|
type: SYSTEM,
|
|
key: '',
|
|
value: '',
|
|
}));
|
|
},
|
|
|
|
addAffinityLabel() {
|
|
this.get('labelArray').pushObject(EmberObject.create({
|
|
type: AFFINITY,
|
|
key: C.LABEL.SCHED_HOST_LABEL,
|
|
value: '',
|
|
}));
|
|
},
|
|
|
|
removeLabel(obj) {
|
|
this.get('labelArray').removeObject(obj);
|
|
},
|
|
|
|
pastedLabels(str, target) {
|
|
let ary = this.get('labelArray');
|
|
|
|
str = str.trim();
|
|
if ( str.indexOf('=') === -1 && str.indexOf(':') === -1) {
|
|
// Just pasting a key
|
|
$(target).val(str); // eslint-disable-line
|
|
|
|
return;
|
|
}
|
|
|
|
let lines = str.split(/\r?\n/);
|
|
|
|
lines.forEach((line) => {
|
|
line = line.trim();
|
|
if ( !line ) {
|
|
return;
|
|
}
|
|
|
|
let idx = line.indexOf('=');
|
|
|
|
if ( idx === -1 ) {
|
|
idx = line.indexOf(':');
|
|
}
|
|
|
|
let key = '';
|
|
let val = '';
|
|
|
|
if ( idx > 0 ) {
|
|
key = line.substr(0, idx).trim();
|
|
val = line.substr(idx + 1).trim();
|
|
} else {
|
|
key = line.trim();
|
|
val = '';
|
|
}
|
|
|
|
let existing = ary.filterBy('key', key)[0];
|
|
|
|
if ( existing ) {
|
|
set(existing, 'value', val);
|
|
} else {
|
|
ary.pushObject(EmberObject.create({
|
|
key,
|
|
value: val,
|
|
type: USER
|
|
}));
|
|
}
|
|
});
|
|
|
|
// Clean up empty user entries
|
|
let toRemove = [];
|
|
|
|
ary.filterBy('type', USER).forEach((item) => {
|
|
if ( !item.get('key') && !item.get('value') ) {
|
|
toRemove.push(item);
|
|
}
|
|
});
|
|
ary.removeObjects(toRemove);
|
|
},
|
|
},
|
|
|
|
// User labels are actual user ones, plus system ones that have no controls in the UI so they are manually entered.
|
|
userLabelArray: computed('labelArray.@each.type', function() {
|
|
return (this.get('labelArray') || []).filter((item) => {
|
|
return isSoftUser(item.get('type'), item.get('key'));
|
|
});
|
|
}),
|
|
|
|
strictUserLabelArray: computed('labelArray.@each.type', function() {
|
|
return (this.get('labelArray') || []).filterBy('type', USER);
|
|
}),
|
|
|
|
systemLabelArray: computed('labelArray.@each.type', function() {
|
|
return (this.get('labelArray') || []).filterBy('type', SYSTEM);
|
|
}),
|
|
|
|
affinityLabelArray: computed('labelArray.@each.type', function() {
|
|
return (this.get('labelArray') || []).filterBy('type', AFFINITY);
|
|
}),
|
|
|
|
getLabelObj(key) {
|
|
let lcKey = (key || '').toLowerCase();
|
|
let ary = this.get('labelArray');
|
|
let item;
|
|
|
|
// Try specific case first
|
|
for ( let i = 0 ; i < ary.get('length') ; i++ ) {
|
|
item = ary.objectAt(i);
|
|
if ( item.get('key') === key ) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
// Then case-insensitive
|
|
for ( var i = 0 ; i < ary.get('length') ; i++ ) {
|
|
item = ary.objectAt(i);
|
|
if ( item.get('key').toLowerCase() === lcKey ) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
getLabel(key) {
|
|
let obj = this.getLabelObj(key);
|
|
|
|
if ( obj ) {
|
|
return obj.get('value');
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
setLabel(key, value) {
|
|
let lcKey = (key || '').toLowerCase();
|
|
let type = 'user';
|
|
|
|
// Rancher keys are always lowercase
|
|
if ( lcKey.indexOf(C.LABEL.AFFINITY_PREFIX) === 0 ) {
|
|
type = 'affinity';
|
|
key = lcKey;
|
|
} else if ( lcKey.indexOf(C.LABEL.SYSTEM_PREFIX) === 0 ) {
|
|
type = 'system';
|
|
key = lcKey;
|
|
}
|
|
|
|
let existing = this.getLabelObj(key);
|
|
|
|
if ( existing ) {
|
|
if ( existing.get('value') !== value ) {
|
|
setProperties(existing, {
|
|
value,
|
|
type,
|
|
});
|
|
}
|
|
} else {
|
|
existing = this.get('labelArray').pushObject(EmberObject.create({
|
|
key,
|
|
value,
|
|
type,
|
|
}));
|
|
}
|
|
|
|
return existing;
|
|
},
|
|
|
|
removeLabel(key, soft = false) {
|
|
if ( soft ) {
|
|
this.setLabel(key, undefined);
|
|
} else {
|
|
let existing = this.getLabelObj(key);
|
|
|
|
if ( existing ) {
|
|
this.get('labelArray').removeObject(existing);
|
|
}
|
|
}
|
|
},
|
|
|
|
initLabels(obj, onlyOfType, onlyKeys, readonlyKeys, labelsToIgnore) {
|
|
let out = [];
|
|
let ignoredLabels = [...C.LABELS_TO_IGNORE];
|
|
|
|
if (!isEmpty(labelsToIgnore)) {
|
|
ignoredLabels.pushObjects(labelsToIgnore);
|
|
}
|
|
|
|
if ( onlyKeys && !isArray(onlyKeys) ) {
|
|
onlyKeys = [onlyKeys];
|
|
}
|
|
|
|
if ( readonlyKeys && !isArray(readonlyKeys) ) {
|
|
readonlyKeys = [readonlyKeys];
|
|
}
|
|
|
|
Object.keys(obj || {}).forEach((key) => {
|
|
let type = 'user';
|
|
|
|
if ( key.indexOf(C.LABEL.AFFINITY_PREFIX) === 0 ) {
|
|
type = 'affinity';
|
|
} else if ( key.indexOf(C.LABEL.SYSTEM_PREFIX) === 0 ) {
|
|
type = 'system';
|
|
}
|
|
|
|
if ( ignoredLabels.indexOf(key) >= 0 ) {
|
|
// Skip ignored labels
|
|
return;
|
|
}
|
|
|
|
if ( onlyOfType ) {
|
|
// Strict User, only those with type actually == user
|
|
if ( onlyOfType === 'strictUser' && type !== 'user' ) {
|
|
return;
|
|
}
|
|
|
|
if ( onlyOfType === 'user' ) {
|
|
// Soft user := user + system things that don't have UI controls
|
|
if ( !isSoftUser(type, key) ) {
|
|
return;
|
|
}
|
|
} else if ( onlyOfType !== type ) {
|
|
// Generally the wrong type, for system or affinity
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( onlyKeys && onlyKeys.indexOf(key) === -1 ) {
|
|
// Skip labels of keys we don't care about
|
|
return;
|
|
}
|
|
|
|
out.push(EmberObject.create({
|
|
key,
|
|
value: obj[key] || '',
|
|
type,
|
|
readonly: readonlyKeys && readonlyKeys.indexOf(key) >= 0
|
|
}));
|
|
});
|
|
|
|
this.set('labelArray', out);
|
|
this.labelsChanged();
|
|
},
|
|
|
|
labelsChanged: debouncedObserver('labelArray.@each.{type,key,value}', function() {
|
|
// Make a map of the keys we care about, and combine multiple values together
|
|
let map = {};
|
|
|
|
(this.get('labelArray') || []).forEach((row) => {
|
|
let key = row.get('key') || '';
|
|
let type = row.get('type') || '';
|
|
|
|
// System and Affinity labels are always lowercase.
|
|
if ( type !== USER ) {
|
|
key = key.toLowerCase();
|
|
}
|
|
|
|
// Pass undefined through, for soft-delete
|
|
if ( row.get('value') === undefined ) {
|
|
map[key] = undefined;
|
|
|
|
return;
|
|
}
|
|
|
|
let value = row.get('value') || '';
|
|
|
|
// Skip empty keys, and system/affinity labels with no value
|
|
if ( !key || (type !== USER && value === '')) {
|
|
return;
|
|
}
|
|
|
|
// System and Affinity values used to be always lowercase.
|
|
// if ( type !== USER )
|
|
// {
|
|
// value = value.toLowerCase();
|
|
// }
|
|
|
|
// Affinity & System labels can be concatenated, Users just overwrite the previous value.
|
|
if ( map[key] && type !== USER ) {
|
|
map[key] = `${ map[key] },${ value }`;
|
|
} else {
|
|
map[key] = value;
|
|
}
|
|
});
|
|
|
|
// Then turn them back into an array because Ember hates maps.
|
|
let out = [];
|
|
|
|
Object.keys(map).forEach((key) => {
|
|
out.push({
|
|
key,
|
|
value: map[key]
|
|
});
|
|
});
|
|
|
|
this.updateLabels(out);
|
|
}),
|
|
|
|
updateLabels(/* labels*/) {
|
|
// Override me to do something
|
|
},
|
|
});
|