Add namespace, pvc, pv and stateful sets

This commit is contained in:
andreas.dinauer 2025-11-08 13:57:31 +01:00
parent 7b05306369
commit 40e2721b82
23 changed files with 361 additions and 32 deletions

View File

@ -1,9 +1,12 @@
import type { Metadata } from "./Metadata";
import type { HasMetadata } from "./repo/ResourceRepo";
export class Deployment
export class Deployment implements HasMetadata
{
metadata?: Metadata;
spec?: DeploymentSpec;
constructor (
public metadata: Metadata,
public spec: DeploymentSpec
) {}
}
export class DeploymentSpec

View File

@ -1,6 +1,7 @@
import type { Metadata } from "./Metadata";
import type { HasMetadata } from "./repo/ResourceRepo";
export class Namespace
export class Namespace implements HasMetadata
{
constructor (
public metadata: Metadata

View File

@ -0,0 +1,9 @@
import type { Metadata } from "./Metadata";
import type { HasMetadata } from "./repo/ResourceRepo";
export class PersistentVolume implements HasMetadata
{
constructor (
public metadata: Metadata
) {}
}

View File

@ -0,0 +1,18 @@
import type { Metadata } from "./Metadata";
import type { HasMetadata } from "./repo/ResourceRepo";
export class PersistentVolumeClaim implements HasMetadata
{
constructor (
public metadata: Metadata,
public spec: PersistentVolumeClaimSpec
) {}
}
export class PersistentVolumeClaimSpec
{
constructor (
public accessModes: string[],
public storageClassName: string
) {}
}

29
classes/ResourceTypes.ts Normal file
View File

@ -0,0 +1,29 @@
export enum ResourceType {
NODE = "nodes",
NAMESPACE = "namespaces",
CUSTOM_RESOURCE_DEFINITION = "custom-resource-definitions",
STATEFUL_SET = "stateful-sets",
DEPLOYMENT = "deployments",
POD = "pods",
SERVICE = "services",
INGRESS = "ingresses",
SECRET = "secrets",
CONFIG_MAP = "config-maps",
PVC = "pvcs",
PV = "pvs"
}
export const RESOURCE_DISPLAY_NAMES: Map<ResourceType, string> = new Map([
[ResourceType.NODE, "Node"],
[ResourceType.NAMESPACE, "Namespace"],
[ResourceType.CUSTOM_RESOURCE_DEFINITION, "CRD"],
[ResourceType.STATEFUL_SET, "Stateful Set"],
[ResourceType.DEPLOYMENT, "Deployment"],
[ResourceType.POD, "Pod"],
[ResourceType.SERVICE, "Service"],
[ResourceType.INGRESS, "Ingress"],
[ResourceType.SECRET, "Secret"],
[ResourceType.CONFIG_MAP, "Config Map"],
[ResourceType.PVC, "PVC"],
[ResourceType.PV, "PV"]
]);

9
classes/StatefulSet.ts Normal file
View File

@ -0,0 +1,9 @@
import type { Metadata } from "./Metadata";
import type { HasMetadata } from "./repo/ResourceRepo";
export class StatefulSet implements HasMetadata
{
constructor (
public metadata: Metadata
) {}
}

View File

@ -3,7 +3,7 @@
<ScrollComponent>
<div class="content-l">
<div class="nav">
<NuxtLink class="resources" v-for="[key, value] of resources" :to="getRoute(key, namespace)" :class="{ 'router-link-active': useRoute().params.resource === key }">{{ value }}</NuxtLink>
<NuxtLink class="resources" v-for="key in ResourceType" :to="getRoute(key, namespace)" :class="{ 'router-link-active': useRoute().params.resource === key }">{{ RESOURCE_DISPLAY_NAMES.get(key) }}</NuxtLink>
</div>
<div class="divider" :class="{ hide: !inNamespaceScopedResource }"></div>
<div class="nav" :class="{ hide: !inNamespaceScopedResource }">
@ -22,8 +22,7 @@
import SidebarTemplate from './SidebarTemplate.vue';
import { StringUtils, useNamespaceStore } from '#imports';
const resources = new Map<string, string>([["nodes", "Nodes"], ["ingresses", "Ingresses"], ["services", "Services"], ["deployments", "Deployments"], ["stateful-sets", "Stateful Sets"], ["pods", "Pods"], ["secrets", "Secrets"], ["config-maps", "Config Maps"], ["custom-resource-definitions", "CDRs"]]);
import { RESOURCE_DISPLAY_NAMES, ResourceType } from '~/classes/ResourceTypes';
const namespaceStore = useNamespaceStore();
@ -56,7 +55,7 @@ const base = computed(() => {
const inNamespaceScopedResource: ComputedRef<boolean> = computed(() => {
const resource = useRoute().params.resource as string;
if(['custom-resource-definitions', 'nodes'].includes(resource))
if(['custom-resource-definitions', 'nodes', 'pvs', 'namespaces'].includes(resource))
{
return false;
}

View File

@ -0,0 +1,16 @@
<template>
<div>
<p class="grid-element">{{ namespace.metadata.name }}</p>
<div class="grid-element">
<ActionButton>delete</ActionButton>
</div>
</div>
</template>
<script setup lang="ts">
import type { Namespace } from '~/classes/Namespace';
defineProps<{
namespace: Namespace
}>();
</script>

View File

@ -0,0 +1,41 @@
<template>
<div>
<p class="grid-element">{{ persistentVolumeClaim.metadata.name }}</p>
<p class="grid-element">{{ persistentVolumeClaim.metadata.namespace }}</p>
<p class="grid-element">{{ calcAge(persistentVolumeClaim.metadata.creationTimestamp) }}</p>
<p class="grid-element">{{ persistentVolumeClaim.spec.storageClassName }}</p>
<p class="grid-element">{{ persistentVolumeClaim.spec.accessModes.map(mode => mapAccessMode(mode)).join(", ") }}</p>
<div class="grid-element">
<ActionButton>delete</ActionButton>
</div>
</div>
</template>
<script setup lang="ts">
import type { PersistentVolumeClaim } from '~/classes/PersistentVolumeClaim';
import { calcAge } from '~/classes/Pod';
defineProps<{
persistentVolumeClaim: PersistentVolumeClaim
}>();
function mapAccessMode(accessMode: string): string
{
switch (accessMode)
{
case "ReadWriteOnce": {
return "RWO"
}
case "ReadOnlyMany": {
return "ROX"
}
case "ReadWriteMany": {
return "RWX";
}
case "ReadWriteOncePod": {
return "RWOP";
}
}
return accessMode;
}
</script>

View File

@ -0,0 +1,18 @@
<template>
<div>
<p class="grid-element">{{ persistentVolume.metadata.name }}</p>
<p class="grid-element">{{ calcAge(persistentVolume.metadata.creationTimestamp) }}</p>
<div class="grid-element">
<ActionButton>delete</ActionButton>
</div>
</div>
</template>
<script setup lang="ts">
import type { PersistentVolume } from '~/classes/PersistentVolume';
import { calcAge } from '~/classes/Pod';
defineProps<{
persistentVolume: PersistentVolume
}>();
</script>

View File

@ -0,0 +1,17 @@
<template>
<div>
<p class="grid-element">{{ statefulSet.metadata.name }}</p>
<p class="grid-element">{{ statefulSet.metadata.namespace }}</p>
<div class="grid-element action-buttons">
<ActionButton>delete</ActionButton>
</div>
</div>
</template>
<script setup lang="ts">
import type { StatefulSet } from '~/classes/StatefulSet';
defineProps<{
statefulSet: StatefulSet
}>();
</script>

View File

@ -1,5 +1,5 @@
<template>
<TableComponent :loading="deployments == null">
<TableComponent :loading="deployments == null" v-for="deployments in [repo.get().value]">
<div class="resource-container deployment-container">
<div class="header">
<p>Name</p>
@ -17,7 +17,13 @@ import type { Deployment } from '~/classes/Deployment';
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import DeploymentComponent from '~/components/deployments/DeploymentComponent.vue';
const deployments = ResourceRepo.init<Deployment>().load('deployments').get();
const repo = ResourceRepo.init<Deployment>();
onMounted(() => {
repo.listen("deployments");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style scoped>

View File

@ -1,5 +1,5 @@
<template>
<TableComponent :loading="ingresses == null">
<TableComponent :loading="ingresses == null" v-for="ingresses in [repo.get().value]">
<div class="resource-container ingress-container">
<div class="header">
<p>Name</p>
@ -17,7 +17,13 @@
import type { Ingress } from '~/classes/Ingress';
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
const ingresses = ResourceRepo.init<Ingress>().load('ingresses').get();
const repo = ResourceRepo.init<Ingress>();
onMounted(() => {
repo.listen("ingresses");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style scoped>

View File

@ -0,0 +1,31 @@
<template>
<TableComponent :loading="namespaces == null" v-for="namespaces in [repo.get().value]">
<div class="resource-container namespace-container">
<div class="header">
<p>Name</p>
<p>Actions</p>
</div>
<NamespaceComponent :namespace="namespace" v-for="namespace, index in namespaces" class="resource" :class="{ even: index % 2 }"></NamespaceComponent>
</div>
</TableComponent>
</template>
<script setup lang="ts">
import type { Namespace } from '~/classes/Namespace';
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import NamespaceComponent from '~/components/NamespaceComponent.vue';
const repo = ResourceRepo.init<Namespace>();
onMounted(() => {
repo.listen("namespaces");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style scoped>
.namespace-container {
grid-template-columns: 1fr auto;
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<TableComponent :loading="persistentVolumeClaims == null" v-for="persistentVolumeClaims in [repo.get().value]">
<div class="resource-container persistent-volume-claim-container">
<div class="header">
<p>Name</p>
<p>Namespace</p>
<p>Alter</p>
<p>Storage Class</p>
<p>Access Modes</p>
<p>Aktionen</p>
</div>
<PersistentVolumeClaimComponent v-for="persistentVolumeClaim, index in persistentVolumeClaims" :persistent-volume-claim="persistentVolumeClaim" class="resource" :class="{ even: index % 2 }"></PersistentVolumeClaimComponent>
</div>
</TableComponent>
</template>
<script setup lang="ts">
import type { PersistentVolumeClaim } from '~/classes/PersistentVolumeClaim';
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import PersistentVolumeClaimComponent from '~/components/PersistentVolumeClaimComponent.vue';
const repo = ResourceRepo.init<PersistentVolumeClaim>();
onMounted(() => {
repo.listen("pvcs");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style scoped>
.persistent-volume-claim-container {
display: grid;
grid-template-columns: auto auto auto auto 1fr auto;
}
</style>

View File

@ -0,0 +1,34 @@
<template>
<TableComponent :loading="persistentVolumes == null" v-for="persistentVolumes in [repo.get().value]">
<div class="resource-container persistent-volume-container">
<div class="header">
<p>Name</p>
<p>Alter</p>
<p>Aktionen</p>
</div>
<PersistentVolumeComponent v-for="persistentVolume, index in persistentVolumes" :persistent-volume="persistentVolume" class="resource" :class="{ even: index % 2 }"></PersistentVolumeComponent>
</div>
</TableComponent>
</template>
<script setup lang="ts">
import type { PersistentVolume } from '~/classes/PersistentVolume';
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import PersistentVolumeComponent from '~/components/PersistentVolumeComponent.vue';
const repo = ResourceRepo.init<PersistentVolume>();
onMounted(() => {
repo.listen("pvs");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style scoped>
.persistent-volume-container {
display: grid;
grid-template-columns: auto 1fr auto;
}
</style>

View File

@ -1,4 +1,5 @@
<template>
<TableComponent :loading="secrets == null" v-for="secrets in [repo.get().value]">
<div class="resource-container secret-container">
<div class="header">
<div class="left-center">
@ -10,17 +11,23 @@
<p>Aktionen</p>
</div>
<SecretComponent v-for="secret, index in secrets" :secret="secret" class="resource" :class="{ even: index % 2 }"></SecretComponent>
<SecretAddComponent v-if="false" ref="secretAddComponent"></SecretAddComponent>
</div>
</TableComponent>
</template>
<script setup lang="ts">
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import type { Secret } from '~/classes/Secret';
import SecretComponent from '~/components/secrets/SecretComponent.vue';
import SecretAddComponent from '~/components/secrets/SecretAddComponent.vue';
const secrets = ResourceRepo.init<Secret>().load("secrets").get();
const repo = ResourceRepo.init<Secret>();
onMounted(() => {
repo.listen("secrets");
});
onUnmounted(() => {
repo.clear();
});
const secretAddComponent = ref();
</script>

View File

@ -1,5 +1,5 @@
<template>
<TableComponent :loading="services == null">
<TableComponent :loading="services == null" v-for="services in [repo.get().value]">
<div class="resource-container service-container">
<div class="header">
<p>Service</p>
@ -16,7 +16,13 @@
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import type { Service } from '~/classes/Service';
const services = ResourceRepo.init<Service>().load('services').get();
const repo = ResourceRepo.init<Service>();
onMounted(() => {
repo.listen("services");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style>

View File

@ -0,0 +1,33 @@
<template>
<TableComponent :loading="statefulSets == null" v-for="statefulSets in [repo.get().value]">
<div class="resource-container deployment-container">
<div class="header">
<p>Name</p>
<p>Namespace</p>
<p>Replicas</p>
<p>Aktionen</p>
</div>
<StatefulSetComponent :stateful-set="statefulSet" v-for="statefulSet, index in statefulSets" class="resource" :class="{ even: index % 2 }"></StatefulSetComponent>
</div>
</TableComponent>
</template>
<script setup lang="ts">
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
import type { StatefulSet } from '~/classes/StatefulSet';
import StatefulSetComponent from '~/components/deployments/StatefulSetComponent.vue';
const repo = ResourceRepo.init<StatefulSet>();
onMounted(() => {
repo.listen("stateful-sets");
});
onUnmounted(() => {
repo.clear();
});
</script>
<style scoped>
.deployment-container {
grid-template-columns: auto 1fr 1fr auto;
}
</style>

View File

@ -23,7 +23,8 @@ export default defineNuxtConfig({
app: {
head: {
link: [
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200' }
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200' },
{ rel: 'icon', type: 'image/x-icon', href: '/transparent_logo.png' }
]
}
},

View File

@ -33,7 +33,7 @@ function logout()
.footer {
background-color: var(--tile-color);
padding: 0.5rem;
border-bottom: 1px solid #c0d1ff;
border-bottom: 1px solid #cddaff;
}
.logo {
width: 2.5rem;

View File

@ -7,6 +7,10 @@
<NodeComponent v-else-if="resource === 'nodes'"></NodeComponent>
<SecretComponent v-else-if="resource === 'secrets'"></SecretComponent>
<ConfigMapList v-else-if="resource === 'config-maps'"></ConfigMapList>
<StatefulSetList v-else-if="resource === 'stateful-sets'"></StatefulSetList>
<PersistentVolumeList v-else-if="resource === 'pvs'"></PersistentVolumeList>
<PersistentVolumeClaimList v-else-if="resource === 'pvcs'"></PersistentVolumeClaimList>
<NamespaceList v-else-if="resource === 'namespaces'"></NamespaceList>
<p v-else>Invalid resource</p>
</template>
@ -19,6 +23,10 @@ import DeploymentComponent from '~/components/inspect/resources/DeploymentList.v
import NodeComponent from '~/components/inspect/resources/NodeList.vue';
import SecretComponent from '~/components/inspect/resources/SecretList.vue';
import ConfigMapList from '~/components/inspect/resources/ConfigMapList.vue';
import StatefulSetList from '~/components/inspect/resources/StatefulSetList.vue';
import PersistentVolumeList from '~/components/inspect/resources/PersistentVolumeList.vue';
import PersistentVolumeClaimList from '~/components/inspect/resources/PersistentVolumeClaimList.vue';
import NamespaceList from '~/components/inspect/resources/NamespaceList.vue';
const resource = useRoute().params.resource as string;
</script>

BIN
public/transparent_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB