mirror of https://github.com/rancher/dashboard.git
238 lines
6.8 KiB
JavaScript
238 lines
6.8 KiB
JavaScript
import { CAPI } from '@shell/config/types';
|
|
import { escapeHtml } from '@shell/utils/string';
|
|
import { sortBy } from '@shell/utils/sort';
|
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
import { handleConflict } from '@shell/plugins/dashboard-store/normalize';
|
|
import { MACHINE_ROLES } from '@shell/config/labels-annotations';
|
|
import { notOnlyOfRole } from '@shell/models/cluster.x-k8s.io.machine';
|
|
import { KIND } from '../config/elemental-types';
|
|
import { KIND as HARVESTER_KIND } from '../config/harvester-manager-types';
|
|
|
|
export default class CapiMachineDeployment extends SteveModel {
|
|
get cluster() {
|
|
if ( !this.spec.clusterName ) {
|
|
return null;
|
|
}
|
|
|
|
const clusterId = `${ this.metadata.namespace }/${ this.spec.clusterName }`;
|
|
|
|
const cluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, clusterId);
|
|
|
|
return cluster;
|
|
}
|
|
|
|
get groupByLabel() {
|
|
const name = this.cluster?.nameDisplay || this.spec.clusterName;
|
|
|
|
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.cluster', { name: escapeHtml(name) });
|
|
}
|
|
|
|
get groupByPoolLabel() {
|
|
return `${ this.$rootGetters['i18n/t']('resourceTable.groupLabel.machinePool', { name: escapeHtml(this.nameDisplay) }) }`;
|
|
}
|
|
|
|
get groupByPoolShortLabel() {
|
|
return `${ this.$rootGetters['i18n/t']('resourceTable.groupLabel.machinePool', { name: escapeHtml(this.nameDisplay) }) }`;
|
|
}
|
|
|
|
get infrastructureRefKind() {
|
|
return this.spec?.template?.spec?.infrastructureRef?.kind;
|
|
}
|
|
|
|
get templateType() {
|
|
return this.spec.template.spec.infrastructureRef.kind ? `rke-machine.cattle.io.${ this.spec.template.spec.infrastructureRef.kind.toLowerCase() }` : null;
|
|
}
|
|
|
|
get template() {
|
|
const ref = this.spec.template.spec.infrastructureRef;
|
|
const id = `${ ref.namespace }/${ ref.name }`;
|
|
const template = this.$rootGetters['management/byId'](this.templateType, id);
|
|
|
|
return template;
|
|
}
|
|
|
|
get providerName() {
|
|
return this.template?.nameDisplay;
|
|
}
|
|
|
|
get providerDisplay() {
|
|
const provider = (this.template?.provider || '').toLowerCase();
|
|
|
|
return this.$rootGetters['i18n/withFallback'](`cluster.provider."${ provider }"`, null, 'generic.unknown', true);
|
|
}
|
|
|
|
get providerLocation() {
|
|
return this.template?.providerLocation || this.t('node.list.poolDescription.noLocation');
|
|
}
|
|
|
|
get providerSize() {
|
|
return this.template?.providerSize || this.t('node.list.poolDescription.noSize');
|
|
}
|
|
|
|
get providerSummary() {
|
|
if (this.template) {
|
|
switch (this.infrastructureRefKind) {
|
|
case HARVESTER_KIND.MACHINE_TEMPLATE:
|
|
return null;
|
|
default:
|
|
return `${ this.providerDisplay } \u2013 ${ this.providerLocation } / ${ this.providerSize } (${ this.providerName })`;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
get desired() {
|
|
return this.spec?.replicas || 0;
|
|
}
|
|
|
|
get pending() {
|
|
return Math.max(0, this.desired - (this.status?.replicas || 0));
|
|
}
|
|
|
|
get outdated() {
|
|
return Math.max(0, (this.status?.replicas || 0) - (this.status?.updatedReplicas || 0));
|
|
}
|
|
|
|
get ready() {
|
|
return Math.max(0, (this.status?.replicas || 0) - (this.status?.unavailableReplicas || 0));
|
|
}
|
|
|
|
get unavailable() {
|
|
return this.status?.unavailableReplicas || 0;
|
|
}
|
|
|
|
get isControlPlane() {
|
|
return `${ this.spec?.template?.metadata?.labels?.[MACHINE_ROLES.CONTROL_PLANE] }` === 'true';
|
|
}
|
|
|
|
get isEtcd() {
|
|
return `${ this.spec?.template?.metadata?.labels?.[MACHINE_ROLES.ETCD] }` === 'true';
|
|
}
|
|
|
|
// use this pool's definition in the cluster's rkeConfig to scale, not this.spec.replicas
|
|
get inClusterSpec() {
|
|
const machineConfigName = this.template?.metadata?.annotations['rke.cattle.io/cloned-from-name'];
|
|
const machinePools = this.cluster.spec.rkeConfig.machinePools;
|
|
|
|
return machinePools.find((pool) => pool.machineConfigRef.name === machineConfigName);
|
|
}
|
|
|
|
scalePool(delta, save = true, depth = 0) {
|
|
// This is used in different places with different scaling rules, so don't check if we can/cannot scale
|
|
if (!this.inClusterSpec) {
|
|
return;
|
|
}
|
|
|
|
const initialValue = this.cluster;
|
|
|
|
this.inClusterSpec.quantity += delta;
|
|
|
|
if ( !save ) {
|
|
return;
|
|
}
|
|
|
|
const value = this.cluster;
|
|
const liveModel = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, this.cluster.id);
|
|
|
|
if ( this.scaleTimer ) {
|
|
clearTimeout(this.scaleTimer);
|
|
}
|
|
|
|
this.scaleTimer = setTimeout(() => {
|
|
this.cluster.save().catch(async(err) => {
|
|
let errors = exceptionToErrorsArray(err);
|
|
|
|
if ( err.status === 409 && depth < 2 ) {
|
|
const conflicts = await handleConflict(
|
|
initialValue,
|
|
value,
|
|
liveModel,
|
|
{
|
|
dispatch: this.$dispatch,
|
|
getters: this.$rootGetters
|
|
},
|
|
'management'
|
|
);
|
|
|
|
if ( conflicts === false ) {
|
|
// It was automatically figured out, save again
|
|
// (pass in the delta again as `this.inClusterSpec.quantity` would have reset from the re-fetch done in `save`)
|
|
return this.scalePool(delta, true, depth + 1);
|
|
} else {
|
|
errors = conflicts;
|
|
}
|
|
}
|
|
|
|
this.$dispatch('growl/fromError', {
|
|
title: 'Error scaling pool',
|
|
err: errors
|
|
}, { root: true });
|
|
});
|
|
}, 1000);
|
|
}
|
|
|
|
// prevent scaling pool to 0 if it would scale down the only etcd or control plane node
|
|
canScaleDownPool() {
|
|
if (!this.canUpdate || this.inClusterSpec?.quantity === 0 || this.infrastructureRefKind === KIND.MACHINE_INV_SELECTOR_TEMPLATES) {
|
|
return false;
|
|
}
|
|
|
|
// scaling workers is always ok
|
|
if (!this.isEtcd && !this.isControlPlane) {
|
|
return true;
|
|
}
|
|
|
|
return notOnlyOfRole(this, this.cluster.machines);
|
|
}
|
|
|
|
// prevent scaling up pool for Elemental machines
|
|
canScaleUpPool() {
|
|
if (this.infrastructureRefKind === KIND.MACHINE_INV_SELECTOR_TEMPLATES) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
get showScalePool() {
|
|
return this.canScaleDownPool() || this.canScaleUpPool();
|
|
}
|
|
|
|
get stateParts() {
|
|
const out = [
|
|
{
|
|
label: 'Pending',
|
|
color: 'bg-info',
|
|
textColor: 'text-info',
|
|
value: this.pending,
|
|
sort: 1,
|
|
},
|
|
{
|
|
label: 'Outdated',
|
|
color: 'bg-warning',
|
|
textColor: 'text-warning',
|
|
value: this.outdated,
|
|
sort: 2,
|
|
},
|
|
{
|
|
label: 'Unavailable',
|
|
color: 'bg-error',
|
|
textColor: 'text-error',
|
|
value: this.unavailable,
|
|
sort: 3,
|
|
},
|
|
{
|
|
label: 'Ready',
|
|
color: 'bg-success',
|
|
textColor: 'text-success',
|
|
value: this.ready,
|
|
sort: 4,
|
|
},
|
|
].filter((x) => x.value > 0);
|
|
|
|
return sortBy(out, 'sort:desc');
|
|
}
|
|
}
|