🚑️ Ensure proper closing of log websocket

This commit is contained in:
andreas.dinauer 2025-11-10 20:39:19 +01:00
parent 2930460102
commit cd2725fba2
28 changed files with 533 additions and 169 deletions

View File

@ -234,7 +234,7 @@
}
.base-shape {
height: 2.5rem;
height: 2.25rem;
padding: 0.5rem;
}

View File

@ -71,3 +71,13 @@ html, body, #__nuxt {
.even > .grid-element {
background-color: rgb(233, 233, 233);
}
.label {
background-color: var(--primary-color);
border-radius: 0.25rem;
color: white;
height: 1.75rem;
display: flex;
align-items: center;
padding: 0rem 0.5rem;
}

View File

@ -0,0 +1,27 @@
export class MonitoringCollection
{
constructor (
public id: string,
public name: string,
public configs: MonitoringConfig[]
) {}
static get(onSuccess: (collections: MonitoringCollection[]) => void)
{
axios.get<MonitoringCollection[]>(ApiConfig.getHttpBase() + '/monitoring-collections')
.then((response) => {
onSuccess(response.data);
});
}
static getById(id: string, onSuccess: (collection: MonitoringCollection) => void)
{
axios.get<MonitoringCollection>(ApiConfig.getHttpBase() + '/monitoring-collections/' + id)
.then((response) => {
onSuccess(response.data);
});
}
}
import axios from "axios";
import type { MonitoringConfig } from "./MonitoringConfig";

View File

@ -0,0 +1,43 @@
import axios from "axios";
import { MonitoringTargetConfigCreation } from "./MonitoringTargetConfig";
import type { MonitoringType } from "./MonitoringType";
export class MonitoringConfig
{
constructor (
public id: string,
public configName: string,
public type: string,
public interval: string,
public volumeConfig: VolumeConfig
) {}
static create(config: MonitoringConfigCreation)
{
axios.post(ApiConfig.getHttpBase() + '/monitorings', config)
.then(() => {
});
}
}
class VolumeConfig
{
constructor (
public mountPath: string,
public containerName: string
) {}
}
export class MonitoringConfigCreation {
configName?: string;
type?: MonitoringType;
interval?: string;
volumeConfig: VolumeConfigCreation = new VolumeConfigCreation();
targetConfig: MonitoringTargetConfigCreation = new MonitoringTargetConfigCreation();
}
class VolumeConfigCreation {
mountPath?: string
containerName?: string
}

View File

@ -0,0 +1,10 @@
import type { TargetType } from "./TargetType";
export class MonitoringTargetConfigCreation {
type?: TargetType;
namespace?: string;
deploymentName?: string;
statefulSetName?: string;
labelKey?: string;
labelValue?: string;
}

View File

@ -0,0 +1,14 @@
export const enum Timeframe
{
LAST_10_MINUTES = "LAST_10_MINUTES",
LAST_30_MINUTES = "LAST_30_MINUTES",
LAST_HOUR = "LAST_HOUR",
LAST_3_HOURS = "LAST_3_HOURS",
LAST_6_HOURS = "LAST_6_HOURS",
LAST_12_HOURS = "LAST_12_HOURS",
TODAY = "TODAY",
LAST_24_HOURS = "LAST_24_HOURS",
LAST_7_DAYS = "LAST_7_DAYS",
LAST_30_DAYS = "LAST_30_DAYS",
LAST_90_DAYS = "LAST_90_DAYS"
}

View File

@ -0,0 +1,6 @@
export enum MonitoringType
{
VOLUME = "VOLUME",
WORKLOAD = "WORKLOAD",
HEALTHCHECK = "HEALTHCHECK"
}

View File

@ -0,0 +1,6 @@
export enum TargetType
{
LABEL = "LABEL",
STATEFUL_SET = "STATEFUL_SET",
DEPLOYENT = "DEPLOYMENT"
}

View File

