mirror of https://github.com/rancher/dashboard.git
347 lines
9.8 KiB
Vue
347 lines
9.8 KiB
Vue
<script>
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
|
|
import CruResource from '@shell/components/CruResource';
|
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
import Labels from '@shell/components/form/Labels';
|
|
import Loading from '@shell/components/Loading';
|
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
import Tabbed from '@shell/components/Tabbed';
|
|
import MetricsRow from '@shell/edit/autoscaling.horizontalpodautoscaler/metrics-row';
|
|
import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
|
|
import { DEFAULT_RESOURCE_METRIC } from '@shell/edit/autoscaling.horizontalpodautoscaler/resource-metric';
|
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
|
|
import { API_SERVICE, SCALABLE_WORKLOAD_TYPES } from '@shell/config/types';
|
|
import isEmpty from 'lodash/isEmpty';
|
|
import find from 'lodash/find';
|
|
import endsWith from 'lodash/endsWith';
|
|
import { findBy } from '@shell/utils/array';
|
|
import HpaScalingRule from '@shell/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue';
|
|
|
|
const RESOURCE_METRICS_API_GROUP = 'metrics.k8s.io';
|
|
|
|
export default {
|
|
name: 'CruHPA',
|
|
|
|
emits: ['input'],
|
|
|
|
components: {
|
|
HpaScalingRule,
|
|
ArrayListGrouped,
|
|
Checkbox,
|
|
CruResource,
|
|
LabeledInput,
|
|
LabeledSelect,
|
|
Labels,
|
|
Loading,
|
|
NameNsDescription,
|
|
MetricsRow,
|
|
Tab,
|
|
Tabbed,
|
|
},
|
|
|
|
mixins: [CreateEditView],
|
|
|
|
props: {
|
|
value: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
|
|
mode: {
|
|
type: String,
|
|
default: 'create',
|
|
},
|
|
},
|
|
|
|
fetch() {
|
|
const promises = [this.loadAPIServices(), this.loadWorkloads()];
|
|
|
|
return Promise.all(promises);
|
|
},
|
|
|
|
data() {
|
|
return { defaultResourceMetric: DEFAULT_RESOURCE_METRIC };
|
|
},
|
|
|
|
computed: {
|
|
allMetrics() {
|
|
return this.value?.spec?.metrics;
|
|
},
|
|
allWorkloadsFiltered() {
|
|
return (
|
|
Object.values(SCALABLE_WORKLOAD_TYPES)
|
|
.flatMap((type) => this.$store.getters['cluster/all'](type))
|
|
.filter(
|
|
// Filter out anything that has an owner, which should probably be the one with the HPA
|
|
// For example ReplicaSets can be associated with a HPA (https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#replicaset-as-a-horizontal-pod-autoscaler-target)
|
|
// but wouldn't make sense if it's owned by a deployment
|
|
(wl) => wl.metadata.namespace === this.value.metadata.namespace && !wl.ownedByWorkload
|
|
)
|
|
);
|
|
},
|
|
allWorkloadsMapped() {
|
|
return this.allWorkloadsFiltered
|
|
// Update to type OBJECT_REFERENCE which can be stored directly as scaleTargetRef
|
|
.map((workload) => ({
|
|
kind: workload.kind,
|
|
name: workload.metadata.name,
|
|
apiVersion: workload.apiVersion,
|
|
}));
|
|
},
|
|
allServices() {
|
|
return this.$store.getters['cluster/all'](API_SERVICE);
|
|
},
|
|
resourceMetricsAvailable() {
|
|
const { allServices } = this;
|
|
|
|
return !isEmpty(
|
|
find(
|
|
allServices,
|
|
(api) => api.name.split('.').length === 4 &&
|
|
endsWith(api.name, RESOURCE_METRICS_API_GROUP)
|
|
)
|
|
);
|
|
},
|
|
selectedTargetRef() {
|
|
const { scaleTargetRef: { name } } = this.value.spec;
|
|
const { allWorkloadsFiltered } = this;
|
|
const match = findBy(allWorkloadsFiltered, 'metadata.name', name);
|
|
|
|
return match ?? null;
|
|
},
|
|
hasScaleDownRules: {
|
|
get() {
|
|
return !!this.value.spec.behavior?.scaleDown;
|
|
},
|
|
set(hasScaleDownRules) {
|
|
if (hasScaleDownRules) {
|
|
if (!this.value.spec.behavior) {
|
|
this.value.spec['behavior'] = {};
|
|
}
|
|
if (!this.value.spec.behavior?.scaleDown) {
|
|
this.value.spec.behavior['scaleDown'] = {};
|
|
}
|
|
} else {
|
|
delete this.value.spec.behavior['scaleDown'];
|
|
}
|
|
}
|
|
},
|
|
hasScaleUpRules: {
|
|
get() {
|
|
return !!this.value.spec.behavior?.scaleUp;
|
|
},
|
|
set(hasScaleUpRules) {
|
|
if (hasScaleUpRules) {
|
|
if (!this.value.spec.behavior) {
|
|
this.value.spec['behavior'] = {};
|
|
}
|
|
if (!this.value.spec.behavior?.scaleUp) {
|
|
this.value.spec.behavior['scaleUp'] = {};
|
|
}
|
|
} else {
|
|
delete this.value.spec.behavior['scaleUp'];
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
created() {
|
|
if (!this?.value?.spec?.type) {
|
|
if (!this.value?.spec) {
|
|
this.initSpec();
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
initSpec() {
|
|
this.value['spec'] = {
|
|
type: 'io.k8s.api.autoscaling.v1.horizontalpodautoscalerspec',
|
|
minReplicas: 1,
|
|
maxReplicas: 10,
|
|
scaleTargetRef: {
|
|
apiVersion: '',
|
|
kind: '',
|
|
name: '',
|
|
},
|
|
metrics: [{ ...this.defaultResourceMetric }]
|
|
};
|
|
},
|
|
async loadWorkloads() {
|
|
await Promise.all(
|
|
Object.values(SCALABLE_WORKLOAD_TYPES).map((type) => this.$store.dispatch('cluster/findAll', { type })
|
|
)
|
|
);
|
|
},
|
|
async loadAPIServices() {
|
|
await this.$store.dispatch('cluster/findAll', { type: API_SERVICE });
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Loading v-if="$fetchState.pending" />
|
|
<form
|
|
v-else
|
|
class="filled-height"
|
|
>
|
|
<CruResource
|
|
:done-route="doneRoute"
|
|
:mode="mode"
|
|
:resource="value"
|
|
:validation-passed="true"
|
|
:errors="errors"
|
|
@error="(e) => (errors = e)"
|
|
@finish="save"
|
|
@cancel="done"
|
|
>
|
|
<NameNsDescription
|
|
v-if="!isView"
|
|
:value="value"
|
|
:mode="mode"
|
|
/>
|
|
|
|
<Tabbed
|
|
:side-tabs="true"
|
|
:use-hash="useTabbedHash"
|
|
>
|
|
<Tab
|
|
name="target"
|
|
:label="t('hpa.tabs.target')"
|
|
:weight="10"
|
|
>
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledSelect
|
|
v-model:value="value.spec.scaleTargetRef"
|
|
:get-option-label="(opt) => opt.name"
|
|
:mode="mode"
|
|
:label="t('hpa.workloadTab.targetReference')"
|
|
:options="allWorkloadsMapped"
|
|
:required="true"
|
|
>
|
|
<template v-slot:option="option">
|
|
{{ option.name }}<span class="pull-right">{{ option.kind }}</span>
|
|
</template>
|
|
</LabeledSelect>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value.number="value.spec.minReplicas"
|
|
:mode="mode"
|
|
:label="t('hpa.workloadTab.min')"
|
|
placeholder="1"
|
|
:required="true"
|
|
type="number"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value.number="value.spec.maxReplicas"
|
|
:mode="mode"
|
|
:label="t('hpa.workloadTab.max')"
|
|
placeholder="1"
|
|
:required="true"
|
|
type="number"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Tab>
|
|
<Tab
|
|
name="metrics"
|
|
:label="t('hpa.tabs.metrics')"
|
|
>
|
|
<ArrayListGrouped
|
|
v-model:value="value.spec.metrics"
|
|
:default-add-value="{ ...defaultResourceMetric }"
|
|
:mode="mode"
|
|
:initial-empty-row="true"
|
|
>
|
|
<template #remove-button="removeProps">
|
|
<button
|
|
v-if="value.spec.metrics.length > 1"
|
|
type="button"
|
|
class="btn role-link close btn-sm"
|
|
@click="removeProps.remove"
|
|
>
|
|
<i class="icon icon-x" />
|
|
</button>
|
|
<span v-else />
|
|
</template>
|
|
<template #default="props">
|
|
<MetricsRow
|
|
v-model:value="props.row.value"
|
|
:mode="mode"
|
|
:metrics-available="resourceMetricsAvailable"
|
|
:referent="selectedTargetRef"
|
|
/>
|
|
</template>
|
|
</ArrayListGrouped>
|
|
</Tab>
|
|
<Tab
|
|
name="behavior"
|
|
:label="t('hpa.tabs.behavior')"
|
|
>
|
|
<div class="col span-12 mb-10">
|
|
<h3>
|
|
{{ t('hpa.scaleDownRules.label') }}
|
|
</h3>
|
|
<div class="row mb-10">
|
|
<Checkbox
|
|
v-model:value="hasScaleDownRules"
|
|
:mode="mode"
|
|
:label="t('hpa.scaleDownRules.enable')"
|
|
/>
|
|
</div>
|
|
<HpaScalingRule
|
|
v-if="hasScaleDownRules"
|
|
:value="value"
|
|
type="scaleDown"
|
|
:mode="mode"
|
|
@update:value="$emit('input', $event)"
|
|
/>
|
|
</div>
|
|
<div class="col span-12">
|
|
<h3>
|
|
{{ t('hpa.scaleUpRules.label') }}
|
|
</h3>
|
|
<div class="row mb-10">
|
|
<Checkbox
|
|
v-model:value="hasScaleUpRules"
|
|
:mode="mode"
|
|
:label="t('hpa.scaleUpRules.enable')"
|
|
/>
|
|
</div>
|
|
<HpaScalingRule
|
|
v-if="hasScaleUpRules"
|
|
:value="value"
|
|
type="scaleUp"
|
|
:mode="mode"
|
|
@update:value="$emit('input', $event)"
|
|
/>
|
|
</div>
|
|
</Tab>
|
|
<Tab
|
|
name="labels-and-annotations"
|
|
:label="t('hpa.tabs.labels')"
|
|
:weight="-1"
|
|
>
|
|
<Labels
|
|
:default-container-class="'labels-and-annotations-container'"
|
|
:value="value"
|
|
:mode="mode"
|
|
:display-side-by-side="false"
|
|
/>
|
|
</Tab>
|
|
</Tabbed>
|
|
</CruResource>
|
|
</form>
|
|
</template>
|