✨ Add per container env
This commit is contained in:
parent
f0e072bc70
commit
48780e5164
@ -26,9 +26,12 @@ class Status
|
|||||||
phase?: PodStatus
|
phase?: PodStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
class Container
|
export class Container
|
||||||
{
|
{
|
||||||
|
constructor (
|
||||||
|
public name: string,
|
||||||
|
public image: string
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PodStatus
|
enum PodStatus
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<p class="grid-element">{{ configMap.metadata.name }}</p>
|
<p class="grid-element" @click="() => show = true">{{ configMap.metadata.name }}</p>
|
||||||
<p class="grid-element">{{ configMap.metadata.namespace }}</p>
|
<p class="grid-element">{{ configMap.metadata.namespace }}</p>
|
||||||
<p class="grid-element">{{ calcAge(configMap.metadata.creationTimestamp) }}</p>
|
<p class="grid-element">{{ calcAge(configMap.metadata.creationTimestamp) }}</p>
|
||||||
<p class="grid-element"><span v-if="configMap.data">{{ Object.keys(configMap.data).length }}</span><span v-else>-</span></p>
|
<p class="grid-element"><span v-if="configMap.data">{{ Object.keys(configMap.data).length }}</span><span v-else>-</span></p>
|
||||||
<div class="grid-element">
|
<div class="grid-element">
|
||||||
<ActionButton>delete</ActionButton>
|
<ActionButton>delete</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
|
<ConfigmapViewComponent v-if="show" :config-map="configMap" @close="show = false"></ConfigmapViewComponent>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ConfigMap } from '~/classes/ConfigMap';
|
import type { ConfigMap } from '~/classes/ConfigMap';
|
||||||
import { calcAge } from '~/classes/Pod';
|
import { calcAge } from '~/classes/Pod';
|
||||||
|
import ConfigmapViewComponent from './view/ConfigmapViewComponent.vue';
|
||||||
|
|
||||||
|
const show = ref(false);
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
configMap: ConfigMap
|
configMap: ConfigMap
|
||||||
23
components/configmap/view/ConfigmapViewComponent.vue
Normal file
23
components/configmap/view/ConfigmapViewComponent.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<PopupTemplate :heading="StringUtils.format('%s/%s', configMap.metadata.namespace, configMap.metadata.name)" @close="emits('close')">
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Data</h3>
|
||||||
|
<p class="tile-m" v-if="configMap.data" v-for="[key, value] in Object.entries(configMap.data)">{{ key }}: {{ value }}</p>
|
||||||
|
<UiPompt v-else :prompt="new Prompt('This config map contains no data.', PromptType.INFO)"></UiPompt>
|
||||||
|
</div>
|
||||||
|
</PopupTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ConfigMap } from '~/classes/ConfigMap';
|
||||||
|
import PopupTemplate from '~/components/popup/PopupTemplate.vue';
|
||||||
|
import { Prompt, PromptType } from '~/components/ui/Prompt';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
configMap: ConfigMap
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'close'): void
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
13
components/deployment/view/DeploymentViewPopup.vue
Normal file
13
components/deployment/view/DeploymentViewPopup.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<PopupTemplate>
|
||||||
|
|
||||||
|
</PopupTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PopupTemplate from "~/components/popup/PopupTemplate.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -16,6 +16,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
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';
|
||||||
|
|
||||||
const repo = ResourceRepo.init<ConfigMap>();
|
const repo = ResourceRepo.init<ConfigMap>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -16,11 +16,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
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/deployments/DeploymentComponent.vue';
|
import DeploymentComponent from '~/components/deployment/DeploymentComponent.vue';
|
||||||
|
|
||||||
const repo = ResourceRepo.init<Deployment>();
|
const repo = ResourceRepo.init<Deployment>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
repo.listen("deployments");
|
repo.listen("deployment");
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
repo.clear();
|
repo.clear();
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
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/deployments/StatefulSetComponent.vue';
|
import StatefulSetComponent from '~/components/statefulset/StatefulSetComponent.vue';
|
||||||
|
|
||||||
const repo = ResourceRepo.init<StatefulSet>();
|
const repo = ResourceRepo.init<StatefulSet>();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
7
components/pod/env/EnvVar.ts
vendored
7
components/pod/env/EnvVar.ts
vendored
@ -7,12 +7,15 @@ export class EnvVar
|
|||||||
public value: string
|
public value: string
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static get(namespace: string, name: string, onSuccess: (envVars: EnvVar[]) => void)
|
static get(namespace: string, name: string, containerName: string, onSuccess: (envVars: EnvVar[]) => void, onError: () => void)
|
||||||
{
|
{
|
||||||
const url = StringUtils.format('%s/pods/%s/%s/env', ApiConfig.getHttpBase(), namespace, name);
|
const url = StringUtils.format('%s/pods/%s/%s/%s/env', ApiConfig.getHttpBase(), namespace, name, containerName);
|
||||||
axios.get<EnvVar[]>(url)
|
axios.get<EnvVar[]>(url)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
onSuccess(response.data);
|
onSuccess(response.data);
|
||||||
})
|
})
|
||||||
|
.catch(() => {
|
||||||
|
onError();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
86
components/pod/view/EnvironmentViewer.vue
Normal file
86
components/pod/view/EnvironmentViewer.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-l">
|
||||||
|
<h2>Env</h2>
|
||||||
|
<ContainerPicker :containers="pod.spec.containers" v-if="pod.spec.containers.length > 0" @selected="payload => container = payload"></ContainerPicker>
|
||||||
|
<UiInput v-if="error === false">
|
||||||
|
<input type="text" name="" id="" v-model="filter" placeholder="Filter...">
|
||||||
|
</UiInput>
|
||||||
|
<ScrollComponent>
|
||||||
|
<div>
|
||||||
|
<div class="env" v-for="env in filteredEnv" v-if="error === false">
|
||||||
|
<UiMask class="spaced-center" :prefix="env.key" :enabled="isSensitive(env.key)" :value="env.value"></UiMask>
|
||||||
|
</div>
|
||||||
|
<div v-if="error === true">
|
||||||
|
<UiPompt :prompt="new Prompt('This pod does not support retrieving environment variables using \'env\'', PromptType.ERROR)"></UiPompt>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollComponent>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Container, Pod } from '~/classes/Pod';
|
||||||
|
import { EnvVar } from '../env/EnvVar';
|
||||||
|
import ContainerPicker from '~/components/ui/ContainerPicker.vue';
|
||||||
|
import UiMask from '~/components/ui/UiMask.vue';
|
||||||
|
import { Prompt, PromptType } from '~/components/ui/Prompt';
|
||||||
|
import UiPompt from '~/components/ui/UiPompt.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
pod: Pod
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const container: Ref<Container | undefined> = ref(undefined);
|
||||||
|
const error = ref(false);
|
||||||
|
|
||||||
|
const envs: Ref<EnvVar[] | undefined> = ref(undefined);
|
||||||
|
watch(container, (container) => {
|
||||||
|
if (container && container.name)
|
||||||
|
{
|
||||||
|
EnvVar.get(props.pod.metadata.namespace, props.pod.metadata.name, container.name, (_envs: EnvVar[]) => {
|
||||||
|
error.value = false;
|
||||||
|
envs.value = _envs;
|
||||||
|
}, () => {
|
||||||
|
error.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
const filter = ref("");
|
||||||
|
const filteredEnv = computed(() => {
|
||||||
|
if (envs.value)
|
||||||
|
{
|
||||||
|
return envs.value.filter(item => item.key.toLowerCase().includes(filter.value.toLowerCase()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function isSensitive(key: string)
|
||||||
|
{
|
||||||
|
const markers = ["token", "key", "password", "secret", "private", "credential", "auth", "jwt"];
|
||||||
|
for (const marker of markers)
|
||||||
|
{
|
||||||
|
if (key.toLowerCase().includes(marker) && !key.toLowerCase().includes("public"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.env {
|
||||||
|
background-color: var(--shade-light);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
.env.env:nth-of-type(2n) {
|
||||||
|
background-color: var(--shade-dark);
|
||||||
|
}
|
||||||
|
.env:hover {
|
||||||
|
background-color: #cecece;
|
||||||
|
}
|
||||||
|
.env {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -38,21 +38,13 @@
|
|||||||
<p class="tile-m" v-for="[key, value] in Object.entries(pod.metadata.labels)"><span>{{ key }}:</span> <span>{{ value }}</span></p>
|
<p class="tile-m" v-for="[key, value] in Object.entries(pod.metadata.labels)"><span>{{ key }}:</span> <span>{{ value }}</span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="content-m">
|
||||||
|
<h3>Containers</h3>
|
||||||
|
<p class="tile-m" v-for="container in pod.spec.containers">{{ container.name }}: {{ container.image }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ScrollComponent>
|
</ScrollComponent>
|
||||||
<div class="content-l">
|
<EnvironmentViewer :pod="pod"></EnvironmentViewer>
|
||||||
<h2>Env</h2>
|
|
||||||
<UiInput>
|
|
||||||
<input type="text" name="" id="" v-model="filter" placeholder="Filter...">
|
|
||||||
</UiInput>
|
|
||||||
<ScrollComponent>
|
|
||||||
<div>
|
|
||||||
<div class="env" v-for="env in filteredEnv">
|
|
||||||
<UiMask class="spaced-center" :prefix="env.key" :enabled="isSensitive(env.key)" :value="env.value"></UiMask>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ScrollComponent>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</PopupTemplate>
|
</PopupTemplate>
|
||||||
</template>
|
</template>
|
||||||
@ -60,9 +52,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { calcAge, type Pod } from '~/classes/Pod';
|
import { calcAge, type Pod } from '~/classes/Pod';
|
||||||
import PhaseComponent from '~/components/PhaseComponent.vue';
|
import PhaseComponent from '~/components/PhaseComponent.vue';
|
||||||
import { EnvVar } from '../env/EnvVar';
|
|
||||||
import ScrollComponent from '~/components/ScrollComponent.vue';
|
import ScrollComponent from '~/components/ScrollComponent.vue';
|
||||||
import UiMask from '~/components/ui/UiMask.vue';
|
import EnvironmentViewer from './EnvironmentViewer.vue';
|
||||||
|
|
||||||
const base = ref();
|
const base = ref();
|
||||||
|
|
||||||
@ -74,21 +65,6 @@ const emits = defineEmits<{
|
|||||||
(e: 'close'): void
|
(e: 'close'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const envs: Ref<EnvVar[] | undefined> = ref(undefined);
|
|
||||||
onMounted(() => {
|
|
||||||
EnvVar.get(props.pod.metadata.namespace, props.pod.metadata.name, (_envs: EnvVar[]) => {
|
|
||||||
envs.value = _envs;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
const filter = ref("");
|
|
||||||
const filteredEnv = computed(() => {
|
|
||||||
if (envs.value)
|
|
||||||
{
|
|
||||||
return envs.value.filter(item => item.key.toLowerCase().includes(filter.value.toLowerCase()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
base.value.open();
|
base.value.open();
|
||||||
}
|
}
|
||||||
@ -103,34 +79,4 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({ open });
|
defineExpose({ open });
|
||||||
|
|
||||||
function isSensitive(key: string)
|
|
||||||
{
|
|
||||||
const markers = ["token", "key", "password", "secret", "private", "credential", "auth", "jwt"];
|
|
||||||
for (const marker of markers)
|
|
||||||
{
|
|
||||||
if (key.toLowerCase().includes(marker) && !key.toLowerCase().includes("public"))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.env {
|
|
||||||
background-color: var(--shade-light);
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
}
|
|
||||||
.env.env:nth-of-type(2n) {
|
|
||||||
background-color: var(--shade-dark);
|
|
||||||
}
|
|
||||||
.env:hover {
|
|
||||||
background-color: #cecece;
|
|
||||||
}
|
|
||||||
.env {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto 1fr;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
54
components/ui/ContainerPicker.vue
Normal file
54
components/ui/ContainerPicker.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tile container-picker">
|
||||||
|
<div class="inner-container-picker">
|
||||||
|
<div class="container pointer" v-for="container in containers" :class="{ selected: container.name === selected?.name }" @click="() => selected = container">{{ container.name }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Container } from '~/classes/Pod';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
containers: Container[]
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const selected: Ref<Container | undefined> = ref(props.containers.at(0));
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'selected', payload: Container): void
|
||||||
|
}>();
|
||||||
|
|
||||||
|
watch(selected, (selection) => {
|
||||||
|
if (selection != null)
|
||||||
|
{
|
||||||
|
console.log("X");
|
||||||
|
emits('selected', selection);
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container-picker {
|
||||||
|
padding: 0.5rem;
|
||||||
|
height: 3rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.inner-container-picker {
|
||||||
|
background-color: white;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
color: black;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.container.selected {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -9,5 +9,6 @@ export class Prompt
|
|||||||
export enum PromptType
|
export enum PromptType
|
||||||
{
|
{
|
||||||
ERROR = "error",
|
ERROR = "error",
|
||||||
SUCCESS = "success"
|
SUCCESS = "success",
|
||||||
|
INFO = "info"
|
||||||
}
|
}
|
||||||
@ -20,15 +20,18 @@ const emit = defineEmits<{
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.prompt {
|
.prompt {
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
height: 2.5rem;
|
min-height: 2.5rem;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
background-color: rgb(218, 57, 57);
|
background-color: rgb(255, 74, 74);
|
||||||
}
|
}
|
||||||
.success {
|
.success {
|
||||||
background-color: rgb(43, 161, 49);
|
background-color: rgb(43, 161, 49);
|
||||||
}
|
}
|
||||||
|
.info {
|
||||||
|
background-color: rgb(22, 79, 163);
|
||||||
|
}
|
||||||
.prompt * {
|
.prompt * {
|
||||||
color: white;
|
color: rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -6,7 +6,7 @@
|
|||||||
<h3>Kubooboo</h3>
|
<h3>Kubooboo</h3>
|
||||||
<div class="left-center">
|
<div class="left-center">
|
||||||
<NuxtLink to="/account/inspect/nodes/_all">Inspect</NuxtLink>
|
<NuxtLink to="/account/inspect/nodes/_all">Inspect</NuxtLink>
|
||||||
<NuxtLink to="/account/monitorings/nodes">Monitorings</NuxtLink>
|
<NuxtLink v-if="false" to="/account/monitorings/nodes">Monitorings</NuxtLink>
|
||||||
<NuxtLink to="/account/settings/password">Settings</NuxtLink>
|
<NuxtLink to="/account/settings/password">Settings</NuxtLink>
|
||||||
<p class="pointer" @click="logout()">Logout</p>
|
<p class="pointer" @click="logout()">Logout</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<CustomResourceDefinitionComponent v-else-if="resource === 'custom-resource-definitions'"></CustomResourceDefinitionComponent>
|
<CustomResourceDefinitionComponent v-else-if="resource === 'custom-resource-definitions'"></CustomResourceDefinitionComponent>
|
||||||
<IngressComponent v-else-if="resource === 'ingresses'"></IngressComponent>
|
<IngressComponent v-else-if="resource === 'ingresses'"></IngressComponent>
|
||||||
<ServiceComponent v-else-if="resource === 'services'"></ServiceComponent>
|
<ServiceComponent v-else-if="resource === 'services'"></ServiceComponent>
|
||||||
<DeploymentComponent v-else-if="resource === 'deployments'"></DeploymentComponent>
|
<DeploymentComponent v-else-if="resource === 'deployment'"></DeploymentComponent>
|
||||||
<NodeComponent v-else-if="resource === 'nodes'"></NodeComponent>
|
<NodeComponent v-else-if="resource === 'nodes'"></NodeComponent>
|
||||||
<SecretComponent v-else-if="resource === 'secrets'"></SecretComponent>
|
<SecretComponent v-else-if="resource === 'secrets'"></SecretComponent>
|
||||||
<ConfigMapList v-else-if="resource === 'config-maps'"></ConfigMapList>
|
<ConfigMapList v-else-if="resource === 'config-maps'"></ConfigMapList>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user