@ -31,6 +31,10 @@ const emits = defineEmits<{
(e: 'close'): void
}>()
function close() {
}
const scrollComponent = ref();
const logRepo = new LogRepo();
@ -40,6 +44,9 @@ onMounted(() => {
scrollComponent.value.scrollToBottom();
});
});
onUnmounted(() => {
logRepo.clear();
});
onMounted(() => {
document.addEventListener('keydown', (event) => {

View File

@ -0,0 +1,26 @@
<template>
<SidebarTemplate>
<div class="content-m">
<NuxtLink class="namespace" to="/account/monitorings/nodes">Nodes</NuxtLink>
<NuxtLink class="namespace" :to="'/account/monitorings/collections/' + collection.id" v-for="collection in collections">{{ collection.name }}</NuxtLink>
</div>
<UiButton icon="add" reverse>Add</UiButton>
</SidebarTemplate>
</template>
<script setup lang="ts">
import { MonitoringCollection } from '~/classes/monitoring/MonitoringCollection';
import SidebarTemplate from './SidebarTemplate.vue';
const collections: Ref<MonitoringCollection[] | undefined> = ref(undefined);
onMounted(() => {
MonitoringCollection.get((_collections: MonitoringCollection[]) => {
collections.value = _collections;
});
});
</script>
<style scoped>
</style>

View File

@ -0,0 +1,55 @@
import dayjs, { Dayjs } from "dayjs";
export function CPU_CHART_CONFIG(from: Date, to: Date) {
const lineColor = '#636363ff';
return {
type: 'line',
options: {
animation: false,
plugins: {
title: {
text: 'Chart.js Time Scale',
display: true
}
},
aspectRatio: 2.75,
scales: {
x: {
type: 'time',
time: {
// Luxon format string
tooltipFormat: 'DD.MM'
},
ticks: {
autoSkip: true,
maxTicksLimit: 6,
align: 'center',
color: lineColor,
callback: function (value: number) {
return dayjs(new Date(value)).format("HH:mm");
}
},
min: from,
max: to,
grid: {
color: lineColor,
borderColor: lineColor,
lineWidth: 1
}
},
y: {
beginAtZero: true,
ticks: {
stepSize: 20,
color: lineColor,
},
grid: {
color: lineColor,
borderColor: lineColor,
lineWidth: 1
}
}
}
}
}
}

View File

@ -11,9 +11,14 @@ export class MonitoredResource<T>
public jobs: IndexCollection[]
) { }
static getMonitorings(monitoringId: string, onSuccess: (monitoredPod: MonitoredResource<Pod>[]) => void)
static getMonitorings(monitoringId: string, from: number, to: number, onSuccess: (monitoredPod: MonitoredResource<Pod>[]) => void)
{
axios.get<MonitoredResource<Pod>[]>(StringUtils.format("%s/monitorings/%s/jobs", ApiConfig.getHttpBase(), monitoringId))
axios.get<MonitoredResource<Pod>[]>(StringUtils.format("%s/monitorings/%s/jobs", ApiConfig.getHttpBase(), monitoringId), {
params: {
from: from,
to: to
}
})
.then(response => {
onSuccess(response.data)
});
@ -43,7 +48,6 @@ export class MonitoredResource<T>
data.push(new Data(metrics.average, dayjs.utc(job.timestamp).local().toDate()));
}
}
console.log(data);
return data;
}
}
@ -52,7 +56,8 @@ export class IndexCollection
{
constructor (
public metrics: Record<string, Metric>,
public timestamp: Date
public timestamp: Date,
public unix: number
) { }
}

View File

@ -0,0 +1,7 @@
export class Period
{
constructor (
public from: number,
public to: number
) {}
}

View File

