Skip to content

Commit 4453845

Browse files
committed
Initial autoscaler implementation
1 parent d554428 commit 4453845

File tree

20 files changed

+947
-192
lines changed

20 files changed

+947
-192
lines changed

shell/assets/translations/en-us.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2206,6 +2206,15 @@ cluster:
22062206
rkeTemplate: RKE Template
22072207

22082208
machinePool:
2209+
automation:
2210+
label: Automation
2211+
autoscaler:
2212+
heading: Autoscaler
2213+
enable: Enable autoscaler and override "Machine Count" value
2214+
machineCountValueOverride: Controlled by autoscaler
2215+
baseUnit: Machines
2216+
min: Min
2217+
max: Max
22092218
name:
22102219
label: Pool Name
22112220
placeholder: A random one will be generated by default
@@ -6513,6 +6522,7 @@ storageClass:
65136522

65146523
tableHeaders:
65156524
assuredConcurrencyShares: Assured Concurrency Shares
6525+
autoscaler: Autoscaler
65166526
accessKey: Access Key
65176527
addressType: Address Type
65186528
accessModes: Access Modes
@@ -9068,3 +9078,25 @@ component:
90689078
weekdaysAt0830: "At 30 minutes past the hour, every 1 hours, starting at 08:00 AM, Monday through Friday"
90699079
marchToMayHourly: "Every hour, only in March, April, and May"
90709080
every4Hours9to17: "At 0 minutes past the hour, every 4 hours, between 09:00 AM and 05:00 PM"
9081+
9082+
autoscaler:
9083+
card:
9084+
title: Autoscaler
9085+
pause: Pause
9086+
resume: Resume
9087+
loadingError: There was a problem loading details
9088+
loadingAlt: Details Loading
9089+
details:
9090+
status: Status
9091+
health: Health
9092+
scaleDown: Scale Down
9093+
scaleUp: Scale Up
9094+
nodes: Nodes
9095+
ready: Ready
9096+
notStarted: Not Started
9097+
inTotal: In Total
9098+
provisioning: Provisioning
9099+
paused: Paused
9100+
unavailable: Unavailable
9101+
tab:
9102+
title: Autoscaler
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<script setup lang="ts">
2+
import { computed, ComputedRef } from 'vue';
3+
import { useFetch } from '@shell/components/Resource/Detail/FetchLoader/composables';
4+
import { useInterval } from '@shell/composables/useInterval';
5+
6+
export interface Props {
7+
value: any;
8+
}
9+
10+
export interface Detail {
11+
label: string;
12+
value?: string | { component: any; props: any };
13+
}
14+
15+
const props = defineProps<Props>();
16+
17+
const fetch = useFetch(async() => {
18+
return await props.value.loadAutoscalerDetails();
19+
});
20+
21+
// The backend only updates the configmap every 10 seconds and we don't cache the configmap in the stores
22+
useInterval(() => fetch.value.refresh(), 10000);
23+
24+
const details: ComputedRef<Detail[]> = computed(() => fetch.value.data);
25+
</script>
26+
27+
<template>
28+
<div class="autoscaler-card">
29+
<div
30+
v-if="fetch.loading && !fetch.refreshing"
31+
class="loading"
32+
>
33+
<i
34+
class="icon icon-lg icon-spinner icon-spin"
35+
:alt="t('autoscaler.card.loadingAlt')"
36+
/>
37+
</div>
38+
39+
<div
40+
v-else-if="fetch.data"
41+
class="details"
42+
>
43+
<div
44+
v-for="(detail) in details"
45+
:key="detail.label"
46+
class="detail"
47+
>
48+
<label
49+
v-if="detail.value"
50+
class="label text-deemphasized"
51+
>
52+
{{ detail.label }}
53+
</label>
54+
<h5 v-else-if="detail.label">
55+
{{ detail.label }}
56+
</h5>
57+
<div
58+
v-if="detail.value"
59+
class="value"
60+
>
61+
<component
62+
:is="detail.value.component"
63+
v-if="typeof detail.value === 'object'"
64+
v-bind="detail.value.props"
65+
/>
66+
<span v-else>{{ detail.value }}</span>
67+
</div>
68+
</div>
69+
</div>
70+
<div
71+
v-else
72+
class="text-warning"
73+
>
74+
{{ t('autoscaler.card.loadingError') }}
75+
</div>
76+
</div>
77+
</template>
78+
79+
<style lang="scss" scoped>
80+
.autoscaler-card {
81+
width: 240px;
82+
83+
.loading {
84+
display: flex;
85+
justify-content: center;
86+
}
87+
.detail {
88+
display: flex;
89+
white-space: nowrap;
90+
width: 244px;
91+
92+
&:not(:last-of-type) {
93+
margin-bottom: 8px;
94+
}
95+
96+
label, .value {
97+
width: 50%;
98+
}
99+
}
100+
101+
h5 {
102+
margin-bottom: 0;
103+
margin-top: 12px;
104+
font-size: 14px;
105+
font-weight: 600;
106+
}
107+
}
108+
</style>

shell/components/AutoscalerTab.vue

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<script setup lang="ts">
2+
import { useFetch } from '@shell/components/Resource/Detail/FetchLoader/composables';
3+
import Tab from '@shell/components/Tabbed/Tab.vue';
4+
import { useI18n } from '@shell/composables/useI18n';
5+
import { computed, onMounted, ref } from 'vue';
6+
import { useStore } from 'vuex';
7+
import SortableTable from '@shell/components/SortableTable/index.vue';
8+
import { useInterval } from '@shell/composables/useInterval';
9+
10+
export interface Props {
11+
value: any;
12+
}
13+
14+
export interface Detail {
15+
label: string;
16+
values: (string | { component: any; props: any })[];
17+
}
18+
19+
const props = withDefaults(defineProps<Props>(), { value: true });
20+
21+
const store = useStore();
22+
const i18n = useI18n(store);
23+
24+
const table = ref<any>(null);
25+
26+
const eventHeaders = [
27+
{
28+
name: 'type',
29+
label: i18n.t('tableHeaders.type'),
30+
value: '_type',
31+
sort: '_type',
32+
},
33+
{
34+
name: 'reason',
35+
label: i18n.t('tableHeaders.reason'),
36+
value: 'reason',
37+
sort: 'reason',
38+
},
39+
{
40+
name: 'date',
41+
label: i18n.t('tableHeaders.updated'),
42+
value: 'firstTimestamp',
43+
sort: 'date:desc',
44+
formatter: 'LiveDate',
45+
formatterOpts: { addSuffix: true },
46+
width: 125
47+
},
48+
{
49+
name: 'message',
50+
label: i18n.t('tableHeaders.message'),
51+
value: 'message',
52+
sort: 'message',
53+
},
54+
];
55+
56+
const fetch = useFetch(async() => {
57+
return await props.value.loadAutoscalerEvents();
58+
});
59+
60+
// From the FAQ there appears to be a 20 second target between detecting scaling needs to happen to scaling beginning
61+
// so I don't see a reason to poll quicker than 20 seconds.
62+
// https://github.com/kubernetes/autoscaler/blob/9befb31fd94d73ae0b888bd9536ae085cd9304e1/cluster-autoscaler/FAQ.md#what-are-the-service-level-objectives-for-cluster-autoscaler
63+
useInterval(() => {
64+
fetch.value.refresh();
65+
}, 20000);
66+
67+
const rows = computed(() => {
68+
return fetch.value.data || [];
69+
});
70+
71+
onMounted(() => {
72+
table.value?.changeSort('date', true);
73+
});
74+
75+
</script>
76+
77+
<template>
78+
<Tab
79+
name="autoscaler"
80+
:label="t('autoscaler.tab.title')"
81+
>
82+
<SortableTable
83+
ref="table"
84+
:headers="eventHeaders"
85+
:namespaced="false"
86+
:row-actions="false"
87+
default-sort-by="date"
88+
:rows="rows"
89+
/>
90+
</Tab>
91+
</template>
92+
93+
<style lang="scss" scoped>
94+
</style>

0 commit comments

Comments
 (0)