✨ Add per container env
This commit is contained in:
parent
f0e072bc70
commit
48780e5164
@ -26,9 +26,12 @@ class Status
|
||||
phase?: PodStatus
|
||||
}
|
||||
|
||||
class Container
|
||||
export class Container
|
||||
{
|
||||
|
||||
constructor (
|
||||
public name: string,
|
||||
public image: string
|
||||
) {}
|
||||
}
|
||||
|
||||
enum PodStatus
|
||||
|
||||
@ -1,18 +1,22 @@
|
||||
<template>
|
||||
<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">{{ 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>
|
||||
<div class="grid-element">
|
||||
<ActionButton>delete</ActionButton>
|
||||
</div>
|
||||
<ConfigmapViewComponent v-if="show" :config-map="configMap" @close="show = false"></ConfigmapViewComponent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ConfigMap } from '~/classes/ConfigMap';
|
||||
import { calcAge } from '~/classes/Pod';
|
||||
import ConfigmapViewComponent from './view/ConfigmapViewComponent.vue';
|
||||
|
||||
const show = ref(false);
|
||||
|
||||
defineProps<{
|
||||
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">
|
||||
import type { ConfigMap } from '~/classes/ConfigMap';
|
||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||
import ConfigMapComponent from '~/components/configmap/ConfigMapComponent.vue';
|
||||
|
||||
const repo = ResourceRepo.init<ConfigMap>();
|
||||
onMounted(() => {
|
||||
|
||||
@ -16,11 +16,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { Deployment } from '~/classes/Deployment';
|
||||
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>();
|
||||
onMounted(() => {
|
||||
repo.listen("deployments");
|
||||
repo.listen("deployment");
|
||||
});
|
||||
onUnmounted(() => {
|
||||
repo.clear();
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ResourceRepo } from '~/classes/repo/ResourceRepo';
|
||||
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>();
|
||||
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
|
||||
) {}
|
||||
|
||||
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)
|
||||
.then((response) => {
|
||||
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>
|
||||
</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>
|
||||
</ScrollComponent>
|
||||
<div class="content-l">
|
||||
<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>
|
||||
<EnvironmentViewer :pod="pod"></EnvironmentViewer>
|
||||
</div>
|
||||
</PopupTemplate>
|
||||
</template>
|
||||
@ -60,9 +52,8 @@
|
||||
<script setup lang="ts">
|
||||
import { calcAge, type Pod } from '~/classes/Pod';
|
||||
import PhaseComponent from '~/components/PhaseComponent.vue';
|
||||
import { EnvVar } from '../env/EnvVar';
|
||||
import ScrollComponent from '~/components/ScrollComponent.vue';
|
||||
import UiMask from '~/components/ui/UiMask.vue';
|
||||
import EnvironmentViewer from './EnvironmentViewer.vue';
|
||||
|
||||
const base = ref();
|
||||
|
||||
@ -74,21 +65,6 @@ const emits = defineEmits<{
|
||||
(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() {
|
||||
base.value.open();
|
||||
}
|
||||
@ -103,34 +79,4 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
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>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
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
|
||||
{
|
||||
ERROR = "error",
|
||||
SUCCESS = "success"
|
||||
SUCCESS = "success",
|
||||
INFO = "info"
|
||||
}
|
||||
@ -20,15 +20,18 @@ const emit = defineEmits<{
|
||||
<style scoped>
|
||||
.prompt {
|
||||
border-radius: 0.25rem;
|
||||
height: 2.5rem;
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
.error {
|
||||
background-color: rgb(218, 57, 57);
|
||||
background-color: rgb(255, 74, 74);
|
||||
}
|
||||
.success {
|
||||
background-color: rgb(43, 161, 49);
|
||||
}
|
||||
.info {
|
||||
background-color: rgb(22, 79, 163);
|
||||
}
|
||||
.prompt * {
|
||||
color: white;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
</style>
|
||||
@ -6,7 +6,7 @@
|
||||
<h3>Kubooboo</h3>
|
||||
<div class="left-center">
|
||||
<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>
|
||||
<p class="pointer" @click="logout()">Logout</p>
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<CustomResourceDefinitionComponent v-else-if="resource === 'custom-resource-definitions'"></CustomResourceDefinitionComponent>
|
||||
<IngressComponent v-else-if="resource === 'ingresses'"></IngressComponent>
|
||||
<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>
|
||||
<SecretComponent v-else-if="resource === 'secrets'"></SecretComponent>
|
||||
<ConfigMapList v-else-if="resource === 'config-maps'"></ConfigMapList>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user