From 7ce2536af87917fb2ce46068af52dc4d68fb803c Mon Sep 17 00:00:00 2001 From: yuanganping Date: Tue, 7 Dec 2021 17:48:26 +0800 Subject: [PATCH 01/29] feat: add sleep mode Signed-off-by: yuanganping --- images/icon.png | Bin 3397 -> 3292 bytes images/icon/namespace_sleep.svg | 1 + package.json | 18 ++++++ package.nls.json | 5 +- src/main/clusters/AccountCluster.ts | 56 ++++++++++++++---- .../commands/SleepingCommand/ForceSleep.ts | 21 +++++++ src/main/commands/SleepingCommand/WakeUp.ts | 19 ++++++ src/main/commands/constants.ts | 4 ++ src/main/commands/index.ts | 6 ++ src/main/domain/IDevSpaceInfo.ts | 1 + src/main/domain/IServiceAccountInfo.ts | 1 + src/main/nodes/DevSpaceNode.ts | 12 +++- 12 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 images/icon/namespace_sleep.svg create mode 100644 src/main/commands/SleepingCommand/ForceSleep.ts create mode 100644 src/main/commands/SleepingCommand/WakeUp.ts diff --git a/images/icon.png b/images/icon.png index 74539272b7db3e2c80e758fe57c9a97ea3ff404f..04a74628f0a836dda918ee3fb7224135fc8efebc 100755 GIT binary patch delta 2523 zcmZwJX*|^V1Hkd`?>AJfHU_5gx~tz(P(-mmW7rJ=-FYZs&iRoet`^_pHHcSL7` zi+Pp+;Jb9zh<1+Qx;*M&w@ejCbl1>*dS*u2Q)rnLet0e-_$9sbyg<4B_WWeajMjs;WNsr(TgS^kxG&JM!bEBBq=z0efr__9in-V!d6x?u*Lud{$0^KDEMvBB4k zqtRnG*NL+BHZO_Pc6-MM*q#*S(g;GD3wzcuzMRF~Wq*AL^KV6b6w5rB=lbwV#N(mo zN;W>wFF3V4(}%`lD%F`ehT-|sst6i*L#jZRpAK;^0)Frub_5|3To|bW5+Da%0sb#R z4BVl|!9i=QG6!=XxVcWfHBeVC-#difwhn4klf$x%-KE!V*cea=sU!F7`s_~T3JuC+ z@kZob*c!kFIBNC3`f+f&e%r0|qEm@Wi(59maQ`NVR%*u&w^GgvanQo=&l#wtqkQ;D zXO(r#$3TYYp=7^H1E3S<8|%M_4ndg`1jk?>=Wafi0+`|7jk}Tv6XA^vgI=EE$!?Dt zR<3^RP=K8OK?xFF6Da z{2wGW@f4m!Xc9aFiRzO8@58V_Wu9jZ5j6zq$n^>a#t|`#{{^VP=^A}NG(}jx3*hG& z(x?EM_j3y3AOAel9Z|vt5;2+Vv_3Ld`d;BKEI{*ZC3CU&ymsMxw1qe~L|rkR$n$r@ zv!=SndKQBjeeG&&OcCFDUumP7f?xt9y_Lm;yZBcuy3EP*lso}>HtTQftU3Fd_yot0 zR=^wRU-pmRO6tgklBRUY2DWDW^M96WdkVACrbr)}yZV=pv{m<8{By0pA&5mx#@HWH!j2Z0uTX8Q1u`))>#L|iUXeCt5PdE-V>*_wu{4C zB=!XLrgpjmAb*mBB5{g92$mVg1@Bc`hZhG_s5;XHElnBkc z!^b%JS(7Vg=BK=Oj8W#1gScY4312=b{C&g#_I~G$#N3;B&wMSWx`cB1tq8KR3DcVE zw{FdzaVi)Z2Ny+(&%oDL!7T5(4wWi$KN+e%QZbTH3xyrEJIB$kl7R4(i}^>mUCs1$39_oN1+Bh#VG8{)v@sPxQ)4~ggL0N zMChi_zWg)vIXz?zvdcR$mb$VL{Q7{cMtugUt435k2v1!;L**ZVZ)HF?Q z`fSe$It3Q1Iyz=oI+BzVH_J-2!qRQvb&a$|<@+V+oxKGGiK#vEVobbEe`-HYAah;qQ<34xT}yGHD>=nCI9cd(j9ecJ0ss_-4P1v}Eqm7Nj+(aYMvnxd@P2hAg;kx{jzajPe+7L|$3S66{mjgm=4 zn@M(scV8^{_y=MUrtmU=^tE9}2HFn%wc2E=;KcJ7-Xl~oYS1%dzeMG(@4z|wDcpTU z0e+?8>^OJy=hh7CuW)z4xaRPdd(#`mFvDE;E`yELOT0894pl++%?XX%IFVz?6p7L? zNw8BtlA=OeuIcWUw{jy-2ny&{V!Smk0eZhLuyZ;4DD77(e{=gVU$3koAMo_8ma^! z;$;t~T0$&YBHwVGX&Y!w?%|>aDn?NnQ4NL6vPMndW|T@@D4-?bQZRUWwmBOo>)8|t z#r-7DRi6j1@#nORkc+d&xKOotu`smw6yP~!)T%Dj#PLArX?{I!Lg@haMI?GCKNfws zqLz}(gtUKYf-NDr6p3Uq)x^x-0g{^V!LeTKcVkAxss6^wF{7V>`s$Wa-FZa7sl? zlw>J{5n=44BuzYJnF;BftjV0pGV?h9!1KI%KF{~n_r>-3{I2VBy|}JPrCw#`@mxF0 z{B48|=9qTHVYM{fJzjX6Vcla3L~aHVAN?ZqEdYQ(l#7!q03u{hBvuT~EN_YODG-h! z`v3Aj$X{{aD^2Xipj!=t<#Oa_jSX@?1AxT3y$#hZ4xD?Pk$jhev2lKX=c?>9-HRb~ zo?e<6*QB&zKCwNFr~5>>ROh0*H@n4}iH*)*XKyRJH#g{VSHyC8eER&_6$&wafDG2n>FA65FKfoq zT8H*lEk-!e{md~DjoiGS7P#8SPm$PI4MSw$5 z(Q_mj8|Q<#$NPiL$z<|}l7S?B$=1-y{vYA=LeDz4hi+kl^wQ84KaZx_Bp2Z2E~~A( zo*(9qBF}9YZCTeey{i{rB`P?Z#hjW4&NgP4`gQ1qG2ms2t=#Q9MWBbYMo|2k82*eM zRcT0(LuO_h<>_*4tUVgbZ9ot7XjSVd68|^THbB`j-q@$7P`?)y;TBl-$Y1N_cwyj} zR5GZAt~YGS&1VmFp*Vt81xd7tUpiQ^9vebFXsniSO=4D&l-$ulNGdQ6Cd} zbuRRpaJP1?=Tx(Yu?#TWIaYw_zQV(EDkBqR6w5Olp$2@ynjo$FqREG0?1gF8uFoMH=sdUCCMEjeNAXg_y z86N*;3f{U!Wu+x5t{p-@+g~s}k}1w_G`E7Y{`Qyo%hxCBdd&Cl-=@}0%tJx6iUq|L z4XE%RPq)Lx!tjE0C7RU8H$I4(1r2)I7r361>F1S)wbD^|9h8IG=04NPF9O$G18XP? zwyDWMd{9(SHX(Buz6N#;wIyah;&RNf3SmZu6xW{y91I`AqsL57QhO)q@=#z;vYS6s zZ=|Yz>j}=_hy#WMse~$pjBRel>XeV}jM&Tf%k#?zZi?`+oF3o-pPn5#Z$2!P!Zq5Z zUT;7Ud697F>R6X4^?M_ywAo#)p;W#Wp zDaCK5-cTmUMn%~{rnvV-asiM=@^yQA*dcSkLUSHvhuYhYUH`NQ?tyyvKnUFZHHFzH z7h&P(fQK1&iMDMmYV6}c%1 z|43|~d1(qSRj*HcrLdG=nMb00{T1r?d(RI9buj9- zWbx=s3^n2NMR*m1;amm1$GiHu)#!iN)JS7a5QNKJC#=**Cmj zy0If`SaLM|fPZkhCYd~ukorgG2a{2|Y|qZ;3B}jeMZKFdjFtRmsxS{pwsBFEwiC5E z%7s$WS>S~*0Vl1`Sg8U$lc!^7ptq5;axiAlN_rp}!zsCJP~*5pgV1phspi%37v`BX z2w9KvQuQze4kj_wTsk!5o$^}9u`+#vlvg+ws5#^8(Hd-~?ldS>yMU?HTj{U&Y^Ud%dzca86keW#<%ypnGB(dz-F+uiu=JeK(w|UH2$o9 zuMo@n;lW?^2R$$NXH#yTX{V_Ab6{^y%9S|muAmrcnA;;(F1ts)e`6?|d?dBbdV3a| z2vrnchURt1K`qgwM5reZ+Lq6ShMB&i1L0%Mz zQoBt5w9(6+!RU*Xq7}B{Q+1Qxr}Jrq-`8f`Nk>hdWUg&axhhq&C0lfH*A=2ii;lgX zzkBM8Vk+a)qWIw`;y=fSG{B3;wo2^-5l9J!41oggJS9kp|0JuD6_bXXlQO7M{*gRY z>nrw+F7$J4qR%5K5;W0u+kl@Ck4$b1J`MjRTv((W4oWZ8i7nduNzv9b_VaU&g@c+? zwPKTh7F+vt9#%>tQk2lA0>N?09b3f_Mm_NQ{aUQnNH@SsF8s9?uD1H{$WU=ayv$kb zocx9cA;Evm^06)Ov#+W8T8a2sK*~-s>k6U|_m4P`RYX7C1L7|pM|@80V`Lr&vWqlm z@KjeN6WIGEkHdARZ7pel+`!wj$T(!LWQ8b2oCBJM42~^G_!l{U`-mTBfLtP{{iw`# zTza#pEN2BgeOx?js-FX_{&)$iniR5>EH)s~1CPIH`mDE<=7ZuOVk}Wlmz%Eg=%Vh9 zvMDy`O!`BYr*i4xGwN!`_uXJsvIYJ(nCD!uTM)YD$TZQ9))Bl)cyB@%b-hFgK_}bG WDZ-O$U;f#Hy{(fC@}ZS)%D(~e2nk&P diff --git a/images/icon/namespace_sleep.svg b/images/icon/namespace_sleep.svg new file mode 100644 index 00000000..8e58330c --- /dev/null +++ b/images/icon/namespace_sleep.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/package.json b/package.json index 52ea73a5..500ffa3c 100644 --- a/package.json +++ b/package.json @@ -222,6 +222,16 @@ "when": "viewItem =~ /^workload-(deployment|statefulSet|job|daemonSet|cronjob|pod)/i", "group": "2" }, + { + "command": "Nocalhost.wakeUp", + "when": "viewItem =~ /^devspace-server-sleeping/i", + "group": "navigation" + }, + { + "command": "Nocalhost.forceSleep", + "when": "viewItem =~ /^devspace-server-unsleeping/i", + "group": "navigation" + }, { "command": "Nocalhost.resetDevspace", "when": "viewItem =~ /^devspace-server/i", @@ -447,6 +457,14 @@ "command": "Nocalhost.resetDevspace", "title": "%Reset%" }, + { + "command": "Nocalhost.wakeUp", + "title": "%wakeUp%" + }, + { + "command": "Nocalhost.forceSleep", + "title": "%forceSleep%" + }, { "command": "Nocalhost.exec", "title": "%Terminal%" diff --git a/package.nls.json b/package.nls.json index 7075619d..cf473385 100644 --- a/package.nls.json +++ b/package.nls.json @@ -41,9 +41,10 @@ "viewDevConfig": "View Dev Configs", "resetPod": "Reset Pod", "startCopyDevMode": "Start DevMode(Duplicate)", - "switchAssociate": "Switch This Service as Current Service", "disassociateAssociate": "Disassociate from Current Directory", "overrideRemoteChang": "Override Remote Changing According to Local Files", - "resumeSync": "Resume File Sync" + "resumeSync": "Resume File Sync", + "forceSleep": "Sleep", + "wakeUp": "Wakeup" } diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 7af5e181..4c858866 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -107,17 +107,17 @@ export default class AccountClusterService { } static getServerClusterRootNodes = async ( - newAccountCluser: AccountClusterNode + newAccountCluster: AccountClusterNode ): Promise => { - if (!newAccountCluser) { + if (!newAccountCluster) { return []; } const accountClusterService = new AccountClusterService( - newAccountCluser.loginInfo + newAccountCluster.loginInfo ); - accountClusterService.accountClusterNode = newAccountCluser; - accountClusterService.jwt = newAccountCluser.jwt; - accountClusterService.refreshToken = newAccountCluser.refreshToken; + accountClusterService.accountClusterNode = newAccountCluster; + accountClusterService.jwt = newAccountCluster.jwt; + accountClusterService.refreshToken = newAccountCluster.refreshToken; const newRootNodes: IRootNode[] = []; @@ -127,9 +127,19 @@ export default class AccountClusterService { (serviceAccounts || []).length }` ); + // get sleep info + const sleepInfo = new Map(); + serviceAccounts.forEach((account) => { + account.namespacePacks.forEach((space) => { + sleepInfo.set( + space.namespace, + space.isAsleep ? "Sleeping" : "Unsleeping" + ); + }); + }); if (!Array.isArray(serviceAccounts) || serviceAccounts.length === 0) { - const msg = `no cluster found for ${newAccountCluser.loginInfo.baseUrl} ${newAccountCluser.loginInfo.username}`; + const msg = `no cluster found for ${newAccountCluster.loginInfo.baseUrl} ${newAccountCluster.loginInfo.username}`; logger.error(msg); throw new Error(msg); @@ -202,13 +212,18 @@ export default class AccountClusterService { } const obj: IRootNode = { - devSpaces, + devSpaces: devSpaces.map((item) => { + return { + ...item, + isAsleep: sleepInfo.get(item.namespace), + }; + }), applications, - userInfo: newAccountCluser.userInfo, + userInfo: newAccountCluster.userInfo, clusterSource: ClusterSource.server, accountClusterService, - id: newAccountCluser.id, - createTime: newAccountCluser.createTime, + id: newAccountCluster.id, + createTime: newAccountCluster.createTime, kubeConfigPath, state: { code: 200 }, }; @@ -217,7 +232,7 @@ export default class AccountClusterService { } await AccountClusterService.cleanDiffKubeConfig( - newAccountCluser, + newAccountCluster, kubeConfigArr ); @@ -299,7 +314,6 @@ export default class AccountClusterService { return newAccountCluser; }; - buildAccountClusterNode = async (): Promise => { await this.login(this.loginInfo); const userInfo = await this.getUserInfo(); @@ -514,4 +528,20 @@ export default class AccountClusterService { } throw new Error("Fail to fetch user infomation."); } + + async wakeUpSpace(id: number) { + const response = await this.instance.post(`/v2/dev_space/${id}/wakeup`); + const flag = response.data.code === 0; + console.log(flag); + if (response.status === 200 && response.data.code === 0) { + host.showInformationMessage("wakeUp success"); + } + } + + async sleepSpace(id: number) { + const response = await this.instance.post(`/v2/dev_space/${id}/sleep`); + if (response.status === 200 && response.data.code === 0) { + host.showInformationMessage("sleep success"); + } + } } diff --git a/src/main/commands/SleepingCommand/ForceSleep.ts b/src/main/commands/SleepingCommand/ForceSleep.ts new file mode 100644 index 00000000..6784d4c4 --- /dev/null +++ b/src/main/commands/SleepingCommand/ForceSleep.ts @@ -0,0 +1,21 @@ +import * as vscode from "vscode"; +import { FORCE_SLEEP } from "../constants"; +import ICommand from "../ICommand"; +import registerCommand from "../register"; +import host from "../../host"; + +import { DevSpaceNode } from "../../nodes/DevSpaceNode"; + +export default class AddKubeconfig implements ICommand { + command: string = FORCE_SLEEP; + + constructor(context: vscode.ExtensionContext) { + registerCommand(context, this.command, false, this.execCommand.bind(this)); + } + + async execCommand(node: DevSpaceNode) { + // force sleep + const spaceId = node.info.spaceId; + node.parent.accountClusterService.sleepSpace(spaceId); + } +} diff --git a/src/main/commands/SleepingCommand/WakeUp.ts b/src/main/commands/SleepingCommand/WakeUp.ts new file mode 100644 index 00000000..2f4eddfd --- /dev/null +++ b/src/main/commands/SleepingCommand/WakeUp.ts @@ -0,0 +1,19 @@ +import * as vscode from "vscode"; +import { WAKE_UP } from "../constants"; +import ICommand from "../ICommand"; +import registerCommand from "../register"; + +import { DevSpaceNode } from "../../nodes/DevSpaceNode"; + +export default class AddKubeconfig implements ICommand { + command: string = WAKE_UP; + + constructor(context: vscode.ExtensionContext) { + registerCommand(context, this.command, false, this.execCommand.bind(this)); + } + + async execCommand(node: DevSpaceNode) { + const spaceId = node.info.spaceId; + node.parent.accountClusterService.wakeUpSpace(spaceId); + } +} diff --git a/src/main/commands/constants.ts b/src/main/commands/constants.ts index c155ba4c..4b62a2e7 100644 --- a/src/main/commands/constants.ts +++ b/src/main/commands/constants.ts @@ -66,3 +66,7 @@ export const ADD_KUBECONFIG = "Nocalhost.addKubeconfig"; export const CLUSTERS_VIEW = "Nocalhost.ClustersView"; export const CONFIG_URI_QUERY = "_configURIQuery"; + +// sleeping +export const FORCE_SLEEP = "Nocalhost.forceSleep"; +export const WAKE_UP = "Nocalhost.wakeUp"; diff --git a/src/main/commands/index.ts b/src/main/commands/index.ts index 67e25a0d..d7ef5236 100644 --- a/src/main/commands/index.ts +++ b/src/main/commands/index.ts @@ -45,6 +45,9 @@ import ResetPluginCommand from "./ResetPluginCommand"; import StartCopyDevModeCommand from "./StartCopyDevModeCommand"; import PortForwardCommand from "./PortForwardCommand/"; +import WakeUpCommand from "./SleepingCommand/WakeUp"; +import ForceSleepCommand from "./SleepingCommand/ForceSleep"; + export default function initCommands( context: vscode.ExtensionContext, appTreeProvider: NocalhostAppProvider @@ -102,4 +105,7 @@ export default function initCommands( new ResetPluginCommand(context); new StartCopyDevModeCommand(context); + + new ForceSleepCommand(context); + new WakeUpCommand(context); } diff --git a/src/main/domain/IDevSpaceInfo.ts b/src/main/domain/IDevSpaceInfo.ts index 65c82db1..c137479e 100644 --- a/src/main/domain/IDevSpaceInfo.ts +++ b/src/main/domain/IDevSpaceInfo.ts @@ -10,5 +10,6 @@ export interface IDevSpaceInfo { storageClass: string; devStartAppendCommand: Array; spaceOwnType?: "Viewer" | "Owner" | string; + isAsleep?: "Sleeping" | "Unsleeping"; [key: string]: any; } diff --git a/src/main/domain/IServiceAccountInfo.ts b/src/main/domain/IServiceAccountInfo.ts index 4741d0c3..b7c33f97 100644 --- a/src/main/domain/IServiceAccountInfo.ts +++ b/src/main/domain/IServiceAccountInfo.ts @@ -9,5 +9,6 @@ export interface IServiceAccountInfo { namespace: string; spacename: string; spaceOwnType?: "Viewer" | "Owner" | string; + isAsleep?: boolean; }>; } diff --git a/src/main/nodes/DevSpaceNode.ts b/src/main/nodes/DevSpaceNode.ts index e7ead2fd..a18c8840 100644 --- a/src/main/nodes/DevSpaceNode.ts +++ b/src/main/nodes/DevSpaceNode.ts @@ -259,7 +259,9 @@ export class DevSpaceNode extends NocalhostFolderNode implements RefreshData { treeItem.iconPath = resolveVSCodeUri("loading.gif"); } else { const iconName = - this.info.spaceOwnType === "Viewer" + this.info.isAsleep === "Sleeping" + ? "namespace_sleep.svg" + : this.info.spaceOwnType === "Viewer" ? "devspace_viewer.svg" : "devspace.svg"; treeItem.iconPath = resolveVSCodeUri(iconName); @@ -267,7 +269,13 @@ export class DevSpaceNode extends NocalhostFolderNode implements RefreshData { treeItem.contextValue = this.getSpaceOwnTypeContextValue( `devspace-${ - this.clusterSource === ClusterSource.local ? "local" : "server" + this.clusterSource === ClusterSource.local + ? "local" + : this.info.isAsleep === "Sleeping" + ? "server-sleeping" + : this.info.isAsleep === "Unsleeping" + ? "server-unsleeping" + : "server" }` ); From 9ff764126c018603c879d8330cf17109d10b79ca Mon Sep 17 00:00:00 2001 From: yuanganping Date: Fri, 10 Dec 2021 10:43:08 +0800 Subject: [PATCH 02/29] feat: add dialog Signed-off-by: yuanganping --- src/main/commands/SleepingCommand/ForceSleep.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/commands/SleepingCommand/ForceSleep.ts b/src/main/commands/SleepingCommand/ForceSleep.ts index 6784d4c4..d1783202 100644 --- a/src/main/commands/SleepingCommand/ForceSleep.ts +++ b/src/main/commands/SleepingCommand/ForceSleep.ts @@ -15,7 +15,15 @@ export default class AddKubeconfig implements ICommand { async execCommand(node: DevSpaceNode) { // force sleep - const spaceId = node.info.spaceId; - node.parent.accountClusterService.sleepSpace(spaceId); + const res = await host.showInformationMessage( + "Confirm to sleep!", + { modal: true }, + "yes" + ); + + if (res === "yes") { + const spaceId = node.info.spaceId; + node.parent.accountClusterService.sleepSpace(spaceId); + } } } From 373463c8a7d6dc1050903702d14dd5d5f66e9419 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Mon, 13 Dec 2021 14:37:47 +0800 Subject: [PATCH 03/29] fix: change sleepStatus field Signed-off-by: yuanganping --- src/main/clusters/AccountCluster.ts | 2 +- src/main/domain/IServiceAccountInfo.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 4c858866..c5651b3b 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -133,7 +133,7 @@ export default class AccountClusterService { account.namespacePacks.forEach((space) => { sleepInfo.set( space.namespace, - space.isAsleep ? "Sleeping" : "Unsleeping" + space.sleepStatus === "asleep" ? "Sleeping" : "Unsleeping" ); }); }); diff --git a/src/main/domain/IServiceAccountInfo.ts b/src/main/domain/IServiceAccountInfo.ts index b7c33f97..0e1e3951 100644 --- a/src/main/domain/IServiceAccountInfo.ts +++ b/src/main/domain/IServiceAccountInfo.ts @@ -10,5 +10,6 @@ export interface IServiceAccountInfo { spacename: string; spaceOwnType?: "Viewer" | "Owner" | string; isAsleep?: boolean; + sleepStatus?: string; }>; } From 1386956ffe56076e82ce0dbcbda29fb6e3472ffa Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 23 Dec 2021 14:36:15 +0800 Subject: [PATCH 04/29] WIP: AccountCluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 111 +++++++++++++++++++------ src/main/ctl/nhctl/cli.ts | 56 ++++++++++++- src/main/domain/IServiceAccountInfo.ts | 9 ++ src/main/extension.ts | 2 +- src/main/state.ts | 7 +- 5 files changed, 155 insertions(+), 30 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 05d69f89..c4184241 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -14,7 +14,12 @@ import { } from "../domain"; import logger from "../utils/logger"; import { keysToCamel } from "../utils"; -import { checkCluster, getAllNamespace, kubeconfig } from "../ctl/nhctl"; +import { + checkCluster, + getAllNamespace, + kubeconfig, + kubeConfigRender, +} from "../ctl/nhctl"; import host from "../host"; import { getStringHash } from "../utils/common"; import { LoginInfo } from "./interface"; @@ -23,6 +28,7 @@ import { KUBE_CONFIG_DIR, SERVER_CLUSTER_LIST } from "../constants"; import { ClusterSource } from "../common/define"; import * as packageJson from "../../../package.json"; import { ClustersState } from "."; +import { ChildProcessWithoutNullStreams } from "child_process"; export class AccountClusterNode { userInfo: IUserInfo; @@ -40,6 +46,7 @@ export default class AccountClusterService { refreshToken: string; lastServiceAccounts: IServiceAccountInfo[]; isRefreshing: boolean; + constructor(public loginInfo: LoginInfo) { var parsed = url.parse(loginInfo.baseUrl); @@ -71,13 +78,13 @@ export default class AccountClusterService { // refresh token if (config.url === "/v1/token/refresh") { host.log( - `Please login again ${this.loginInfo.baseUrl || ""}:${ + `Please login again ${this.loginInfo.baseUrl || ""}:${ this.loginInfo.username || "" }`, true ); host.showWarnMessage( - `Please login again ${this.loginInfo.baseUrl || ""}:${ + `Please login again ${this.loginInfo.baseUrl || ""}:${ this.loginInfo.username || "" }` ); @@ -107,19 +114,17 @@ export default class AccountClusterService { } static getServerClusterRootNodes = async ( - newAccountCluser: AccountClusterNode + newAccountCluster: AccountClusterNode ): Promise => { - if (!newAccountCluser) { + if (!newAccountCluster) { return []; } const accountClusterService = new AccountClusterService( - newAccountCluser.loginInfo + newAccountCluster.loginInfo ); - accountClusterService.accountClusterNode = newAccountCluser; - accountClusterService.jwt = newAccountCluser.jwt; - accountClusterService.refreshToken = newAccountCluser.refreshToken; - - const newRootNodes: IRootNode[] = []; + accountClusterService.accountClusterNode = newAccountCluster; + accountClusterService.jwt = newAccountCluster.jwt; + accountClusterService.refreshToken = newAccountCluster.refreshToken; let serviceAccounts = await accountClusterService.getServiceAccount(); logger.info( @@ -129,7 +134,7 @@ export default class AccountClusterService { ); if (!Array.isArray(serviceAccounts) || serviceAccounts.length === 0) { - const msg = `no cluster found for ${newAccountCluser.loginInfo.baseUrl} ${newAccountCluser.loginInfo.username}`; + const msg = `no cluster found for ${newAccountCluster.loginInfo.baseUrl} ${newAccountCluster.loginInfo.username}`; logger.error(msg); throw new Error(msg); @@ -143,10 +148,17 @@ export default class AccountClusterService { ); const kubeConfigArr: Array = []; + const newRootNodes: IRootNode[] = []; for (const sa of serviceAccounts) { let devSpaces: Array | undefined = new Array(); + const kubeconfig = await AccountClusterService.vClusterProcess(sa); + + if (kubeconfig) { + sa.kubeconfig = kubeconfig; + } + const { id, kubeConfigPath } = await AccountClusterService.saveKubeConfig( sa ); @@ -154,12 +166,12 @@ export default class AccountClusterService { kubeConfigArr.push(id); if (sa.privilege) { - const devs = await getAllNamespace({ + const devArray = await getAllNamespace({ kubeConfigPath: kubeConfigPath, namespace: "default", }); - for (const dev of devs) { + for (const dev of devArray) { dev.storageClass = sa.storageClass; dev.devStartAppendCommand = [ "--priority-class", @@ -180,7 +192,7 @@ export default class AccountClusterService { dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; } } - devSpaces.push(...devs); + devSpaces.push(...devArray); } else { for (const ns of sa.namespacePacks) { const devInfo: IDevSpaceInfo = { @@ -201,29 +213,78 @@ export default class AccountClusterService { } } + const state = await checkCluster(kubeConfigPath); const obj: IRootNode = { devSpaces, applications, - userInfo: newAccountCluser.userInfo, + userInfo: newAccountCluster.userInfo, clusterSource: ClusterSource.server, accountClusterService, - id: newAccountCluser.id, - createTime: newAccountCluser.createTime, + id: newAccountCluster.id, + createTime: newAccountCluster.createTime, kubeConfigPath, - state: await checkCluster(kubeConfigPath), + state, }; newRootNodes.push(obj); } await AccountClusterService.cleanDiffKubeConfig( - newAccountCluser, + newAccountCluster, kubeConfigArr ); return newRootNodes; }; + static virtualClusterProc: { + [key: string]: { proc: ChildProcessWithoutNullStreams; kubeconfig: string }; + } = {}; + + static async vClusterProcess(sa: IServiceAccountInfo) { + const { virtualCluster, kubeconfigType } = sa; + if ( + kubeconfigType === "vcluster" && + virtualCluster.serviceType === "ClusterIP" + ) { + const { + serviceNamespace, + servicePort, + hostClusterContext, + serviceAddress, + } = virtualCluster; + + let oldProc = AccountClusterService.virtualClusterProc[serviceAddress]; + if (oldProc?.proc.killed === false) { + return oldProc.kubeconfig; + } + + const { proc, kubeconfig } = await kubeConfigRender({ + namespace: serviceNamespace, + kubeconfig: sa.kubeconfig, + remotePort: servicePort, + context: hostClusterContext, + serviceAddress: serviceAddress, + }); + + oldProc = AccountClusterService.virtualClusterProc[serviceAddress]; + if (oldProc?.proc.killed === false) { + proc.kill(); + return oldProc.kubeconfig; + } + + host.getContext().subscriptions.push({ + dispose: proc.kill, + }); + + AccountClusterService.virtualClusterProc[serviceAddress] = { + proc, + kubeconfig, + }; + + return kubeconfig; + } + } static async cleanDiffKubeConfig( accountCluser: AccountClusterNode, configs: Array @@ -273,7 +334,7 @@ export default class AccountClusterService { loginInfo: LoginInfo ): Promise => { const accountServer = new AccountClusterService(loginInfo); - const newAccountCluser = await accountServer.buildAccountClusterNode(); + const newAccountCluster = await accountServer.buildAccountClusterNode(); let globalAccountClusterList = host.getGlobalState(SERVER_CLUSTER_LIST); if (!Array.isArray(globalAccountClusterList)) { @@ -285,19 +346,19 @@ export default class AccountClusterService { ); const oldAccountIndex = globalAccountClusterList.findIndex( - (it: AccountClusterNode) => it.id === newAccountCluser.id + (it: AccountClusterNode) => it.id === newAccountCluster.id ); if (oldAccountIndex !== -1) { - globalAccountClusterList.splice(oldAccountIndex, 1, newAccountCluser); + globalAccountClusterList.splice(oldAccountIndex, 1, newAccountCluster); } else { - globalAccountClusterList.push(newAccountCluser); + globalAccountClusterList.push(newAccountCluster); } globalAccountClusterList = uniqBy(globalAccountClusterList, "id"); host.setGlobalState(SERVER_CLUSTER_LIST, globalAccountClusterList); - return newAccountCluser; + return newAccountCluster; }; buildAccountClusterNode = async (): Promise => { diff --git a/src/main/ctl/nhctl/cli.ts b/src/main/ctl/nhctl/cli.ts index f162cdef..12970607 100644 --- a/src/main/ctl/nhctl/cli.ts +++ b/src/main/ctl/nhctl/cli.ts @@ -9,7 +9,8 @@ import * as semver from "semver"; import * as os from "os"; import * as path from "path"; import * as fs from "fs"; -import { spawn } from "child_process"; + +import { ChildProcessWithoutNullStreams, spawn } from "child_process"; import { exec, ExecParam, execWithProgress, getExecCommand } from "../shell"; import host, { Host } from "../../host"; @@ -1604,3 +1605,56 @@ export async function associateQuery(param: { .toJson() .exec(); } +export async function kubeConfigRender(param: { + serviceAddress: string; + namespace: string; + kubeconfig: string; + context: string; + remotePort: string; +}) { + const { + namespace, + serviceAddress, + remotePort, + context, + kubeconfig: kubeconfigStr, + } = param; + + const commands = [ + NhctlCommand.nhctlPath, + "kubeconfig", + "render", + "--namespace", + namespace, + "--kubeconfig", + "-", + "--context", + context, + serviceAddress, + `:${remotePort}`, + ]; + const END_Symbol = "EOF\n"; + return new Promise<{ + kubeconfig: string; + proc: ChildProcessWithoutNullStreams; + }>((res, rej) => { + const { proc, promise } = exec({ + command: commands.join(" "), + output: { out: false, err: true }, + }); + proc.stdout.on("data", (chuck: Buffer) => { + const str = chuck.toString(); + + if (str.endsWith(END_Symbol)) { + const kubeconfig = str.substring(0, str.lastIndexOf(END_Symbol)); + res({ kubeconfig, proc }); + return; + } + }); + + proc.stdin.write(kubeconfigStr); + proc.stdin.end(); + + promise.catch(rej); + }); +} diff --git a/src/main/domain/IServiceAccountInfo.ts b/src/main/domain/IServiceAccountInfo.ts index 4741d0c3..87f86884 100644 --- a/src/main/domain/IServiceAccountInfo.ts +++ b/src/main/domain/IServiceAccountInfo.ts @@ -4,6 +4,15 @@ export interface IServiceAccountInfo { storageClass: string; privilege: boolean; privilegeType?: "CLUSTER_ADMIN" | "CLUSTER_VIEWER"; + kubeconfigType?: "vcluster"; + virtualCluster?: { + serviceType: "ClusterIP" | "LoadBalancer" | "NodePort"; + servicePort: string; + serviceAddress: string; + serviceNamespace: string; + hostClusterContext: string; + virtualClusterContext: string; + }; namespacePacks: Array<{ spaceId: number; namespace: string; diff --git a/src/main/extension.ts b/src/main/extension.ts index 5d85df57..8c52942b 100644 --- a/src/main/extension.ts +++ b/src/main/extension.ts @@ -144,7 +144,7 @@ export async function activate(context: vscode.ExtensionContext) { true ); - await state.refreshTree(); + state.refreshTree(false, 15_000); launchDevSpace(); diff --git a/src/main/state.ts b/src/main/state.ts index b9bbeba0..1ada9e7c 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -159,7 +159,7 @@ class State { return this.running; } - async refreshTree(force = false) { + async refreshTree(force = false, delay = 0) { const isExist = isExistCluster(); await vscode.commands.executeCommand( @@ -173,9 +173,10 @@ class State { "Nocalhost.visibleTree", isExist ); - await vscode.commands.executeCommand("Nocalhost.refresh"); - this.startAutoRefresh(force); + setTimeout(() => { + vscode.commands.executeCommand("Nocalhost.refresh"); + }, delay); } setRunning(running: boolean) { From 82bb011a2d609c8a780477e7d6221612ab6422fa Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 23 Dec 2021 15:06:15 +0800 Subject: [PATCH 05/29] WIP: VCluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 6 ------ src/main/extension.ts | 2 +- src/main/nodes/NocalhostRootNode.ts | 3 ++- src/main/state.ts | 10 ++++++---- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index c4184241..d9bb23a3 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -267,12 +267,6 @@ export default class AccountClusterService { serviceAddress: serviceAddress, }); - oldProc = AccountClusterService.virtualClusterProc[serviceAddress]; - if (oldProc?.proc.killed === false) { - proc.kill(); - return oldProc.kubeconfig; - } - host.getContext().subscriptions.push({ dispose: proc.kill, }); diff --git a/src/main/extension.ts b/src/main/extension.ts index 8c52942b..21496283 100644 --- a/src/main/extension.ts +++ b/src/main/extension.ts @@ -144,7 +144,7 @@ export async function activate(context: vscode.ExtensionContext) { true ); - state.refreshTree(false, 15_000); + state.refreshTree(false); launchDevSpace(); diff --git a/src/main/nodes/NocalhostRootNode.ts b/src/main/nodes/NocalhostRootNode.ts index 3d2725e4..c4f6083c 100644 --- a/src/main/nodes/NocalhostRootNode.ts +++ b/src/main/nodes/NocalhostRootNode.ts @@ -319,7 +319,7 @@ export class NocalhostRootNode implements BaseNocalhostNode { public label: string = NOCALHOST; public type = ROOT; - constructor(public parent: BaseNocalhostNode | null) { + constructor(public parent: BaseNocalhostNode | null, public hasInit = false) { console.log(AppNode); state.setNode(this.getNodeStateId(), this); } @@ -393,6 +393,7 @@ export class NocalhostRootNode implements BaseNocalhostNode { return orderBy(nodes, ["label"]); }); + this.hasInit = true; return children; } diff --git a/src/main/state.ts b/src/main/state.ts index 1ada9e7c..9abf9710 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -92,6 +92,9 @@ class State { try { const rootNode = this.getNode("Nocalhost") as BaseNocalhostNode; if (rootNode) { + if (rootNode.hasInit === false) { + return; + } await rootNode.updateData(null, token).catch(() => {}); } @@ -159,7 +162,7 @@ class State { return this.running; } - async refreshTree(force = false, delay = 0) { + async refreshTree(force = false) { const isExist = isExistCluster(); await vscode.commands.executeCommand( @@ -173,10 +176,9 @@ class State { "Nocalhost.visibleTree", isExist ); + await vscode.commands.executeCommand("Nocalhost.refresh"); - setTimeout(() => { - vscode.commands.executeCommand("Nocalhost.refresh"); - }, delay); + this.startAutoRefresh(force); } setRunning(running: boolean) { From 0cfaa2ab52208c99e51bf15bbdf5c8345b380cb8 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 23 Dec 2021 17:26:48 +0800 Subject: [PATCH 06/29] WIP: vCluster Signed-off-by: zhangjian --- src/main/ctl/nhctl/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/ctl/nhctl/cli.ts b/src/main/ctl/nhctl/cli.ts index 12970607..8d2a55c2 100644 --- a/src/main/ctl/nhctl/cli.ts +++ b/src/main/ctl/nhctl/cli.ts @@ -1640,7 +1640,7 @@ export async function kubeConfigRender(param: { }>((res, rej) => { const { proc, promise } = exec({ command: commands.join(" "), - output: { out: false, err: true }, + output: false, }); proc.stdout.on("data", (chuck: Buffer) => { const str = chuck.toString(); From 0639b6b5b50e4215c6e9b45e3b6cb58a82a50a69 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 23 Dec 2021 18:07:58 +0800 Subject: [PATCH 07/29] WIP: VCluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 115 ++++++++++++++++------------ 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index d9bb23a3..26a51045 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -148,9 +148,8 @@ export default class AccountClusterService { ); const kubeConfigArr: Array = []; - const newRootNodes: IRootNode[] = []; - for (const sa of serviceAccounts) { + const serviceNodes = serviceAccounts.map(async (sa) => { let devSpaces: Array | undefined = new Array(); const kubeconfig = await AccountClusterService.vClusterProcess(sa); @@ -165,56 +164,59 @@ export default class AccountClusterService { kubeConfigArr.push(id); - if (sa.privilege) { - const devArray = await getAllNamespace({ - kubeConfigPath: kubeConfigPath, - namespace: "default", - }); - - for (const dev of devArray) { - dev.storageClass = sa.storageClass; - dev.devStartAppendCommand = [ - "--priority-class", - "nocalhost-container-critical", - ]; - dev.kubeconfig = sa.kubeconfig; - - const ns = sa.namespacePacks?.find( - (ns) => ns.namespace === dev.namespace - ); - - dev.spaceId = ns?.spaceId; - dev.spaceName = ns?.spacename; - - if (sa.privilegeType === "CLUSTER_ADMIN") { - dev.spaceOwnType = "Owner"; - } else if (sa.privilegeType === "CLUSTER_VIEWER") { - dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; - } - } - devSpaces.push(...devArray); - } else { - for (const ns of sa.namespacePacks) { - const devInfo: IDevSpaceInfo = { - id: ns.spaceId, - spaceName: ns.spacename, - namespace: ns.namespace, - kubeconfig: sa.kubeconfig, - accountClusterService, - clusterId: sa.clusterId, - storageClass: sa.storageClass, - spaceOwnType: ns.spaceOwnType, - devStartAppendCommand: [ + const state = await checkCluster(kubeConfigPath); + + if (state.code === 200) { + if (sa.privilege) { + const devArray = await getAllNamespace({ + kubeConfigPath: kubeConfigPath, + namespace: "default", + }); + + for (const dev of devArray) { + dev.storageClass = sa.storageClass; + dev.devStartAppendCommand = [ "--priority-class", "nocalhost-container-critical", - ], - }; - devSpaces.push(devInfo); + ]; + dev.kubeconfig = sa.kubeconfig; + + const ns = sa.namespacePacks?.find( + (ns) => ns.namespace === dev.namespace + ); + + dev.spaceId = ns?.spaceId; + dev.spaceName = ns?.spacename; + + if (sa.privilegeType === "CLUSTER_ADMIN") { + dev.spaceOwnType = "Owner"; + } else if (sa.privilegeType === "CLUSTER_VIEWER") { + dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; + } + } + devSpaces.push(...devArray); + } else { + for (const ns of sa.namespacePacks) { + const devInfo: IDevSpaceInfo = { + id: ns.spaceId, + spaceName: ns.spacename, + namespace: ns.namespace, + kubeconfig: sa.kubeconfig, + accountClusterService, + clusterId: sa.clusterId, + storageClass: sa.storageClass, + spaceOwnType: ns.spaceOwnType, + devStartAppendCommand: [ + "--priority-class", + "nocalhost-container-critical", + ], + }; + devSpaces.push(devInfo); + } } } - const state = await checkCluster(kubeConfigPath); - const obj: IRootNode = { + return { devSpaces, applications, userInfo: newAccountCluster.userInfo, @@ -225,16 +227,27 @@ export default class AccountClusterService { kubeConfigPath, state, }; - - newRootNodes.push(obj); - } + }); await AccountClusterService.cleanDiffKubeConfig( newAccountCluster, kubeConfigArr ); - return newRootNodes; + return await (await Promise.allSettled(serviceNodes)).map((item) => { + if (item.status === "fulfilled") { + return item.value; + } + return { + devSpaces: [], + userInfo: newAccountCluster.userInfo, + clusterSource: ClusterSource.server, + accountClusterService, + id: newAccountCluster.id, + createTime: newAccountCluster.createTime, + kubeConfigPath: null, + } as IRootNode; + }); }; static virtualClusterProc: { From 0320df57540a629b1e6322cf2e1a7c2fb0160c75 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 27 Dec 2021 12:32:38 +0800 Subject: [PATCH 08/29] WIP: VCluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 170 ++++++++-------- src/main/clusters/LocalCuster.ts | 66 +++---- src/main/clusters/utils.ts | 4 +- src/main/commands/DeleteKubeConfigCommand.ts | 4 +- src/main/commands/SignOutCommand.ts | 4 +- src/main/ctl/nhctl/cli.ts | 8 +- src/main/debug/nocalhost/start.ts | 3 - src/main/domain/IRootNode.ts | 2 + src/main/extension.ts | 4 +- src/main/host.ts | 4 +- src/main/nodes/KubeConfigNode.ts | 193 +++++++++++++------ src/main/nodes/NocalhostRootNode.ts | 31 +-- 12 files changed, 279 insertions(+), 214 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 26a51045..b93ff1c3 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -3,11 +3,11 @@ import * as semver from "semver"; import * as url from "url"; import { uniqBy, difference } from "lodash"; import * as path from "path"; +import { promises as fs } from "fs"; import { IResponseData, IServiceAccountInfo, - IDevSpaceInfo, IV2ApplicationInfo, IUserInfo, IRootNode, @@ -16,8 +16,7 @@ import logger from "../utils/logger"; import { keysToCamel } from "../utils"; import { checkCluster, - getAllNamespace, - kubeconfig, + kubeconfigCommand, kubeConfigRender, } from "../ctl/nhctl"; import host from "../host"; @@ -149,75 +148,76 @@ export default class AccountClusterService { const kubeConfigArr: Array = []; - const serviceNodes = serviceAccounts.map(async (sa) => { - let devSpaces: Array | undefined = new Array(); - - const kubeconfig = await AccountClusterService.vClusterProcess(sa); + const serviceNodes = serviceAccounts.map(async (serviceAccount) => { + const kubeconfig = await AccountClusterService.vClusterProcess( + serviceAccount + ); if (kubeconfig) { - sa.kubeconfig = kubeconfig; + serviceAccount.kubeconfig = kubeconfig; } const { id, kubeConfigPath } = await AccountClusterService.saveKubeConfig( - sa + serviceAccount ); kubeConfigArr.push(id); const state = await checkCluster(kubeConfigPath); - if (state.code === 200) { - if (sa.privilege) { - const devArray = await getAllNamespace({ - kubeConfigPath: kubeConfigPath, - namespace: "default", - }); - - for (const dev of devArray) { - dev.storageClass = sa.storageClass; - dev.devStartAppendCommand = [ - "--priority-class", - "nocalhost-container-critical", - ]; - dev.kubeconfig = sa.kubeconfig; - - const ns = sa.namespacePacks?.find( - (ns) => ns.namespace === dev.namespace - ); - - dev.spaceId = ns?.spaceId; - dev.spaceName = ns?.spacename; - - if (sa.privilegeType === "CLUSTER_ADMIN") { - dev.spaceOwnType = "Owner"; - } else if (sa.privilegeType === "CLUSTER_VIEWER") { - dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; - } - } - devSpaces.push(...devArray); - } else { - for (const ns of sa.namespacePacks) { - const devInfo: IDevSpaceInfo = { - id: ns.spaceId, - spaceName: ns.spacename, - namespace: ns.namespace, - kubeconfig: sa.kubeconfig, - accountClusterService, - clusterId: sa.clusterId, - storageClass: sa.storageClass, - spaceOwnType: ns.spaceOwnType, - devStartAppendCommand: [ - "--priority-class", - "nocalhost-container-critical", - ], - }; - devSpaces.push(devInfo); - } - } - } + // if (state.code === 200) { + // if (sa.privilege) { + // const devArray = await getAllNamespace({ + // kubeConfigPath: kubeConfigPath, + // namespace: "default", + // }); + + // for (const dev of devArray) { + // dev.storageClass = sa.storageClass; + // dev.devStartAppendCommand = [ + // "--priority-class", + // "nocalhost-container-critical", + // ]; + // dev.kubeconfig = sa.kubeconfig; + + // const ns = sa.namespacePacks?.find( + // (ns) => ns.namespace === dev.namespace + // ); + + // dev.spaceId = ns?.spaceId; + // dev.spaceName = ns?.spacename; + + // if (sa.privilegeType === "CLUSTER_ADMIN") { + // dev.spaceOwnType = "Owner"; + // } else if (sa.privilegeType === "CLUSTER_VIEWER") { + // dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; + // } + // } + // devSpaces.push(...devArray); + // } else { + // for (const ns of sa.namespacePacks) { + // const devInfo: IDevSpaceInfo = { + // id: ns.spaceId, + // spaceName: ns.spacename, + // namespace: ns.namespace, + // kubeconfig: sa.kubeconfig, + // accountClusterService, + // clusterId: sa.clusterId, + // storageClass: sa.storageClass, + // spaceOwnType: ns.spaceOwnType, + // devStartAppendCommand: [ + // "--priority-class", + // "nocalhost-container-critical", + // ], + // }; + // devSpaces.push(devInfo); + // } + // } + // } return { - devSpaces, + serviceAccount, + devSpaces: [], applications, userInfo: newAccountCluster.userInfo, clusterSource: ClusterSource.server, @@ -229,10 +229,10 @@ export default class AccountClusterService { }; }); - await AccountClusterService.cleanDiffKubeConfig( - newAccountCluster, - kubeConfigArr - ); + // await AccountClusterService.cleanDiffKubeConfig( + // newAccountCluster, + // kubeConfigArr + // ); return await (await Promise.allSettled(serviceNodes)).map((item) => { if (item.status === "fulfilled") { @@ -280,10 +280,21 @@ export default class AccountClusterService { serviceAddress: serviceAddress, }); + const kubeConfigPath = path.resolve( + KUBE_CONFIG_DIR, + getStringHash(kubeconfig.trim()) + ); + host.getContext().subscriptions.push({ - dispose: proc.kill, - }); + dispose: () => { + logger.debug("dispose", kubeConfigPath); + + proc.kill(); + kubeconfigCommand(kubeConfigPath, "remove"); + return fs.unlink(kubeConfigPath); + }, + }); AccountClusterService.virtualClusterProc[serviceAddress] = { proc, kubeconfig, @@ -293,16 +304,16 @@ export default class AccountClusterService { } } static async cleanDiffKubeConfig( - accountCluser: AccountClusterNode, + accountCluster: AccountClusterNode, configs: Array ) { - const { baseUrl, username } = accountCluser.loginInfo; + const { baseUrl, username } = accountCluster.loginInfo; const KEY = `USER_LINK:${baseUrl}@${username}`; - const prevData = host.getGlobalState(KEY); + const prevData = host.getGlobalState>(KEY); if (prevData) { - const diff = difference(prevData as Array, configs); + const diff = difference(prevData, configs); if (diff.length === 0) { return; @@ -310,13 +321,9 @@ export default class AccountClusterService { await Promise.allSettled( diff.map((id) => { - return new Promise(async (res) => { - const file = path.resolve(KUBE_CONFIG_DIR, id); - - await kubeconfig(file, "remove"); + const file = path.resolve(KUBE_CONFIG_DIR, id); - res(); - }); + kubeconfigCommand(file, "remove"); }) ); } @@ -332,7 +339,7 @@ export default class AccountClusterService { if (!(await isExist(kubeConfigPath))) { await writeFileLock(kubeConfigPath, accountInfo.kubeconfig); - kubeconfig(kubeConfigPath, "add"); + kubeconfigCommand(kubeConfigPath, "add"); } return { id, kubeConfigPath }; @@ -343,17 +350,18 @@ export default class AccountClusterService { const accountServer = new AccountClusterService(loginInfo); const newAccountCluster = await accountServer.buildAccountClusterNode(); - let globalAccountClusterList = host.getGlobalState(SERVER_CLUSTER_LIST); + let globalAccountClusterList = host.getGlobalState< + Array + >(SERVER_CLUSTER_LIST); + if (!Array.isArray(globalAccountClusterList)) { globalAccountClusterList = []; } - globalAccountClusterList = globalAccountClusterList.filter( - (it: AccountClusterNode) => it.id - ); + globalAccountClusterList = globalAccountClusterList.filter((it) => it.id); const oldAccountIndex = globalAccountClusterList.findIndex( - (it: AccountClusterNode) => it.id === newAccountCluster.id + (it) => it.id === newAccountCluster.id ); if (oldAccountIndex !== -1) { diff --git a/src/main/clusters/LocalCuster.ts b/src/main/clusters/LocalCuster.ts index 9556d778..0e2ceaa3 100644 --- a/src/main/clusters/LocalCuster.ts +++ b/src/main/clusters/LocalCuster.ts @@ -5,10 +5,9 @@ import * as fs from "fs"; import { LOCAL_PATH, KUBE_CONFIG_DIR } from "../constants"; import { isExistSync, writeFileAsync } from "../utils/fileUtil"; import { IRootNode } from "../domain"; -import { IDevSpaceInfo, IV2ApplicationInfo } from "../domain"; +import { IV2ApplicationInfo } from "../domain"; import { getStringHash } from "../utils/common"; -import * as yaml from "yaml"; -import { checkCluster, getAllNamespace, kubeconfig } from "../ctl/nhctl"; +import { checkCluster, kubeconfigCommand } from "../ctl/nhctl"; import { ClusterSource } from "../common/define"; import { ClustersState } from "."; @@ -26,7 +25,8 @@ export default class LocalCluster { static getClusterNodeByKubeConfigPath( kubeConfigPath: string ): LocalClusterNode { - const localClusterNodes = host.getGlobalState(LOCAL_PATH) || []; + const localClusterNodes = + host.getGlobalState>(LOCAL_PATH) || []; return (localClusterNodes || []).find( (it: LocalClusterNode) => it.filePath === kubeConfigPath ); @@ -41,35 +41,35 @@ export default class LocalCluster { const { filePath, createTime } = newLocalCluster; let kubeConfig = ""; let applications: IV2ApplicationInfo[] = []; - let devSpaces: Array | undefined = new Array(); - if (!isExistSync(filePath)) { - host.log(`no such file or directory: ${filePath}`); - return; - } - const kubeStr = fs.readFileSync(filePath); - const kubeConfigObj = yaml.parse(`${kubeStr}`); - kubeConfig = `${kubeStr}`; - const contexts = kubeConfigObj["contexts"]; - if (!contexts || contexts.length === 0) { - return; - } - let defaultNamespace = contexts[0]["context"]["namespace"] || ""; - if (kubeConfigObj["current-context"]) { - const currentContext = contexts.find( - (it: any) => it.name === kubeConfigObj["current-context"] - ); - if (currentContext) { - defaultNamespace = currentContext.context.namespace; - } - } + // let devSpaces: Array | undefined = new Array(); + // if (!isExistSync(filePath)) { + // host.log(`no such file or directory: ${filePath}`); + // return; + // } + // const kubeStr = fs.readFileSync(filePath); + // const kubeConfigObj = yaml.parse(`${kubeStr}`); + // kubeConfig = `${kubeStr}`; + // const contexts = kubeConfigObj["contexts"]; + // if (!contexts || contexts.length === 0) { + // return; + // } + // let defaultNamespace = contexts[0]["context"]["namespace"] || ""; + // if (kubeConfigObj["current-context"]) { + // const currentContext = contexts.find( + // (it: any) => it.name === kubeConfigObj["current-context"] + // ); + // if (currentContext) { + // defaultNamespace = currentContext.context.namespace; + // } + // } const state = await checkCluster(filePath); - if (state.code === 200) { - devSpaces = await getAllNamespace({ - kubeConfigPath: filePath, - namespace: defaultNamespace as string, - }); - } + // if (state.code === 200) { + // devSpaces = await getAllNamespace({ + // kubeConfigPath: filePath, + // namespace: defaultNamespace as string, + // }); + // } const contextObj = { applicationName: "default.application", @@ -90,7 +90,7 @@ export default class LocalCluster { }); const obj: IRootNode = { id: newLocalCluster.id, - devSpaces, + devSpaces: [], clusterName: newLocalCluster.clusterNickName, createTime, clusterSource: ClusterSource.local, @@ -147,7 +147,7 @@ export default class LocalCluster { localClusterNodes.push(newCluster); host.setGlobalState(LOCAL_PATH, localClusterNodes); - await kubeconfig(resultFilePath, "add"); + await kubeconfigCommand(resultFilePath, "add"); return newCluster; } else { diff --git a/src/main/clusters/utils.ts b/src/main/clusters/utils.ts index 0f21b87f..2c55502f 100644 --- a/src/main/clusters/utils.ts +++ b/src/main/clusters/utils.ts @@ -3,7 +3,7 @@ import host from "../host"; export function isExistCluster() { const globalClusterAccountList = - host.getGlobalState(SERVER_CLUSTER_LIST) || []; - const localPaths = (host.getGlobalState(LOCAL_PATH) as string[]) || []; + host.getGlobalState>(SERVER_CLUSTER_LIST) || []; + const localPaths = host.getGlobalState>(LOCAL_PATH) || []; return globalClusterAccountList.length > 0 || localPaths.length > 0; } diff --git a/src/main/commands/DeleteKubeConfigCommand.ts b/src/main/commands/DeleteKubeConfigCommand.ts index 4f9d6800..20d31d5d 100644 --- a/src/main/commands/DeleteKubeConfigCommand.ts +++ b/src/main/commands/DeleteKubeConfigCommand.ts @@ -10,7 +10,7 @@ import host from "../host"; import { LocalClusterNode } from "../clusters/LocalCuster"; import { KubeConfigNode } from "../nodes/KubeConfigNode"; import Bookinfo from "../common/bookinfo"; -import { kubeconfig } from "../ctl/nhctl"; +import { kubeconfigCommand } from "../ctl/nhctl"; import { NocalhostRootNode } from "../nodes/NocalhostRootNode"; import messageBus from "../utils/messageBus"; @@ -58,7 +58,7 @@ export default class DeleteKubeConfigCommand implements ICommand { deleted.forEach(async (f) => { if (await isExist(f.filePath)) { - await kubeconfig(f.filePath, "remove"); + await kubeconfigCommand(f.filePath, "remove"); fs.unlinkSync(f.filePath); } diff --git a/src/main/commands/SignOutCommand.ts b/src/main/commands/SignOutCommand.ts index 8eee510e..bb946764 100644 --- a/src/main/commands/SignOutCommand.ts +++ b/src/main/commands/SignOutCommand.ts @@ -11,7 +11,7 @@ import host from "../host"; import { IUserInfo } from "../domain"; import { KubeConfigNode } from "../nodes/KubeConfigNode"; import Bookinfo from "../common/bookinfo"; -import { kubeconfig } from "../ctl/nhctl"; +import { kubeconfigCommand } from "../ctl/nhctl"; import { LoginInfo } from "../clusters/interface"; import { NocalhostRootNode } from "../nodes/NocalhostRootNode"; import messageBus from "../utils/messageBus"; @@ -73,7 +73,7 @@ export default class SignOutCommand implements ICommand { return new Promise(async (res, rej) => { const file = path.resolve(KUBE_CONFIG_DIR, id); - await kubeconfig(file, "remove"); + await kubeconfigCommand(file, "remove"); res(); }); diff --git a/src/main/ctl/nhctl/cli.ts b/src/main/ctl/nhctl/cli.ts index 8d2a55c2..b873b9f7 100644 --- a/src/main/ctl/nhctl/cli.ts +++ b/src/main/ctl/nhctl/cli.ts @@ -1523,7 +1523,7 @@ export async function checkCluster( return result; } -export async function kubeconfig( +export async function kubeconfigCommand( kubeConfigPath: string, command: "add" | "remove" ) { @@ -1638,6 +1638,10 @@ export async function kubeConfigRender(param: { kubeconfig: string; proc: ChildProcessWithoutNullStreams; }>((res, rej) => { + const command = commands.join(" "); + + console.time(command); + const { proc, promise } = exec({ command: commands.join(" "), output: false, @@ -1646,6 +1650,8 @@ export async function kubeConfigRender(param: { const str = chuck.toString(); if (str.endsWith(END_Symbol)) { + console.timeEnd(command); + const kubeconfig = str.substring(0, str.lastIndexOf(END_Symbol)); res({ kubeconfig, proc }); return; diff --git a/src/main/debug/nocalhost/start.ts b/src/main/debug/nocalhost/start.ts index 448c9349..7944e91b 100644 --- a/src/main/debug/nocalhost/start.ts +++ b/src/main/debug/nocalhost/start.ts @@ -107,9 +107,6 @@ async function getResourceNode() { if (node.clusterSource === ClusterSource.local) { name = await getClusterName({ clusterSource: ClusterSource.server, - devSpaces: node.devSpaceInfos, - applications: [], - state: { code: 200 }, kubeConfigPath: node.kubeConfigPath, }); } diff --git a/src/main/domain/IRootNode.ts b/src/main/domain/IRootNode.ts index 2398a65a..cceb6ea7 100644 --- a/src/main/domain/IRootNode.ts +++ b/src/main/domain/IRootNode.ts @@ -6,8 +6,10 @@ import { IV2ApplicationInfo } from "./IV2ApplicationInfo"; import { ClusterSource } from "../common/define"; import AccountClusterService from "../clusters/AccountCluster"; import { ClustersState } from "../clusters"; +import { IServiceAccountInfo } from "."; export interface IRootNode { + serviceAccount?: IServiceAccountInfo; devSpaces: IDevSpaceInfo[]; applications: IV2ApplicationInfo[]; clusterSource?: ClusterSource; diff --git a/src/main/extension.ts b/src/main/extension.ts index 21496283..0ebaac4b 100644 --- a/src/main/extension.ts +++ b/src/main/extension.ts @@ -55,6 +55,7 @@ import SyncServiceCommand from "./commands/SyncServiceCommand"; import { ShellExecError } from "./ctl/shell"; import { createSyncManage } from "./component/syncManage"; import { activateNocalhostDebug } from "./debug/nocalhost"; +import { KubeConfigNode } from "./nodes/KubeConfigNode"; // The example uses the file message format. const localize = nls.config({ messageFormat: nls.MessageFormat.file })(); @@ -90,7 +91,8 @@ export async function activate(context: vscode.ExtensionContext) { const node = e.element; if ( node instanceof KubernetesResourceFolder || - node instanceof DevSpaceNode + node instanceof DevSpaceNode || + node instanceof KubeConfigNode ) { state.refreshFolderMap.set(node.getNodeStateId(), true); } diff --git a/src/main/host.ts b/src/main/host.ts index 7facac6d..40fac0c0 100644 --- a/src/main/host.ts +++ b/src/main/host.ts @@ -48,12 +48,12 @@ export class Host implements vscode.Disposable { this.context.globalState.update(key, state); } - public getGlobalState(key: string) { + public getGlobalState(key: string) { if (!this.context) { throw new Error("not initialized extension"); } - return this.context.globalState.get(key) as any; + return this.context.globalState.get(key) as T; } public removeGlobalState(key: string) { diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index f45ee7c6..3c947651 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -1,23 +1,30 @@ import * as vscode from "vscode"; import { orderBy } from "lodash"; -import { HELM_NH_CONFIG_DIR } from "../constants"; +import * as fs from "fs"; +import * as yaml from "yaml"; + import state from "../state"; import AccountClusterService from "../clusters/AccountCluster"; import { ID_SPLIT } from "./nodeContants"; -import * as path from "path"; import { ClusterSource } from "../common/define"; import { BaseNocalhostNode } from "./types/nodeType"; import { NocalhostFolderNode } from "./abstract/NocalhostFolderNode"; import { NocalhostRootNode } from "./NocalhostRootNode"; -import { resolveVSCodeUri, writeFileLock } from "../utils/fileUtil"; -import { DevSpaceNode } from "./DevSpaceNode"; -import { IUserInfo, IDevSpaceInfo, IV2ApplicationInfo } from "../domain"; +import { isExistSync, resolveVSCodeUri } from "../utils/fileUtil"; +import { + IUserInfo, + IV2ApplicationInfo, + IRootNode, + IDevSpaceInfo, +} from "../domain"; import { ClustersState } from "../clusters"; +import host from "../host"; +import { getAllNamespace } from "../ctl/nhctl"; +import { DevSpaceNode } from "./DevSpaceNode"; export class KubeConfigNode extends NocalhostFolderNode { public label: string; public type = "KUBECONFIG"; - public devSpaceInfos: IDevSpaceInfo[]; public userInfo: IUserInfo; public clusterSource: ClusterSource; public applications: Array; @@ -31,47 +38,124 @@ export class KubeConfigNode extends NocalhostFolderNode { public accountClusterService: AccountClusterService; private state: ClustersState; - constructor(props: { - id: string; - parent: NocalhostRootNode; - label: string; - devSpaceInfos: IDevSpaceInfo[]; - applications: Array; - clusterSource: ClusterSource; - kubeConfigPath: string; - userInfo: IUserInfo; - accountClusterService?: AccountClusterService; - state: ClustersState; - }) { + constructor( + id: string, + parent: NocalhostRootNode, + label: string, + private rootNode: IRootNode + ) { super(); + const { - id, - parent, - label, - devSpaceInfos, applications, clusterSource, kubeConfigPath, userInfo, accountClusterService, - } = props; + } = rootNode; + this.id = id; this.parent = parent; this.clusterSource = clusterSource; - this.label = - label || (devSpaceInfos.length > 0 ? devSpaceInfos[0].namespace : ""); - this.devSpaceInfos = devSpaceInfos; + + this.label = label; this.applications = applications; this.installedApps = []; this.kubeConfigPath = kubeConfigPath; this.userInfo = userInfo; this.accountClusterService = accountClusterService; - this.state = props.state; + this.state = rootNode.state; state.setNode(this.getNodeStateId(), this); } - updateData(): any { - return []; + async updateData() { + if (this.state.code !== 200) { + return []; + } + + let devSpaces = Array.of(); + const { kubeConfigPath } = this; + + if (this.clusterSource === ClusterSource.local) { + if (!isExistSync(kubeConfigPath)) { + host.log(`no such file or directory: ${kubeConfigPath}`); + return; + } + + const kubeStr = fs.readFileSync(kubeConfigPath); + const kubeConfigObj = yaml.parse(`${kubeStr}`); + const contexts = kubeConfigObj["contexts"]; + if (!contexts || contexts.length === 0) { + return; + } + + let namespace = contexts[0]["context"]["namespace"] || ""; + + if (kubeConfigObj["current-context"]) { + const currentContext = contexts.find( + (it: any) => it.name === kubeConfigObj["current-context"] + ); + if (currentContext) { + namespace = currentContext.context.namespace; + } + } + + devSpaces = await getAllNamespace({ + kubeConfigPath: kubeConfigPath, + namespace, + }); + } else { + const sa = this.rootNode.serviceAccount; + + if (sa.privilege) { + devSpaces = await getAllNamespace({ + kubeConfigPath: kubeConfigPath, + namespace: "default", + }); + + for (const dev of devSpaces) { + dev.storageClass = sa.storageClass; + dev.devStartAppendCommand = [ + "--priority-class", + "nocalhost-container-critical", + ]; + dev.kubeconfig = sa.kubeconfig; + + const ns = sa.namespacePacks?.find( + (ns) => ns.namespace === dev.namespace + ); + + dev.spaceId = ns?.spaceId; + dev.spaceName = ns?.spacename; + + if (sa.privilegeType === "CLUSTER_ADMIN") { + dev.spaceOwnType = "Owner"; + } else if (sa.privilegeType === "CLUSTER_VIEWER") { + dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; + } + } + } else { + for (const ns of sa.namespacePacks) { + const devInfo: IDevSpaceInfo = { + id: ns.spaceId, + spaceName: ns.spacename, + namespace: ns.namespace, + kubeconfig: sa.kubeconfig, + accountClusterService: this.accountClusterService, + clusterId: sa.clusterId, + storageClass: sa.storageClass, + spaceOwnType: ns.spaceOwnType, + devStartAppendCommand: [ + "--priority-class", + "nocalhost-container-critical", + ], + }; + devSpaces.push(devInfo); + } + } + } + + return devSpaces; } public getKubeConfigPath() { @@ -79,43 +163,26 @@ export class KubeConfigNode extends NocalhostFolderNode { } async getChildren(parent?: BaseNocalhostNode): Promise { - let res = { - devSpaces: this.devSpaceInfos, - applications: this.applications, - }; - const devs: (DevSpaceNode & { order?: boolean; isSpace?: boolean })[] = []; - - res.applications.forEach(async (app) => { - let context = app.context; - const obj = { - nocalhostConfig: "", - }; - if (context) { - let jsonObj = JSON.parse(context); - obj.nocalhostConfig = jsonObj["nocalhostConfig"]; - } - - const nhConfigPath = path.resolve(HELM_NH_CONFIG_DIR, `${app.id}_config`); - await writeFileLock(nhConfigPath, obj.nocalhostConfig || ""); - }); - for (const d of res.devSpaces) { - const node = new DevSpaceNode( - this, - d.spaceName, - d, - res.applications, - this.clusterSource + const devSpaces = await this.updateData(); + + const devSpace = devSpaces.map((devSpace) => { + return Object.assign( + new DevSpaceNode( + this, + devSpace.spaceName, + devSpace, + this.applications, + this.clusterSource + ), + { + order: devSpace.spaceOwnType !== "Viewer", + isSpace: devSpace.spaceId > 0, + } ); - devs.push( - Object.assign(node, { - order: d.spaceOwnType !== "Viewer", - isSpace: d.spaceId > 0, - }) - ); - } + }); return orderBy( - devs, + devSpace, ["order", "isSpace", "label"], ["desc", "desc", "asc"] ); diff --git a/src/main/nodes/NocalhostRootNode.ts b/src/main/nodes/NocalhostRootNode.ts index c4f6083c..e424937a 100644 --- a/src/main/nodes/NocalhostRootNode.ts +++ b/src/main/nodes/NocalhostRootNode.ts @@ -24,7 +24,9 @@ import { KubeConfigNode } from "./KubeConfigNode"; import { ROOT } from "./nodeContants"; import { BaseNocalhostNode } from "./types/nodeType"; -export async function getClusterName(res: IRootNode) { +export async function getClusterName( + res: Pick +) { if (!res.kubeConfigPath) { return "unknown"; } @@ -277,7 +279,7 @@ export class NocalhostRootNode implements BaseNocalhostNode { } resources = sortResources(resources); - await this.cleanDiffDevSpace(resources); + // await this.cleanDiffDevSpace(resources); state.setData(this.getNodeStateId(), resources, false); } @@ -331,18 +333,7 @@ export class NocalhostRootNode implements BaseNocalhostNode { async getKubeConfigNode(res: IRootNode) { const clusterName = await getClusterName(res); - return new KubeConfigNode({ - id: res.id, - label: clusterName, - parent: this, - kubeConfigPath: res.kubeConfigPath, - devSpaceInfos: res.devSpaces, - applications: res.applications, - userInfo: res.userInfo, - clusterSource: res.clusterSource, - accountClusterService: res.accountClusterService, - state: res.state, - }); + return new KubeConfigNode(res.id, this, clusterName, res); } async getChildren( parent?: BaseNocalhostNode @@ -373,16 +364,8 @@ export class NocalhostRootNode implements BaseNocalhostNode { logger.error("get serverCluster error", result.reason, res.userInfo); } - return new KubeConfigNode({ - id: res.id, - label: res.clusterName, - parent: this, - kubeConfigPath: res.kubeConfigPath, - devSpaceInfos: res.devSpaces, - applications: res.applications, - userInfo: res.userInfo, - clusterSource: res.clusterSource, - accountClusterService: res.accountClusterService, + return new KubeConfigNode(res.id, this, res.clusterName, { + ...res, state: { code: 201, info, From 55341c92de2ec6112716b18d997d744cb4a8526a Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 27 Dec 2021 12:40:49 +0800 Subject: [PATCH 09/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 50 ----------------------------- src/main/clusters/LocalCuster.ts | 30 +---------------- src/main/nodes/KubeConfigNode.ts | 7 +++- src/main/state.ts | 6 ++-- 4 files changed, 10 insertions(+), 83 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index b93ff1c3..dab7c242 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -165,56 +165,6 @@ export default class AccountClusterService { const state = await checkCluster(kubeConfigPath); - // if (state.code === 200) { - // if (sa.privilege) { - // const devArray = await getAllNamespace({ - // kubeConfigPath: kubeConfigPath, - // namespace: "default", - // }); - - // for (const dev of devArray) { - // dev.storageClass = sa.storageClass; - // dev.devStartAppendCommand = [ - // "--priority-class", - // "nocalhost-container-critical", - // ]; - // dev.kubeconfig = sa.kubeconfig; - - // const ns = sa.namespacePacks?.find( - // (ns) => ns.namespace === dev.namespace - // ); - - // dev.spaceId = ns?.spaceId; - // dev.spaceName = ns?.spacename; - - // if (sa.privilegeType === "CLUSTER_ADMIN") { - // dev.spaceOwnType = "Owner"; - // } else if (sa.privilegeType === "CLUSTER_VIEWER") { - // dev.spaceOwnType = ns?.spaceOwnType ?? "Viewer"; - // } - // } - // devSpaces.push(...devArray); - // } else { - // for (const ns of sa.namespacePacks) { - // const devInfo: IDevSpaceInfo = { - // id: ns.spaceId, - // spaceName: ns.spacename, - // namespace: ns.namespace, - // kubeconfig: sa.kubeconfig, - // accountClusterService, - // clusterId: sa.clusterId, - // storageClass: sa.storageClass, - // spaceOwnType: ns.spaceOwnType, - // devStartAppendCommand: [ - // "--priority-class", - // "nocalhost-container-critical", - // ], - // }; - // devSpaces.push(devInfo); - // } - // } - // } - return { serviceAccount, devSpaces: [], diff --git a/src/main/clusters/LocalCuster.ts b/src/main/clusters/LocalCuster.ts index 0e2ceaa3..7e14e348 100644 --- a/src/main/clusters/LocalCuster.ts +++ b/src/main/clusters/LocalCuster.ts @@ -39,37 +39,9 @@ export default class LocalCluster { return; } const { filePath, createTime } = newLocalCluster; - let kubeConfig = ""; let applications: IV2ApplicationInfo[] = []; - // let devSpaces: Array | undefined = new Array(); - // if (!isExistSync(filePath)) { - // host.log(`no such file or directory: ${filePath}`); - // return; - // } - // const kubeStr = fs.readFileSync(filePath); - // const kubeConfigObj = yaml.parse(`${kubeStr}`); - // kubeConfig = `${kubeStr}`; - // const contexts = kubeConfigObj["contexts"]; - // if (!contexts || contexts.length === 0) { - // return; - // } - // let defaultNamespace = contexts[0]["context"]["namespace"] || ""; - // if (kubeConfigObj["current-context"]) { - // const currentContext = contexts.find( - // (it: any) => it.name === kubeConfigObj["current-context"] - // ); - // if (currentContext) { - // defaultNamespace = currentContext.context.namespace; - // } - // } - const state = await checkCluster(filePath); - // if (state.code === 200) { - // devSpaces = await getAllNamespace({ - // kubeConfigPath: filePath, - // namespace: defaultNamespace as string, - // }); - // } + const state = await checkCluster(filePath); const contextObj = { applicationName: "default.application", diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index 3c947651..b0eceac6 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -155,6 +155,7 @@ export class KubeConfigNode extends NocalhostFolderNode { } } + state.setData(this.getNodeStateId(), devSpaces); return devSpaces; } @@ -163,7 +164,11 @@ export class KubeConfigNode extends NocalhostFolderNode { } async getChildren(parent?: BaseNocalhostNode): Promise { - const devSpaces = await this.updateData(); + let devSpaces = state.getData>(this.getNodeStateId()); + + if (!devSpaces) { + devSpaces = await this.updateData(); + } const devSpace = devSpaces.map((devSpace) => { return Object.assign( diff --git a/src/main/state.ts b/src/main/state.ts index 9abf9710..5c7093e5 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -11,7 +11,7 @@ class State { private stateMap = new Map(); private nodeMap = new Map(); - private dataMap = new Map(); + private dataMap = new Map(); public refreshFolderMap = new Map(); private renderTime: NodeJS.Timeout; @@ -56,8 +56,8 @@ class State { } } - public getData(id: string) { - return this.dataMap.get(id); + public getData(id: string) { + return this.dataMap.get(id) as T; } private autoRefreshTimeId: NodeJS.Timeout | null = null; From 211c84c52f57826e33ffc038c88b589b25003f51 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 27 Dec 2021 15:15:11 +0800 Subject: [PATCH 10/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 35 ++++++-- src/main/clusters/LocalCuster.ts | 17 ++++ src/main/ctl/nhctl/cli.ts | 7 +- src/main/nodes/DevSpaceNode.ts | 15 ++-- src/main/nodes/KubeConfigNode.ts | 44 +++++++--- src/main/nodes/NocalhostRootNode.ts | 128 +++++++--------------------- 6 files changed, 122 insertions(+), 124 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index dab7c242..276388bb 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -38,6 +38,23 @@ export class AccountClusterNode { refreshToken: string | null; state: ClustersState; } + +export function buildRootNodeForAccountCluster( + accountCluster: AccountClusterNode, + state: ClustersState +): IRootNode { + return { + applications: [], + devSpaces: [], + userInfo: accountCluster.userInfo, + clusterSource: ClusterSource.server, + accountClusterService: new AccountClusterService(accountCluster.loginInfo), + id: accountCluster.id, + createTime: accountCluster.createTime, + kubeConfigPath: null, + state, + }; +} export default class AccountClusterService { instance: AxiosInstance; accountClusterNode: AccountClusterNode; @@ -179,10 +196,10 @@ export default class AccountClusterService { }; }); - // await AccountClusterService.cleanDiffKubeConfig( - // newAccountCluster, - // kubeConfigArr - // ); + await AccountClusterService.cleanDiffKubeConfig( + newAccountCluster, + kubeConfigArr + ); return await (await Promise.allSettled(serviceNodes)).map((item) => { if (item.status === "fulfilled") { @@ -401,9 +418,8 @@ export default class AccountClusterService { } } - // update login infoo updateLoginInfo() { - const newAccountCluser = { + const newAccountCluster = { ...this.accountClusterNode, jwt: this.jwt, refreshToken: this.refreshToken, @@ -416,14 +432,15 @@ export default class AccountClusterService { (it: AccountClusterNode) => it.id ); const oldAccountIndex = globalAccountClusterList.findIndex( - (it: AccountClusterNode) => it.id === newAccountCluser.id + (it: AccountClusterNode) => it.id === newAccountCluster.id ); if (oldAccountIndex !== -1) { - globalAccountClusterList.splice(oldAccountIndex, 1, newAccountCluser); + globalAccountClusterList.splice(oldAccountIndex, 1, newAccountCluster); } else { - globalAccountClusterList.push(newAccountCluser); + globalAccountClusterList.push(newAccountCluster); } globalAccountClusterList = uniqBy(globalAccountClusterList, "id"); + host.setGlobalState(SERVER_CLUSTER_LIST, globalAccountClusterList); } diff --git a/src/main/clusters/LocalCuster.ts b/src/main/clusters/LocalCuster.ts index 7e14e348..53f8a0cb 100644 --- a/src/main/clusters/LocalCuster.ts +++ b/src/main/clusters/LocalCuster.ts @@ -20,6 +20,23 @@ export class LocalClusterNode { public clusterNickName?: string ) {} } +export function buildRootNodeForLocalCluster( + localCluster: LocalClusterNode, + state: ClustersState +): IRootNode { + const { filePath, createTime } = localCluster; + + return { + id: localCluster.id, + devSpaces: [], + clusterName: localCluster.clusterNickName, + createTime, + clusterSource: ClusterSource.local, + applications: [], + kubeConfigPath: filePath, + state, + }; +} export default class LocalCluster { static getClusterNodeByKubeConfigPath( diff --git a/src/main/ctl/nhctl/cli.ts b/src/main/ctl/nhctl/cli.ts index b873b9f7..a2a87d6d 100644 --- a/src/main/ctl/nhctl/cli.ts +++ b/src/main/ctl/nhctl/cli.ts @@ -1633,24 +1633,27 @@ export async function kubeConfigRender(param: { serviceAddress, `:${remotePort}`, ]; + const END_Symbol = "EOF\n"; + return new Promise<{ kubeconfig: string; proc: ChildProcessWithoutNullStreams; }>((res, rej) => { const command = commands.join(" "); - console.time(command); + const time = Date.now(); const { proc, promise } = exec({ command: commands.join(" "), output: false, }); + proc.stdout.on("data", (chuck: Buffer) => { const str = chuck.toString(); if (str.endsWith(END_Symbol)) { - console.timeEnd(command); + logger.debug(command, Date.now() - time); const kubeconfig = str.substring(0, str.lastIndexOf(END_Symbol)); res({ kubeconfig, proc }); diff --git a/src/main/nodes/DevSpaceNode.ts b/src/main/nodes/DevSpaceNode.ts index e7ead2fd..2b725c51 100644 --- a/src/main/nodes/DevSpaceNode.ts +++ b/src/main/nodes/DevSpaceNode.ts @@ -17,6 +17,15 @@ import { StorageFolder } from "./storage/StorageFolder"; import { BaseNocalhostNode } from "./types/nodeType"; import { WorkloadFolderNode } from "./workloads/WorkloadFolderNode"; +export function getDevSpaceLabel(info: IDevSpaceInfo) { + let { spaceName: label, namespace } = info; + + if (label && namespace !== label) { + label += `(${namespace})`; + } + + return label || namespace; +} export class DevSpaceNode extends NocalhostFolderNode implements RefreshData { public label: string; public type = NodeType.devSpace; @@ -32,7 +41,6 @@ export class DevSpaceNode extends NocalhostFolderNode implements RefreshData { constructor( parent: BaseNocalhostNode, - label: string, info: IDevSpaceInfo, applications: Array, clusterSource: ClusterSource @@ -45,10 +53,7 @@ export class DevSpaceNode extends NocalhostFolderNode implements RefreshData { this.installedApps = []; this.clusterSource = clusterSource; - if (label && info.namespace !== label) { - label += `(${info.namespace})`; - } - this.label = label || info.namespace; + this.label = getDevSpaceLabel(info); state.setNode(this.getNodeStateId(), this); } diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index b0eceac6..e7b3f7d2 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import { orderBy } from "lodash"; +import { difference, orderBy } from "lodash"; import * as fs from "fs"; import * as yaml from "yaml"; @@ -20,7 +20,7 @@ import { import { ClustersState } from "../clusters"; import host from "../host"; import { getAllNamespace } from "../ctl/nhctl"; -import { DevSpaceNode } from "./DevSpaceNode"; +import { DevSpaceNode, getDevSpaceLabel } from "./DevSpaceNode"; export class KubeConfigNode extends NocalhostFolderNode { public label: string; @@ -156,15 +156,45 @@ export class KubeConfigNode extends NocalhostFolderNode { } state.setData(this.getNodeStateId(), devSpaces); + + this.cleanDiffDevSpace(devSpaces); + return devSpaces; } + private async cleanDiffDevSpace(resources: Array) { + const old = state.getData>(this.getNodeStateId()); + + if (old && old.length && resources.length) { + const getId = (devSpace: IDevSpaceInfo) => + `${this.getNodeStateId()}${ID_SPLIT}${getDevSpaceLabel(devSpace)}`; + + const diff: string[] = difference(old.map(getId), resources.map(getId)); + + if (diff.length) { + diff.forEach((id) => { + state.disposeNode({ + getNodeStateId() { + return id; + }, + }); + }); + } + } + } + public getKubeConfigPath() { return this.kubeConfigPath; } async getChildren(parent?: BaseNocalhostNode): Promise { - let devSpaces = state.getData>(this.getNodeStateId()); + let devSpaces = Array.of(); + + if (this.state.code !== 200) { + return []; + } + + devSpaces = state.getData>(this.getNodeStateId()); if (!devSpaces) { devSpaces = await this.updateData(); @@ -172,13 +202,7 @@ export class KubeConfigNode extends NocalhostFolderNode { const devSpace = devSpaces.map((devSpace) => { return Object.assign( - new DevSpaceNode( - this, - devSpace.spaceName, - devSpace, - this.applications, - this.clusterSource - ), + new DevSpaceNode(this, devSpace, this.applications, this.clusterSource), { order: devSpace.spaceOwnType !== "Viewer", isSpace: devSpace.spaceId > 0, diff --git a/src/main/nodes/NocalhostRootNode.ts b/src/main/nodes/NocalhostRootNode.ts index e424937a..a9dfb7b7 100644 --- a/src/main/nodes/NocalhostRootNode.ts +++ b/src/main/nodes/NocalhostRootNode.ts @@ -1,11 +1,16 @@ -import { difference, get, orderBy } from "lodash"; +import * as assert from "assert"; +import { get, orderBy } from "lodash"; import * as vscode from "vscode"; import { sortResources } from "../clusters"; import AccountClusterService, { AccountClusterNode, + buildRootNodeForAccountCluster, } from "../clusters/AccountCluster"; import { LoginInfo } from "../clusters/interface"; -import LocalCusterService, { LocalClusterNode } from "../clusters/LocalCuster"; +import LocalCusterService, { + buildRootNodeForLocalCluster, + LocalClusterNode, +} from "../clusters/LocalCuster"; import { ClusterSource } from "../common/define"; import { GLOBAL_TIMEOUT, @@ -62,13 +67,15 @@ export class NocalhostRootNode implements BaseNocalhostNode { let nodes = await asyncLimit( localClusterNodes, (localCluster) => { - if ( - token?.isCancellationRequested || - !isExistSync(localCluster.filePath) - ) { + if (token?.isCancellationRequested) { return Promise.reject(); } + assert( + isExistSync(localCluster.filePath), + `kubeconfig not exist:${localCluster.filePath}` + ); + return LocalCusterService.getLocalClusterRootNode(localCluster); }, GLOBAL_TIMEOUT @@ -82,20 +89,10 @@ export class NocalhostRootNode implements BaseNocalhostNode { logger.error("get localCluster error", result.reason, localCluster); - const root: IRootNode = { - ...localCluster, - clusterSource: ClusterSource.local, - clusterName: localCluster.clusterNickName, - kubeConfigPath: localCluster.filePath, - devSpaces: [], - applications: [], - state: { - code: 201, - info: result.reason, - }, - }; - - return root; + return buildRootNodeForLocalCluster(localCluster, { + code: 201, + info: result.reason, + }); }); }); @@ -137,20 +134,12 @@ export class NocalhostRootNode implements BaseNocalhostNode { logger.error("get serverCluster error", result.reason, account); - const rootNode: IRootNode = { - ...account, - clusterSource: ClusterSource.server, - accountClusterService: new AccountClusterService(account.loginInfo), - devSpaces: [], - applications: [], - kubeConfigPath: null, - state: { + return [ + buildRootNodeForAccountCluster(account, { code: 201, info: result.reason?.message, - }, - }; - - return [rootNode]; + }), + ]; }) .flat(1); }); @@ -179,8 +168,6 @@ export class NocalhostRootNode implements BaseNocalhostNode { let resultData = sortResources(data.flat(1)); - await this.cleanDiffDevSpace(resultData); - state.setData(this.getNodeStateId(), resultData, isInit); return resultData; @@ -202,18 +189,9 @@ export class NocalhostRootNode implements BaseNocalhostNode { } addResources = [ - await LocalCusterService.getLocalClusterRootNode(node).catch((err) => { - return { - ...node, - kubeConfigPath: node.filePath, - devSpaces: [], - applications: [], - state: { - code: 201, - info: err.message, - }, - }; - }), + await LocalCusterService.getLocalClusterRootNode(node).catch((err) => + buildRootNodeForLocalCluster(node, { code: 201, info: err.message }) + ), ]; } else { if ( @@ -227,22 +205,12 @@ export class NocalhostRootNode implements BaseNocalhostNode { addResources = await AccountClusterService.getServerClusterRootNodes( node - ).catch((err) => { - return [ - { - ...node, - kubeConfigPath: null, - clusterSource: ClusterSource.server, - accountClusterService: new AccountClusterService(node.loginInfo), - devSpaces: [], - applications: [], - state: { - code: 201, - info: err.message, - }, - }, - ]; - }); + ).catch((err) => [ + buildRootNodeForAccountCluster(node, { + code: 201, + info: err.message, + }), + ]); } if (resources) { resources = resources.concat(addResources); @@ -279,46 +247,10 @@ export class NocalhostRootNode implements BaseNocalhostNode { } resources = sortResources(resources); - // await this.cleanDiffDevSpace(resources); - state.setData(this.getNodeStateId(), resources, false); } } - private async cleanDiffDevSpace(resources: IRootNode[]) { - const old = state.getData(this.getNodeStateId()) as IRootNode[]; - - if (old && old.length && resources.length) { - const getId = async (resource: IRootNode) => { - const kubeconfigNode = await this.getKubeConfigNode(resource); - const children = await kubeconfigNode.getChildren(); - - let arrayId = [kubeconfigNode.getNodeStateId()]; - - arrayId = arrayId.concat( - children.map((child) => child.getNodeStateId()) - ); - - return arrayId; - }; - - const oldId = (await Promise.all(old.map(getId))).flat(1); - const newId = (await Promise.all(resources.map(getId))).flat(1); - - const diff: string[] = difference(oldId, newId); - - if (diff.length) { - diff.forEach((id) => { - state.disposeNode({ - getNodeStateId() { - return id; - }, - }); - }); - } - } - } - public label: string = NOCALHOST; public type = ROOT; constructor(public parent: BaseNocalhostNode | null, public hasInit = false) { From f439103bbf42bed0e97aa1c1048cffd8b1963656 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 27 Dec 2021 17:43:57 +0800 Subject: [PATCH 11/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 32 +++++++++---------- src/main/ctl/nhctl/cli.ts | 6 ++-- src/main/debug/provider/goDebugProvider.ts | 4 +-- .../debug/provider/java/javaDebugProvider.ts | 4 +-- .../debug/provider/pythonDebugProvider.ts | 4 +-- src/main/extension.ts | 4 +-- src/main/host.ts | 20 ++++++------ src/main/nodes/KubeConfigNode.ts | 2 -- src/main/state.ts | 6 ++-- src/main/utils/logger.ts | 31 +++++++++++------- 10 files changed, 58 insertions(+), 55 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 276388bb..b9ea5b92 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -4,6 +4,8 @@ import * as url from "url"; import { uniqBy, difference } from "lodash"; import * as path from "path"; import { promises as fs } from "fs"; +import * as assert from "assert"; +import { ChildProcessWithoutNullStreams } from "child_process"; import { IResponseData, @@ -12,7 +14,7 @@ import { IUserInfo, IRootNode, } from "../domain"; -import logger from "../utils/logger"; +import logger, { loggerDebug } from "../utils/logger"; import { keysToCamel } from "../utils"; import { checkCluster, @@ -27,7 +29,6 @@ import { KUBE_CONFIG_DIR, SERVER_CLUSTER_LIST } from "../constants"; import { ClusterSource } from "../common/define"; import * as packageJson from "../../../package.json"; import { ClustersState } from "."; -import { ChildProcessWithoutNullStreams } from "child_process"; export class AccountClusterNode { userInfo: IUserInfo; @@ -55,6 +56,9 @@ export function buildRootNodeForAccountCluster( state, }; } +const virtualClusterProcMap: { + [key: string]: { proc: ChildProcessWithoutNullStreams; kubeconfig: string }; +} = {}; export default class AccountClusterService { instance: AxiosInstance; accountClusterNode: AccountClusterNode; @@ -149,12 +153,10 @@ export default class AccountClusterService { }` ); - if (!Array.isArray(serviceAccounts) || serviceAccounts.length === 0) { - const msg = `no cluster found for ${newAccountCluster.loginInfo.baseUrl} ${newAccountCluster.loginInfo.username}`; - - logger.error(msg); - throw new Error(msg); - } + assert( + Array.isArray(serviceAccounts) && serviceAccounts.length > 0, + `no cluster found for ${newAccountCluster.loginInfo.baseUrl} ${newAccountCluster.loginInfo.username}` + ); const applications: IV2ApplicationInfo[] = await accountClusterService.getV2Application(); logger.info( @@ -217,10 +219,6 @@ export default class AccountClusterService { }); }; - static virtualClusterProc: { - [key: string]: { proc: ChildProcessWithoutNullStreams; kubeconfig: string }; - } = {}; - static async vClusterProcess(sa: IServiceAccountInfo) { const { virtualCluster, kubeconfigType } = sa; if ( @@ -234,7 +232,7 @@ export default class AccountClusterService { serviceAddress, } = virtualCluster; - let oldProc = AccountClusterService.virtualClusterProc[serviceAddress]; + let oldProc = virtualClusterProcMap[serviceAddress]; if (oldProc?.proc.killed === false) { return oldProc.kubeconfig; } @@ -254,7 +252,7 @@ export default class AccountClusterService { host.getContext().subscriptions.push({ dispose: () => { - logger.debug("dispose", kubeConfigPath); + loggerDebug.debug("dispose", kubeConfigPath); proc.kill(); kubeconfigCommand(kubeConfigPath, "remove"); @@ -262,7 +260,7 @@ export default class AccountClusterService { return fs.unlink(kubeConfigPath); }, }); - AccountClusterService.virtualClusterProc[serviceAddress] = { + virtualClusterProcMap[serviceAddress] = { proc, kubeconfig, }; @@ -458,7 +456,7 @@ export default class AccountClusterService { } catch (e) { logger.error("getServiceAccount", e); - throw new Error( + throw Error( `failed to get cluster for ${this.loginInfo.baseUrl}@${this.loginInfo.username}` ); } @@ -489,7 +487,7 @@ export default class AccountClusterService { async checkServerVersion(): Promise { const res = await this.getVersion(); - const log = `checkVersion serverVersion:${res.data?.version} packageVerison:${packageJson.nhctl.serverVersion}`; + const log = `checkVersion serverVersion:${res.data?.version} packageVersion:${packageJson.nhctl.serverVersion}`; logger.info(log); if (res.data?.version) { diff --git a/src/main/ctl/nhctl/cli.ts b/src/main/ctl/nhctl/cli.ts index a2a87d6d..e801cb5f 100644 --- a/src/main/ctl/nhctl/cli.ts +++ b/src/main/ctl/nhctl/cli.ts @@ -21,7 +21,7 @@ import * as packageJson from "../../../../package.json"; import { NH_BIN } from "../../constants"; import services from "../../common/DataCenter/services"; import { SvcProfile, NodeInfo } from "../../nodes/types/nodeType"; -import logger from "../../utils/logger"; +import logger, { loggerDebug } from "../../utils/logger"; import { IDevSpaceInfo } from "../../domain"; import { Resource, ResourceStatus } from "../../nodes/types/resourceType"; import { downloadNhctl, lock, unlock } from "../../utils/download"; @@ -1533,7 +1533,7 @@ export async function kubeconfigCommand( .toJson() .exec(); - logger.debug(`kubeconfig ${command}:${kubeConfigPath}`); + loggerDebug.debug(`kubeconfig ${command}:${kubeConfigPath}`); return result; } @@ -1653,7 +1653,7 @@ export async function kubeConfigRender(param: { const str = chuck.toString(); if (str.endsWith(END_Symbol)) { - logger.debug(command, Date.now() - time); + loggerDebug.debug(command, Date.now() - time); const kubeconfig = str.substring(0, str.lastIndexOf(END_Symbol)); res({ kubeconfig, proc }); diff --git a/src/main/debug/provider/goDebugProvider.ts b/src/main/debug/provider/goDebugProvider.ts index f6db284d..597e815e 100644 --- a/src/main/debug/provider/goDebugProvider.ts +++ b/src/main/debug/provider/goDebugProvider.ts @@ -7,7 +7,7 @@ import { commands, DebugConfiguration } from "vscode"; import { v4 } from "uuid"; import { delay } from "lodash"; -import logger from "../../utils/logger"; +import logger, { loggerDebug } from "../../utils/logger"; import { getPromiseWithAbort } from "../../utils"; import host from "../../host"; import { IDebugProvider } from "./IDebugProvider"; @@ -215,6 +215,6 @@ export class GoDebugProvider extends IDebugProvider { assert(result); - logger.debug("dlv GetVersion", result); + loggerDebug.debug("dlv GetVersion", result); } } diff --git a/src/main/debug/provider/java/javaDebugProvider.ts b/src/main/debug/provider/java/javaDebugProvider.ts index b7988cd6..1d363297 100644 --- a/src/main/debug/provider/java/javaDebugProvider.ts +++ b/src/main/debug/provider/java/javaDebugProvider.ts @@ -3,7 +3,7 @@ import * as assert from "assert"; import * as AsyncRetry from "async-retry"; import { IDebugProvider } from "../IDebugProvider"; -import logger from "../../../utils/logger"; +import { loggerDebug } from "../../../utils/logger"; import { JDWP } from "./jdwp"; export class JavaDebugProvider extends IDebugProvider { @@ -68,7 +68,7 @@ export class JavaDebugProvider extends IDebugProvider { const result = await jdwp.getVersion(); assert(result); - logger.debug("jdwp version", result); + loggerDebug.debug("jdwp version", result); await jdwp.destroy(); } diff --git a/src/main/debug/provider/pythonDebugProvider.ts b/src/main/debug/provider/pythonDebugProvider.ts index 5f48ff52..2fb03b40 100644 --- a/src/main/debug/provider/pythonDebugProvider.ts +++ b/src/main/debug/provider/pythonDebugProvider.ts @@ -2,7 +2,7 @@ import { DebugConfiguration } from "vscode"; import * as assert from "assert"; import { IDebugProvider } from "./IDebugProvider"; -import logger from "../../utils/logger"; +import logger, { loggerDebug } from "../../utils/logger"; import { SocketDebugClient } from "../SocketDebugClient"; export class PythonDebugProvider extends IDebugProvider { @@ -43,7 +43,7 @@ export class PythonDebugProvider extends IDebugProvider { assert(result.success); - logger.debug("debugpy debugpySystemInfo", result); + loggerDebug.debug("debugpy debugpySystemInfo", result); debugClient.destroy(); } diff --git a/src/main/extension.ts b/src/main/extension.ts index 0ebaac4b..e690a444 100644 --- a/src/main/extension.ts +++ b/src/main/extension.ts @@ -40,7 +40,7 @@ import { BaseNocalhostNode, DeploymentStatus } from "./nodes/types/nodeType"; import NocalhostWebviewPanel from "./webview/NocalhostWebviewPanel"; import TextDocumentContentProvider from "./textDocumentContentProvider"; import { checkVersion } from "./ctl/nhctl"; -import logger from "./utils/logger"; +import logger, { loggerDebug } from "./utils/logger"; import * as fileUtil from "./utils/fileUtil"; import { KubernetesResourceFolder } from "./nodes/abstract/KubernetesResourceFolder"; // import { registerYamlSchemaSupport } from "./yaml/yamlSchema"; @@ -161,7 +161,7 @@ function bindEvent() { return; } - logger.debug("refreshTree", value); + loggerDebug.debug("refreshTree", value); state.startAutoRefresh(true); }); diff --git a/src/main/host.ts b/src/main/host.ts index 40fac0c0..0b97f4af 100644 --- a/src/main/host.ts +++ b/src/main/host.ts @@ -5,8 +5,6 @@ import { Progress, QuickPickOptions, } from "vscode"; -import { execSync } from "child_process"; -import logger from "./utils/logger"; export class Host implements vscode.Disposable { private outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel( @@ -17,7 +15,7 @@ export class Host implements vscode.Disposable { 100 ); - private devspaceDisposesMap = new Map< + private devSpaceDisposesMap = new Map< string, Map< string, @@ -89,7 +87,7 @@ export class Host implements vscode.Disposable { } public disposeApp(devspaceName: string, id: string) { - const appMap = this.devspaceDisposesMap.get(devspaceName); + const appMap = this.devSpaceDisposesMap.get(devspaceName); if (!appMap) { return; } @@ -107,7 +105,7 @@ export class Host implements vscode.Disposable { } public disposeDevspace(devspaceName: string) { - const appMap = this.devspaceDisposesMap.get(devspaceName); + const appMap = this.devSpaceDisposesMap.get(devspaceName); if (!appMap) { return; } @@ -117,11 +115,11 @@ export class Host implements vscode.Disposable { }); appMap.clear(); - this.devspaceDisposesMap.delete(devspaceName); + this.devSpaceDisposesMap.delete(devspaceName); } public disposeWorkload(devspaceName: string, appId: string, id: string) { - const appMap = this.devspaceDisposesMap.get(devspaceName); + const appMap = this.devSpaceDisposesMap.get(devspaceName); if (!appMap) { return; } @@ -147,10 +145,10 @@ export class Host implements vscode.Disposable { id: string, obj: { dispose: () => any } ) { - let appMap = this.devspaceDisposesMap.get(devspaceName); + let appMap = this.devSpaceDisposesMap.get(devspaceName); if (!appMap) { appMap = new Map(); - this.devspaceDisposesMap.set(devspaceName, appMap); + this.devSpaceDisposesMap.set(devspaceName, appMap); } let workloadMap = appMap.get(appId); if (!workloadMap) { @@ -317,10 +315,10 @@ export class Host implements vscode.Disposable { this.statusBar.dispose(); this.outputChannel.dispose(); - this.devspaceDisposesMap.forEach((m, key) => { + this.devSpaceDisposesMap.forEach((m, key) => { this.disposeDevspace(key); }); - this.devspaceDisposesMap = new Map(); + this.devSpaceDisposesMap = new Map(); } getCurrentRootPath() { diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index e7b3f7d2..5a7486f7 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -157,8 +157,6 @@ export class KubeConfigNode extends NocalhostFolderNode { state.setData(this.getNodeStateId(), devSpaces); - this.cleanDiffDevSpace(devSpaces); - return devSpaces; } diff --git a/src/main/state.ts b/src/main/state.ts index 5c7093e5..bdb29441 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import { isEqual } from "lodash"; import { isExistCluster } from "./clusters/utils"; import { BaseNocalhostNode } from "./nodes/types/nodeType"; -import logger from "./utils/logger"; +import logger, { loggerDebug } from "./utils/logger"; import { asyncLimit } from "./utils"; import { GLOBAL_TIMEOUT } from "./constants"; @@ -262,7 +262,7 @@ class State { for (let key of this.stateMap.keys()) { if (key.startsWith(stateId)) { - logger.debug("stateMap", key); + loggerDebug.debug("stateMap", key); this.stateMap.delete(key); } } @@ -273,7 +273,7 @@ class State { for (let key of this.refreshFolderMap.keys()) { if (key.startsWith(stateId)) { - logger.debug("cleanAutoRefresh", key); + loggerDebug.debug("cleanAutoRefresh", key); this.refreshFolderMap.delete(key); } } diff --git a/src/main/utils/logger.ts b/src/main/utils/logger.ts index da74ef5b..2915c538 100644 --- a/src/main/utils/logger.ts +++ b/src/main/utils/logger.ts @@ -3,25 +3,34 @@ import * as path from "path"; import { PLUGIN_CONFIG_DIR } from "../constants"; const loggerPath = path.resolve(PLUGIN_CONFIG_DIR, "vsc_log"); +const debugPath = path.resolve(PLUGIN_CONFIG_DIR, "vsc_log.debug"); + +const defaultAppender: log4js.Appender = { + type: "dateFile", + filename: loggerPath, + maxLogSize: 10485760, + daysToKeep: 3, + pattern: "yyyy-MM-dd", + backups: 3, + compress: false, +}; log4js.configure({ appenders: { - everything: { - type: "dateFile", - filename: loggerPath, - maxLogSize: 10485760, - daysToKeep: 3, - pattern: "yyyy-MM-dd", - backups: 3, - compress: false, - }, + default: defaultAppender, + debug: { ...defaultAppender, filename: debugPath }, }, categories: { - default: { appenders: ["everything"], level: "debug" }, + default: { appenders: ["default"], level: "info" }, + debug: { appenders: ["debug"], level: "debug" }, }, }); const logger = log4js.getLogger(); -logger.level = "debug"; + +const loggerDebug = log4js.getLogger("debug"); +loggerDebug.level = "debug"; export default logger; + +export { loggerDebug }; From 12089cae1cd51ba1c60c5a5406a142f9bae47502 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 27 Dec 2021 18:04:24 +0800 Subject: [PATCH 12/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 2 -- src/main/clusters/LocalCuster.ts | 2 -- src/main/domain/IRootNode.ts | 1 - 3 files changed, 5 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index b9ea5b92..ab6c9953 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -46,7 +46,6 @@ export function buildRootNodeForAccountCluster( ): IRootNode { return { applications: [], - devSpaces: [], userInfo: accountCluster.userInfo, clusterSource: ClusterSource.server, accountClusterService: new AccountClusterService(accountCluster.loginInfo), @@ -208,7 +207,6 @@ export default class AccountClusterService { return item.value; } return { - devSpaces: [], userInfo: newAccountCluster.userInfo, clusterSource: ClusterSource.server, accountClusterService, diff --git a/src/main/clusters/LocalCuster.ts b/src/main/clusters/LocalCuster.ts index 53f8a0cb..e8266bd3 100644 --- a/src/main/clusters/LocalCuster.ts +++ b/src/main/clusters/LocalCuster.ts @@ -28,7 +28,6 @@ export function buildRootNodeForLocalCluster( return { id: localCluster.id, - devSpaces: [], clusterName: localCluster.clusterNickName, createTime, clusterSource: ClusterSource.local, @@ -79,7 +78,6 @@ export default class LocalCluster { }); const obj: IRootNode = { id: newLocalCluster.id, - devSpaces: [], clusterName: newLocalCluster.clusterNickName, createTime, clusterSource: ClusterSource.local, diff --git a/src/main/domain/IRootNode.ts b/src/main/domain/IRootNode.ts index cceb6ea7..a00afdde 100644 --- a/src/main/domain/IRootNode.ts +++ b/src/main/domain/IRootNode.ts @@ -10,7 +10,6 @@ import { IServiceAccountInfo } from "."; export interface IRootNode { serviceAccount?: IServiceAccountInfo; - devSpaces: IDevSpaceInfo[]; applications: IV2ApplicationInfo[]; clusterSource?: ClusterSource; accountClusterService?: AccountClusterService; From 40a4627e44ac7e628932482d6f93221fa65e614c Mon Sep 17 00:00:00 2001 From: zhangjian Date: Tue, 28 Dec 2021 12:44:14 +0800 Subject: [PATCH 13/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 60 +++++++++++++------------- src/main/commands/SignOutCommand.ts | 67 ++++++++++++++++++++++------- src/main/domain/IRootNode.ts | 6 +-- src/main/nodes/KubeConfigNode.ts | 17 +++----- src/main/nodes/NocalhostRootNode.ts | 11 +++-- 5 files changed, 95 insertions(+), 66 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index ab6c9953..8c680fd7 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -47,15 +47,16 @@ export function buildRootNodeForAccountCluster( return { applications: [], userInfo: accountCluster.userInfo, + loginInfo: accountCluster.loginInfo, clusterSource: ClusterSource.server, - accountClusterService: new AccountClusterService(accountCluster.loginInfo), + // accountClusterService: new AccountClusterService(accountCluster.loginInfo), id: accountCluster.id, createTime: accountCluster.createTime, kubeConfigPath: null, state, }; } -const virtualClusterProcMap: { +export const virtualClusterProcMap: { [key: string]: { proc: ChildProcessWithoutNullStreams; kubeconfig: string }; } = {}; export default class AccountClusterService { @@ -66,7 +67,7 @@ export default class AccountClusterService { lastServiceAccounts: IServiceAccountInfo[]; isRefreshing: boolean; - constructor(public loginInfo: LoginInfo) { + constructor(private loginInfo: LoginInfo) { var parsed = url.parse(loginInfo.baseUrl); if (!parsed.protocol) { @@ -133,17 +134,17 @@ export default class AccountClusterService { } static getServerClusterRootNodes = async ( - newAccountCluster: AccountClusterNode + accountCluster: AccountClusterNode ): Promise => { - if (!newAccountCluster) { + if (!accountCluster) { return []; } const accountClusterService = new AccountClusterService( - newAccountCluster.loginInfo + accountCluster.loginInfo ); - accountClusterService.accountClusterNode = newAccountCluster; - accountClusterService.jwt = newAccountCluster.jwt; - accountClusterService.refreshToken = newAccountCluster.refreshToken; + accountClusterService.accountClusterNode = accountCluster; + accountClusterService.jwt = accountCluster.jwt; + accountClusterService.refreshToken = accountCluster.refreshToken; let serviceAccounts = await accountClusterService.getServiceAccount(); logger.info( @@ -154,7 +155,7 @@ export default class AccountClusterService { assert( Array.isArray(serviceAccounts) && serviceAccounts.length > 0, - `no cluster found for ${newAccountCluster.loginInfo.baseUrl} ${newAccountCluster.loginInfo.username}` + `no cluster found for ${accountCluster.loginInfo.baseUrl} ${accountCluster.loginInfo.username}` ); const applications: IV2ApplicationInfo[] = await accountClusterService.getV2Application(); @@ -167,7 +168,7 @@ export default class AccountClusterService { const kubeConfigArr: Array = []; const serviceNodes = serviceAccounts.map(async (serviceAccount) => { - const kubeconfig = await AccountClusterService.vClusterProcess( + const kubeconfig = await AccountClusterService.startVClusterProcess( serviceAccount ); @@ -187,18 +188,19 @@ export default class AccountClusterService { serviceAccount, devSpaces: [], applications, - userInfo: newAccountCluster.userInfo, + loginInfo: accountCluster.loginInfo, + userInfo: accountCluster.userInfo, clusterSource: ClusterSource.server, accountClusterService, - id: newAccountCluster.id, - createTime: newAccountCluster.createTime, + id: accountCluster.id, + createTime: accountCluster.createTime, kubeConfigPath, state, - }; + } as IRootNode; }); await AccountClusterService.cleanDiffKubeConfig( - newAccountCluster, + accountCluster, kubeConfigArr ); @@ -207,17 +209,17 @@ export default class AccountClusterService { return item.value; } return { - userInfo: newAccountCluster.userInfo, + userInfo: accountCluster.userInfo, clusterSource: ClusterSource.server, - accountClusterService, - id: newAccountCluster.id, - createTime: newAccountCluster.createTime, + loginInfo: accountCluster.loginInfo, + id: accountCluster.id, + createTime: accountCluster.createTime, kubeConfigPath: null, } as IRootNode; }); }; - static async vClusterProcess(sa: IServiceAccountInfo) { + static async startVClusterProcess(sa: IServiceAccountInfo) { const { virtualCluster, kubeconfigType } = sa; if ( kubeconfigType === "vcluster" && @@ -230,9 +232,9 @@ export default class AccountClusterService { serviceAddress, } = virtualCluster; - let oldProc = virtualClusterProcMap[serviceAddress]; - if (oldProc?.proc.killed === false) { - return oldProc.kubeconfig; + let oldInfo = virtualClusterProcMap[serviceAddress]; + if (oldInfo?.proc.killed === false) { + return oldInfo.kubeconfig; } const { proc, kubeconfig } = await kubeConfigRender({ @@ -282,13 +284,11 @@ export default class AccountClusterService { return; } - await Promise.allSettled( - diff.map((id) => { - const file = path.resolve(KUBE_CONFIG_DIR, id); + diff.map((id) => { + const file = path.resolve(KUBE_CONFIG_DIR, id); - kubeconfigCommand(file, "remove"); - }) - ); + kubeconfigCommand(file, "remove"); + }); } host.setGlobalState(KEY, configs); diff --git a/src/main/commands/SignOutCommand.ts b/src/main/commands/SignOutCommand.ts index bb946764..f8c5b645 100644 --- a/src/main/commands/SignOutCommand.ts +++ b/src/main/commands/SignOutCommand.ts @@ -1,5 +1,6 @@ import * as vscode from "vscode"; import * as path from "path"; +import { promises as fs } from "fs"; import ICommand from "./ICommand"; import { SIGN_OUT } from "./constants"; @@ -8,13 +9,15 @@ import registerCommand from "./register"; import state from "../state"; import { KUBE_CONFIG_DIR, NOCALHOST, SERVER_CLUSTER_LIST } from "../constants"; import host from "../host"; -import { IUserInfo } from "../domain"; +import { IRootNode, IUserInfo } from "../domain"; import { KubeConfigNode } from "../nodes/KubeConfigNode"; import Bookinfo from "../common/bookinfo"; import { kubeconfigCommand } from "../ctl/nhctl"; import { LoginInfo } from "../clusters/interface"; import { NocalhostRootNode } from "../nodes/NocalhostRootNode"; import messageBus from "../utils/messageBus"; +import { virtualClusterProcMap } from "../clusters/AccountCluster"; +import { getStringHash } from "../utils/common"; export default class SignOutCommand implements ICommand { command: string = SIGN_OUT; @@ -49,9 +52,10 @@ export default class SignOutCommand implements ICommand { Bookinfo.cleanCheck(node); const rootNode = state.getNode(NOCALHOST) as NocalhostRootNode; - await rootNode.deleteCluster(node.accountClusterService.loginInfo); - this.cleanKubeConfig(node.accountClusterService.loginInfo); + this.dispose(node); + + rootNode.deleteCluster(node.rootNode.loginInfo); messageBus.emit("refreshTree", {}); } catch (error) { @@ -61,24 +65,57 @@ export default class SignOutCommand implements ICommand { } } + dispose(node: KubeConfigNode) { + this.killVClusterProcess(node); + this.cleanKubeConfig(node.rootNode.loginInfo); + } + + async killVClusterProcess(node: KubeConfigNode) { + const rootNode = state.getData>("Nocalhost"); + + const { username, baseUrl } = node.rootNode.loginInfo; + + rootNode.forEach((root) => { + if ( + root.loginInfo.username !== username || + root.loginInfo.baseUrl !== baseUrl + ) { + return; + } + const { virtualCluster, kubeconfigType } = root.serviceAccount; + if ( + kubeconfigType === "vcluster" && + virtualCluster.serviceType === "ClusterIP" + ) { + const { serviceAddress } = virtualCluster; + + let info = virtualClusterProcMap[serviceAddress]; + + if (info?.proc.killed === false) { + info.proc.kill(); + + fs.unlink(root.kubeConfigPath); + + delete virtualClusterProcMap[serviceAddress]; + } + } + }); + } + + getFilePath(id: string) { + return path.resolve(KUBE_CONFIG_DIR, id); + } + cleanKubeConfig(loginInfo: LoginInfo) { const { baseUrl, username } = loginInfo; const KEY = `USER_LINK:${baseUrl}@${username}`; - const prevData = host.getGlobalState(KEY); + const prevData = host.getGlobalState>(KEY); if (prevData) { - Promise.allSettled( - (prevData as Array).map((id) => { - return new Promise(async (res, rej) => { - const file = path.resolve(KUBE_CONFIG_DIR, id); - - await kubeconfigCommand(file, "remove"); - - res(); - }); - }) - ); + prevData.map((id) => { + kubeconfigCommand(this.getFilePath(id), "remove"); + }); } host.removeGlobalState(KEY); diff --git a/src/main/domain/IRootNode.ts b/src/main/domain/IRootNode.ts index a00afdde..27345da8 100644 --- a/src/main/domain/IRootNode.ts +++ b/src/main/domain/IRootNode.ts @@ -1,18 +1,18 @@ import { IUserInfo } from "./IUserInfo"; -import { IDevSpaceInfo } from "./IDevSpaceInfo"; import { IV2ApplicationInfo } from "./IV2ApplicationInfo"; import { ClusterSource } from "../common/define"; -import AccountClusterService from "../clusters/AccountCluster"; import { ClustersState } from "../clusters"; import { IServiceAccountInfo } from "."; +import { LoginInfo } from "../clusters/interface"; export interface IRootNode { serviceAccount?: IServiceAccountInfo; applications: IV2ApplicationInfo[]; clusterSource?: ClusterSource; - accountClusterService?: AccountClusterService; + loginInfo?: LoginInfo; + // accountClusterService?: AccountClusterService; id?: string; createTime?: number; clusterName?: string; diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index 5a7486f7..b50e32d4 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -35,24 +35,17 @@ export class KubeConfigNode extends NocalhostFolderNode { }[] = []; public id: string; public kubeConfigPath: string; - public accountClusterService: AccountClusterService; private state: ClustersState; constructor( id: string, parent: NocalhostRootNode, label: string, - private rootNode: IRootNode + public rootNode: IRootNode ) { super(); - const { - applications, - clusterSource, - kubeConfigPath, - userInfo, - accountClusterService, - } = rootNode; + const { applications, clusterSource, kubeConfigPath, userInfo } = rootNode; this.id = id; this.parent = parent; @@ -63,7 +56,6 @@ export class KubeConfigNode extends NocalhostFolderNode { this.installedApps = []; this.kubeConfigPath = kubeConfigPath; this.userInfo = userInfo; - this.accountClusterService = accountClusterService; this.state = rootNode.state; state.setNode(this.getNodeStateId(), this); @@ -141,7 +133,6 @@ export class KubeConfigNode extends NocalhostFolderNode { spaceName: ns.spacename, namespace: ns.namespace, kubeconfig: sa.kubeconfig, - accountClusterService: this.accountClusterService, clusterId: sa.clusterId, storageClass: sa.storageClass, spaceOwnType: ns.spaceOwnType, @@ -155,6 +146,8 @@ export class KubeConfigNode extends NocalhostFolderNode { } } + this.cleanDiffDevSpace(devSpaces); + state.setData(this.getNodeStateId(), devSpaces); return devSpaces; @@ -228,7 +221,7 @@ export class KubeConfigNode extends NocalhostFolderNode { }`; if (this.clusterSource === ClusterSource.server) { - const { username, baseUrl } = this.accountClusterService.loginInfo; + const { username, baseUrl } = this.rootNode.loginInfo; treeItem.tooltip = `${this.label} [${username} on ${baseUrl}]`; } diff --git a/src/main/nodes/NocalhostRootNode.ts b/src/main/nodes/NocalhostRootNode.ts index a9dfb7b7..11c9cf30 100644 --- a/src/main/nodes/NocalhostRootNode.ts +++ b/src/main/nodes/NocalhostRootNode.ts @@ -175,7 +175,7 @@ export class NocalhostRootNode implements BaseNocalhostNode { public async addCluster(node: AccountClusterNode | LocalClusterNode) { let addResources: IRootNode[]; - let resources = state.getData(this.getNodeStateId()) as IRootNode[]; + let resources = state.getData>(this.getNodeStateId()); if (node instanceof LocalClusterNode) { if ( @@ -221,8 +221,8 @@ export class NocalhostRootNode implements BaseNocalhostNode { state.setData(this.getNodeStateId(), resources, false); } - public async deleteCluster(info: LoginInfo | string) { - let resources = state.getData(this.getNodeStateId()) as IRootNode[]; + public deleteCluster(info: LoginInfo | string) { + let resources = state.getData>(this.getNodeStateId()); if (resources) { if (typeof info === "string") { @@ -234,13 +234,12 @@ export class NocalhostRootNode implements BaseNocalhostNode { ) ); } else { - resources = resources.filter((item: any) => { + resources = resources.filter((item) => { if (item.clusterSource === ClusterSource.local) { return true; } - const node = item as KubeConfigNode; - const { username, baseUrl } = node.accountClusterService?.loginInfo; + const { username, baseUrl } = item.loginInfo; return !(username === info.username && baseUrl === info.baseUrl); }); From 1218ec6b95f38775fb227651b4e6c3b4dfb7b187 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Tue, 28 Dec 2021 14:58:35 +0800 Subject: [PATCH 14/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 1 - src/main/commands/ResetDevspaceCommand.ts | 6 +++++- src/main/commands/ShowApplicationsCommand.ts | 6 ++---- src/main/commands/UpgradeCommand.ts | 6 ++---- src/main/nodes/KubeConfigNode.ts | 7 +++++++ src/main/nodes/types/nodeType.ts | 4 ++-- src/main/nodes/workloads/KubernetesResourceDevMode.ts | 10 ++++++++-- src/main/state.ts | 9 ++++++++- 8 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 8c680fd7..066610c1 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -49,7 +49,6 @@ export function buildRootNodeForAccountCluster( userInfo: accountCluster.userInfo, loginInfo: accountCluster.loginInfo, clusterSource: ClusterSource.server, - // accountClusterService: new AccountClusterService(accountCluster.loginInfo), id: accountCluster.id, createTime: accountCluster.createTime, kubeConfigPath: null, diff --git a/src/main/commands/ResetDevspaceCommand.ts b/src/main/commands/ResetDevspaceCommand.ts index 08313957..fa7d10a5 100644 --- a/src/main/commands/ResetDevspaceCommand.ts +++ b/src/main/commands/ResetDevspaceCommand.ts @@ -10,6 +10,8 @@ import { DevSpaceNode } from "../nodes/DevSpaceNode"; import { NocalhostRootNode } from "../nodes/NocalhostRootNode"; import Bookinfo from "../common/bookinfo"; import messageBus from "../utils/messageBus"; +import { KubeConfigNode } from "../nodes/KubeConfigNode"; +import AccountClusterService from "../clusters/AccountCluster"; export default class ResetDevspaceCommand implements ICommand { command: string = RESET_DEVSPACE; @@ -45,7 +47,9 @@ export default class ResetDevspaceCommand implements ICommand { node.info.namespace, node.info.spaceName ).finally(async () => { - await node.parent.accountClusterService.resetDevSpace(node.info.id); + await (node.parent as KubeConfigNode).accountClusterService?.resetDevSpace( + node.info.id + ); const nocalhostRootNode = node.parent.parent as NocalhostRootNode; diff --git a/src/main/commands/ShowApplicationsCommand.ts b/src/main/commands/ShowApplicationsCommand.ts index 0a30da71..d48bac22 100644 --- a/src/main/commands/ShowApplicationsCommand.ts +++ b/src/main/commands/ShowApplicationsCommand.ts @@ -8,6 +8,7 @@ import { DevSpaceNode } from "../nodes/DevSpaceNode"; import AccountClusterService from "../clusters/AccountCluster"; import { ClusterSource } from "../common/define"; import { NhctlCommand } from "../ctl/nhctl"; +import { KubeConfigNode } from "../nodes/KubeConfigNode"; export default class ShowApplicationsCommand implements ICommand { command: string = SHOW_APP; @@ -30,10 +31,7 @@ export default class ShowApplicationsCommand implements ICommand { }).exec(); if (node.clusterSource === ClusterSource.server) { - const accountClusterService: AccountClusterService = - node.parent.accountClusterService; - - accountClusterService.checkServerVersion(); + await (node.parent as KubeConfigNode).accountClusterService?.checkServerVersion(); } const apps = node.getUninstallApps(); diff --git a/src/main/commands/UpgradeCommand.ts b/src/main/commands/UpgradeCommand.ts index 058b1a2c..a76d2f16 100644 --- a/src/main/commands/UpgradeCommand.ts +++ b/src/main/commands/UpgradeCommand.ts @@ -13,6 +13,7 @@ import { AppNode } from "../nodes/AppNode"; import AccountClusterService from "../clusters/AccountCluster"; import { DevSpaceNode } from "../nodes/DevSpaceNode"; import { ClusterSource } from "../common/define"; +import { KubeConfigNode } from "../nodes/KubeConfigNode"; export default class UpgradeCommand implements ICommand { command: string = UPGRADE_APP; @@ -28,10 +29,7 @@ export default class UpgradeCommand implements ICommand { const devSpaceNode = appNode.parent as DevSpaceNode; if (devSpaceNode.clusterSource === ClusterSource.server) { - const accountClusterService: AccountClusterService = - devSpaceNode.parent.accountClusterService; - - accountClusterService.checkServerVersion(); + await (devSpaceNode.parent as KubeConfigNode).accountClusterService?.checkServerVersion(); } let refOrVersion: string | undefined; diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index b50e32d4..3a8fc1ed 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -60,6 +60,13 @@ export class KubeConfigNode extends NocalhostFolderNode { state.setNode(this.getNodeStateId(), this); } + get accountClusterService() { + if (this.clusterSource === ClusterSource.local) { + return null; + } + + return new AccountClusterService(this.rootNode.loginInfo); + } async updateData() { if (this.state.code !== 200) { return []; diff --git a/src/main/nodes/types/nodeType.ts b/src/main/nodes/types/nodeType.ts index 513d5b59..6c082ba9 100644 --- a/src/main/nodes/types/nodeType.ts +++ b/src/main/nodes/types/nodeType.ts @@ -61,12 +61,12 @@ export interface SvcProfile { export interface BaseNocalhostNode { label: string; type: string; - accountClusterService?: AccountClusterService; hasInit?: boolean; parent: BaseNocalhostNode | undefined | null; updateData?: ( init?: boolean, - token?: vscode.CancellationToken + token?: vscode.CancellationToken, + isCancel?: () => boolean ) => Promise; getNodeStateId(): string; getChildren( diff --git a/src/main/nodes/workloads/KubernetesResourceDevMode.ts b/src/main/nodes/workloads/KubernetesResourceDevMode.ts index f3ddea4d..52792186 100644 --- a/src/main/nodes/workloads/KubernetesResourceDevMode.ts +++ b/src/main/nodes/workloads/KubernetesResourceDevMode.ts @@ -52,7 +52,10 @@ export const kubernetesResourceDevMode = (resourceNode: any) => ( }); return this.sortResource(result); }; - prototype.updateData = async function (isInit?: boolean): Promise { + prototype.updateData = async function ( + isInit?: boolean, + token?: vscode.CancellationToken + ): Promise { const appNode = this.getAppNode(); // description const list: INhCtlGetResult[] = @@ -74,7 +77,10 @@ export const kubernetesResourceDevMode = (resourceNode: any) => ( resource: list, appConfig, }; - state.setData(this.getNodeStateId(), obj, isInit); + + if (!token?.isCancellationRequested) { + state.setData(this.getNodeStateId(), obj, isInit); + } return obj; }; diff --git a/src/main/state.ts b/src/main/state.ts index bdb29441..b2f98a27 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -104,7 +104,7 @@ class State { const node = this.getNode(id) as BaseNocalhostNode; if (!token.isCancellationRequested && node && expanded) { - return node.updateData(); + return node.updateData(null, token); } return Promise.resolve(); @@ -267,6 +267,13 @@ class State { } } + for (let key of this.dataMap.keys()) { + if (key.startsWith(stateId)) { + loggerDebug.debug("dataMap", key); + this.dataMap.delete(key); + } + } + if (!deleteRefresh) { return; } From 6b03e37dd8d1a34cdc0d377a0e787bede21c9c5e Mon Sep 17 00:00:00 2001 From: zhangjian Date: Tue, 28 Dec 2021 15:02:12 +0800 Subject: [PATCH 15/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 066610c1..503d7134 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -550,6 +550,6 @@ export default class AccountClusterService { const { data } = response.data; return data; } - throw new Error("Fail to fetch user infomation."); + throw Error("Fail to fetch user information."); } } From 3f9dc3df43e60066cf734e213409b10355e71d55 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 6 Jan 2022 11:45:00 +0800 Subject: [PATCH 16/29] WIP Signed-off-by: zhangjian --- package.json | 2 +- src/main/commands/ResetDevspaceCommand.ts | 57 +++++++++++------------ src/main/nodes/DevSpaceNode.ts | 13 +++++- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 25dade14..804f0336 100644 --- a/package.json +++ b/package.json @@ -232,7 +232,7 @@ }, { "command": "Nocalhost.resetDevspace", - "when": "viewItem =~ /^devspace-server/i", + "when": "viewItem =~ /^devspace-server(? { - await (node.parent as KubeConfigNode).accountClusterService?.resetDevSpace( - node.info.id - ); - const nocalhostRootNode = node.parent.parent as NocalhostRootNode; + await this.reset(host, node) + .then(() => { + messageBus.emit("refreshTree", {}); + }) + .finally(async () => { + state.deleteAppState(node.getNodeStateId(), "resetting"); + const parent = node.parent; - await nocalhostRootNode.updateData(); + await node.parent.updateData(); - vscode.commands.executeCommand("Nocalhost.refresh", nocalhostRootNode); + vscode.commands.executeCommand("Nocalhost.refresh", parent); - state.delete(node.info.spaceName); - }); - - messageBus.emit("refreshTree", {}); + state.delete(node.info.spaceName); + }); } - private async reset( - host: Host, - kubeconfigPath: string, - namespace: string, - devspaceName: string - ) { - host.log(`Reseting devspace: ${devspaceName}`, true); - await nhctl.resetApp(kubeconfigPath, namespace, devspaceName); - host.removeGlobalState(devspaceName); - host.log(`Devspace ${devspaceName} reset`, true); - host.showInformationMessage(`Devspace ${devspaceName} reset`); + private async reset(host: Host, node: DevSpaceNode) { + const { + getKubeConfigPath, + info: { namespace, spaceName }, + } = node; + + host.log(`Resetting devspace: ${spaceName}`, true); + + await nhctl.resetApp(getKubeConfigPath.call(node), namespace, spaceName); + + host.removeGlobalState(spaceName); + host.log(`Devspace ${spaceName} reset`, true); + host.showInformationMessage(`Devspace ${spaceName} reset`); + + await (node.parent as KubeConfigNode).accountClusterService?.resetDevSpace( + node.info.id + ); } } diff --git a/src/main/nodes/DevSpaceNode.ts b/src/main/nodes/DevSpaceNode.ts index 2b725c51..42db7b8e 100644 --- a/src/main/nodes/DevSpaceNode.ts +++ b/src/main/nodes/DevSpaceNode.ts @@ -2,7 +2,7 @@ import { difference } from "lodash"; import * as vscode from "vscode"; import { ClusterSource } from "../common/define"; import * as nhctl from "../ctl/nhctl"; -import { IDevSpaceInfo, IV2ApplicationInfo } from "../domain"; +import { IDevSpaceInfo, IRootNode, IV2ApplicationInfo } from "../domain"; import state from "../state"; import { resolveVSCodeUri } from "../utils/fileUtil"; import { NocalhostFolderNode } from "./abstract/NocalhostFolderNode"; @@ -276,6 +276,17 @@ export class DevSpaceNode extends NocalhostFolderNode implements RefreshData { }` ); + if ("rootNode" in this.parent) { + const { rootNode } = (this.parent as unknown) as { rootNode: IRootNode }; + + if ( + rootNode.clusterSource === ClusterSource.server && + rootNode.serviceAccount.kubeconfigType === "vcluster" + ) { + treeItem.contextValue += "-vcluster"; + } + } + return Promise.resolve(treeItem); } From e4384f3d0569937c0e9761b0b66caf1ad7ac64d2 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 6 Jan 2022 15:44:53 +0800 Subject: [PATCH 17/29] WIP Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 84 +++++++++++++---------- src/main/commands/ResetDevspaceCommand.ts | 5 +- src/main/commands/SignInCommand.ts | 8 +-- src/main/commands/SignOutCommand.ts | 70 ++++++++++--------- src/main/domain/IRootNode.ts | 10 ++- src/main/nodes/KubeConfigNode.ts | 27 ++++---- src/main/nodes/NocalhostRootNode.ts | 7 +- 7 files changed, 114 insertions(+), 97 deletions(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 503d7134..821ca981 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -46,13 +46,12 @@ export function buildRootNodeForAccountCluster( ): IRootNode { return { applications: [], - userInfo: accountCluster.userInfo, - loginInfo: accountCluster.loginInfo, clusterSource: ClusterSource.server, id: accountCluster.id, createTime: accountCluster.createTime, kubeConfigPath: null, state, + clusterInfo: accountCluster, }; } export const virtualClusterProcMap: { @@ -60,22 +59,30 @@ export const virtualClusterProcMap: { } = {}; export default class AccountClusterService { instance: AxiosInstance; - accountClusterNode: AccountClusterNode; jwt: string; refreshToken: string; lastServiceAccounts: IServiceAccountInfo[]; isRefreshing: boolean; - - constructor(private loginInfo: LoginInfo) { + loginInfo: LoginInfo; + constructor(private accountCluster: AccountClusterNode) { + const { loginInfo, jwt, refreshToken } = accountCluster; var parsed = url.parse(loginInfo.baseUrl); if (!parsed.protocol) { loginInfo.baseUrl = "http://" + loginInfo.baseUrl; } + this.jwt = jwt; + this.refreshToken = refreshToken; this.isRefreshing = true; + this.loginInfo = loginInfo; + + this.initInstance(); + } + + private initInstance() { this.instance = axios.create({ - baseURL: loginInfo.baseUrl, + baseURL: this.loginInfo.baseUrl, timeout: 1000 * 20, }); this.instance.interceptors.request.use((config) => { @@ -138,12 +145,7 @@ export default class AccountClusterService { if (!accountCluster) { return []; } - const accountClusterService = new AccountClusterService( - accountCluster.loginInfo - ); - accountClusterService.accountClusterNode = accountCluster; - accountClusterService.jwt = accountCluster.jwt; - accountClusterService.refreshToken = accountCluster.refreshToken; + const accountClusterService = new AccountClusterService(accountCluster); let serviceAccounts = await accountClusterService.getServiceAccount(); logger.info( @@ -187,35 +189,35 @@ export default class AccountClusterService { serviceAccount, devSpaces: [], applications, - loginInfo: accountCluster.loginInfo, - userInfo: accountCluster.userInfo, clusterSource: ClusterSource.server, - accountClusterService, id: accountCluster.id, createTime: accountCluster.createTime, kubeConfigPath, state, + clusterInfo: accountCluster, } as IRootNode; }); + const rootNodes = await (await Promise.allSettled(serviceNodes)).map( + (item) => { + if (item.status === "fulfilled") { + return item.value; + } + return { + clusterSource: ClusterSource.server, + id: accountCluster.id, + createTime: accountCluster.createTime, + kubeConfigPath: null, + } as IRootNode; + } + ); + await AccountClusterService.cleanDiffKubeConfig( accountCluster, kubeConfigArr ); - return await (await Promise.allSettled(serviceNodes)).map((item) => { - if (item.status === "fulfilled") { - return item.value; - } - return { - userInfo: accountCluster.userInfo, - clusterSource: ClusterSource.server, - loginInfo: accountCluster.loginInfo, - id: accountCluster.id, - createTime: accountCluster.createTime, - kubeConfigPath: null, - } as IRootNode; - }); + return rootNodes; }; static async startVClusterProcess(sa: IServiceAccountInfo) { @@ -276,6 +278,8 @@ export default class AccountClusterService { const prevData = host.getGlobalState>(KEY); + host.setGlobalState(KEY, configs); + if (prevData) { const diff = difference(prevData, configs); @@ -289,8 +293,6 @@ export default class AccountClusterService { kubeconfigCommand(file, "remove"); }); } - - host.setGlobalState(KEY, configs); } static async saveKubeConfig(accountInfo: IServiceAccountInfo) { @@ -306,10 +308,20 @@ export default class AccountClusterService { return { id, kubeConfigPath }; } + static appendClusterByLoginInfo = async ( loginInfo: LoginInfo ): Promise => { - const accountServer = new AccountClusterService(loginInfo); + const accountServer = new AccountClusterService({ + loginInfo, + userInfo: null, + jwt: null, + id: null, + createTime: Date.now(), + refreshToken: null, + state: { code: 200 }, + }); + const newAccountCluster = await accountServer.buildAccountClusterNode(); let globalAccountClusterList = host.getGlobalState< @@ -400,11 +412,11 @@ export default class AccountClusterService { } deleteAccountNode() { - if (this.accountClusterNode) { + if (this.accountCluster) { let globalClusterRootNodes: AccountClusterNode[] = host.getGlobalState(SERVER_CLUSTER_LIST) || []; const index = globalClusterRootNodes.findIndex( - ({ id }) => id === this.accountClusterNode.id + ({ id }) => id === this.accountCluster.id ); if (index !== -1) { globalClusterRootNodes.splice(index, 1); @@ -415,7 +427,7 @@ export default class AccountClusterService { updateLoginInfo() { const newAccountCluster = { - ...this.accountClusterNode, + ...this.accountCluster, jwt: this.jwt, refreshToken: this.refreshToken, }; @@ -460,7 +472,7 @@ export default class AccountClusterService { } async getApplication() { - const { userInfo } = this.accountClusterNode; + const { userInfo } = this.accountCluster; try { const response = await this.instance.get( `/v1/users/${userInfo.id}/applications` @@ -498,7 +510,7 @@ export default class AccountClusterService { } async getV2Application(): Promise { - const { userInfo } = this.accountClusterNode; + const { userInfo } = this.accountCluster; const userId = userInfo.id; if (!userId) { return []; diff --git a/src/main/commands/ResetDevspaceCommand.ts b/src/main/commands/ResetDevspaceCommand.ts index c3bc811b..79778ff9 100644 --- a/src/main/commands/ResetDevspaceCommand.ts +++ b/src/main/commands/ResetDevspaceCommand.ts @@ -45,10 +45,9 @@ export default class ResetDevspaceCommand implements ICommand { messageBus.emit("refreshTree", {}); }) .finally(async () => { - state.deleteAppState(node.getNodeStateId(), "resetting"); - const parent = node.parent; + const parent = node.parent.parent; - await node.parent.updateData(); + await parent.updateData(); vscode.commands.executeCommand("Nocalhost.refresh", parent); diff --git a/src/main/commands/SignInCommand.ts b/src/main/commands/SignInCommand.ts index 450593a3..e5d0337f 100644 --- a/src/main/commands/SignInCommand.ts +++ b/src/main/commands/SignInCommand.ts @@ -8,13 +8,7 @@ import { AccountCluster as AccountClusterService } from "../clusters"; import state from "../state"; import { NocalhostRootNode } from "../nodes/NocalhostRootNode"; import { NOCALHOST } from "../constants"; - -interface LoginInfo { - username: string; - from?: "plugin"; - password: string; - baseUrl: string; -} +import { LoginInfo } from "../clusters/interface"; export default class SignInCommand implements ICommand { command: string = SIGN_IN; diff --git a/src/main/commands/SignOutCommand.ts b/src/main/commands/SignOutCommand.ts index f8c5b645..beccceac 100644 --- a/src/main/commands/SignOutCommand.ts +++ b/src/main/commands/SignOutCommand.ts @@ -16,14 +16,18 @@ import { kubeconfigCommand } from "../ctl/nhctl"; import { LoginInfo } from "../clusters/interface"; import { NocalhostRootNode } from "../nodes/NocalhostRootNode"; import messageBus from "../utils/messageBus"; -import { virtualClusterProcMap } from "../clusters/AccountCluster"; -import { getStringHash } from "../utils/common"; +import { + AccountClusterNode, + virtualClusterProcMap, +} from "../clusters/AccountCluster"; +import { ClusterSource } from "../common/define"; export default class SignOutCommand implements ICommand { command: string = SIGN_OUT; constructor(context: vscode.ExtensionContext) { registerCommand(context, this.command, false, this.execCommand.bind(this)); } + loginInfo: LoginInfo; async execCommand(node: KubeConfigNode) { if (!node) { host.showWarnMessage("Failed to get node configs, please try again."); @@ -53,9 +57,11 @@ export default class SignOutCommand implements ICommand { const rootNode = state.getNode(NOCALHOST) as NocalhostRootNode; - this.dispose(node); + this.loginInfo = node.accountClusterNode.loginInfo; - rootNode.deleteCluster(node.rootNode.loginInfo); + this.dispose(); + + rootNode.deleteCluster(this.loginInfo); messageBus.emit("refreshTree", {}); } catch (error) { @@ -65,49 +71,51 @@ export default class SignOutCommand implements ICommand { } } - dispose(node: KubeConfigNode) { - this.killVClusterProcess(node); - this.cleanKubeConfig(node.rootNode.loginInfo); + dispose() { + this.killVClusterProcess(); + this.cleanKubeConfig(); } - async killVClusterProcess(node: KubeConfigNode) { + async killVClusterProcess() { const rootNode = state.getData>("Nocalhost"); - const { username, baseUrl } = node.rootNode.loginInfo; + const { username, baseUrl } = this.loginInfo; + + rootNode + .filter((node) => node.clusterSource === ClusterSource.server) + .forEach((root) => { + const { loginInfo } = root.clusterInfo as AccountClusterNode; + + if (loginInfo.username !== username || loginInfo.baseUrl !== baseUrl) { + return; + } - rootNode.forEach((root) => { - if ( - root.loginInfo.username !== username || - root.loginInfo.baseUrl !== baseUrl - ) { - return; - } - const { virtualCluster, kubeconfigType } = root.serviceAccount; - if ( - kubeconfigType === "vcluster" && - virtualCluster.serviceType === "ClusterIP" - ) { - const { serviceAddress } = virtualCluster; + const { virtualCluster, kubeconfigType } = root.serviceAccount; + if ( + kubeconfigType === "vcluster" && + virtualCluster.serviceType === "ClusterIP" + ) { + const { serviceAddress } = virtualCluster; - let info = virtualClusterProcMap[serviceAddress]; + let info = virtualClusterProcMap[serviceAddress]; - if (info?.proc.killed === false) { - info.proc.kill(); + if (info?.proc.killed === false) { + info.proc.kill(); - fs.unlink(root.kubeConfigPath); + fs.unlink(root.kubeConfigPath); - delete virtualClusterProcMap[serviceAddress]; + delete virtualClusterProcMap[serviceAddress]; + } } - } - }); + }); } getFilePath(id: string) { return path.resolve(KUBE_CONFIG_DIR, id); } - cleanKubeConfig(loginInfo: LoginInfo) { - const { baseUrl, username } = loginInfo; + cleanKubeConfig() { + const { baseUrl, username } = this.loginInfo; const KEY = `USER_LINK:${baseUrl}@${username}`; const prevData = host.getGlobalState>(KEY); diff --git a/src/main/domain/IRootNode.ts b/src/main/domain/IRootNode.ts index 27345da8..803a6636 100644 --- a/src/main/domain/IRootNode.ts +++ b/src/main/domain/IRootNode.ts @@ -1,22 +1,20 @@ -import { IUserInfo } from "./IUserInfo"; - import { IV2ApplicationInfo } from "./IV2ApplicationInfo"; import { ClusterSource } from "../common/define"; -import { ClustersState } from "../clusters"; +import { AccountClusterNode, ClustersState } from "../clusters"; import { IServiceAccountInfo } from "."; -import { LoginInfo } from "../clusters/interface"; +import { LocalClusterNode } from "../clusters/LocalCuster"; export interface IRootNode { serviceAccount?: IServiceAccountInfo; applications: IV2ApplicationInfo[]; clusterSource?: ClusterSource; - loginInfo?: LoginInfo; // accountClusterService?: AccountClusterService; id?: string; createTime?: number; clusterName?: string; - userInfo?: IUserInfo; kubeConfigPath: string; state: ClustersState; + + clusterInfo?: AccountClusterNode | LocalClusterNode; } diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index 3a8fc1ed..2af1140c 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -4,19 +4,16 @@ import * as fs from "fs"; import * as yaml from "yaml"; import state from "../state"; -import AccountClusterService from "../clusters/AccountCluster"; +import AccountClusterService, { + AccountClusterNode, +} from "../clusters/AccountCluster"; import { ID_SPLIT } from "./nodeContants"; import { ClusterSource } from "../common/define"; import { BaseNocalhostNode } from "./types/nodeType"; import { NocalhostFolderNode } from "./abstract/NocalhostFolderNode"; import { NocalhostRootNode } from "./NocalhostRootNode"; import { isExistSync, resolveVSCodeUri } from "../utils/fileUtil"; -import { - IUserInfo, - IV2ApplicationInfo, - IRootNode, - IDevSpaceInfo, -} from "../domain"; +import { IV2ApplicationInfo, IRootNode, IDevSpaceInfo } from "../domain"; import { ClustersState } from "../clusters"; import host from "../host"; import { getAllNamespace } from "../ctl/nhctl"; @@ -25,7 +22,6 @@ import { DevSpaceNode, getDevSpaceLabel } from "./DevSpaceNode"; export class KubeConfigNode extends NocalhostFolderNode { public label: string; public type = "KUBECONFIG"; - public userInfo: IUserInfo; public clusterSource: ClusterSource; public applications: Array; public parent: NocalhostRootNode; @@ -45,7 +41,7 @@ export class KubeConfigNode extends NocalhostFolderNode { ) { super(); - const { applications, clusterSource, kubeConfigPath, userInfo } = rootNode; + const { applications, clusterSource, kubeConfigPath } = rootNode; this.id = id; this.parent = parent; @@ -55,17 +51,23 @@ export class KubeConfigNode extends NocalhostFolderNode { this.applications = applications; this.installedApps = []; this.kubeConfigPath = kubeConfigPath; - this.userInfo = userInfo; this.state = rootNode.state; state.setNode(this.getNodeStateId(), this); } + get accountClusterNode() { + if (this.clusterSource === ClusterSource.server) { + return this.rootNode.clusterInfo as AccountClusterNode; + } + } get accountClusterService() { if (this.clusterSource === ClusterSource.local) { return null; } - return new AccountClusterService(this.rootNode.loginInfo); + return new AccountClusterService( + this.rootNode.clusterInfo as AccountClusterNode + ); } async updateData() { if (this.state.code !== 200) { @@ -228,7 +230,8 @@ export class KubeConfigNode extends NocalhostFolderNode { }`; if (this.clusterSource === ClusterSource.server) { - const { username, baseUrl } = this.rootNode.loginInfo; + const { username, baseUrl } = (this.rootNode + .clusterInfo as AccountClusterNode).loginInfo; treeItem.tooltip = `${this.label} [${username} on ${baseUrl}]`; } diff --git a/src/main/nodes/NocalhostRootNode.ts b/src/main/nodes/NocalhostRootNode.ts index 11c9cf30..a1e78707 100644 --- a/src/main/nodes/NocalhostRootNode.ts +++ b/src/main/nodes/NocalhostRootNode.ts @@ -239,7 +239,10 @@ export class NocalhostRootNode implements BaseNocalhostNode { return true; } - const { username, baseUrl } = item.loginInfo; + const { + username, + baseUrl, + } = (item.clusterInfo as AccountClusterNode).loginInfo; return !(username === info.username && baseUrl === info.baseUrl); }); @@ -292,7 +295,7 @@ export class NocalhostRootNode implements BaseNocalhostNode { if (result.reason instanceof Error) { info = result.reason.message; - logger.error("get serverCluster error", result.reason, res.userInfo); + logger.error("get cluster error", result.reason, res.clusterInfo); } return new KubeConfigNode(res.id, this, res.clusterName, { From 65901353c72177ad9743e96afd081dff174629dc Mon Sep 17 00:00:00 2001 From: yuanganping Date: Fri, 7 Jan 2022 15:37:35 +0800 Subject: [PATCH 18/29] fix: change sleep Signed-off-by: yuanganping --- package.json | 2 +- src/main/commands/SleepingCommand/ForceSleep.ts | 2 +- src/main/commands/SleepingCommand/WakeUp.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 02acc753..be0ac142 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nocalhost", - "version": "v0.6.2-78-gd84bf0c-alpha", + "version": "v0.6.2-82-gd19eb2e-alpha", "displayName": "Nocalhost", "description": "Makes developing with Kubernetes feel like on local. IDE tool for cloud-native development", "license": "Apache-2.0", diff --git a/src/main/commands/SleepingCommand/ForceSleep.ts b/src/main/commands/SleepingCommand/ForceSleep.ts index 6bbf63cf..cdfdac04 100644 --- a/src/main/commands/SleepingCommand/ForceSleep.ts +++ b/src/main/commands/SleepingCommand/ForceSleep.ts @@ -30,7 +30,7 @@ export default class ForceSleep implements ICommand { const service = new AccountClusterService( clusterInfo as AccountClusterNode ); - service.wakeUpSpace(spaceId); + service.sleepSpace(spaceId); } } } diff --git a/src/main/commands/SleepingCommand/WakeUp.ts b/src/main/commands/SleepingCommand/WakeUp.ts index 05eabad1..9c2656d3 100644 --- a/src/main/commands/SleepingCommand/WakeUp.ts +++ b/src/main/commands/SleepingCommand/WakeUp.ts @@ -24,6 +24,5 @@ export default class WakeUp implements ICommand { ); service.wakeUpSpace(spaceId); console.log(spaceId); - // node.parent.accountClusterService.wakeUpSpace(spaceId); } } From 1e50d45546f042a6a3e4036718077e7c136219f7 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Fri, 7 Jan 2022 15:49:39 +0800 Subject: [PATCH 19/29] fix: del log Signed-off-by: yuanganping --- src/main/commands/SleepingCommand/WakeUp.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/commands/SleepingCommand/WakeUp.ts b/src/main/commands/SleepingCommand/WakeUp.ts index 9c2656d3..1bff473a 100644 --- a/src/main/commands/SleepingCommand/WakeUp.ts +++ b/src/main/commands/SleepingCommand/WakeUp.ts @@ -18,11 +18,9 @@ export default class WakeUp implements ICommand { async execCommand(node: DevSpaceNode) { const spaceId = node.info.spaceId; const clusterInfo = node.parent?.rootNode?.clusterInfo; - const service = new AccountClusterService( clusterInfo as AccountClusterNode ); service.wakeUpSpace(spaceId); - console.log(spaceId); } } From c45f87de470a2dfe5a5bb7a035901b00ed7f5423 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Fri, 7 Jan 2022 17:55:51 +0800 Subject: [PATCH 20/29] fix: vcluster no sleep mode --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index be0ac142..34e28301 100644 --- a/package.json +++ b/package.json @@ -232,12 +232,12 @@ }, { "command": "Nocalhost.wakeUp", - "when": "viewItem =~ /^devspace-server-sleeping(?!-vcluster)/i", + "when": "viewItem =~ /^devspace-server-sleeping(? Date: Fri, 7 Jan 2022 18:11:14 +0800 Subject: [PATCH 21/29] fix: vcluster Signed-off-by: yuanganping --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 34e28301..be0ac142 100644 --- a/package.json +++ b/package.json @@ -232,12 +232,12 @@ }, { "command": "Nocalhost.wakeUp", - "when": "viewItem =~ /^devspace-server-sleeping(? Date: Sat, 8 Jan 2022 12:06:37 +0800 Subject: [PATCH 22/29] WIP Signed-off-by: zhangjian <315626713@qq.com> --- src/main/commands/SignOutCommand.ts | 2 +- src/main/nodes/KubeConfigNode.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/commands/SignOutCommand.ts b/src/main/commands/SignOutCommand.ts index beccceac..b77370d7 100644 --- a/src/main/commands/SignOutCommand.ts +++ b/src/main/commands/SignOutCommand.ts @@ -57,7 +57,7 @@ export default class SignOutCommand implements ICommand { const rootNode = state.getNode(NOCALHOST) as NocalhostRootNode; - this.loginInfo = node.accountClusterNode.loginInfo; + this.loginInfo = node.getClusterNode().loginInfo; this.dispose(); diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index 9dd22c48..5ea2ed95 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -18,6 +18,7 @@ import { ClustersState } from "../clusters"; import host from "../host"; import { getAllNamespace } from "../ctl/nhctl"; import { DevSpaceNode, getDevSpaceLabel } from "./DevSpaceNode"; +import { LocalClusterNode } from "../clusters/LocalCuster"; export class KubeConfigNode extends NocalhostFolderNode { public label: string; @@ -55,11 +56,11 @@ export class KubeConfigNode extends NocalhostFolderNode { state.setNode(this.getNodeStateId(), this); } - get accountClusterNode() { - if (this.clusterSource === ClusterSource.server) { - return this.rootNode.clusterInfo as AccountClusterNode; - } + + getClusterNode() { + return this.rootNode.clusterInfo as T; } + get accountClusterService() { if (this.clusterSource === ClusterSource.local) { return null; From 67a480cb0f5b433f1c9ae576094a6343e8e920c7 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 10 Jan 2022 10:28:40 +0800 Subject: [PATCH 23/29] WIP: vcluster Signed-off-by: zhangjian --- images/icon/vcluster_active.svg | 1 + images/icon/vcluster_warning.svg | 1 + src/main/nodes/KubeConfigNode.ts | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 images/icon/vcluster_active.svg create mode 100644 images/icon/vcluster_warning.svg diff --git a/images/icon/vcluster_active.svg b/images/icon/vcluster_active.svg new file mode 100644 index 00000000..aaf84295 --- /dev/null +++ b/images/icon/vcluster_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icon/vcluster_warning.svg b/images/icon/vcluster_warning.svg new file mode 100644 index 00000000..b16a1dff --- /dev/null +++ b/images/icon/vcluster_warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts index 5ea2ed95..39467142 100644 --- a/src/main/nodes/KubeConfigNode.ts +++ b/src/main/nodes/KubeConfigNode.ts @@ -239,18 +239,30 @@ export class KubeConfigNode extends NocalhostFolderNode { treeItem.tooltip = `${this.label} [${username} on ${baseUrl}]`; } + const clusterType = this.clusterType; + treeItem.description = "Active"; - treeItem.iconPath = resolveVSCodeUri("cluster_active.svg"); + treeItem.iconPath = resolveVSCodeUri(`${clusterType}_active.svg`); if (this.state.code !== 200) { treeItem.tooltip = this.state.info; - treeItem.iconPath = resolveVSCodeUri("cluster_warning.svg"); + treeItem.iconPath = resolveVSCodeUri(`${clusterType}_warning.svg`); treeItem.description = "Unable to Connect"; } return Promise.resolve(treeItem); } + get clusterType(): "vcluster" | "cluster" { + if ( + this.clusterSource === ClusterSource.server && + this.rootNode.serviceAccount?.kubeconfigType === "vcluster" + ) { + return "vcluster"; + } + return "cluster"; + } + getNodeStateId(): string { return `${this.id}${this.parent.getNodeStateId()}${ID_SPLIT}${this.label}`; } From a6957216c878dbb10303241065b2379ac738d6d6 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Thu, 13 Jan 2022 10:55:01 +0800 Subject: [PATCH 24/29] chore: update version Signed-off-by: zhangjian --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c82f7b80..92a58fe7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nocalhost", - "version": "v0.6.2-82-gd19eb2e-alpha", + "version": "v0.6.13", "displayName": "Nocalhost", "description": "Makes developing with Kubernetes feel like on local. IDE tool for cloud-native development", "license": "Apache-2.0", From be4e15daf3a55d19aefb12b6a258017de20b9aa7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Jan 2022 03:48:16 +0000 Subject: [PATCH 25/29] Build(deps): Bump follow-redirects from 1.13.0 to 1.14.7 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.13.0 to 1.14.7. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.13.0...v1.14.7) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- yarn.lock | 13 ++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8584a188..2cbfdc68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5235,9 +5235,9 @@ } }, "follow-redirects": { - "version": "1.14.1", - "resolved": "https://enzuo-npm.pkg.coding.net/blog/public/__tarballs/follow-redirects/1.14.1/__path/npm/follow-redirects/-/follow-redirects-1.14.1/d9114ded0a1cfdd334e164e6662ad02bfd91ff43.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" }, "for-in": { "version": "1.0.2", diff --git a/yarn.lock b/yarn.lock index f0688025..54b219a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4054,15 +4054,10 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.10.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" - integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== - -follow-redirects@^1.13.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.2.tgz#cecb825047c00f5e66b142f90fed4f515dec789b" - integrity sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA== +follow-redirects@^1.10.0, follow-redirects@^1.13.2: + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== for-in@^1.0.2: version "1.0.2" From fb4281a4fb33a7524f68c97c7e071ee92d19799e Mon Sep 17 00:00:00 2001 From: zhangjian <1018zhangjian@gmail.com> Date: Mon, 17 Jan 2022 00:08:31 +0800 Subject: [PATCH 26/29] WIP Signed-off-by: zhangjian <1018zhangjian@gmail.com> --- package.json | 2 +- src/main/clusters/AccountCluster.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 92a58fe7..d94b05a6 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "main": "./dist/extension.js", "nhctl": { "serverVersion": "0.4.7", - "version": "0.6.1" + "version": "0.6.13" }, "contributes": { "configuration": [ diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 8041392b..59f134cc 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -29,6 +29,7 @@ import { KUBE_CONFIG_DIR, SERVER_CLUSTER_LIST } from "../constants"; import { ClusterSource } from "../common/define"; import * as packageJson from "../../../package.json"; import { ClustersState } from "."; +import { kill } from "process"; export class AccountClusterNode { userInfo: IUserInfo; @@ -256,6 +257,8 @@ export default class AccountClusterService { loggerDebug.debug("dispose", kubeConfigPath); proc.kill(); + kill(1 + proc.pid); + kubeconfigCommand(kubeConfigPath, "remove"); return fs.unlink(kubeConfigPath); From f2aa5468ecdc6023256f0f9b19e67987d201e312 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 17 Jan 2022 10:48:15 +0800 Subject: [PATCH 27/29] WIP Signed-off-by: zhangjian --- src/main/clusters/AccountCluster.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clusters/AccountCluster.ts b/src/main/clusters/AccountCluster.ts index 59f134cc..8c4e7685 100644 --- a/src/main/clusters/AccountCluster.ts +++ b/src/main/clusters/AccountCluster.ts @@ -257,7 +257,6 @@ export default class AccountClusterService { loggerDebug.debug("dispose", kubeConfigPath); proc.kill(); - kill(1 + proc.pid); kubeconfigCommand(kubeConfigPath, "remove"); From 902cd5fbedbf834554989e5d98f2972130068741 Mon Sep 17 00:00:00 2001 From: zhangjian Date: Mon, 17 Jan 2022 11:33:46 +0800 Subject: [PATCH 28/29] WIP: vcluster Signed-off-by: zhangjian --- src/main/ctl/shell.ts | 6 ++++-- src/main/debug/remoteTerminal.ts | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/ctl/shell.ts b/src/main/ctl/shell.ts index cc634207..c5ee6f99 100644 --- a/src/main/ctl/shell.ts +++ b/src/main/ctl/shell.ts @@ -155,7 +155,9 @@ export function createProcess(param: ExecParam) { command = `sudo -p "Password:" -S ${command}`; } - const proc = spawn(command, [], { shell: true, env }); + const commands = command.split(" "); + + const proc = spawn(commands.shift(), commands, { env }); const { err, out } = getOutput(output); let stderr = ""; @@ -287,5 +289,5 @@ export function getExecCommand(command: string) { if (host.isWindow() && process.env.ComSpec.endsWith("Git\\bin\\bash.exe")) { command = command.replaceAll(path.sep, "\\\\\\"); } - return command; + return command.trim(); } diff --git a/src/main/debug/remoteTerminal.ts b/src/main/debug/remoteTerminal.ts index d8d0045d..8dad3247 100644 --- a/src/main/debug/remoteTerminal.ts +++ b/src/main/debug/remoteTerminal.ts @@ -4,6 +4,7 @@ import { ChildProcessWithoutNullStreams, spawn } from "child_process"; import host from "../host"; import logger from "../utils/logger"; import { getExecCommand } from "../ctl/shell"; + type SpawnClose = (code: number, signal: NodeJS.Signals) => void; type RemoteTerminalType = { terminal: { @@ -79,9 +80,9 @@ export class RemoteTerminal implements vscode.Terminal { host.log(log); logger.info(log); - const proc = spawn(command, [], { - shell: true, - }); + const commands = command.split(" "); + + const proc = spawn(commands.shift(), commands); proc.stdout.on("data", (data: Buffer) => { const str = data.toString(); From 737c4feacd8522533cbfd9eb40efb78dadd62253 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Tue, 18 Jan 2022 19:18:49 +0800 Subject: [PATCH 29/29] fix: del log Signed-off-by: yuanganping --- src/main/state.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/state.ts b/src/main/state.ts index b9bbeba0..a3c79971 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -87,7 +87,6 @@ class State { const refresh = async () => { const { token } = action; - let time = Date.now(); try { const rootNode = this.getNode("Nocalhost") as BaseNocalhostNode; @@ -120,10 +119,6 @@ class State { await this.startAutoRefresh(); }, 10 * 1000); } - - logger.info( - `refresh size:${this.refreshFolderMap.size} time:${Date.now() - time}` - ); }; this.cancellationToken = action;