From 86d645a682de3bf6f51be86e306724f75cf6d6a8 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Tue, 19 Nov 2024 22:02:22 +0100 Subject: [PATCH 1/5] fix standallone running instances --- server/src/modules/settings.ts | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/server/src/modules/settings.ts b/server/src/modules/settings.ts index 19a904d2..24f8c7fc 100644 --- a/server/src/modules/settings.ts +++ b/server/src/modules/settings.ts @@ -42,14 +42,14 @@ export class Settings { const namespace = process.env.KUBERO_NAMESPACE || "kubero" let kuberoes = await this.kubectl.getKuberoConfig(namespace) -/* + let configMap: KuberoConfig if (process.env.NODE_ENV === "production") { configMap = new KuberoConfig(kuberoes.spec.kubero.config) } else { configMap = new KuberoConfig(this.readConfig()) } -*/ + let config: any = {} config.settings = kuberoes.spec @@ -176,10 +176,33 @@ export class Settings { } public async getDefaultRegistry(): Promise { - const namespace = process.env.KUBERO_NAMESPACE || "kubero" - let kuberoes = await this.kubectl.getKuberoConfig(namespace) + + let registry = process.env.KUBERO_REGISTRY || { + account:{ + hash: '$2y$05$czQZpvtDYc5OzM/1r1pH0eAplT/okohh/mXoWl/Y65ZP/8/jnSWZq', + password: 'kubero', + username: 'kubero', + + }, + create: false, + enabled: false, + host: 'registry.demo.kubero.dev', + port: 443, + storage: '1Gi', + storageClassName: null, + subpath: "", + + } + try { + const namespace = process.env.KUBERO_NAMESPACE || "kubero" + const kuberoes = await this.kubectl.getKuberoConfig(namespace) + registry = kuberoes.spec.registry + } catch (error) { + console.log("Error getting kuberoes config") + } + return registry + - return kuberoes.spec.registry } public async getDomains(): Promise { From 200de789d9102923fb3153e037752a31bbff8770 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Wed, 27 Nov 2024 23:57:06 +0100 Subject: [PATCH 2/5] add setup view --- client/package.json | 3 +- client/src/components/pipelines/list.vue | 23 +- client/src/components/setup/index.vue | 393 +++++++++++++++++++++++ client/src/layouts/setup/Setup.vue | 10 + client/src/layouts/setup/View.vue | 14 + client/src/router/index.ts | 11 + client/src/views/Setup.vue | 7 + client/yarn.lock | 5 + server/src/modules/kubectl.ts | 48 ++- server/src/modules/settings.ts | 32 ++ server/src/routes/config.ts | 41 +++ 11 files changed, 580 insertions(+), 7 deletions(-) create mode 100644 client/src/components/setup/index.vue create mode 100644 client/src/layouts/setup/Setup.vue create mode 100644 client/src/layouts/setup/View.vue create mode 100644 client/src/views/Setup.vue diff --git a/client/package.json b/client/package.json index 9677276b..8e26e197 100644 --- a/client/package.json +++ b/client/package.json @@ -28,7 +28,8 @@ "vue3-apexcharts": "^1.5.2", "vue3-cookies": "^1.0.6", "vuetify": "^3.4.0", - "xterm": "^5.3.0" + "xterm": "^5.3.0", + "yaml": "^2.6.1" }, "devDependencies": { "@babel/types": "^7.21.4", diff --git a/client/src/components/pipelines/list.vue b/client/src/components/pipelines/list.vue index 8846649d..78cb71ad 100644 --- a/client/src/components/pipelines/list.vue +++ b/client/src/components/pipelines/list.vue @@ -10,13 +10,14 @@ New Pipeline - + Create your first pipeline + + +

Kubero can't reach your kubernetes cluster. Please proceed with the setup to continue.

+ + Start Setup +
+
@@ -113,6 +130,7 @@ import axios from "axios"; import { ref, defineComponent } from 'vue' import Breadcrumbs from "../breadcrumbs.vue"; import { useKuberoStore } from '../../stores/kubero' +import { mapState } from 'pinia' import Swal from 'sweetalert2'; type Pipeline = { @@ -172,6 +190,9 @@ export default defineComponent({ } ], }}, + computed: { + ...mapState(useKuberoStore, ['kubero']), + }, methods: { async loadPipelinesList() { const self = this; diff --git a/client/src/components/setup/index.vue b/client/src/components/setup/index.vue new file mode 100644 index 00000000..20c050dd --- /dev/null +++ b/client/src/components/setup/index.vue @@ -0,0 +1,393 @@ + + + + + \ No newline at end of file diff --git a/client/src/layouts/setup/Setup.vue b/client/src/layouts/setup/Setup.vue new file mode 100644 index 00000000..8946e389 --- /dev/null +++ b/client/src/layouts/setup/Setup.vue @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/client/src/layouts/setup/View.vue b/client/src/layouts/setup/View.vue new file mode 100644 index 00000000..ac4db202 --- /dev/null +++ b/client/src/layouts/setup/View.vue @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 088b43ed..5c0a9607 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -95,6 +95,17 @@ const routes = [ }, ], }, + { + path: '/setup', + component: () => import('@/layouts/setup/Setup.vue'), + children: [ + { + path: '/setup', + name: 'Setup', + component: () => import('@/views/Setup.vue'), + }, + ], + }, { path: '/popup', component: () => import('@/layouts/default/Popup.vue'), diff --git a/client/src/views/Setup.vue b/client/src/views/Setup.vue new file mode 100644 index 00000000..8eeff2cd --- /dev/null +++ b/client/src/views/Setup.vue @@ -0,0 +1,7 @@ + + + diff --git a/client/yarn.lock b/client/yarn.lock index 9f3e0f36..db0f23a6 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1956,6 +1956,11 @@ xterm@^5.3.0: resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46" integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg== +yaml@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" + integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" diff --git a/server/src/modules/kubectl.ts b/server/src/modules/kubectl.ts index cd4311e1..81efaa88 100644 --- a/server/src/modules/kubectl.ts +++ b/server/src/modules/kubectl.ts @@ -54,8 +54,14 @@ export class Kubectl { private exec: Exec = {} as Exec; constructor() { - //this.config = config; this.kc = new KubeConfig(); + this.log = new KubeLog(this.kc); + this.kubeVersion = new VersionInfo(); + this.initKubeConfig(); + } + + private initKubeConfig() { + //this.config = config; //this.kc.loadFromDefault(); // should not be used since we want also load from base64 ENV var if (process.env.KUBECONFIG_BASE64) { @@ -76,7 +82,6 @@ export class Kubectl { } } - this.log = new KubeLog(this.kc); try { this.versionApi = this.kc.makeApiClient(VersionApi); @@ -96,7 +101,6 @@ export class Kubectl { return; } - this.kubeVersion = new VersionInfo(); this.getKubeVersion() .then(v => { this.kubeVersion = v; @@ -111,7 +115,6 @@ export class Kubectl { debug.log("ℹ️ Operator version: " + v); this.kuberoOperatorVersion = v || 'unknown'; }) - } public async getKubeVersion(): Promise{ @@ -177,7 +180,7 @@ export class Kubectl { ) return pipelines.body as IKubectlPipelineList; } catch (error) { - //debug.log(error); + debug.log(error); debug.log("getPipelinesList: error getting pipelines"); } const pipelines = {} as IKubectlPipelineList; @@ -1227,4 +1230,39 @@ export class Kubectl { debug.log("getJobs: error getting jobs"); } } + + public async validateKubeconfig(kubeconfig: string, kubeContext: string): Promise<{error: any, valid: boolean}> { + // validate config for setup process + + //let buff = Buffer.from(configBase64, 'base64'); + //const kubeconfig = buff.toString('ascii'); + + const kc = new KubeConfig(); + kc.loadFromString(kubeconfig); + kc.setCurrentContext(kubeContext); + + try { + const versionApi = kc.makeApiClient(VersionApi); + let versionInfo = await versionApi.getCode() + console.log(JSON.stringify(versionInfo.body)); + return { error: null, valid: true }; + } catch (error: any) { + console.log("Error validating kubeconfig: " + error); + console.log(error); + return {error: error.message, valid: false}; + } + } + + public updateKubectlConfig(kubeconfig: string, kubeContext: string) { + // update kubeconfig in the kubectl instance + /* + this.kc.loadFromString(kubeconfig); + this.kc.setCurrentContext(kubeContext); + */ + this.initKubeConfig(); + console.log(kubeContext, this.kc.getCurrentContext()); + + console.log("Kubeconfig updated"); + } + } \ No newline at end of file diff --git a/server/src/modules/settings.ts b/server/src/modules/settings.ts index 24f8c7fc..0b69f54a 100644 --- a/server/src/modules/settings.ts +++ b/server/src/modules/settings.ts @@ -219,4 +219,36 @@ export class Settings { private checkAdminDisabled() { return this.runningConfig.kubero.admin?.disabled || false } + + public async validateKubeconfig(kubeConfig: string, kubeContext: string): Promise { + if (process.env.KUBERO_SETUP != "enabled") { + return { + error: "Setup is disabled. Set env KUBERO_SETUP=enabled and retry", + status: "error" + } + } + return this.kubectl.validateKubeconfig(kubeConfig, kubeContext) + } + + public updateRunningConfig(kubeConfig: string, kubeContext: string, kuberoNamespace: string, KuberoSessionKey: string, kuberoWebhookSecret: string): {error: string, status: string} { + + if (process.env.KUBERO_SETUP != "enabled") { + return { + error: "Setup is disabled. Set env KUBERO_SETUP=enabled and retry", + status: "error" + } + } + + process.env.KUBERO_CONTEXT = kubeContext + process.env.KUBERO_NAMESPACE = kuberoNamespace + process.env.KUBERO_SESSION_KEY = KuberoSessionKey + process.env.KUBECONFIG_BASE64 = kubeConfig + process.env.KUBERO_SETUP = "disabled" + + this.kubectl.updateKubectlConfig(kubeConfig, kubeContext) + return { + error: "", + status: "ok" + } + } } \ No newline at end of file diff --git a/server/src/routes/config.ts b/server/src/routes/config.ts index 15a7a4cb..418c2dde 100644 --- a/server/src/routes/config.ts +++ b/server/src/routes/config.ts @@ -10,6 +10,7 @@ export const bearerMiddleware = auth.getBearerMiddleware(); import debug from 'debug'; +import { spawn } from 'child_process'; debug('app:routes') Router.get('/config', authMiddleware, async function (req: Request, res: Response) { @@ -66,6 +67,46 @@ Router.get('/config/registry', authMiddleware, async function (req: Request, res res.send(await req.app.locals.settings.getDefaultRegistry()); }); +Router.post('/config/k8s/kubeconfig/validate', authMiddleware, async function (req: Request, res: Response) { + // #swagger.tags = ['UI'] + // #swagger.summary = 'Validate the kubeconfig' + // #swagger.description = 'Validate the kubeconfig for setup process' + // #swagger.parameters['kubeconfig'] = { description: 'Kubeconfig' } + const kubeconfig = req.body.kubeconfig; + const kubeContext = req.body.context; + const result = await req.app.locals.settings.validateKubeconfig(kubeconfig, kubeContext); + res.send(result); +}); + +Router.post('/config/setup/save', authMiddleware, async function (req: Request, res: Response) { + // #swagger.tags = ['UI'] + // #swagger.summary = 'Save the initial Kubero configuration' + // #swagger.description = 'Save the initial Kubero configuration' + // #swagger.parameters['KUBECONFIG_BASE64'] = { description: 'Base 64 encoded Kubeconfig' } + // #swagger.parameters['KUBERO_CONTEXT'] = { description: 'Kubernetes context' } + // #swagger.parameters['KUBERO_NAMESPACE'] = { description: 'Kubero namespace' } + // #swagger.parameters['KUBERO_SESSION_KEY'] = { description: 'Kubero UI session key' } + const kubeconfigBase64 = req.body.KUBECONFIG_BASE64; + const kubeContext = req.body.KUBERO_CONTEXT; + const kuberoNamespace = req.body.KUBERO_NAMESPACE; + const KuberoSessionKey = req.body.KUBERO_SESSION_KEY; + const kuberoWebhookSecret = req.body.KUBERO_WEBHOOK_SECRET; + + // Base64 decode the kubeconfig + const kubeconfigDecoded = Buffer.from(kubeconfigBase64, 'base64').toString('utf-8'); + const resultValidation = await req.app.locals.settings.validateKubeconfig(kubeconfigDecoded, kubeContext); + if (resultValidation.valid === false) { + res.send(resultValidation); + return; + } + + const resultUpdateConfig = await req.app.locals.settings.updateRunningConfig(kubeconfigBase64, kubeContext, kuberoNamespace, KuberoSessionKey, kuberoWebhookSecret); + + req.app.locals.kubero.updateState(); + + res.send(resultUpdateConfig); +}); + Router.get('/cli/config/k8s/context', bearerMiddleware, async function (req: Request, res: Response) { // #swagger.tags = ['Config'] From 20f667cad21ac081c3e3423930ce3704419d166e Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Thu, 28 Nov 2024 21:31:06 +0100 Subject: [PATCH 3/5] improve setup UI --- client/src/components/setup/index.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/src/components/setup/index.vue b/client/src/components/setup/index.vue index 20c050dd..e3a296c0 100644 --- a/client/src/components/setup/index.vue +++ b/client/src/components/setup/index.vue @@ -251,12 +251,14 @@ users: dotenvFile += `${key}=${this.dotenv[key]}\n` } } - return YAML.stringify(this.dotenv) + return dotenvFile }, actionValidation(): boolean | 'next' | 'prev' { if (this.kubeconfigValid && this.step === '1') { return 'prev' - } else if (this.saveSuccess == 'ok' && this.step === '2') { + } else if (this.saveSuccess != 'ok' && this.step === '2') { + return 'next' + }else if (this.saveSuccess == 'ok' && this.step === '2') { return false } else if (this.step === '3') { return 'next' @@ -343,7 +345,8 @@ users: }, async validateKubeconfig(): Promise { if (this.kubeConfig === '') { - this.kubeconfigError = 'kubeconfig is empty' + //this.kubeconfigError = 'kubeconfig is empty' + this.kubeconfigError = '' return false } if (this.kubeContext === '') { From 5ba3f2329669f7b600d5b8cc73d0f4cfa53ea9e7 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Sun, 8 Dec 2024 15:40:55 +0100 Subject: [PATCH 4/5] improve setup process --- client/package.json | 1 + client/src/components/setup/index.vue | 175 ++++++++++++++++++++++++-- client/src/plugins/code-block.ts | 5 + client/src/plugins/index.ts | 3 + client/yarn.lock | 130 +++++++++++++++++++ server/src/modules/kubectl.ts | 41 ++++++ server/src/modules/settings.ts | 38 ++++++ server/src/routes/config.ts | 9 ++ 8 files changed, 389 insertions(+), 13 deletions(-) create mode 100644 client/src/plugins/code-block.ts diff --git a/client/package.json b/client/package.json index 8e26e197..5ef8a6b4 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@mdi/font": "7.0.96", + "@wdns/vue-code-block": "^2.3.3", "apexcharts": "^3.49.0", "axios": "^1.7.4", "chart.js": "^4.4.1", diff --git a/client/src/components/setup/index.vue b/client/src/components/setup/index.vue index e3a296c0..970764e7 100644 --- a/client/src/components/setup/index.vue +++ b/client/src/components/setup/index.vue @@ -3,13 +3,11 @@ align="center" justify="space-around" > - - - + - - @@ -237,10 +363,18 @@ users: KUBERO_SESSION_KEY: '', KUBERO_WEBHOOK_SECRET: '' } as Record, - kubeconfigValid: false + kubeconfigValid: false, + installedComponents: { + operator: false, + ingress: false, + metrics: false + }, } }, computed: { + componentsInstalled() { + return this.installedComponents.operator && this.installedComponents.ingress + }, toDotenv() { let dotenvFile = '' for (const key in this.dotenv) { @@ -260,7 +394,11 @@ users: return 'next' }else if (this.saveSuccess == 'ok' && this.step === '2') { return false - } else if (this.step === '3') { + } else if (!this.componentsInstalled && this.step === '3') { + return 'next' + } else if (this.componentsInstalled && this.step === '3') { + return false + } else if (this.step === '4') { return 'next' } else { return true @@ -269,6 +407,14 @@ users: } }, methods: { + checkInstalled(component: 'operator' | 'ingress' | 'metrics') { + axios.get(`/api/config/setup/check/${component}`) + .then((response) => { + if (response.status === 200 && response.data.status === 'ok') { + this.installedComponents[component] = true + } + }) + }, reload() { // perform a full page reload window.location.href = '/' @@ -279,6 +425,9 @@ users: if (response.status === 200 && response.data.status === 'ok') { this.saveSuccess = 'ok' this.saveErrorMessage = '' + this.checkInstalled('operator') + this.checkInstalled('ingress') + this.checkInstalled('metrics') } else { this.saveSuccess = 'error' this.saveErrorMessage = response.data.error diff --git a/client/src/plugins/code-block.ts b/client/src/plugins/code-block.ts new file mode 100644 index 00000000..b278d3a9 --- /dev/null +++ b/client/src/plugins/code-block.ts @@ -0,0 +1,5 @@ +import { createVCodeBlock } from '@wdns/vue-code-block'; + +export default createVCodeBlock({ + // options +}); \ No newline at end of file diff --git a/client/src/plugins/index.ts b/client/src/plugins/index.ts index ecc16c1a..1b6a4365 100644 --- a/client/src/plugins/index.ts +++ b/client/src/plugins/index.ts @@ -8,6 +8,7 @@ import vuetify from './vuetify' import router from '../router' import pinia from './pinia' +import vCodeBlock from './code-block' // Types import type { App } from 'vue' @@ -15,6 +16,8 @@ import type { App } from 'vue' export function registerPlugins (app: App) { app .use(pinia) + // @ts-ignore: Type missmatch + .use(vCodeBlock) .use(vuetify) .use(router) } diff --git a/client/yarn.lock b/client/yarn.lock index db0f23a6..c7831e26 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -476,6 +476,17 @@ path-browserify "^1.0.1" vscode-uri "^3.0.8" +"@vue/compiler-core@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz#b0ae6c4347f60c03e849a05d34e5bf747c9bda05" + integrity sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q== + dependencies: + "@babel/parser" "^7.25.3" + "@vue/shared" "3.5.13" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.0" + "@vue/compiler-core@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.9.tgz#d51fbfe6c18479b27fe6b1723344ba0832e4aacb" @@ -487,6 +498,14 @@ estree-walker "^2.0.2" source-map-js "^1.2.0" +"@vue/compiler-dom@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz#bb1b8758dbc542b3658dda973b98a1c9311a8a58" + integrity sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA== + dependencies: + "@vue/compiler-core" "3.5.13" + "@vue/shared" "3.5.13" + "@vue/compiler-dom@3.5.9", "@vue/compiler-dom@^3.4.0": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.9.tgz#6fa2b7e536ae4c416fc2d60b7e9e33b3410eac7a" @@ -495,6 +514,21 @@ "@vue/compiler-core" "3.5.9" "@vue/shared" "3.5.9" +"@vue/compiler-sfc@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz#461f8bd343b5c06fac4189c4fef8af32dea82b46" + integrity sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ== + dependencies: + "@babel/parser" "^7.25.3" + "@vue/compiler-core" "3.5.13" + "@vue/compiler-dom" "3.5.13" + "@vue/compiler-ssr" "3.5.13" + "@vue/shared" "3.5.13" + estree-walker "^2.0.2" + magic-string "^0.30.11" + postcss "^8.4.48" + source-map-js "^1.2.0" + "@vue/compiler-sfc@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.9.tgz#020b7654f1fde7c606a49ec4e4d2838e8e1a43c5" @@ -510,6 +544,14 @@ postcss "^8.4.47" source-map-js "^1.2.0" +"@vue/compiler-ssr@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz#e771adcca6d3d000f91a4277c972a996d07f43ba" + integrity sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA== + dependencies: + "@vue/compiler-dom" "3.5.13" + "@vue/shared" "3.5.13" + "@vue/compiler-ssr@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.9.tgz#e30f8e866589392421abcbfc0e0241470f3ca9a6" @@ -554,6 +596,13 @@ muggle-string "^0.4.1" path-browserify "^1.0.1" +"@vue/reactivity@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.13.tgz#b41ff2bb865e093899a22219f5b25f97b6fe155f" + integrity sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg== + dependencies: + "@vue/shared" "3.5.13" + "@vue/reactivity@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.9.tgz#8864a55e4c495666f3c679beb8f734489eeb042e" @@ -561,6 +610,14 @@ dependencies: "@vue/shared" "3.5.9" +"@vue/runtime-core@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz#1fafa4bf0b97af0ebdd9dbfe98cd630da363a455" + integrity sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw== + dependencies: + "@vue/reactivity" "3.5.13" + "@vue/shared" "3.5.13" + "@vue/runtime-core@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.9.tgz#e47f890734039f77dac86328cc059cf8188c5729" @@ -569,6 +626,16 @@ "@vue/reactivity" "3.5.9" "@vue/shared" "3.5.9" +"@vue/runtime-dom@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz#610fc795de9246300e8ae8865930d534e1246215" + integrity sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog== + dependencies: + "@vue/reactivity" "3.5.13" + "@vue/runtime-core" "3.5.13" + "@vue/shared" "3.5.13" + csstype "^3.1.3" + "@vue/runtime-dom@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.9.tgz#088746207f74963d09b31ce7b79add0bf96aa337" @@ -579,6 +646,14 @@ "@vue/shared" "3.5.9" csstype "^3.1.3" +"@vue/server-renderer@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz#429ead62ee51de789646c22efe908e489aad46f7" + integrity sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA== + dependencies: + "@vue/compiler-ssr" "3.5.13" + "@vue/shared" "3.5.13" + "@vue/server-renderer@3.5.9": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.9.tgz#3bf0736001623960d120ef01dee5045fad6efadb" @@ -587,6 +662,11 @@ "@vue/compiler-ssr" "3.5.9" "@vue/shared" "3.5.9" +"@vue/shared@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f" + integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ== + "@vue/shared@3.5.9", "@vue/shared@^3.4.0": version "3.5.9" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.9.tgz#713257216ea2cbf4e200cb9ae395c34ae2349385" @@ -600,6 +680,16 @@ find-cache-dir "^3.3.2" upath "^2.0.1" +"@wdns/vue-code-block@^2.3.3": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@wdns/vue-code-block/-/vue-code-block-2.3.3.tgz#8b9ade0530e9b710e44b7b1ba5a4a48d6a71cf06" + integrity sha512-eOsCTatfi/8/zcgk7yzjuu+t4Ms4Te9SwYUE5PA/+JYcgp+JXAnYBgvqwPFVoTVKq3IpQCiGZg2zMblssvUCUQ== + dependencies: + highlight.js "^11.8.0" + prismjs "^1.29.0" + ua-parser-js "^1.0.38" + vue "^3.4.31" + "@yr/monotone-cubic-spline@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz#7272d89f8e4f6fb7a1600c28c378cc18d3b577b9" @@ -1174,6 +1264,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +highlight.js@^11.8.0: + version "11.10.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92" + integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ== + ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -1477,6 +1572,11 @@ picocolors@^1.1.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -1514,11 +1614,25 @@ postcss@^8.4.43, postcss@^8.4.47: picocolors "^1.1.0" source-map-js "^1.2.1" +postcss@^8.4.48: + version "8.4.49" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -1777,6 +1891,11 @@ typescript@^5.0.0: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== +ua-parser-js@^1.0.38: + version "1.0.39" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" + integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -1909,6 +2028,17 @@ vue@^3.0.0, vue@^3.4.0: "@vue/server-renderer" "3.5.9" "@vue/shared" "3.5.9" +vue@^3.4.31: + version "3.5.13" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.13.tgz#9f760a1a982b09c0c04a867903fc339c9f29ec0a" + integrity sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ== + dependencies: + "@vue/compiler-dom" "3.5.13" + "@vue/compiler-sfc" "3.5.13" + "@vue/runtime-dom" "3.5.13" + "@vue/server-renderer" "3.5.13" + "@vue/shared" "3.5.13" + vuetify@^3.4.0: version "3.7.2" resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.7.2.tgz#e37fa4c191ea00144de5943315cbf77ddb80448d" diff --git a/server/src/modules/kubectl.ts b/server/src/modules/kubectl.ts index 81efaa88..311acd33 100644 --- a/server/src/modules/kubectl.ts +++ b/server/src/modules/kubectl.ts @@ -1265,4 +1265,45 @@ export class Kubectl { console.log("Kubeconfig updated"); } + public async checkNamespace(namespace: string): Promise { + try { + const ns = await this.coreV1Api.readNamespace(namespace); + return true; + } catch (error) { + return false; + } + } + + public async checkPod(namespace: string, podName: string): Promise { + try { + const pod = await this.coreV1Api.readNamespacedPod(podName, namespace); + return true; + } catch (error) { + return false; + } + } + + public async checkDeployment(namespace: string, deploymentName: string): Promise { + try { + const deployment = await this.appsV1Api.readNamespacedDeployment(deploymentName, namespace); + return true; + } catch (error) { + return false; + } + } + + public async checkCustomResourceDefinition(plural: string): Promise { + try { + const crd = await this.customObjectsApi.listClusterCustomObject( + 'apiextensions.k8s.io', + 'v1', + plural + ); + return true; + } catch (error) { + console.log(error); + return false; + } + } + } \ No newline at end of file diff --git a/server/src/modules/settings.ts b/server/src/modules/settings.ts index 0b69f54a..32a05e8b 100644 --- a/server/src/modules/settings.ts +++ b/server/src/modules/settings.ts @@ -251,4 +251,42 @@ export class Settings { status: "ok" } } + + public async checkComponent(component: string): Promise { + let ret = { + //reason : "Component not found", + status: "error" + } + + if (component === "operator") { + //let operator = await this.kubectl.checkCustomResourceDefinition("kuberoes.application.kubero.dev") + let operator = await this.kubectl.checkNamespace("kubero-operator-system") + if (operator) { + ret.status = "ok" + } + } + + if (component === "metrics") { + let metrics = await this.kubectl.checkDeployment("kube-system", "metrics-server") + if (metrics) { + ret.status = "ok" + } + } + + if (component === "debug") { + let metrics = await this.kubectl.checkNamespace("default") + if (metrics) { + ret.status = "ok" + } + } + + if (component === "ingress") { + let ingress = await this.kubectl.checkNamespace("ingress-nginx") + if (ingress) { + ret.status = "ok" + } + } + + return ret + } } \ No newline at end of file diff --git a/server/src/routes/config.ts b/server/src/routes/config.ts index 418c2dde..f1b5ae6a 100644 --- a/server/src/routes/config.ts +++ b/server/src/routes/config.ts @@ -107,6 +107,15 @@ Router.post('/config/setup/save', authMiddleware, async function (req: Request, res.send(resultUpdateConfig); }); +Router.get('/config/setup/check/:component', authMiddleware, async function (req: Request, res: Response) { + // #swagger.tags = ['UI'] + // #swagger.summary = 'Check if a specific component is installed' + // #swagger.parameters['component'] = { description: 'Component to check' } + const component = req.params.component; + const result = await req.app.locals.settings.checkComponent(component); + res.send(result); +}); + Router.get('/cli/config/k8s/context', bearerMiddleware, async function (req: Request, res: Response) { // #swagger.tags = ['Config'] From 0f7f4f9024c85e239ff8ddc5b20ee527de9fccf9 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Sun, 8 Dec 2024 17:22:05 +0100 Subject: [PATCH 5/5] improve log output druing startup --- client/src/components/setup/index.vue | 2 +- server/src/kubero.ts | 3 +- server/src/modules/kubectl.ts | 63 +++++++++++++++++++-------- server/src/modules/settings.ts | 2 + 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/client/src/components/setup/index.vue b/client/src/components/setup/index.vue index 970764e7..a92e5fe4 100644 --- a/client/src/components/setup/index.vue +++ b/client/src/components/setup/index.vue @@ -14,7 +14,7 @@ diff --git a/server/src/kubero.ts b/server/src/kubero.ts index e4456b15..3969286b 100644 --- a/server/src/kubero.ts +++ b/server/src/kubero.ts @@ -944,7 +944,8 @@ export class Kubero { } } } catch (error) { - console.log('Error: getSleepEnabled: ', error) + console.log('❌ getSleepEnabled: could not check for Zeropod') + //console.log(error) return false } diff --git a/server/src/modules/kubectl.ts b/server/src/modules/kubectl.ts index 311acd33..38a50173 100644 --- a/server/src/modules/kubectl.ts +++ b/server/src/modules/kubectl.ts @@ -65,20 +65,21 @@ export class Kubectl { //this.kc.loadFromDefault(); // should not be used since we want also load from base64 ENV var if (process.env.KUBECONFIG_BASE64) { - debug.log("Use kubectl config from base64"); let buff = Buffer.from(process.env.KUBECONFIG_BASE64, 'base64'); const kubeconfig = buff.toString('ascii'); this.kc.loadFromString(kubeconfig); + + debug.log("ℹ️ Kubeconfig loaded from base64"); } else if(process.env.KUBECONFIG_PATH) { - debug.log("Use kubectl config from file " + process.env.KUBECONFIG_PATH); this.kc.loadFromFile(process.env.KUBECONFIG_PATH); + debug.log("ℹ️ Kubeconfig loaded from file " + process.env.KUBECONFIG_PATH); } else{ try { this.kc.loadFromCluster(); - debug.log("Kubeconfig loaded from cluster"); + debug.log("ℹ️ Kubeconfig loaded from cluster"); } catch (error) { debug.log("❌ Error loading from cluster"); - debug.log(error); + //debug.log(error); } } @@ -95,19 +96,25 @@ export class Kubectl { this.exec = new Exec(this.kc) this.customObjectsApi = this.kc.makeApiClient(CustomObjectsApi); } catch (error) { - debug.log("❌ Error creating api clients. Check kubeconfig, cluster connectivity and context"); - debug.log(error); + console.log("❌ Error creating api clients. Check kubeconfig, cluster connectivity and context"); + //debug.log(error); this.kubeVersion = void 0; return; } this.getKubeVersion() .then(v => { + if (v && v.gitVersion) { + console.log("ℹ️ Kube version: " + v.gitVersion); + } else { + console.log("❌ Failed to get Kubernetes version"); + process.env.KUBERO_SETUP = 'enabled'; + } this.kubeVersion = v; }) .catch(error => { - debug.log("❌ Error getting kube version"); - debug.log(error); + console.log("❌ Failed to get Kubernetes version"); + //debug.log(error); }); this.getOperatorVersion() @@ -121,7 +128,7 @@ export class Kubectl { // TODO and WARNING: This does not respect the context set by the user! try { let versionInfo = await this.versionApi.getCode() - debug.debug(JSON.stringify(versionInfo.body)); + //debug.debug(JSON.stringify(versionInfo.body)); return versionInfo.body; } catch (error) { debug.log("getKubeVersion: error getting kube version"); @@ -136,7 +143,8 @@ export class Kubectl { if (contextName) { const pods = await this.getPods(namespace, contextName) .catch(error => { - debug.log("Failed to get Operator Version", error); + debug.log("❌ Failed to get Operator Version"); + //debug.log(error); //return 'error'; }); if (pods) { @@ -180,8 +188,8 @@ export class Kubectl { ) return pipelines.body as IKubectlPipelineList; } catch (error) { - debug.log(error); - debug.log("getPipelinesList: error getting pipelines"); + //debug.log(error); + debug.log("❌ getPipelinesList: error getting pipelines"); } const pipelines = {} as IKubectlPipelineList; pipelines.items = []; @@ -200,7 +208,8 @@ export class Kubectl { "kuberopipelines", pipeline ).catch(error => { - debug.log(error); + debug.log("❌ Error creating pipeline: " + pl.name); + //debug.log(error); }); } @@ -218,7 +227,8 @@ export class Kubectl { pl.name, pipeline ).catch(error => { - debug.log(error); + debug.log("❌ Error updating pipeline: " + pl.name); + //debug.log(error); }); } @@ -246,7 +256,8 @@ export class Kubectl { "kuberopipelines", pipelineName ).catch(error => { - debug.log(error); + //debug.log(error); + debug.log("getPipeline: error getting pipeline"); throw error; }); if (pipeline) { @@ -346,7 +357,7 @@ export class Kubectl { ) return appslist.body as IKubectlAppList; } catch (error) { - debug.log(error); + //debug.log(error); debug.log("getAppsList: error getting apps"); } const appslist = {} as IKubectlAppList; @@ -412,7 +423,7 @@ export class Kubectl { //let operators = response.body as KubernetesListObject; operators = response.body as any // TODO : fix type. This is a hacky way to get the type to work } catch (error) { - debug.log(error); + //debug.log(error); debug.log("error getting operators"); } @@ -1077,7 +1088,7 @@ export class Kubectl { //console.log(config.body); return config.body; } catch (error) { - debug.log(error); + //debug.log(error); debug.log("getKuberoConfig: error getting config"); } } @@ -1306,4 +1317,20 @@ export class Kubectl { } } + public async createNamespace(namespace: string): Promise { + const ns = { + apiVersion: 'v1', + kind: 'Namespace', + metadata: { + name: namespace + } + } + try { + return await this.coreV1Api.createNamespace(ns); + } catch (error) { + //console.log(error); + console.log('ERROR creating namespace'); + } + } + } \ No newline at end of file diff --git a/server/src/modules/settings.ts b/server/src/modules/settings.ts index 32a05e8b..49a04c49 100644 --- a/server/src/modules/settings.ts +++ b/server/src/modules/settings.ts @@ -246,6 +246,8 @@ export class Settings { process.env.KUBERO_SETUP = "disabled" this.kubectl.updateKubectlConfig(kubeConfig, kubeContext) + + this.kubectl.createNamespace(kuberoNamespace) return { error: "", status: "ok"