mirror of https://github.com/rancher/dashboard.git
Weight when defining service
This commit is contained in:
parent
65c2de2cd8
commit
26bbd9d30d
|
|
@ -23,6 +23,10 @@ export default {
|
|||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
isDemo: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
|
@ -39,10 +43,6 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -50,6 +50,10 @@ export default {
|
|||
realMode: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
registerAfterHook: {
|
||||
type: Function,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -91,7 +95,9 @@ export default {
|
|||
build,
|
||||
scaleInput,
|
||||
scaleMode,
|
||||
buildModeLabels: BUILD_MODES,
|
||||
buildModeLabels: BUILD_MODES,
|
||||
initialWeightPercent: this.value.weightsPercent.desired,
|
||||
weightPercent: this.value.weightsPercent.desired,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -117,6 +123,40 @@ export default {
|
|||
};
|
||||
});
|
||||
},
|
||||
|
||||
showWeight() {
|
||||
return this.realMode === 'stage' || this.realMode === 'edit';
|
||||
},
|
||||
|
||||
showVersion() {
|
||||
return this.realMode === 'stage' || this.realMode === 'edit';
|
||||
},
|
||||
|
||||
showScale() {
|
||||
return true;
|
||||
},
|
||||
|
||||
showNamespace() {
|
||||
return this.realMode === 'create';
|
||||
},
|
||||
|
||||
extraColumns() {
|
||||
const out = [];
|
||||
|
||||
if ( this.showVersion ) {
|
||||
out.push('version');
|
||||
}
|
||||
|
||||
if ( this.showWeight ) {
|
||||
out.push('weight');
|
||||
}
|
||||
|
||||
if ( this.showScale ) {
|
||||
out.push('scale');
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
|
@ -125,6 +165,20 @@ export default {
|
|||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.registerAfterHook(() => {
|
||||
if ( this.realMode !== 'stage' && this.realMode !== 'edit' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this.weightPercent === this.initialWeightPercent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.value.saveWeightPercent(this.weightPercent);
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
update() {
|
||||
switch (this.buildMode) {
|
||||
|
|
@ -188,7 +242,8 @@ export default {
|
|||
:value="value"
|
||||
:mode="mode"
|
||||
:name-label="isSidecar ? 'Container Name' : 'Service Name'"
|
||||
:extra-column="!isSidecar"
|
||||
:extra-columns="extraColumns"
|
||||
:namespaced="showNamespace"
|
||||
>
|
||||
<template v-if="isSidecar" #name>
|
||||
<LabeledInput
|
||||
|
|
@ -210,7 +265,19 @@ export default {
|
|||
@input="updateGeneratedName"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="!isSidecar" #extra>
|
||||
|
||||
<template #version>
|
||||
<LabeledInput
|
||||
key="version"
|
||||
v-model="spec.version"
|
||||
:mode="mode"
|
||||
label="Version"
|
||||
:disabled="realMode !== 'stage'"
|
||||
@input="updateGeneratedName"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #scale>
|
||||
<LabeledInput
|
||||
key="scale"
|
||||
v-model="scaleInput"
|
||||
|
|
@ -226,14 +293,23 @@ export default {
|
|||
</template>
|
||||
</LabeledInput>
|
||||
</template>
|
||||
<template v-if="realMode === 'edit' || realMode === 'stage'" #namespace>
|
||||
|
||||
<template #weight>
|
||||
<LabeledInput
|
||||
key="version"
|
||||
v-model="spec.version"
|
||||
:mode="mode"
|
||||
label="Version"
|
||||
@input="updateGeneratedName"
|
||||
/>
|
||||
ref="weightPercent"
|
||||
v-model.number="weightPercent"
|
||||
type="number"
|
||||
label="Weight"
|
||||
size="4"
|
||||
min="0"
|
||||
max="100"
|
||||
>
|
||||
<template #suffix>
|
||||
<div class="addon">
|
||||
%
|
||||
</div>
|
||||
</template>
|
||||
</LabeledInput>
|
||||
</template>
|
||||
</NameNsDescription>
|
||||
<div class="spacer"></div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Labels from './Labels';
|
|||
import Security from './Security';
|
||||
import Upgrading from './Upgrading';
|
||||
import Volumes from './Volumes';
|
||||
import { CONFIG_MAP, SECRET } from '@/config/types';
|
||||
import { CONFIG_MAP, SECRET, RIO } from '@/config/types';
|
||||
import LoadDeps from '@/mixins/load-deps';
|
||||
import Loading from '@/components/Loading';
|
||||
import Tab from '@/components/Tabbed/Tab';
|
||||
|
|
@ -108,6 +108,7 @@ export default {
|
|||
const hash = await allHash({
|
||||
configMaps: this.$store.dispatch('cluster/findAll', { type: CONFIG_MAP }),
|
||||
secrets: this.$store.dispatch('cluster/findAll', { type: SECRET }),
|
||||
services: this.$store.dispatch('cluster/findAll', { type: RIO.SERVICE }),
|
||||
});
|
||||
|
||||
this.allSecrets = hash.secrets;
|
||||
|
|
@ -199,6 +200,7 @@ function matchingNamespaceGroupedByKey(ary, namespace) {
|
|||
:mode="mode"
|
||||
:real-mode="realMode || mode"
|
||||
:is-demo="isDemo"
|
||||
:register-after-hook="registerAfterHook"
|
||||
/>
|
||||
<div class="spacer"></div>
|
||||
<a href="#" @click.prevent="toggleTabs">
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export default {
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
extraColumn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
extraColumns: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
nameLabel: {
|
||||
type: String,
|
||||
|
|
@ -112,11 +112,11 @@ export default {
|
|||
},
|
||||
|
||||
colSpan() {
|
||||
const cols = 1 + (this.namespaced ? 1 : 0) + (this.extraColumn ? 1 : 0);
|
||||
const cols = 1 + (this.namespaced ? 1 : 0) + this.extraColumns.length;
|
||||
const span = 12 / cols;
|
||||
|
||||
return `span-${ span }`;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
|
@ -164,8 +164,8 @@ export default {
|
|||
/>
|
||||
</slot>
|
||||
</div>
|
||||
<div v-if="extraColumn" :class="{col: true, [colSpan]: true}">
|
||||
<slot name="extra">
|
||||
<div v-for="slot in extraColumns" :key="slot" :class="{col: true, [colSpan]: true}">
|
||||
<slot :name="slot">
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
<script>
|
||||
import { RIO } from '@/config/types';
|
||||
import { filterBy } from '@/utils/array';
|
||||
import LabeledInput from '@/components/form/LabeledInput';
|
||||
|
||||
export default {
|
||||
|
|
@ -26,33 +24,8 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
servicesForApp() {
|
||||
const services = this.$store.getters['cluster/all'](RIO.SERVICE);
|
||||
|
||||
return filterBy(services, {
|
||||
'app': this.row.app,
|
||||
'metadata.namespace': this.row.metadata.namespace,
|
||||
});
|
||||
},
|
||||
|
||||
totalForApp() {
|
||||
let desired = 0;
|
||||
let current = 0;
|
||||
let count = 0;
|
||||
|
||||
for ( const service of this.servicesForApp ) {
|
||||
const weights = service.weights;
|
||||
|
||||
desired += weights.desired || 0;
|
||||
current += weights.current || 0;
|
||||
count++;
|
||||
}
|
||||
|
||||
return {
|
||||
desired,
|
||||
current,
|
||||
count
|
||||
};
|
||||
return this.row.weightsOfApp;
|
||||
},
|
||||
|
||||
desired() {
|
||||
|
|
@ -82,7 +55,7 @@ export default {
|
|||
},
|
||||
|
||||
canAdjust() {
|
||||
return this.totalForApp.count > 1;
|
||||
return this.totalForApp.count > 1 && this.current !== 100;
|
||||
},
|
||||
|
||||
newWeightValid() {
|
||||
|
|
@ -94,59 +67,15 @@ export default {
|
|||
|
||||
methods: {
|
||||
onShown() {
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.$refs.newPercent.focus();
|
||||
});
|
||||
}, 250);
|
||||
},
|
||||
|
||||
setWeight() {
|
||||
const currentPercent = this.desired || 0;
|
||||
const newPercent = this.newPercent || 0;
|
||||
const totalWeight = this.totalForApp.desired;
|
||||
const count = this.totalForApp.count;
|
||||
|
||||
if ( currentPercent === 100 ) {
|
||||
if ( newPercent === 100 ) {
|
||||
return;
|
||||
} else if ( newPercent === 0 ) {
|
||||
this.row.saveWeight(0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const weight = newWeight(100 - newPercent) / (count - 1);
|
||||
|
||||
for ( const svc of this.servicesForApp ) {
|
||||
if ( svc === this.row ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
svc.saveWeight(weight);
|
||||
}
|
||||
} else if ( totalWeight === 0 || newPercent === 100 ) {
|
||||
this.row.saveWeight(10000);
|
||||
|
||||
for ( const svc of this.servicesForApp ) {
|
||||
if ( svc === this.row ) {
|
||||
continue;
|
||||
}
|
||||
svc.saveWeight(0);
|
||||
}
|
||||
} else {
|
||||
const weight = newWeight(newPercent);
|
||||
|
||||
this.row.saveWeight(weight);
|
||||
}
|
||||
|
||||
function newWeight(percent) {
|
||||
if ( percent === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const out = Math.round(totalWeight / (1 - (percent / 100))) - totalWeight;
|
||||
|
||||
return out;
|
||||
}
|
||||
this.row.saveWeightPercent(newPercent);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -75,6 +75,14 @@ export const FRIENDLY = {
|
|||
model.spec.app = model.app;
|
||||
delete model.spec.version;
|
||||
}
|
||||
|
||||
if ( mode === _CREATE ) {
|
||||
model.spec.weight = 10000;
|
||||
} else if ( mode === _CLONE ) {
|
||||
delete model.spec.weight;
|
||||
} else if ( mode === _STAGE ) {
|
||||
model.spec.weight = 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { RIO } from './types';
|
||||
|
||||
// Note: 'id' is always the last sort, so you don't have to specify it here.
|
||||
|
||||
export const STATE = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { allHash } from '@/utils/promise';
|
||||
import { findBy } from '@/utils/array';
|
||||
import { sortBy } from '@/utils/sort';
|
||||
|
||||
let NEXT_ID = 1;
|
||||
|
||||
|
|
@ -21,11 +22,11 @@ export default {
|
|||
throw new Error('Must specify key');
|
||||
}
|
||||
|
||||
const hooks = (this[key] || []).sortBy('priority', 'name');
|
||||
const hooks = sortBy(this[key] || [], ['priority', 'name']);
|
||||
const promises = {};
|
||||
|
||||
hooks.forEach((x) => {
|
||||
promises[x.name] = x.fn(...args);
|
||||
promises[x.name] = x.fn.apply(this, args);
|
||||
});
|
||||
|
||||
return allHash(promises);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ChildHook from './child-hook';
|
||||
import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from './child-hook';
|
||||
import { _CREATE, _EDIT, _VIEW } from '@/config/query-params';
|
||||
|
||||
export default {
|
||||
|
|
@ -82,28 +82,27 @@ export default {
|
|||
async save(buttonDone) {
|
||||
this.errors = null;
|
||||
try {
|
||||
await this.applyHooks(BEFORE_SAVE_HOOKS);
|
||||
|
||||
if ( this.isCreate ) {
|
||||
await this.schema.followLink('collection', {
|
||||
urlSuffix: ( this.namespaceSuffixOnCreate ? `/${ this.value.metadata.namespace }` : null),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json',
|
||||
},
|
||||
data: this.value,
|
||||
});
|
||||
let url = this.schema.linkFor('collection');
|
||||
|
||||
if ( this.namespaceSuffixOnCreate ) {
|
||||
url += `/${ this.value.metadata.namespace }`;
|
||||
}
|
||||
|
||||
const res = await this.value.save({ url });
|
||||
|
||||
Object.assign(this.value, res);
|
||||
await this.value.$dispatch('load', this.value);
|
||||
} else {
|
||||
await this.value.followLink('update', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json',
|
||||
},
|
||||
data: this.value,
|
||||
});
|
||||
await this.value.save();
|
||||
}
|
||||
|
||||
await this.applyHooks(AFTER_SAVE_HOOKS);
|
||||
|
||||
buttonDone(true);
|
||||
|
||||
this.done();
|
||||
} catch (err) {
|
||||
if ( err && err.response && err.response.data ) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import day from 'dayjs';
|
||||
import { insertAt } from '@/utils/array';
|
||||
import { insertAt, filterBy } from '@/utils/array';
|
||||
import { ADD_SIDECAR, _FLAGGED, MODE, _STAGE } from '@/config/query-params';
|
||||
import { escapeHtml } from '@/utils/string';
|
||||
import { DATE_FORMAT, TIME_FORMAT } from '@/store/prefs';
|
||||
import { addParams } from '@/utils/url';
|
||||
import { PRIVATE } from '@/plugins/norman/resource-proxy';
|
||||
import { RIO } from '@/config/types';
|
||||
|
||||
const EMPTY = {};
|
||||
|
||||
|
|
@ -228,8 +229,136 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
allVersions() {
|
||||
const services = this.$getters['all'](RIO.SERVICE);
|
||||
|
||||
const out = filterBy(services, {
|
||||
'app': this.app,
|
||||
'metadata.namespace': this.metadata.namespace,
|
||||
});
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
weightsOfApp() {
|
||||
let desired = 0;
|
||||
let current = 0;
|
||||
let count = 0;
|
||||
|
||||
for ( const service of this.allVersions ) {
|
||||
const weights = service.weights;
|
||||
|
||||
desired += weights.desired || 0;
|
||||
current += weights.current || 0;
|
||||
count++;
|
||||
}
|
||||
|
||||
return {
|
||||
desired,
|
||||
current,
|
||||
count
|
||||
};
|
||||
},
|
||||
|
||||
weights() {
|
||||
let current = 0;
|
||||
let desired = 0;
|
||||
|
||||
const fromSpec = this.spec.weight;
|
||||
|
||||
if ( this.status ) {
|
||||
const fromStatus = this.status.computedWeight;
|
||||
|
||||
if ( typeof fromStatus === 'number' ) {
|
||||
current = fromStatus;
|
||||
} else if ( typeof fromSpec === 'number' ) {
|
||||
current = fromSpec;
|
||||
}
|
||||
|
||||
if ( typeof fromSpec === 'number' ) {
|
||||
desired = fromSpec;
|
||||
} else if ( typeof fromStatus === 'number' ) {
|
||||
desired = fromStatus;
|
||||
}
|
||||
}
|
||||
|
||||
return { current, desired };
|
||||
},
|
||||
|
||||
weightsPercent() {
|
||||
const self = this.weights;
|
||||
const app = this.weightsOfApp;
|
||||
|
||||
let desired = 0;
|
||||
let current = 0;
|
||||
|
||||
if ( self.desired && app.desired ) {
|
||||
desired = self.desired / app.desired * 100;
|
||||
}
|
||||
|
||||
if ( self.current && app.current ) {
|
||||
current = self.current / app.current * 100;
|
||||
}
|
||||
|
||||
return { current, desired };
|
||||
},
|
||||
|
||||
saveWeightPercent() {
|
||||
return (newPercent) => {
|
||||
const appInfo = this.weightsOfApp;
|
||||
const totalWeight = appInfo.desired;
|
||||
const currentPercent = (totalWeight === 0 ? 0 : this.weights.desired / totalWeight);
|
||||
const currentWeight = this.spec.weight || 0;
|
||||
const totalOfOthers = totalWeight - currentWeight;
|
||||
const count = appInfo.count;
|
||||
|
||||
if ( currentPercent === 100 ) {
|
||||
if ( newPercent === 100 ) {
|
||||
return;
|
||||
} else if ( newPercent === 0 ) {
|
||||
return this.saveWeight(0);
|
||||
}
|
||||
|
||||
const weight = newWeight(100 - newPercent) / (count - 1);
|
||||
|
||||
for ( const svc of this.allVersions ) {
|
||||
if ( svc.id === this.id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
svc.saveWeight(weight);
|
||||
}
|
||||
} else if ( totalOfOthers === 0 || newPercent === 100 ) {
|
||||
this.saveWeight(10000);
|
||||
|
||||
for ( const svc of this.allVersions ) {
|
||||
if ( svc.id === this.id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
svc.saveWeight(0);
|
||||
}
|
||||
} else {
|
||||
const weight = newWeight(newPercent);
|
||||
|
||||
this.saveWeight(weight);
|
||||
}
|
||||
|
||||
function newWeight(percent) {
|
||||
if ( percent === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const out = Math.round(totalOfOthers / (1 - (percent / 100))) - totalOfOthers;
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
saveWeight() {
|
||||
return async(neu) => {
|
||||
console.log('Save Weight', this.spec.app, this.spec.version, neu);
|
||||
try {
|
||||
await this.patch([{
|
||||
op: 'replace',
|
||||
|
|
@ -242,32 +371,6 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
weights() {
|
||||
let current = 0;
|
||||
let desired = 0;
|
||||
const spec = this.spec.weight;
|
||||
|
||||
if ( !this.status ) {
|
||||
return { current, desired };
|
||||
}
|
||||
|
||||
const status = this.status.computedWeight;
|
||||
|
||||
if ( typeof status === 'number' ) {
|
||||
current = status;
|
||||
} else if ( typeof spec === 'number' ) {
|
||||
current = spec;
|
||||
}
|
||||
|
||||
if ( typeof spec === 'number' ) {
|
||||
desired = spec;
|
||||
} else if ( typeof status === 'number' ) {
|
||||
desired = status;
|
||||
}
|
||||
|
||||
return { current, desired };
|
||||
},
|
||||
|
||||
async pauseOrResume(pause = true) {
|
||||
try {
|
||||
await this.patch({
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export default {
|
|||
},
|
||||
|
||||
async asyncData(ctx) {
|
||||
const { route } = ctx;
|
||||
const { resource } = ctx.params;
|
||||
const friendly = FRIENDLY[resource];
|
||||
const type = friendly.type;
|
||||
|
|
|
|||
|
|
@ -433,20 +433,36 @@ export default {
|
|||
},
|
||||
|
||||
save() {
|
||||
return (opt = {}) => {
|
||||
return async(opt = {}) => {
|
||||
delete this.__rehydrate;
|
||||
|
||||
if ( !opt.url ) {
|
||||
opt.url = this.linkFor('self');
|
||||
opt.url = this.linkFor('update') || this.linkFor('self');
|
||||
}
|
||||
|
||||
if ( !opt.method ) {
|
||||
opt.method = (this.id ? 'put' : 'post');
|
||||
}
|
||||
|
||||
if ( !opt.headers ) {
|
||||
opt.headers = {};
|
||||
}
|
||||
|
||||
if ( !opt.headers['content-type'] ) {
|
||||
opt.headers['content-type'] = 'application/json';
|
||||
}
|
||||
|
||||
if ( !opt.headers['accept'] ) {
|
||||
opt.headers['accept'] = 'application/json';
|
||||
}
|
||||
|
||||
opt.data = this;
|
||||
|
||||
return this.$dispatch('request', opt);
|
||||
const res = await this.$dispatch('request', opt);
|
||||
|
||||
await this.$dispatch('load', res);
|
||||
|
||||
return res;
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue