Deployments + rescale introduced

This commit is contained in:
Andreas Dinauer 2025-06-07 13:03:09 +02:00
parent 551d6b6faa
commit a8050fa958
8 changed files with 260 additions and 3 deletions

View File

@ -133,7 +133,7 @@
}
.tile, *[class^='tile-'], *[class*=' tile-'] {
background-color: var(--background-color);
background-color: #ebebeb;
border-radius: 0.25rem;
overflow: hidden;
width: 100%;

12
classes/Deployment.ts Normal file
View File

@ -0,0 +1,12 @@
import type { Metadata } from "./Metadata";
export class Deployment
{
metadata?: Metadata;
spec?: DeploymentSpec;
}
export class DeploymentSpec
{
replicas?: number;
}

View File

@ -0,0 +1,62 @@
<template>
<SmallPopupTemplate :heading="'Rescale ' + deployment.metadata?.name" ref="base">
<div class="content-l">
<div class="tile-m">
<p class="grayed-out">Deployment</p>
<p>{{ deployment.metadata?.namespace }}/{{ deployment.metadata?.name }}</p>
</div>
<UiInput>
<div class="rescale-input" v-if="deployment.spec?.replicas">
<UiButton class="square" icon="remove" @click="deployment.spec.replicas--"></UiButton>
<input type="number" v-model="deployment.spec.replicas" min="1">
<UiButton class="square" icon="add" @click="deployment.spec.replicas++"></UiButton>
</div>
</UiInput>
<div class="center">
<UiButton @click="() => rescale(deployment)">Rescale</UiButton>
</div>
</div>
</SmallPopupTemplate>
</template>
<script setup lang="ts">
import type { Deployment } from '~/classes/Deployment';
import { rescaleDeployment } from '~/requests/deployments';
const base = ref();
defineProps<{
deployment: Deployment
}>();
function open()
{
base.value.open();
}
defineExpose({
open
})
function rescale(deployment: Deployment)
{
if(deployment.spec && deployment.spec.replicas)
{
rescaleDeployment(deployment, deployment.spec.replicas, () => {
base.value.close();
});
}
else
{
throw new Error("Replica count is null");
}
}
</script>
<style scoped>
.rescale-input {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 1rem;
}
</style>

View File

@ -6,6 +6,7 @@
<NuxtLink class="resources" to="/dashboard/nodes">Nodes</NuxtLink>
<NuxtLink class="resources" to="/dashboard/ingresses">Ingresses</NuxtLink>
<NuxtLink class="resources" to="/dashboard/services">Services</NuxtLink>
<NuxtLink class="resources" to="/dashboard/deployments">Deployments</NuxtLink>
<NuxtLink class="resources" to="/dashboard/pods">Pods</NuxtLink>
</div>
<div class="divider" :class="{ hide: !inNamespaceScopedResource }"></div>

View File

@ -0,0 +1,61 @@
<template>
<div class="overlay center" @click="close" v-show="visible">
<div class="popup tile" @click.stop>
<div class="popup__header">
<h2>{{ heading }}</h2>
<UiButton class="square" icon="close" @click="close"></UiButton>
</div>
<div class="popup__body">
<slot></slot>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import UiButton from './ui/UiButton.vue';
const visible = ref(false);
function close() {
visible.value = false;
}
function open() {
visible.value = true;
}
defineProps({
heading: String
})
defineExpose({
close,
open
})
</script>
<style>
.overlay {
background-color: rgba(0, 0, 0, 0.514);
backdrop-filter: blur(0.1rem);
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 1rem;
}
.popup {
width: min(100%, 640px);
background-color: white;
padding: 1rem;
}
.popup__header {
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<div>
<p class="grid-element" v-if="deployment.metadata">{{ deployment.metadata.name }}</p>
<p class="grid-element" v-if="deployment.metadata">{{ deployment.metadata.namespace }}</p>
<p class="grid-element" v-if="deployment.spec">{{ deployment.spec.replicas }}</p>
<div class="grid-element action-buttons">
<ActionButton>delete</ActionButton>
<ActionButton>autorenew</ActionButton>
<ActionButton @click="() => rescaleDeploymentPopup.open()">height</ActionButton>
</div>
<RescaleDeploymentPopup ref="rescaleDeploymentPopup" :deployment="deployment"></RescaleDeploymentPopup>
</div>
</template>
<script setup lang="ts">
import { Deployment } from '~/classes/Deployment';
import { rescaleDeployment } from '~/requests/deployments';
import RescaleDeploymentPopup from '../RescaleDeploymentPopup.vue';
defineProps<{
deployment: Deployment
}>();
const rescaleDeploymentPopup = ref();
</script>

View File

@ -1,3 +1,50 @@
<template>
<div class="resource-container deployment-container">
<div class="header">
<p>Name</p>
<p>Namespace</p>
<p>Replicas</p>
<p>Aktionen</p>
</div>
<DeploymentComponent :deployment="deployment" v-for="deployment, index in deployments" class="resource" :class="{ even: index % 2 }"></DeploymentComponent>
</div>
</template>
<script setup lang="ts">
import { Deployment } from '~/classes/Deployment';
import { useNamespaceStore } from '#imports';
import type { Namespace } from '~/classes/Namespace';
import { getDeployments } from '~/requests/deployments';
import DeploymentComponent from '~/components/deployments/DeploymentComponent.vue';
const deployments: Ref<Deployment[] | undefined> = ref(undefined);
const namespace = computed(useNamespaceStore().getCurrentNamespace);
let interval: NodeJS.Timeout | undefined = undefined;
onMounted(() => {
watch(namespace, (newNamespace) => {
loadDeployments(newNamespace);
clearInterval(interval);
interval = setInterval(() => {
loadDeployments(newNamespace);
}, 10000);
}, { immediate: true })
})
onUnmounted(() => {
clearInterval(interval);
});
function loadDeployments(namespace?: Namespace)
{
getDeployments(namespace?.metadata?.name, (_deployments: Deployment[]) => {
deployments.value = _deployments;
});
}
</script>
<style scoped>
.deployment-container {
grid-template-columns: 1fr 1fr 1fr auto;
}
</style>

49
requests/deployments.ts Normal file
View File

@ -0,0 +1,49 @@
import axios from "axios";
import type { Deployment } from "~/classes/Deployment";
export function getDeployments(namespace: string | undefined, onSuccess: (deployments: Deployment[]) => void)
{
axios.get<Deployment[]>(useRuntimeConfig().public.apiBase + '/deployments', {
headers: {
Authorization: "Bearer " + requireToken()
},
params: getParams(namespace)
})
.then((response) => {
onSuccess(response.data);
})
.catch();
}
export function rescaleDeployment(deployment: Deployment | undefined, replicaCount: number, onSuccess: () => void)
{
if(deployment == null || deployment.metadata == null)
{
throw new Error("Deployment or metadata cannot be null");
}
if(deployment.metadata.name == null || deployment.metadata.namespace == null)
{
throw new Error("Name or namespace cannot be null");
}
axios.patch<Deployment[]>(useRuntimeConfig().public.apiBase + '/deployments/' + deployment.metadata.namespace + "/" + deployment.metadata.name, replicaCount, {
headers: {
Authorization: "Bearer " + requireToken(),
"Content-Type": "text/plain"
}
})
.then(() => {
onSuccess();
})
.catch();
}
function getParams(namespace: string | undefined)
{
if(namespace != undefined)
{
return {
namespace: namespace
}
}
return undefined;
}