@ -0,0 +1,69 @@
<template>
<UiInput class="timeframe">
<select name="" id="" v-model="timeframe">
<option :value="Timeframe.LAST_10_MINUTES">Letzte 10 Minuten</option>
<option :value="Timeframe.LAST_30_MINUTES">Letzte 30 Minuten</option>
<option :value="Timeframe.LAST_HOUR">Letzte Stunde</option>
<option :value="Timeframe.LAST_3_HOURS">Letzte 3 Stunden</option>
<option :value="Timeframe.LAST_6_HOURS">Letzte 6 Stunden</option>
<option :value="Timeframe.LAST_12_HOURS">Letzte 12 Stunden</option>
<option :value="Timeframe.LAST_24_HOURS">Letzte 24 Stunden</option>
<option :value="Timeframe.LAST_7_DAYS">Letzte 7 Tage</option>
<option :value="Timeframe.LAST_30_DAYS">Letzte 30 Tage</option>
<option :value="Timeframe.LAST_90_DAYS">Letzte 90 Tage</option>
<option :value="undefined">Custom...</option>
</select>
</UiInput>
</template>
<script setup lang="ts">
import { Timeframe } from '~/classes/monitoring/MonitoringTimeframe';
import { Period } from './Period';
import utc from 'dayjs/plugin/utc';
import dayjs from 'dayjs';
const timeframe = ref(Timeframe.LAST_HOUR);
const period = computed((): Period => {
dayjs.extend(utc)
const now = dayjs().utc().unix();
switch (timeframe.value) {
case Timeframe.LAST_10_MINUTES:
return new Period(dayjs().utc().subtract(10, 'minutes').unix(), now)
case Timeframe.LAST_30_MINUTES:
return new Period(dayjs().utc().subtract(30, 'minutes').unix(), now)
case Timeframe.LAST_HOUR:
return new Period(dayjs().utc().subtract(1, 'hours').unix(), now)
case Timeframe.LAST_3_HOURS:
return new Period(dayjs().utc().subtract(3, 'hours').unix(), now)
case Timeframe.LAST_6_HOURS:
return new Period(dayjs().utc().subtract(6, 'hours').unix(), now)
case Timeframe.LAST_12_HOURS:
return new Period(dayjs().utc().subtract(12, 'hours').unix(), now)
case Timeframe.LAST_24_HOURS:
return new Period(dayjs().utc().subtract(24, 'hours').unix(), now)
case Timeframe.LAST_7_DAYS:
return new Period(dayjs().utc().subtract(7, 'days').unix(), now)
case Timeframe.LAST_30_DAYS:
return new Period(dayjs().utc().subtract(30, 'days').unix(), now)
case Timeframe.LAST_90_DAYS:
return new Period(dayjs().utc().subtract(90, 'days').unix(), now)
default:
throw new Error("Invalid timeframe input.");
}
})
onMounted(() => {
watch(period, (selection: Period) => {
emit('select', selection);
}, { immediate: true });
});
const emit = defineEmits<{
(e: 'select', payload: Period): void
}>();
</script>
<style scoped>
</style>

View File

@ -1,102 +0,0 @@
<template>
<div class="padding-xl content-l">
<h1>Monitorings</h1>
<div class="left-center">
<UiInput class="timeframe">
<select name="" id="" v-model="timeframe">
<option :value="Timeframe.LAST_HOUR">Letzte Stunde</option>
<option :value="Timeframe.LAST_3_HOURS">Letzte 3 Stunden</option>
<option :value="Timeframe.LAST_6_HOURS">Letzte 6 Stunden</option>
<option :value="Timeframe.LAST_12_HOURS">Letzte 12 Stunden</option>
<option :value="Timeframe.LAST_24_HOURS">Letzte 24 Stunden</option>
<option :value="Timeframe.LAST_7_DAYS">Letzte 7 Tage</option>
<option :value="Timeframe.LAST_30_DAYS">Letzte 30 Tage</option>
<option :value="Timeframe.LAST_90_DAYS">Letzte 90 Tage</option>
<option :value="undefined">Custom...</option>
</select>
</UiInput>
<div class="left-center" v-if="timeframe == null">
<UiInput>
<input type="datetime-local">
</UiInput>
<p>bis</p>
<UiInput>
<input type="datetime-local">
</UiInput>
</div>
</div>
<div v-for="monitoring in monitorings">
<VolumeMonitoringConfigComponent :monitoring="monitoring" v-if="monitoring.type === 'VOLUME'"></VolumeMonitoringConfigComponent>
<MemoryMonitoringConfigComponent :monitoring="monitoring" v-if="monitoring.type === 'MEMORY'"></MemoryMonitoringConfigComponent>
</div>
</div>
</template>
<script setup lang="ts">
import 'chartjs-adapter-luxon';
import { VolumeMonitoringConfig } from './MonitoringConfig';
import VolumeMonitoringConfigComponent from './volumes/VolumeMonitoringConfigComponent.vue';
import MemoryMonitoringConfigComponent from './memory/MemoryMonitoringConfigComponent.vue';
import utc from 'dayjs/plugin/utc';
import dayjs from 'dayjs';
const enum Timeframe
{
LAST_HOUR = "LAST_HOUR",
LAST_3_HOURS = "LAST_3_HOURS",
LAST_6_HOURS = "LAST_6_HOURS",
LAST_12_HOURS = "LAST_12_HOURS",
TODAY = "TODAY",
LAST_24_HOURS = "LAST_24_HOURS",
LAST_7_DAYS = "LAST_7_DAYS",
LAST_30_DAYS = "LAST_30_DAYS",
LAST_90_DAYS = "LAST_90_DAYS"
}
const monitorings: Ref<VolumeMonitoringConfig[] | undefined> = ref(undefined);
const timeframe: Ref<string | undefined> = ref("LAST_HOUR");
onMounted(() => {
VolumeMonitoringConfig.get((_monitorings) => {
monitorings.value = _monitorings;
});
});
const startTime = computed(() => {
dayjs.extend(utc)
switch (timeframe.value)
{
case Timeframe.LAST_HOUR: {
return dayjs().utc().subtract(1, 'hours');
}
case Timeframe.LAST_3_HOURS: {
return dayjs().utc().subtract(3, 'hours');
}
case Timeframe.LAST_6_HOURS: {
return dayjs().utc().subtract(6, 'hours');
}
case Timeframe.LAST_12_HOURS: {
return dayjs().utc().subtract(12, 'hours');
}
case Timeframe.LAST_24_HOURS: {
return dayjs().utc().subtract(24, 'hours');
}
case Timeframe.LAST_7_DAYS: {
return dayjs().utc().subtract(7, 'days');
}
case Timeframe.LAST_30_DAYS: {
return dayjs().utc().subtract(30, 'days');
}
case Timeframe.LAST_90_DAYS: {
return dayjs().utc().subtract(90, 'days');
}
}
})
</script>
<style scoped>
.timeframe {
width: 13rem;
}
</style>

