✨ Add Popup for node inspection
This commit is contained in:
parent
9afc5d8eab
commit
5104631bd2
2
app.vue
2
app.vue
@ -1,11 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<NuxtPage></NuxtPage>
|
<NuxtPage></NuxtPage>
|
||||||
|
<component :is="usePopup().get()"></component>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RouteLocation } from 'vue-router';
|
import type { RouteLocation } from 'vue-router';
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
// Guard dashboard and redirect to login
|
// Guard dashboard and redirect to login
|
||||||
useRouter().beforeEach((route: RouteLocation) => {
|
useRouter().beforeEach((route: RouteLocation) => {
|
||||||
|
|||||||
@ -1,29 +1,100 @@
|
|||||||
import { Metadata } from "../Metadata";
|
import { Metadata } from "../Metadata";
|
||||||
import { type HasMetadata } from "../repo/ResourceRepo";
|
import { type HasMetadata } from "../repo/ResourceRepo";
|
||||||
|
import {State} from "~/classes/Threshold";
|
||||||
|
|
||||||
export class Node implements HasMetadata
|
export class Node implements HasMetadata
|
||||||
{
|
{
|
||||||
constructor (
|
constructor (
|
||||||
public metadata: Metadata,
|
public metadata: Metadata,
|
||||||
public metrics: NodeMetrics,
|
public metrics: NodeMetrics,
|
||||||
public status: Status
|
public status: Status,
|
||||||
|
public spec: Spec
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
static cpuUsageFlag(usage: number): State
|
||||||
|
{
|
||||||
|
if(usage < 20)
|
||||||
|
{
|
||||||
|
return State.GREEN;
|
||||||
|
}
|
||||||
|
if(usage < 60)
|
||||||
|
{
|
||||||
|
return State.ORANGE;
|
||||||
|
}
|
||||||
|
return State.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static memoryUsageFlag(usage: number)
|
||||||
|
{
|
||||||
|
if(usage < 30)
|
||||||
|
{
|
||||||
|
return State.GREEN;
|
||||||
|
}
|
||||||
|
if(usage < 75)
|
||||||
|
{
|
||||||
|
return State.ORANGE;
|
||||||
|
}
|
||||||
|
return State.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static diskUsageFlag(usage: number)
|
||||||
|
{
|
||||||
|
if(usage < 30)
|
||||||
|
{
|
||||||
|
return State.GREEN;
|
||||||
|
}
|
||||||
|
if(usage < 70)
|
||||||
|
{
|
||||||
|
return State.ORANGE;
|
||||||
|
}
|
||||||
|
return State.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isReady(node: Node): boolean | undefined
|
||||||
|
{
|
||||||
|
const conditions = node.status.conditions;
|
||||||
|
if(conditions != undefined)
|
||||||
|
{
|
||||||
|
for(const condition of conditions)
|
||||||
|
{
|
||||||
|
if(condition.type === "Ready" && condition.status === "True")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeMetrics
|
class NodeMetrics
|
||||||
{
|
{
|
||||||
absoluteCpuUsage?: number;
|
absoluteCpuUsage?: number;
|
||||||
relativeCpuUsage?: number;
|
relativeCpuUsage?: number;
|
||||||
|
totalCpu?: number;
|
||||||
absoluteMemory?: number;
|
absoluteMemory?: number;
|
||||||
relativeMemory?: number;
|
relativeMemory?: number;
|
||||||
|
totalMemory?: number;
|
||||||
runningPods?: number;
|
runningPods?: number;
|
||||||
relativeDiskUsage?: number;
|
relativeDiskUsage?: number;
|
||||||
totalDiskSpace?: number;
|
totalDiskSpace?: number;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Spec
|
||||||
|
{
|
||||||
|
constructor (
|
||||||
|
public taints?: Taint[]
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
class Status
|
class Status
|
||||||
{
|
{
|
||||||
|
constructor (
|
||||||
|
public nodeInfo: NodeInfo
|
||||||
|
) {}
|
||||||
|
|
||||||
conditions?: Condition[]
|
conditions?: Condition[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,3 +103,19 @@ class Condition
|
|||||||
type?: string;
|
type?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Taint
|
||||||
|
{
|
||||||
|
constructor (
|
||||||
|
public key: string,
|
||||||
|
public effect: string
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeInfo
|
||||||
|
{
|
||||||
|
constructor (
|
||||||
|
public osImage: string,
|
||||||
|
public kubeletVersion: string
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ defineProps<{
|
|||||||
background-color: rgb(139, 255, 139);
|
background-color: rgb(139, 255, 139);
|
||||||
}
|
}
|
||||||
.outer.green .inner {
|
.outer.green .inner {
|
||||||
background-color: green;
|
background-color: #11cc11;
|
||||||
}
|
}
|
||||||
|
|
||||||
.outer.orange {
|
.outer.orange {
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ConfigMap } from '~/classes/ConfigMap';
|
import type { ConfigMap } from '~/classes/ConfigMap';
|
||||||
import PopupTemplate from '~/components/popup/PopupTemplate.vue';
|
import PopupTemplate from '~/components/popup/PopupTemplate.vue';
|
||||||
import { Prompt, PromptType } from '~/components/ui/Prompt';
|
import { Prompt, PromptType } from '~/components/ui/prompt/Prompt';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
configMap: ConfigMap
|
configMap: ConfigMap
|
||||||
|
|||||||
@ -1,32 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<p class="grid-element" @click="() => showViewPopup = true">{{ deployment.metadata.name }}</p>
|
<p class="grid-element" @click="usePopup().open(DeploymentViewPopup, deployment)">{{ deployment.metadata.name }}</p>
|
||||||
<p class="grid-element">{{ deployment.metadata.namespace }}</p>
|
<p class="grid-element">{{ deployment.metadata.namespace }}</p>
|
||||||
<p class="grid-element">{{ calcAge(deployment.metadata.creationTimestamp) }}</p>
|
<p class="grid-element">{{ calcAge(deployment.metadata.creationTimestamp) }}</p>
|
||||||
<p class="grid-element">{{ deployment.spec.replicas }}</p>
|
<p class="grid-element">{{ deployment.spec.replicas }}</p>
|
||||||
<div class="grid-element action-buttons">
|
<div class="grid-element action-buttons">
|
||||||
<ActionButton @click="() => showLogsPopup = true">text_snippet</ActionButton>
|
<ActionButton @click="usePopup().open(DeploymentLogPopup, deployment)">text_snippet</ActionButton>
|
||||||
<ActionButton>autorenew</ActionButton>
|
<ActionButton>autorenew</ActionButton>
|
||||||
<ActionButton @click="() => rescaleDeploymentPopup.open()">height</ActionButton>
|
<ActionButton @click="usePopup().open(DeploymentRescalePopup, deployment)">height</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
<RescaleDeploymentPopup ref="rescaleDeploymentPopup" :deployment="deployment"></RescaleDeploymentPopup>
|
|
||||||
<DeploymentViewPopup v-if="showViewPopup" :deployment="deployment" @close="() => showViewPopup = false"></DeploymentViewPopup>
|
|
||||||
<DeploymentLogPopup v-if="showLogsPopup" :deployment="deployment" @close="() => showLogsPopup = false"></DeploymentLogPopup>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Deployment } from '~/classes/Deployment';
|
import { Deployment } from '~/classes/Deployment';
|
||||||
import { calcAge } from '~/classes/Pod';
|
import { calcAge } from '~/classes/Pod';
|
||||||
import RescaleDeploymentPopup from '../RescaleDeploymentPopup.vue';
|
import DeploymentRescalePopup from './view/DeploymentRescalePopup.vue';
|
||||||
import DeploymentViewPopup from "~/components/deployment/view/DeploymentViewPopup.vue";
|
import DeploymentViewPopup from "~/components/deployment/view/DeploymentViewPopup.vue";
|
||||||
import DeploymentLogPopup from "~/components/inspect/logs/DeploymentLogPopup.vue";
|
import DeploymentLogPopup from "~/components/deployment/view/DeploymentLogPopup.vue";
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
deployment: Deployment
|
deployment: Deployment
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const rescaleDeploymentPopup = ref();
|
|
||||||
const showViewPopup = ref(false);
|
|
||||||
const showLogsPopup = ref(false);
|
|
||||||
</script>
|
</script>
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<PopupTemplate :heading="'Logs: ' + deployment.metadata.namespace + '/' + deployment.metadata.name" ref="base" @close="emits('close')">
|
<PopupTemplate :heading="StringUtils.format('Logs: %s/%s', deployment.metadata.namespace, deployment.metadata.name)">
|
||||||
<div class="log-container" v-if="pods">
|
<div class="log-container">
|
||||||
<div class="spaced-center">
|
<div class="spaced-center">
|
||||||
<div class="left-center">
|
<div class="left-center">
|
||||||
<UiButton @click="() => config.timestamps = true" v-if="!config.timestamps"><span>Show Timestamp</span> <UiIcon>visibility</UiIcon></UiButton>
|
<UiButton @click="() => config.timestamps = true" v-if="!config.timestamps"><span>Show Timestamp</span> <UiIcon>visibility</UiIcon></UiButton>
|
||||||
@ -20,47 +20,27 @@
|
|||||||
import { LogRepo } from '~/classes/LogRepo';
|
import { LogRepo } from '~/classes/LogRepo';
|
||||||
import { Pod } from '~/classes/Pod';
|
import { Pod } from '~/classes/Pod';
|
||||||
import { Log } from '~/requests/logs';
|
import { Log } from '~/requests/logs';
|
||||||
import Console from "~/components/inspect/logs/Console.vue";
|
import Console from "~/components/inspect/console/Console.vue";
|
||||||
import type {Deployment} from "~/classes/Deployment";
|
import type {Deployment} from "~/classes/Deployment";
|
||||||
import UiIcon from "~/components/ui/UiIcon.vue";
|
import UiIcon from "~/components/ui/UiIcon.vue";
|
||||||
import { ConsoleConfig } from '~/components/inspect/logs/ConsoleConfig'
|
import { ConsoleConfig } from '~/components/inspect/console/ConsoleConfig'
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
const config = ref(ConsoleConfig.default(true, true, false));
|
const config = ref(ConsoleConfig.default(true, true, false));
|
||||||
|
|
||||||
const base = ref();
|
|
||||||
|
|
||||||
const logs: Ref<Log[]> = ref([]);
|
const logs: Ref<Log[]> = ref([]);
|
||||||
const pods: Ref<Pod[] | undefined> = ref(undefined);
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const deployment = usePopup().data() as Deployment;
|
||||||
deployment: Deployment
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const emits = defineEmits<{
|
|
||||||
(e: 'close'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const logRepo = new LogRepo();
|
const logRepo = new LogRepo();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
logRepo.listen("deployments", props.deployment.metadata.namespace, props.deployment.metadata.name,(_logs: Log[]) => {
|
logRepo.listen("deployments", deployment.metadata.namespace, deployment.metadata.name,(_logs: Log[]) => {
|
||||||
logs.value.push(..._logs);
|
logs.value.push(..._logs);
|
||||||
});
|
});
|
||||||
Pod.getByDeployment(props.deployment.metadata.namespace, props.deployment.metadata.name, (_pods: Pod[]) => {
|
|
||||||
pods.value = _pods;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
logRepo.clear();
|
logRepo.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
document.addEventListener('keydown', (event) => {
|
|
||||||
if(event.key === 'Escape')
|
|
||||||
{
|
|
||||||
emits('close');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -22,28 +22,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Deployment } from '~/classes/Deployment';
|
import type { Deployment } from '~/classes/Deployment';
|
||||||
import { rescaleDeployment } from '~/requests/deployments';
|
import { rescaleDeployment } from '~/requests/deployments';
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
const base = ref();
|
const deployment = usePopup().data() as Deployment;
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
deployment: Deployment
|
|
||||||
}>();
|
|
||||||
|
|
||||||
function open()
|
|
||||||
{
|
|
||||||
base.value.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
open
|
|
||||||
})
|
|
||||||
|
|
||||||
function rescale(deployment: Deployment)
|
function rescale(deployment: Deployment)
|
||||||
{
|
{
|
||||||
if(deployment.spec && deployment.spec.replicas)
|
if(deployment.spec && deployment.spec.replicas)
|
||||||
{
|
{
|
||||||
rescaleDeployment(deployment, deployment.spec.replicas, () => {
|
rescaleDeployment(deployment, deployment.spec.replicas, () => {
|
||||||
base.value.close();
|
usePopup().close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -71,10 +71,9 @@
|
|||||||
import PopupTemplate from "~/components/popup/PopupTemplate.vue";
|
import PopupTemplate from "~/components/popup/PopupTemplate.vue";
|
||||||
import type { Deployment } from "~/classes/Deployment";
|
import type { Deployment } from "~/classes/Deployment";
|
||||||
import {calcAge, Pod} from "~/classes/Pod";
|
import {calcAge, Pod} from "~/classes/Pod";
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
const props = defineProps<{
|
const deployment = usePopup().data() as Deployment;
|
||||||
deployment: Deployment
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
(e: 'close'): void
|
(e: 'close'): void
|
||||||
@ -92,7 +91,7 @@ onMounted(() => {
|
|||||||
const pods: Ref<Pod[] | undefined> = ref();
|
const pods: Ref<Pod[] | undefined> = ref();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
Pod.getByDeployment(props.deployment.metadata.namespace, props.deployment.metadata.name, (_pods: Pod[]) => {
|
Pod.getByDeployment(deployment.metadata.namespace, deployment.metadata.name, (_pods: Pod[]) => {
|
||||||
pods.value = _pods;
|
pods.value = _pods;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
24
components/inspect/ResourceType.ts
Normal file
24
components/inspect/ResourceType.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export enum ResourceType
|
||||||
|
{
|
||||||
|
// Cluster Resources
|
||||||
|
NODE = "nodes",
|
||||||
|
NAMESPACE = "namespaces",
|
||||||
|
CUSTOM_RESOURCE_DEFINITION = "custom-resource-definitions",
|
||||||
|
|
||||||
|
// Workloads
|
||||||
|
STATEFUL_SET = "stateful-sets",
|
||||||
|
DEPLOYMENT = "deployments",
|
||||||
|
POD = "pods",
|
||||||
|
|
||||||
|
// Networking
|
||||||
|
SERVICE = "services",
|
||||||
|
INGRESS = "ingresses",
|
||||||
|
|
||||||
|
// Config
|
||||||
|
SECRET = "secrets",
|
||||||
|
CONFIG_MAP = "config-maps",
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
PVC = "pvcs",
|
||||||
|
PV = "pvs",
|
||||||
|
}
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {Log} from "~/requests/logs";
|
import type {Log} from "~/requests/logs";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type {ConsoleConfig} from "~/components/inspect/logs/ConsoleConfig";
|
import type {ConsoleConfig} from "~/components/inspect/console/ConsoleConfig";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
config: ConsoleConfig
|
config: ConsoleConfig
|
||||||
@ -16,10 +16,11 @@
|
|||||||
import type { ConfigMap } from '~/classes/ConfigMap';
|
import type { ConfigMap } from '~/classes/ConfigMap';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import ConfigMapComponent from '~/components/configmap/ConfigMapComponent.vue';
|
import ConfigMapComponent from '~/components/configmap/ConfigMapComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<ConfigMap>();
|
const repo = ResourceRepo.init<ConfigMap>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("config-maps");
|
repo.listen(ResourceType.CONFIG_MAP);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -16,10 +16,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import type {CustomResourceDefinition} from "~/classes/CustomResourceDefinition";
|
import type {CustomResourceDefinition} from "~/classes/CustomResourceDefinition";
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<CustomResourceDefinition>();
|
const repo = ResourceRepo.init<CustomResourceDefinition>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("custom-resource-definitions");
|
repo.listen(ResourceType.CUSTOM_RESOURCE_DEFINITION);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -17,10 +17,11 @@
|
|||||||
import type { Deployment } from '~/classes/Deployment';
|
import type { Deployment } from '~/classes/Deployment';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import DeploymentComponent from "~/components/deployment/DeploymentComponent.vue";
|
import DeploymentComponent from "~/components/deployment/DeploymentComponent.vue";
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Deployment>();
|
const repo = ResourceRepo.init<Deployment>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("deployments");
|
repo.listen(ResourceType.DEPLOYMENT);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -16,10 +16,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Ingress } from '~/classes/Ingress';
|
import type { Ingress } from '~/classes/Ingress';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Ingress>();
|
const repo = ResourceRepo.init<Ingress>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("ingresses");
|
repo.listen(ResourceType.INGRESS);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -14,10 +14,11 @@
|
|||||||
import type { Namespace } from '~/classes/Namespace';
|
import type { Namespace } from '~/classes/Namespace';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import NamespaceComponent from '~/components/NamespaceComponent.vue';
|
import NamespaceComponent from '~/components/NamespaceComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Namespace>();
|
const repo = ResourceRepo.init<Namespace>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("namespaces");
|
repo.listen(ResourceType.NAMESPACE);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<p>Memory</p>
|
<p>Memory</p>
|
||||||
<p>Disk Usage</p>
|
<p>Disk Usage</p>
|
||||||
</div>
|
</div>
|
||||||
<NodeComponent :node-stats="ns" v-for="(ns, index) in node" class="resource" :class="{ even: index % 2 }"></NodeComponent>
|
<NodeComponent :node="ns" v-for="(ns, index) in node" class="resource" :class="{ even: index % 2 }"></NodeComponent>
|
||||||
</div>
|
</div>
|
||||||
</TableComponent>
|
</TableComponent>
|
||||||
</template>
|
</template>
|
||||||
@ -18,9 +18,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Node } from '~/classes/node/Node';
|
import type { Node } from '~/classes/node/Node';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import NodeComponent from '~/components/NodeComponent.vue';
|
import NodeComponent from '~/components/node/NodeComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const node = ResourceRepo.init<Node>().load('nodes').get();
|
const node = ResourceRepo.init<Node>().load(ResourceType.NODE).get();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@ -17,11 +17,12 @@
|
|||||||
import type { PersistentVolumeClaim } from '~/classes/PersistentVolumeClaim';
|
import type { PersistentVolumeClaim } from '~/classes/PersistentVolumeClaim';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import PersistentVolumeClaimComponent from '~/components/PersistentVolumeClaimComponent.vue';
|
import PersistentVolumeClaimComponent from '~/components/PersistentVolumeClaimComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<PersistentVolumeClaim>();
|
const repo = ResourceRepo.init<PersistentVolumeClaim>();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("pvcs");
|
repo.listen(ResourceType.PVC);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -14,11 +14,12 @@
|
|||||||
import type { PersistentVolume } from '~/classes/PersistentVolume';
|
import type { PersistentVolume } from '~/classes/PersistentVolume';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import PersistentVolumeComponent from '~/components/PersistentVolumeComponent.vue';
|
import PersistentVolumeComponent from '~/components/PersistentVolumeComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<PersistentVolume>();
|
const repo = ResourceRepo.init<PersistentVolume>();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("pvs");
|
repo.listen(ResourceType.PV);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -20,10 +20,11 @@
|
|||||||
import type { Pod } from '~/classes/Pod';
|
import type { Pod } from '~/classes/Pod';
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import PodComponent from '~/components/pod/PodComponent.vue';
|
import PodComponent from '~/components/pod/PodComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Pod>();
|
const repo = ResourceRepo.init<Pod>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("pods");
|
repo.listen(ResourceType.POD);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -20,11 +20,12 @@
|
|||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import type { Secret } from '~/classes/Secret';
|
import type { Secret } from '~/classes/Secret';
|
||||||
import SecretComponent from '~/components/secrets/SecretComponent.vue';
|
import SecretComponent from '~/components/secrets/SecretComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Secret>();
|
const repo = ResourceRepo.init<Secret>();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("secrets");
|
repo.listen(ResourceType.SECRET);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -15,10 +15,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import type { Service } from '~/classes/Service';
|
import type { Service } from '~/classes/Service';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Service>();
|
const repo = ResourceRepo.init<Service>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("services");
|
repo.listen(ResourceType.SERVICE);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -16,10 +16,11 @@
|
|||||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||||
import type { StatefulSet } from '~/classes/StatefulSet';
|
import type { StatefulSet } from '~/classes/StatefulSet';
|
||||||
import StatefulSetComponent from '~/components/statefulset/StatefulSetComponent.vue';
|
import StatefulSetComponent from '~/components/statefulset/StatefulSetComponent.vue';
|
||||||
|
import {ResourceType} from "~/classes/ResourceTypes";
|
||||||
|
|
||||||
const repo = ResourceRepo.init<StatefulSet>();
|
const repo = ResourceRepo.init<StatefulSet>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("stateful-sets");
|
repo.listen(ResourceType.STATEFUL_SET);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -1,37 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<p class="grid-element" v-if="nodeStats && nodeStats.metadata">{{ nodeStats.metadata.name }}</p>
|
<p class="grid-element" v-if="node && node.metadata" @click="usePopup().open(NodeViewComponent, node)">{{
|
||||||
|
node.metadata.name
|
||||||
|
}}</p>
|
||||||
<div class="grid-element">
|
<div class="grid-element">
|
||||||
<p>{{ calcAge(nodeStats.metadata?.creationTimestamp) }}</p>
|
<p>{{ calcAge(node.metadata.creationTimestamp) }}</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="grid-element">{{ nodeStats.metrics.runningPods }}</p>
|
<p class="grid-element">{{ node.metrics.runningPods }}</p>
|
||||||
<div class="grid-element">
|
<div class="grid-element">
|
||||||
<NodeReadyComponent :ready="isReady(nodeStats)"></NodeReadyComponent>
|
<NodeReadyComponent :ready="isReady(node)"></NodeReadyComponent>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-element">
|
<div class="grid-element">
|
||||||
<p class="usage" :class="cpuUsageFlag(nodeStats.metrics.relativeCpuUsage)">{{ nodeStats.metrics.relativeCpuUsage }}%</p>
|
<p class="usage" :class="cpuUsageFlag(node.metrics.relativeCpuUsage)">{{ node.metrics.relativeCpuUsage }}%</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-element">
|
<div class="grid-element">
|
||||||
<p class="usage" :class="ramUsageFlag(nodeStats.metrics.relativeMemory)">{{ nodeStats.metrics.relativeMemory }}%</p>
|
<p class="usage" :class="ramUsageFlag(node.metrics.relativeMemory)">{{ node.metrics.relativeMemory }}%</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-element">
|
<div class="grid-element">
|
||||||
<p class="usage" :class="diskUsageFlag(nodeStats.metrics.relativeDiskUsage)"><span v-if="nodeStats.metrics.relativeDiskUsage">{{ nodeStats.metrics.relativeDiskUsage }}%</span><span v-else>-</span></p>
|
<p class="usage" :class="diskUsageFlag(node.metrics.relativeDiskUsage)"><span v-if="node.metrics.relativeDiskUsage">{{ node.metrics.relativeDiskUsage }}%</span><span v-else>-</span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Node } from '~/classes/node/Node';
|
import type { Node } from '~/classes/node/Node';
|
||||||
import NodeReadyComponent from './NodeReadyComponent.vue';
|
import NodeReadyComponent from '../NodeReadyComponent.vue';
|
||||||
import { calcAge } from '~/classes/Pod';
|
import { calcAge } from '~/classes/Pod';
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
import NodeViewComponent from "~/components/node/view/NodeViewComponent.vue";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
nodeStats: Node;
|
node: Node;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function isReady(nodeStats: Node): boolean | undefined
|
function isReady(node: Node): boolean | undefined
|
||||||
{
|
{
|
||||||
const conditions = nodeStats.status.conditions;
|
const conditions = node.status.conditions;
|
||||||
if(conditions != undefined)
|
if(conditions != undefined)
|
||||||
{
|
{
|
||||||
for(const condition of conditions)
|
for(const condition of conditions)
|
||||||
96
components/node/view/NodeViewComponent.vue
Normal file
96
components/node/view/NodeViewComponent.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<PopupTemplate :heading="StringUtils.format('Node: %s', node.metadata.name)">
|
||||||
|
<div class="content-l">
|
||||||
|
<h2>General</h2>
|
||||||
|
<div class="content-l">
|
||||||
|
<div class="col-3">
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Node</h3>
|
||||||
|
<p class="tile-m">{{ node.metadata.name }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Status</h3>
|
||||||
|
<div class="left-center">
|
||||||
|
<NodeReadyComponent :ready="Node.isReady(node)"></NodeReadyComponent>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Metrics</h3>
|
||||||
|
<div class="col-3">
|
||||||
|
<div class="tile-l content-m">
|
||||||
|
<p>Total CPU</p>
|
||||||
|
<p class="metric" v-if="node.metrics.totalCpu != null">{{ (node.metrics.totalCpu / 1000).toFixed(2) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="tile-l content-m">
|
||||||
|
<p>Total Disk Space</p>
|
||||||
|
<p class="metric" v-if="node.metrics.totalDiskSpace != null" v-for="diskSpace in [Memory.format(node.metrics.totalDiskSpace)]">{{ diskSpace.value.toFixed(2) }} {{ formatUnit(diskSpace.unit) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="tile-l content-m">
|
||||||
|
<p>Total Memory</p>
|
||||||
|
<p class="metric" v-if="node.metrics.totalMemory != null" v-for="memory in [Memory.format(node.metrics.totalMemory)]">{{ memory.value.toFixed(2) }} {{ formatUnit(memory.unit) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="tile-l content-m" v-if="node.metrics.relativeCpuUsage">
|
||||||
|
<div class="left-center">
|
||||||
|
<p>Used CPU</p>
|
||||||
|
<Pulse :threshold="new Threshold(node.metrics.relativeCpuUsage, Node.cpuUsageFlag)"></Pulse>
|
||||||
|
</div>
|
||||||
|
<p class="metric">{{ node.metrics.relativeCpuUsage }}%</p>
|
||||||
|
</div>
|
||||||
|
<div class="tile-l content-m" v-if="node.metrics.relativeDiskUsage != null">
|
||||||
|
<div class="left-center">
|
||||||
|
<p>Used Disk Space</p>
|
||||||
|
<Pulse :threshold="new Threshold(node.metrics.relativeDiskUsage, Node.diskUsageFlag)"></Pulse>
|
||||||
|
</div>
|
||||||
|
<p class="metric">{{ node.metrics.relativeDiskUsage }}%</p>
|
||||||
|
</div>
|
||||||
|
<div class="tile-l content-m" v-if="node.metrics.relativeMemory != null">
|
||||||
|
<div class="left-center">
|
||||||
|
<p>Used Memory</p>
|
||||||
|
<Pulse :threshold="new Threshold(node.metrics.relativeMemory, Node.memoryUsageFlag)"></Pulse>
|
||||||
|
</div>
|
||||||
|
<p class="metric">{{ node.metrics.relativeMemory }}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-m" v-if="node.spec.taints">
|
||||||
|
<h3>Taints ({{ node.spec.taints.length }})</h3>
|
||||||
|
<div class="content-s">
|
||||||
|
<p class="tile-m" v-for="taint in node.spec.taints">{{ taint.key }}: {{ taint.effect }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Operating System</h3>
|
||||||
|
<p class="tile-m">{{ node.status.nodeInfo.osImage }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Kubelet Version</h3>
|
||||||
|
<p class="tile-m">{{ node.status.nodeInfo.kubeletVersion }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopupTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PopupTemplate from "~/components/popup/PopupTemplate.vue";
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
import {Node} from "~/classes/node/Node";
|
||||||
|
import { Memory, MemoryUnity } from '~/utils/Memory';
|
||||||
|
import { Threshold } from '~/classes/Threshold';
|
||||||
|
|
||||||
|
const node = usePopup().data() as Node;
|
||||||
|
|
||||||
|
function formatUnit(unit: MemoryUnity)
|
||||||
|
{
|
||||||
|
return new Map<MemoryUnity, String>([[MemoryUnity.RAW, "Bytes"], [MemoryUnity.KI, "Ki"], [MemoryUnity.MI, "Mi"], [MemoryUnity.GI, "Gi"], [MemoryUnity.TI, "Ti"]]).get(unit);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.metric {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<p class="grid-element pointer" v-if="pod.metadata" @click="() => showViewPopup = true">{{ pod.metadata.name }}</p>
|
<p class="grid-element pointer" v-if="pod.metadata" @click="usePopup().open(PodViewPopup, props.pod)">{{ pod.metadata.name }}</p>
|
||||||
<p class="grid-element" v-if="pod.metadata">{{ pod.metadata.namespace }}</p>
|
<p class="grid-element" v-if="pod.metadata">{{ pod.metadata.namespace }}</p>
|
||||||
<p class="grid-element no-wrap" v-if="pod.metadata">{{ calcAge(pod.metadata.creationTimestamp) }}</p>
|
<p class="grid-element no-wrap" v-if="pod.metadata">{{ calcAge(pod.metadata.creationTimestamp) }}</p>
|
||||||
<p class="grid-element" v-if="pod.spec">{{ pod.spec.nodeName }}</p>
|
<p class="grid-element" v-if="pod.spec">{{ pod.spec.nodeName }}</p>
|
||||||
@ -10,12 +10,9 @@
|
|||||||
<PhaseComponent v-if="pod.status" :phase="pod.status.phase"></PhaseComponent>
|
<PhaseComponent v-if="pod.status" :phase="pod.status.phase"></PhaseComponent>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-element action-buttons">
|
<div class="grid-element action-buttons">
|
||||||
<ActionButton @click="showLogPopup = true">text_snippet</ActionButton>
|
<ActionButton @click="usePopup().open(PodLogPopup, props.pod)">text_snippet</ActionButton>
|
||||||
<ActionButton @click="showDeletePopup = true">delete</ActionButton>
|
<ActionButton @click="usePopup().open(PodDeletePopup, props.pod)">delete</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
<PodDeletePopup v-if="showDeletePopup" :pod="pod" @close="showDeletePopup = false"></PodDeletePopup>
|
|
||||||
<PodLogPopup v-if="showLogPopup" :pod="pod" @close="showLogPopup = false"></PodLogPopup>
|
|
||||||
<PodViewPopup v-if="showViewPopup" :pod="pod" @close="showViewPopup = false"></PodViewPopup>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -23,13 +20,11 @@
|
|||||||
import type { Pod } from '~/classes/Pod';
|
import type { Pod } from '~/classes/Pod';
|
||||||
import { calcAge } from '~/classes/Pod';
|
import { calcAge } from '~/classes/Pod';
|
||||||
import PodDeletePopup from './view/PodDeletePopup.vue';
|
import PodDeletePopup from './view/PodDeletePopup.vue';
|
||||||
import PodLogPopup from "~/components/inspect/logs/PodLogPopup.vue";
|
import PodLogPopup from "~/components/pod/view/PodLogPopup.vue";
|
||||||
|
import PodViewPopup from "~/components/pod/view/PodViewPopup.vue";
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
pod: Pod
|
pod: Pod
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const showDeletePopup = ref(false);
|
|
||||||
const showLogPopup = ref(false);
|
|
||||||
const showViewPopup = ref(false);
|
|
||||||
</script>
|
</script>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<UiMask class="spaced-center" :prefix="env.key" :enabled="isSensitive(env.key)" :value="env.value"></UiMask>
|
<UiMask class="spaced-center" :prefix="env.key" :enabled="isSensitive(env.key)" :value="env.value"></UiMask>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="error === true">
|
<div v-if="error === true">
|
||||||
<UiPompt :prompt="new Prompt('This pod does not support retrieving environment variables using \'env\'', PromptType.ERROR)"></UiPompt>
|
<UiPrompt :prompt="new Prompt('This pod does not support retrieving environment variables using \'env\'', PromptType.ERROR)"></UiPrompt>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ScrollComponent>
|
</ScrollComponent>
|
||||||
@ -19,12 +19,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Prompt, PromptType } from '~/components/ui/prompt/Prompt';
|
||||||
import type { Container, Pod } from '~/classes/Pod';
|
import type { Container, Pod } from '~/classes/Pod';
|
||||||
import { EnvVar } from '../env/EnvVar';
|
import {EnvVar} from "~/components/pod/env/EnvVar";
|
||||||
import ContainerPicker from '~/components/ui/ContainerPicker.vue';
|
import ContainerPicker from '~/components/ui/ContainerPicker.vue';
|
||||||
import UiMask from '~/components/ui/UiMask.vue';
|
import UiMask from '~/components/ui/UiMask.vue';
|
||||||
import { Prompt, PromptType } from '~/components/ui/Prompt';
|
import UiPrompt from '~/components/ui/prompt/UiPrompt.vue'
|
||||||
import UiPompt from '~/components/ui/UiPompt.vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
pod: Pod
|
pod: Pod
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<PopupTemplate size="SMALL" heading="Delete Pod" @close="emits('close')">
|
<PopupTemplate size="SMALL" heading="Delete Pod">
|
||||||
<div class="content-l">
|
<div class="content-l">
|
||||||
<div class="content-m">
|
<div class="content-m">
|
||||||
<div class="tile-m">
|
<div class="tile-m">
|
||||||
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<UiButton :loading="loading" class="width-6rem hollow">Cancel</UiButton>
|
<UiButton :loading="loading" class="width-6rem hollow" @click="usePopup().close()">Cancel</UiButton>
|
||||||
<UiButton :loading="loading" class="width-6rem" icon="delete" reverse :onclick="() => del()">Delete</UiButton>
|
<UiButton :loading="loading" class="width-6rem" icon="delete" reverse :onclick="() => del()">Delete</UiButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -23,24 +23,19 @@
|
|||||||
import type { Metadata } from '~/classes/Metadata';
|
import type { Metadata } from '~/classes/Metadata';
|
||||||
import { type Pod } from '~/classes/Pod';
|
import { type Pod } from '~/classes/Pod';
|
||||||
import { deletePod } from '~/requests/pod';
|
import { deletePod } from '~/requests/pod';
|
||||||
|
import { usePopup } from "~/components/popup/Popup";
|
||||||
|
|
||||||
const props = defineProps<{
|
const pod = usePopup().data() as Pod;
|
||||||
pod: Pod
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
function del()
|
function del()
|
||||||
{
|
{
|
||||||
const metadata: Metadata = props.pod.metadata;
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
const metadata: Metadata = pod.metadata;
|
||||||
deletePod(metadata.namespace, metadata.name, () => {
|
deletePod(metadata.namespace, metadata.name, () => {
|
||||||
emits('close');
|
usePopup().close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits<{
|
|
||||||
(e: 'close'): void
|
|
||||||
}>()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -16,9 +16,10 @@
|
|||||||
import { LogRepo } from '~/classes/LogRepo';
|
import { LogRepo } from '~/classes/LogRepo';
|
||||||
import type { Pod } from '~/classes/Pod';
|
import type { Pod } from '~/classes/Pod';
|
||||||
import { Log } from '~/requests/logs';
|
import { Log } from '~/requests/logs';
|
||||||
import Console from "~/components/inspect/logs/Console.vue";
|
import Console from "~/components/inspect/console/Console.vue";
|
||||||
import UiIcon from "~/components/ui/UiIcon.vue";
|
import UiIcon from "~/components/ui/UiIcon.vue";
|
||||||
import { ConsoleConfig } from '~/components/inspect/logs/ConsoleConfig'
|
import { ConsoleConfig } from '~/components/inspect/console/ConsoleConfig'
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
const config = ref(ConsoleConfig.default(true, false, false));
|
const config = ref(ConsoleConfig.default(true, false, false));
|
||||||
|
|
||||||
@ -26,9 +27,7 @@ const base = ref();
|
|||||||
|
|
||||||
const logs: Ref<Log[]> = ref([]);
|
const logs: Ref<Log[]> = ref([]);
|
||||||
|
|
||||||
const props = defineProps<{
|
const pod = usePopup().data() as Pod;
|
||||||
pod: Pod
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
(e: 'close'): void
|
(e: 'close'): void
|
||||||
@ -36,7 +35,7 @@ const emits = defineEmits<{
|
|||||||
|
|
||||||
const logRepo = new LogRepo();
|
const logRepo = new LogRepo();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
logRepo.listen("pods", props.pod.metadata.namespace, props.pod.metadata.name,(_logs: Log[]) => {
|
logRepo.listen("pods", pod.metadata.namespace, pod.metadata.name,(_logs: Log[]) => {
|
||||||
logs.value.push(..._logs);
|
logs.value.push(..._logs);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<PopupTemplate ref="base" :heading="StringUtils.format('%s/%s', pod.metadata.namespace, pod.metadata.name)" @close="emits('close')">
|
<PopupTemplate ref="base" :heading="StringUtils.format('%s/%s', pod.metadata.namespace, pod.metadata.name)">
|
||||||
<div class="col-2 expand">
|
<div class="col-2 expand">
|
||||||
<ScrollComponent>
|
<ScrollComponent>
|
||||||
<div class="content-l">
|
<div class="content-l">
|
||||||
@ -54,29 +54,7 @@ import { calcAge, type Pod } from '~/classes/Pod';
|
|||||||
import PhaseComponent from '~/components/PhaseComponent.vue';
|
import PhaseComponent from '~/components/PhaseComponent.vue';
|
||||||
import ScrollComponent from '~/components/ScrollComponent.vue';
|
import ScrollComponent from '~/components/ScrollComponent.vue';
|
||||||
import EnvironmentViewer from './EnvironmentViewer.vue';
|
import EnvironmentViewer from './EnvironmentViewer.vue';
|
||||||
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
const base = ref();
|
const pod = usePopup().data() as Pod;
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
pod: Pod
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const emits = defineEmits<{
|
|
||||||
(e: 'close'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
function open() {
|
|
||||||
base.value.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
document.addEventListener('keydown', (event) => {
|
|
||||||
if(event.key === 'Escape')
|
|
||||||
{
|
|
||||||
base.value.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
defineExpose({ open });
|
|
||||||
</script>
|
</script>
|
||||||
54
components/popup/Popup.ts
Normal file
54
components/popup/Popup.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type {DefineComponent} from "vue";
|
||||||
|
|
||||||
|
export const usePopup = defineStore('popup', {
|
||||||
|
state: () => ({
|
||||||
|
component: shallowRef<DefineComponent<any, any, any> | undefined>(undefined),
|
||||||
|
payload: undefined as any,
|
||||||
|
isOpen: false as boolean
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
data(): any {
|
||||||
|
return () => {
|
||||||
|
return this.payload;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: (state) => {
|
||||||
|
return () => {
|
||||||
|
return state.component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
open(component: DefineComponent<any, any, any>, payload?: any) {
|
||||||
|
this.component = component;
|
||||||
|
this.payload = payload;
|
||||||
|
this.isOpen = true
|
||||||
|
disableScrolling();
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.component = undefined;
|
||||||
|
this.payload = undefined;
|
||||||
|
this.isOpen = false;
|
||||||
|
enableScrolling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function disableScrolling()
|
||||||
|
{
|
||||||
|
const body = document.getElementsByTagName('body');
|
||||||
|
for(const element of body)
|
||||||
|
{
|
||||||
|
element.style.overflow = "hidden";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableScrolling()
|
||||||
|
{
|
||||||
|
const body = document.getElementsByTagName('body');
|
||||||
|
for(const element of body)
|
||||||
|
{
|
||||||
|
element.style.overflow = "visible";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="overlay center" @click="close" v-show="visible || true">
|
<div class="overlay center" @click="usePopup().close()">
|
||||||
<div class="popup" :class="size" @click.stop>
|
<div class="popup" :class="size" @click.stop>
|
||||||
<div class="popup__header">
|
<div class="popup__header">
|
||||||
<h2>{{ heading }}</h2>
|
<h2>{{ heading }}</h2>
|
||||||
<UiButton icon="close" @click="() => close()" class="square"></UiButton>
|
<UiButton icon="close" @click="usePopup().close()" class="square"></UiButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="popup__body">
|
<div class="popup__body">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
@ -13,50 +13,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const visible = ref(false);
|
import {usePopup} from "~/components/popup/Popup";
|
||||||
|
|
||||||
const emits = defineEmits<{
|
|
||||||
(e: 'close'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
enableScrolling();
|
|
||||||
emits('close');
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function open() {
|
|
||||||
disableScrolling();
|
|
||||||
visible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
heading: String,
|
heading: String,
|
||||||
size: String
|
size: String
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
document.addEventListener('keydown', (event) => {
|
||||||
close,
|
if(event.key === 'Escape')
|
||||||
open
|
{
|
||||||
})
|
usePopup().close();
|
||||||
|
}
|
||||||
function disableScrolling()
|
});
|
||||||
{
|
|
||||||
const body = document.getElementsByTagName('body');
|
|
||||||
for(const element of body)
|
|
||||||
{
|
|
||||||
element.style.overflow = "hidden";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableScrolling()
|
|
||||||
{
|
|
||||||
const body = document.getElementsByTagName('body');
|
|
||||||
for(const element of body)
|
|
||||||
{
|
|
||||||
element.style.overflow = "visible";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -69,7 +38,6 @@ function enableScrolling()
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
padding: 0.5rem;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,6 @@ const emits = defineEmits<{
|
|||||||
watch(selected, (selection) => {
|
watch(selected, (selection) => {
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
console.log("X");
|
|
||||||
emits('selected', selection);
|
emits('selected', selection);
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|||||||
@ -1,32 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<PodComponent v-if="resource === 'pods'"></PodComponent>
|
<Component v-if="components[resource]" :is="components[resource]"></Component>
|
||||||
<CustomResourceDefinitionComponent v-else-if="resource === 'custom-resource-definitions'"></CustomResourceDefinitionComponent>
|
|
||||||
<IngressComponent v-else-if="resource === 'ingresses'"></IngressComponent>
|
|
||||||
<ServiceComponent v-else-if="resource === 'services'"></ServiceComponent>
|
|
||||||
<DeploymentList v-else-if="resource === 'deployments'"></DeploymentList>
|
|
||||||
<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>
|
<p v-else>Invalid resource</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PodComponent from '~/components/inspect/resources/PodList.vue';
|
import PodList from '~/components/inspect/resources/PodList.vue';
|
||||||
import CustomResourceDefinitionComponent from '~/components/inspect/resources/CustomResourceDefinitionList.vue';
|
import CustomResourceDefinitionList from '~/components/inspect/resources/CustomResourceDefinitionList.vue';
|
||||||
import IngressComponent from '~/components/inspect/resources/IngressList.vue';
|
import IngressList from '~/components/inspect/resources/IngressList.vue';
|
||||||
import ServiceComponent from '~/components/inspect/resources/ServiceList.vue';
|
import ServiceList from '~/components/inspect/resources/ServiceList.vue';
|
||||||
import DeploymentList from "~/components/inspect/resources/DeploymentList.vue";
|
import DeploymentList from "~/components/inspect/resources/DeploymentList.vue";
|
||||||
import NodeComponent from '~/components/inspect/resources/NodeList.vue';
|
import NodeList from '~/components/inspect/resources/NodeList.vue';
|
||||||
import SecretComponent from '~/components/inspect/resources/SecretList.vue';
|
import SecretList from '~/components/inspect/resources/SecretList.vue';
|
||||||
import ConfigMapList from '~/components/inspect/resources/ConfigMapList.vue';
|
import ConfigMapList from '~/components/inspect/resources/ConfigMapList.vue';
|
||||||
import StatefulSetList from '~/components/inspect/resources/StatefulSetList.vue';
|
import StatefulSetList from '~/components/inspect/resources/StatefulSetList.vue';
|
||||||
import PersistentVolumeList from '~/components/inspect/resources/PersistentVolumeList.vue';
|
import PersistentVolumeList from '~/components/inspect/resources/PersistentVolumeList.vue';
|
||||||
import PersistentVolumeClaimList from '~/components/inspect/resources/PersistentVolumeClaimList.vue';
|
import PersistentVolumeClaimList from '~/components/inspect/resources/PersistentVolumeClaimList.vue';
|
||||||
import NamespaceList from '~/components/inspect/resources/NamespaceList.vue';
|
import NamespaceList from '~/components/inspect/resources/NamespaceList.vue';
|
||||||
|
import type { Component } from "vue";
|
||||||
|
import {ResourceType} from "~/components/inspect/ResourceType";
|
||||||
|
|
||||||
const resource = useRoute().params.resource as string;
|
const resource = useRoute().params.resource as ResourceType;
|
||||||
|
|
||||||
|
const components: Record<ResourceType, Component> = {
|
||||||
|
[ResourceType.NODE]: NodeList,
|
||||||
|
[ResourceType.NAMESPACE]: NamespaceList,
|
||||||
|
[ResourceType.CUSTOM_RESOURCE_DEFINITION]: CustomResourceDefinitionList,
|
||||||
|
|
||||||
|
[ResourceType.POD]: PodList,
|
||||||
|
[ResourceType.DEPLOYMENT]: DeploymentList,
|
||||||
|
[ResourceType.STATEFUL_SET]: StatefulSetList,
|
||||||
|
|
||||||
|
[ResourceType.SERVICE]: ServiceList,
|
||||||
|
[ResourceType.INGRESS]: IngressList,
|
||||||
|
|
||||||
|
[ResourceType.SECRET]: SecretList,
|
||||||
|
[ResourceType.CONFIG_MAP]: ConfigMapList,
|
||||||
|
|
||||||
|
[ResourceType.PV]: PersistentVolumeList,
|
||||||
|
[ResourceType.PVC]: PersistentVolumeClaimList,
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content-l">
|
<div class="content-l">
|
||||||
<h2>Change Password</h2>
|
<h2>Change Password</h2>
|
||||||
<UiPompt :prompt="prompt" @close="prompt = undefined"></UiPompt>
|
<UiPrompt :prompt="prompt" @close="prompt = undefined"></UiPrompt>
|
||||||
<div class="col-2 tile-l">
|
<div class="col-2 tile-l">
|
||||||
<UiInput label="New Password" required>
|
<UiInput label="New Password" required>
|
||||||
<input type="password" v-model="password">
|
<input type="password" v-model="password">
|
||||||
@ -17,9 +17,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Prompt, PromptType } from '~/components/ui/Prompt';
|
import { Prompt, PromptType } from '~/components/ui/prompt/Prompt';
|
||||||
import { changePassword } from '~/requests/user';
|
import { changePassword } from '~/requests/user';
|
||||||
import UiPompt from '~/components/ui/UiPompt.vue';
|
import UiPrompt from '~/components/ui/prompt/UiPrompt.vue';
|
||||||
|
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const repeatPassword = ref('');
|
const repeatPassword = ref('');
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<div class="content-l">
|
<div class="content-l">
|
||||||
<h2 class="center">Kubooboo</h2>
|
<h2 class="center">Kubooboo</h2>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<img class="logo" src="@/assets/transparent_logo.png" alt=""></img>
|
<img class="logo" src="@/assets/transparent_logo.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<h1>Login</h1>
|
<h1>Login</h1>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Node } from "~/classes/Node";
|
import {Node} from "~/classes/node/Node";
|
||||||
|
|
||||||
export function getNodes(onSuccess: (nodes: Node[]) => void)
|
export function getNodes(onSuccess: (nodes: Node[]) => void)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
import type {DefineComponent} from "vue";
|
|
||||||
|
|
||||||
export const usePopup = defineStore('namespace', {
|
|
||||||
state: () => ({
|
|
||||||
component: undefined as DefineComponent | undefined,
|
|
||||||
data: undefined as any,
|
|
||||||
isOpen: false as boolean
|
|
||||||
}),
|
|
||||||
getters: {
|
|
||||||
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
open(component: DefineComponent, data?: any) {
|
|
||||||
this.component = component;
|
|
||||||
this.data = data;
|
|
||||||
this.isOpen = true;
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
this.component = undefined;
|
|
||||||
this.data = undefined;
|
|
||||||
this.isOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
23
utils/Memory.ts
Normal file
23
utils/Memory.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export class Memory
|
||||||
|
{
|
||||||
|
constructor (
|
||||||
|
public value: number,
|
||||||
|
public unit: MemoryUnity
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static format(input: number)
|
||||||
|
{
|
||||||
|
let dimension: number = 0;
|
||||||
|
while (input > 1024)
|
||||||
|
{
|
||||||
|
input = input / 1024;
|
||||||
|
dimension++;
|
||||||
|
}
|
||||||
|
return new Memory(input, dimension as MemoryUnity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MemoryUnity
|
||||||
|
{
|
||||||
|
RAW, KI, MI, GI, TI
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user