View File

@ -0,0 +1,29 @@
<template>
<div class="content-l">
<p>{{ pod.resource.metadata.namespace }} / {{ pod.resource.metadata.name }}</p>
<Chart :datasets="dataset" :config="CPU_CHART_CONFIG(new Date(period.from * 1000), new Date(period.to * 1000))"></Chart>
</div>
</template>
<script setup lang="ts">
import { Data, Dataset } from '~/components/chart/Dataset';
import { MonitoredResource } from '../MonitoredResource';
import { MEMORY_CHART_CONFIG } from '../memory/ChartConfig';
import type { Pod } from '~/classes/Pod';
import type { Period } from '../Period';
import { CPU_CHART_CONFIG } from '../CpuChartConfig';
const props = defineProps<{
pod: MonitoredResource<Pod>,
period: Period
}>();
const dataset = computed(() => {
const result = [] as Data[];
for (const job of props.pod.jobs)
{
result.push(new Data(job.metrics["CPU"].average, new Date(job.unix * 1000)));
}
return new Dataset("M", result);
});
</script>

View File

@ -0,0 +1,32 @@
<template>
<div class="content-m">
<div class="left-center">
<h3>{{ monitoring.configName }}</h3>
<p v-if="monitoring.type === 'WORKLOAD'" class="label">CPU</p>
</div>
<div class="col-3">
<CpuMonitoredPodComponent class="tile-l" v-for="pod in pods" :pod="pod" :period="period"></CpuMonitoredPodComponent>
</div>
</div>
</template>
<script setup lang="ts">
import type { Pod } from '~/classes/Pod';
import { MonitoredResource } from '../MonitoredResource';
import CpuMonitoredPodComponent from './CpuMonitoredPodComponent.vue';
import type { Period } from '../Period';
import { MonitoringConfig } from '~/classes/monitoring/MonitoringConfig';
const props = defineProps<{
monitoring: MonitoringConfig,
period: Period
}>();
const pods: Ref<MonitoredResource<Pod>[] | undefined> = ref(undefined);
onMounted(() => {
MonitoredResource.getMonitorings(props.monitoring.id, props.period.from, props.period.to, (_pods: MonitoredResource<Pod>[]) => {
pods.value = _pods;
});
});
</script>

View File

@ -1,7 +1,8 @@
import { elements } from "chart.js";
import dayjs from "dayjs"
export function MEMORY_CHART_CONFIG() {
export function MEMORY_CHART_CONFIG(from: Date, to: Date) {
const lineColor = '#636363ff';
return {
type: 'line',
options: {
@ -23,26 +24,28 @@ export function MEMORY_CHART_CONFIG() {
autoSkip: true,
maxTicksLimit: 6,
align: 'center',
color: '#cacacaff',
color: lineColor,
callback: function (value: Date) {
return dayjs(value).format("HH:mm");
return dayjs(new Date(value)).format("HH:mm");
}
},
grid: {
color: '#cacacaff',
borderColor: '#cacacaff',
color: lineColor,
borderColor: lineColor,
lineWidth: 1
},
min: from,
max: to,
},
y: {
beginAtZero: true,
ticks: {
maxTicksLimit: 6,
color: '#cacacaff'
color: lineColor
},
grid: {
color: '#cacacaff',
borderColor: '#cacacaff',
color: lineColor,
borderColor: lineColor,
lineWidth: 1
}
}

View File

@ -1,28 +1,31 @@
<template>
<div class="content-l">
<p>{{ pod.pod.metadata.namespace }} / {{ pod.pod.metadata.name }}</p>
<Chart :datasets="dataset" :config="MEMORY_CHART_CONFIG()"></Chart>
<p>{{ pod.resource.metadata.namespace }} / {{ pod.resource.metadata.name }}</p>
<Chart :datasets="dataset" :config="MEMORY_CHART_CONFIG(new Date(period.from * 1000), new Date(period.to * 1000))"></Chart>
</div>
</template>
<script setup lang="ts">
import { Data, Dataset } from '~/components/chart/Dataset';
import { MonitoredPod } from '../MonitoredResource';
import { MonitoredResource } from '../MonitoredResource';
import { MEMORY_CHART_CONFIG } from './ChartConfig';
import type { Pod } from '~/classes/Pod';
import type { Period } from '../Period';
const units = new Map<number, string>([[0, "B"], [1, "Ki"], [2, "Mi"], [3, "Gi"], [4, "Ti"]]);
const props = defineProps<{
pod: MonitoredPod
pod: MonitoredResource<Pod>,
period: Period
}>();
const dataset = computed(() => {
const result = [] as Data[];
const max = Math.max(...props.pod.jobs.map(job => job.average));
const max = Math.max(...props.pod.jobs.map(job => job.metrics["MEMORY"].average));
const dimension = getDimension(max);
for (const job of props.pod.jobs)
{
result.push(new Data(job.average / (Math.pow(1024, dimension)), job.timestamp));
result.push(new Data(job.metrics["MEMORY"].average / (Math.pow(1024, dimension)), new Date(job.unix * 1000)));
}
return new Dataset(units.get(dimension)!, result);
});

View File

@ -1,8 +1,11 @@
<template>
<div class="content-l">
<h2>{{ createLabel(monitoring.type) }} Monitoring: {{ monitoring.configName }}</h2>
<div class="content-m">
<div class="left-center">
<h3>{{ monitoring.configName }}</h3>
<p v-if="monitoring.type === 'WORKLOAD'" class="label">Memory</p>
</div>
<div class="col-3">
<MemoryMonitoredPodComponent class="tile-l" v-for="pod in pods" :pod="pod"></MemoryMonitoredPodComponent>
<MemoryMonitoredPodComponent class="tile-l" v-for="pod in pods" :pod="pod" :period="period"></MemoryMonitoredPodComponent>
</div>
</div>
</template>
@ -12,15 +15,17 @@ import type { Pod } from '~/classes/Pod';
import { MonitoredResource } from '../MonitoredResource';
import { VolumeMonitoringConfig } from '../MonitoringConfig';
import MemoryMonitoredPodComponent from './MemoryMonitoredPodComponent.vue';
import type { Period } from '../Period';
const props = defineProps<{
monitoring: VolumeMonitoringConfig
monitoring: VolumeMonitoringConfig,
period: Period
}>();
const pods: Ref<MonitoredResource<Pod>[] | undefined> = ref(undefined);
onMounted(() => {
MonitoredResource.getMonitorings(props.monitoring.id, (_pods: MonitoredResource<Pod>[]) => {
MonitoredResource.getMonitorings(props.monitoring.id, props.period.from, props.period.to, (_pods: MonitoredResource<Pod>[]) => {
pods.value = _pods;
});
});

View File

@ -1,8 +1,7 @@
import dayjs, { Dayjs } from "dayjs";
import utc from 'dayjs/plugin/utc';
export function VOLUME_CHART_CONFIG(from: Date, to: Date) {
dayjs.extend(utc);
const lineColor = '#636363ff';
return {
type: 'line',
options: {
@ -25,18 +24,16 @@ export function VOLUME_CHART_CONFIG(from: Date, to: Date) {
autoSkip: true,
maxTicksLimit: 6,
align: 'center',
color: '#cacacaff',
callback: function (value: Date) {
const offset = new Date().getTimezoneOffset();
const date: Dayjs = dayjs(value).utcOffset(-offset);
return date.format("HH:mm");
color: lineColor,
callback: function (value: number) {
return dayjs(new Date(value)).format("HH:mm");
}
},
min: from,
max: to,
grid: {
color: '#cacacaff',
borderColor: '#cacacaff',
color: lineColor,
borderColor: lineColor,
lineWidth: 1
}
},
@ -45,11 +42,11 @@ export function VOLUME_CHART_CONFIG(from: Date, to: Date) {
max: 100,
ticks: {
stepSize: 20,
color: '#cacacaff',
color: lineColor,
},
grid: {
color: '#cacacaff',
borderColor: '#cacacaff',
color: lineColor,
borderColor: lineColor,
lineWidth: 1
}
}

View File

@ -1,25 +1,28 @@
<template>
<div class="content-l">
<p>{{ pod.pod.metadata.namespace }} / {{ pod.pod.metadata.name }}</p>
<Chart :datasets="dataset" :config="VOLUME_CHART_CONFIG()"></Chart>
<p>{{ pod.resource.metadata.namespace }} / {{ pod.resource.metadata.name }}</p>
<Chart :datasets="dataset" :config="VOLUME_CHART_CONFIG(new Date(period.from * 1000), new Date(period.to * 1000))"></Chart>
</div>
</template>
<script setup lang="ts">
import { Data, Dataset } from '~/components/chart/Dataset';
import { MonitoredPod } from '../MonitoredResource';
import { MonitoredResource } from '../MonitoredResource';
import { VOLUME_CHART_CONFIG } from './ChartConfig';
import type { Pod } from '~/classes/Pod';
import type { Period } from '../Period';
const props = defineProps<{
pod: MonitoredPod
pod: MonitoredResource<Pod>,
period: Period
}>();
const dataset = computed(() => {
const result = [] as Data[];
for (const job of props.pod.jobs)
{
result.push(new Data(job.average, job.timestamp));
result.push(new Data(job.metrics["VOLUME"].average, new Date(job.unix * 1000)));
}
return new Dataset("", result);
});

View File

@ -1,29 +1,31 @@
<template>
<div class="content-l">
<div class="content-s">
<h2>{{ createLabel(monitoring.type) }} Monitoring: {{ monitoring.configName }}</h2>
<p>{{ monitoring.volumeConfig!.mountPath }}</p>
<div class="content-m">
<div class="left-center">
<h3>{{ monitoring.configName }}</h3>
<p v-if="monitoring.type === 'VOLUME'" class="label">Volume: {{ monitoring.volumeConfig.mountPath }}</p>
</div>
<div class="col-3">
<MonitoredPodComponent class="tile-l" v-for="pod in pods" :pod="pod"></MonitoredPodComponent>
<MonitoredPodComponent class="tile-l" v-for="pod in pods" :pod="pod" :period="period"></MonitoredPodComponent>
</div>
</div>
</template>
<script setup lang="ts">
import type { VolumeMonitoringConfig } from '../MonitoringConfig';
import MonitoredPodComponent from './VolumeMonitoredPodComponent.vue';
import { MonitoredResource } from '../MonitoredResource';
import type { Pod } from '~/classes/Pod';
import type { MonitoringConfig } from '~/classes/monitoring/MonitoringConfig';
import type { Period } from '../Period';
const props = defineProps<{
monitoring: VolumeMonitoringConfig
monitoring: MonitoringConfig,
period: Period
}>();
const pods: Ref<MonitoredResource<Pod>[] | undefined> = ref(undefined);
onMounted(() => {
MonitoredResource.getMonitorings(props.monitoring.id, (_pods: MonitoredResource<Pod>[]) => {
MonitoredResource.getMonitorings(props.monitoring.id, props.period.from, props.period.to, (_pods: MonitoredResource<Pod>[]) => {
pods.value = _pods;
});
});

View File

@ -1,14 +1,12 @@
<template>
<div class="monitorings-page">
<SidebarTemplate>
<NuxtLink class="resources" to="/account/monitorings/nodes">Nodes</NuxtLink>
</SidebarTemplate>
<MonitoringSidebar></MonitoringSidebar>
<NuxtPage></NuxtPage>
</div>
</template>
<script setup lang="ts">
import SidebarTemplate from '~/components/SidebarTemplate.vue';
import MonitoringSidebar from '~/components/MonitoringSidebar.vue';
</script>
<style scoped>

View File

@ -1,14 +0,0 @@
<template>
<VolumeMonitoringPage></VolumeMonitoringPage>
</template>
<script setup lang="ts">
import VolumeMonitoringPage from '~/components/monitorings/VolumeMonitoringPage.vue';
</script>
<style scoped>
.monitorings-page {
display: grid;
grid-template-columns: auto 1fr;
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div class="content-l">
<h2>Add Monitoring Config</h2>
<div class="content-l">
<div class="col-3 tile-l">
<UiInput label="Name">
<input type="text" v-model="config.configName">
</UiInput>
<UiInput label="Type">
<select v-model="config.type">
<option :value="MonitoringType.VOLUME">Volume</option>
<option :value="MonitoringType.WORKLOAD">Workload</option>
<option :value="MonitoringType.HEALTHCHECK">Healthcheck</option>
</select>
</UiInput>
<UiInput label="Interval (e.g. 30s, 5m)">
<input type="text" v-model="config.interval">
</UiInput>
</div>
<div v-if="config.type === MonitoringType.VOLUME" class="tile-l col-2">
<UiInput label="Mount Path">
<input type="text" v-model="config.volumeConfig.mountPath">
</UiInput>
<UiInput label="Container Name">
<input type="text" v-model="config.volumeConfig.containerName">
</UiInput>
</div>
<div class="col-2 tile-l">
<UiInput label="Target Typ">
<select v-model="config.targetConfig.type">
<option :value="TargetType.LABEL">Label</option>
<option :value="TargetType.STATEFUL_SET">Stateful Set</option>
<option :value="TargetType.DEPLOYENT">Deployment</option>
</select>
</UiInput>
<UiInput label="Target Namespace">
<input type="text" v-model="config.targetConfig.namespace">
</UiInput>
</div>
<div v-if="config.targetConfig.type === TargetType.LABEL" class="col-2 tile-l">
<UiInput label="Key">
<input type="text" v-model="config.targetConfig.labelKey">
</UiInput>
<UiInput label="Value">
<input type="text" v-model="config.targetConfig.labelValue">
</UiInput>
</div>
<div class="center">
<UiButton @click="() => create()">Add</UiButton>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { MonitoringConfig, MonitoringConfigCreation } from '~/classes/monitoring/MonitoringConfig';
import { MonitoringType } from '~/classes/monitoring/MonitoringType';
import { TargetType } from '~/classes/monitoring/TargetType';
const config = ref(new MonitoringConfigCreation());
function create()
{
MonitoringConfig.create(config.value);
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,40 @@
<template>
<div v-if="collection" class="content-l">
<div class="spaced-center">
<div class="left-center">
<h2>{{ collection.name }}</h2>
<UiButton icon="add" reverse @click="() => useRouter().push(useRoute().fullPath + '/add')">Add Config</UiButton>
</div>
<TimeframePicker @select="(_period: Period) => period = _period"></TimeframePicker>
</div>
<div class="content-l" v-for="config in collection.configs" v-if="period">
<VolumeMonitoringConfigComponent :monitoring="config" :period="period" v-if="config.type === 'VOLUME'"></VolumeMonitoringConfigComponent>
<MemoryMonitoringConfigComponent :monitoring="config" :period="period" v-if="config.type === 'WORKLOAD'"></MemoryMonitoringConfigComponent>
<CpuMonitoringConfigComponent :monitoring="config" :period="period" v-if="config.type === 'WORKLOAD'"></CpuMonitoringConfigComponent>
</div>
</div>
</template>
<script setup lang="ts">
import { MonitoringCollection } from '~/classes/monitoring/MonitoringCollection';
import VolumeMonitoringConfigComponent from '~/components/monitorings/volumes/VolumeMonitoringConfigComponent.vue';
import TimeframePicker from '~/components/monitorings/TimeframePicker.vue';
import type { Period } from '~/components/monitorings/Period';
import MemoryMonitoringConfigComponent from '~/components/monitorings/memory/MemoryMonitoringConfigComponent.vue';
import CpuMonitoringConfigComponent from '~/components/monitorings/cpu/CpuMonitoringConfigComponent.vue';
const period: Ref<Period | undefined> = ref(undefined);
const collection: Ref<MonitoringCollection | undefined> = ref(undefined);
onMounted(() => {
const id = useRoute().params.collectionId as string;
MonitoringCollection.getById(id, (_collection: MonitoringCollection) => {
collection.value = _collection;
});
});
</script>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>