diff --git a/images/icon.png b/images/icon.png
index 74539272..04a74628 100755
Binary files a/images/icon.png and b/images/icon.png differ
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/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/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/package.json b/package.json
index 3349fa24..c2a217ab 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "nocalhost",
- "version": "0.6.9",
+ "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",
@@ -36,7 +36,7 @@
"main": "./dist/extension.js",
"nhctl": {
"serverVersion": "0.4.7",
- "version": "0.6.1"
+ "version": "0.6.13"
},
"contributes": {
"configuration": [
@@ -230,9 +230,19 @@
"when": "viewItem =~ /^workload-(deployment|statefulSet|job|daemonSet|cronjob|pod|crd-resources)-dev-(?!vpn_)/i",
"group": "2"
},
+ {
+ "command": "Nocalhost.wakeUp",
+ "when": "viewItem =~ /^devspace-server-sleeping(?!-vcluster)/i",
+ "group": "navigation"
+ },
+ {
+ "command": "Nocalhost.forceSleep",
+ "when": "viewItem =~ /^devspace-server-unsleeping(?!-vcluster)/i",
+ "group": "navigation"
+ },
{
"command": "Nocalhost.resetDevspace",
- "when": "viewItem =~ /^devspace-server/i",
+ "when": "viewItem =~ /^devspace-server(? {
@@ -71,13 +105,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 +141,12 @@ export default class AccountClusterService {
}
static getServerClusterRootNodes = async (
- newAccountCluser: AccountClusterNode
+ accountCluster: AccountClusterNode
): Promise => {
- if (!newAccountCluser) {
+ if (!accountCluster) {
return [];
}
- const accountClusterService = new AccountClusterService(
- newAccountCluser.loginInfo
- );
- accountClusterService.accountClusterNode = newAccountCluser;
- accountClusterService.jwt = newAccountCluser.jwt;
- accountClusterService.refreshToken = newAccountCluser.refreshToken;
-
- const newRootNodes: IRootNode[] = [];
+ const accountClusterService = new AccountClusterService(accountCluster);
let serviceAccounts = await accountClusterService.getServiceAccount();
logger.info(
@@ -128,12 +155,10 @@ export default class AccountClusterService {
}`
);
- if (!Array.isArray(serviceAccounts) || serviceAccounts.length === 0) {
- const msg = `no cluster found for ${newAccountCluser.loginInfo.baseUrl} ${newAccountCluser.loginInfo.username}`;
- logger.error(msg);
-
- throw Error("No clusters");
- }
+ assert(
+ Array.isArray(serviceAccounts) && serviceAccounts.length > 0,
+ "No clusters"
+ );
const applications: IV2ApplicationInfo[] = await accountClusterService.getV2Application();
logger.info(
@@ -144,116 +169,132 @@ export default class AccountClusterService {
const kubeConfigArr: Array = [];
- for (const sa of serviceAccounts) {
- let devSpaces: Array | undefined = new Array();
+ const serviceNodes = serviceAccounts.map(async (serviceAccount) => {
+ const kubeconfig = await AccountClusterService.startVClusterProcess(
+ serviceAccount
+ );
+
+ if (kubeconfig) {
+ serviceAccount.kubeconfig = kubeconfig;
+ }
const { id, kubeConfigPath } = await AccountClusterService.saveKubeConfig(
- sa
+ serviceAccount
);
kubeConfigArr.push(id);
- if (sa.privilege) {
- const devs = await getAllNamespace({
- kubeConfigPath: kubeConfigPath,
- namespace: "default",
- });
-
- for (const dev of devs) {
- 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(...devs);
- } 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 = {
- devSpaces,
+ return {
+ serviceAccount,
+ devSpaces: [],
applications,
- userInfo: newAccountCluser.userInfo,
clusterSource: ClusterSource.server,
- accountClusterService,
- id: newAccountCluser.id,
- createTime: newAccountCluser.createTime,
+ id: accountCluster.id,
+ createTime: accountCluster.createTime,
kubeConfigPath,
- state: await checkCluster(kubeConfigPath),
- };
+ state,
+ clusterInfo: accountCluster,
+ } as IRootNode;
+ });
- newRootNodes.push(obj);
- }
+ 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(
- newAccountCluser,
+ accountCluster,
kubeConfigArr
);
- return newRootNodes;
+ return rootNodes;
};
+ static async startVClusterProcess(sa: IServiceAccountInfo) {
+ const { virtualCluster, kubeconfigType } = sa;
+ if (
+ kubeconfigType === "vcluster" &&
+ virtualCluster.serviceType === "ClusterIP"
+ ) {
+ const {
+ serviceNamespace,
+ servicePort,
+ hostClusterContext,
+ serviceAddress,
+ } = virtualCluster;
+
+ let oldInfo = virtualClusterProcMap[serviceAddress];
+ if (oldInfo?.proc.killed === false) {
+ return oldInfo.kubeconfig;
+ }
+
+ const { proc, kubeconfig } = await kubeConfigRender({
+ namespace: serviceNamespace,
+ kubeconfig: sa.kubeconfig,
+ remotePort: servicePort,
+ context: hostClusterContext,
+ serviceAddress: serviceAddress,
+ });
+
+ const kubeConfigPath = path.resolve(
+ KUBE_CONFIG_DIR,
+ getStringHash(kubeconfig.trim())
+ );
+
+ host.getContext().subscriptions.push({
+ dispose: () => {
+ loggerDebug.debug("dispose", kubeConfigPath);
+
+ proc.kill();
+
+ kubeconfigCommand(kubeConfigPath, "remove");
+
+ return fs.unlink(kubeConfigPath);
+ },
+ });
+ virtualClusterProcMap[serviceAddress] = {
+ proc,
+ kubeconfig,
+ };
+
+ return kubeconfig;
+ }
+ }
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);
+
+ host.setGlobalState(KEY, configs);
if (prevData) {
- const diff = difference(prevData as Array, configs);
+ const diff = difference(prevData, configs);
if (diff.length === 0) {
return;
}
- await Promise.allSettled(
- diff.map((id) => {
- return new Promise(async (res) => {
- const file = path.resolve(KUBE_CONFIG_DIR, id);
+ diff.map((id) => {
+ const file = path.resolve(KUBE_CONFIG_DIR, id);
- await kubeconfig(file, "remove");
-
- res();
- });
- })
- );
+ kubeconfigCommand(file, "remove");
+ });
}
-
- host.setGlobalState(KEY, configs);
}
static async saveKubeConfig(accountInfo: IServiceAccountInfo) {
@@ -264,42 +305,52 @@ export default class AccountClusterService {
if (!(await isExist(kubeConfigPath))) {
await writeFileLock(kubeConfigPath, accountInfo.kubeconfig);
- kubeconfig(kubeConfigPath, "add");
+ kubeconfigCommand(kubeConfigPath, "add");
}
return { id, kubeConfigPath };
}
+
static appendClusterByLoginInfo = async (
loginInfo: LoginInfo
): Promise => {
- const accountServer = new AccountClusterService(loginInfo);
- const newAccountCluser = await accountServer.buildAccountClusterNode();
+ 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<
+ Array
+ >(SERVER_CLUSTER_LIST);
- let globalAccountClusterList = host.getGlobalState(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 === newAccountCluser.id
+ (it) => 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 => {
await this.login(this.loginInfo);
const userInfo = await this.getUserInfo();
@@ -362,11 +413,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);
@@ -375,10 +426,9 @@ export default class AccountClusterService {
}
}
- // update login infoo
updateLoginInfo() {
- const newAccountCluser = {
- ...this.accountClusterNode,
+ const newAccountCluster = {
+ ...this.accountCluster,
jwt: this.jwt,
refreshToken: this.refreshToken,
};
@@ -390,14 +440,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);
}
@@ -415,14 +466,14 @@ 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}`
);
}
}
async getApplication() {
- const { userInfo } = this.accountClusterNode;
+ const { userInfo } = this.accountCluster;
try {
const response = await this.instance.get(
`/v1/users/${userInfo.id}/applications`
@@ -446,7 +497,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) {
@@ -460,7 +511,7 @@ export default class AccountClusterService {
}
async getV2Application(): Promise {
- const { userInfo } = this.accountClusterNode;
+ const { userInfo } = this.accountCluster;
const userId = userInfo.id;
if (!userId) {
return [];
@@ -512,6 +563,21 @@ 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.");
+ }
+
+ async wakeUpSpace(id: number) {
+ const response = await this.instance.post(`/v2/dev_space/${id}/wakeup`);
+ const flag = response.data.code === 0;
+ 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/clusters/LocalCuster.ts b/src/main/clusters/LocalCuster.ts
index 9556d778..e8266bd3 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 ".";
@@ -21,12 +20,29 @@ export class LocalClusterNode {
public clusterNickName?: string
) {}
}
+export function buildRootNodeForLocalCluster(
+ localCluster: LocalClusterNode,
+ state: ClustersState
+): IRootNode {
+ const { filePath, createTime } = localCluster;
+
+ return {
+ id: localCluster.id,
+ clusterName: localCluster.clusterNickName,
+ createTime,
+ clusterSource: ClusterSource.local,
+ applications: [],
+ kubeConfigPath: filePath,
+ state,
+ };
+}
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
);
@@ -39,37 +55,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",
@@ -90,7 +78,6 @@ export default class LocalCluster {
});
const obj: IRootNode = {
id: newLocalCluster.id,
- devSpaces,
clusterName: newLocalCluster.clusterNickName,
createTime,
clusterSource: ClusterSource.local,
@@ -147,7 +134,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/ResetDevspaceCommand.ts b/src/main/commands/ResetDevspaceCommand.ts
index 08313957..79778ff9 100644
--- a/src/main/commands/ResetDevspaceCommand.ts
+++ b/src/main/commands/ResetDevspaceCommand.ts
@@ -7,9 +7,9 @@ import state from "../state";
import host, { Host } from "../host";
import * as nhctl from "../ctl/nhctl";
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";
export default class ResetDevspaceCommand implements ICommand {
command: string = RESET_DEVSPACE;
@@ -39,36 +39,38 @@ export default class ResetDevspaceCommand implements ICommand {
await vscode.commands.executeCommand("Nocalhost.refresh", node);
host.disposeDevspace(node.info.spaceName);
- await this.reset(
- host,
- node.getKubeConfigPath(),
- node.info.namespace,
- node.info.spaceName
- ).finally(async () => {
- await node.parent.accountClusterService.resetDevSpace(node.info.id);
- const nocalhostRootNode = node.parent.parent as NocalhostRootNode;
+ await this.reset(host, node)
+ .then(() => {
+ messageBus.emit("refreshTree", {});
+ })
+ .finally(async () => {
+ const parent = node.parent.parent;
- await nocalhostRootNode.updateData();
+ await 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/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/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 8eee510e..b77370d7 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,19 +9,25 @@ 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 { kubeconfig } from "../ctl/nhctl";
+import { kubeconfigCommand } from "../ctl/nhctl";
import { LoginInfo } from "../clusters/interface";
import { NocalhostRootNode } from "../nodes/NocalhostRootNode";
import messageBus from "../utils/messageBus";
+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.");
@@ -49,9 +56,12 @@ 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.loginInfo = node.getClusterNode().loginInfo;
+
+ this.dispose();
+
+ rootNode.deleteCluster(this.loginInfo);
messageBus.emit("refreshTree", {});
} catch (error) {
@@ -61,24 +71,59 @@ export default class SignOutCommand implements ICommand {
}
}
- cleanKubeConfig(loginInfo: LoginInfo) {
- const { baseUrl, username } = loginInfo;
- const KEY = `USER_LINK:${baseUrl}@${username}`;
+ dispose() {
+ this.killVClusterProcess();
+ this.cleanKubeConfig();
+ }
- const prevData = host.getGlobalState(KEY);
+ async killVClusterProcess() {
+ const rootNode = state.getData>("Nocalhost");
- if (prevData) {
- Promise.allSettled(
- (prevData as Array).map((id) => {
- return new Promise(async (res, rej) => {
- const file = path.resolve(KUBE_CONFIG_DIR, id);
+ const { username, baseUrl } = this.loginInfo;
- await kubeconfig(file, "remove");
+ rootNode
+ .filter((node) => node.clusterSource === ClusterSource.server)
+ .forEach((root) => {
+ const { loginInfo } = root.clusterInfo as AccountClusterNode;
- res();
- });
- })
- );
+ if (loginInfo.username !== username || 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() {
+ const { baseUrl, username } = this.loginInfo;
+ const KEY = `USER_LINK:${baseUrl}@${username}`;
+
+ const prevData = host.getGlobalState>(KEY);
+
+ if (prevData) {
+ prevData.map((id) => {
+ kubeconfigCommand(this.getFilePath(id), "remove");
+ });
}
host.removeGlobalState(KEY);
diff --git a/src/main/commands/SleepingCommand/ForceSleep.ts b/src/main/commands/SleepingCommand/ForceSleep.ts
new file mode 100644
index 00000000..cdfdac04
--- /dev/null
+++ b/src/main/commands/SleepingCommand/ForceSleep.ts
@@ -0,0 +1,36 @@
+import * as vscode from "vscode";
+import { FORCE_SLEEP } from "../constants";
+import ICommand from "../ICommand";
+import registerCommand from "../register";
+import host from "../../host";
+import AccountClusterService, {
+ AccountClusterNode,
+} from "../../clusters/AccountCluster";
+
+import { DevSpaceNode } from "../../nodes/DevSpaceNode";
+
+export default class ForceSleep 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 res = await host.showInformationMessage(
+ "Confirm to sleep!",
+ { modal: true },
+ "yes"
+ );
+
+ if (res === "yes") {
+ const spaceId = node.info.spaceId;
+ const clusterInfo = node.parent?.rootNode?.clusterInfo;
+ const service = new AccountClusterService(
+ clusterInfo as AccountClusterNode
+ );
+ service.sleepSpace(spaceId);
+ }
+ }
+}
diff --git a/src/main/commands/SleepingCommand/WakeUp.ts b/src/main/commands/SleepingCommand/WakeUp.ts
new file mode 100644
index 00000000..1bff473a
--- /dev/null
+++ b/src/main/commands/SleepingCommand/WakeUp.ts
@@ -0,0 +1,26 @@
+import * as vscode from "vscode";
+import { WAKE_UP } from "../constants";
+import ICommand from "../ICommand";
+import registerCommand from "../register";
+
+import { DevSpaceNode } from "../../nodes/DevSpaceNode";
+import AccountClusterService, {
+ AccountClusterNode,
+} from "../../clusters/AccountCluster";
+
+export default class WakeUp 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;
+ const clusterInfo = node.parent?.rootNode?.clusterInfo;
+ const service = new AccountClusterService(
+ clusterInfo as AccountClusterNode
+ );
+ service.wakeUpSpace(spaceId);
+ }
+}
diff --git a/src/main/commands/UpgradeCommand.ts b/src/main/commands/UpgradeCommand.ts
index 37d73a77..9dce1c99 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/commands/constants.ts b/src/main/commands/constants.ts
index e02211e6..6026a364 100644
--- a/src/main/commands/constants.ts
+++ b/src/main/commands/constants.ts
@@ -65,3 +65,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 f473b802..386cd6d5 100644
--- a/src/main/commands/index.ts
+++ b/src/main/commands/index.ts
@@ -48,6 +48,9 @@ import StartProxyModeCommand from "./proxy/StartProxyModeCommand";
import ResumeProxyModeCommand from "./proxy/ResumeProxyModeCommand";
import EndProxyModeCommand from "./proxy/EndProxyModeCommand";
+import WakeUpCommand from "./SleepingCommand/WakeUp";
+import ForceSleepCommand from "./SleepingCommand/ForceSleep";
+
export default function initCommands(
context: vscode.ExtensionContext,
appTreeProvider: NocalhostAppProvider
@@ -109,4 +112,6 @@ export default function initCommands(
new StartProxyModeCommand(context);
new ResumeProxyModeCommand(context);
new EndProxyModeCommand(context);
+ new ForceSleepCommand(context);
+ new WakeUpCommand(context);
}
diff --git a/src/main/ctl/nhctl/cli.ts b/src/main/ctl/nhctl/cli.ts
index ac84709c..be5d06e8 100644
--- a/src/main/ctl/nhctl/cli.ts
+++ b/src/main/ctl/nhctl/cli.ts
@@ -10,6 +10,8 @@ import * as os from "os";
import * as path from "path";
import * as fs from "fs";
+import { ChildProcessWithoutNullStreams } from "child_process";
+
import { exec, ExecParam, execWithProgress } from "../shell";
import host, { Host } from "../../host";
import * as yaml from "yaml";
@@ -19,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";
@@ -1472,7 +1474,7 @@ export async function checkCluster(
return result;
}
-export async function kubeconfig(
+export async function kubeconfigCommand(
kubeConfigPath: string,
command: "add" | "remove"
) {
@@ -1482,7 +1484,7 @@ export async function kubeconfig(
.toJson()
.exec();
- logger.debug(`kubeconfig ${command}:${kubeConfigPath}`);
+ loggerDebug.debug(`kubeconfig ${command}:${kubeConfigPath}`);
return result;
}
@@ -1554,6 +1556,68 @@ 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 command = commands.join(" ");
+
+ 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)) {
+ loggerDebug.debug(command, Date.now() - time);
+
+ const kubeconfig = str.substring(0, str.lastIndexOf(END_Symbol));
+ res({ kubeconfig, proc });
+ return;
+ }
+ });
+
+ proc.stdin.write(kubeconfigStr);
+ proc.stdin.end();
+
+ promise.catch(rej);
+ });
+}
export async function vpn(param: {
subCommand: "connect" | "disconnect" | "reconnect";
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/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/debug/provider/goDebugProvider.ts b/src/main/debug/provider/goDebugProvider.ts
index f6f99c97..3b3b3cbf 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 f6df1a42..e9a9f1e2 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 {
@@ -54,7 +54,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/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();
diff --git a/src/main/domain/IDevSpaceInfo.ts b/src/main/domain/IDevSpaceInfo.ts
index 65c82db1..500a359f 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?: "asleep" | "wakeup";
[key: string]: any;
}
diff --git a/src/main/domain/IRootNode.ts b/src/main/domain/IRootNode.ts
index 2398a65a..803a6636 100644
--- a/src/main/domain/IRootNode.ts
+++ b/src/main/domain/IRootNode.ts
@@ -1,21 +1,20 @@
-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 { AccountClusterNode, ClustersState } from "../clusters";
+import { IServiceAccountInfo } from ".";
+import { LocalClusterNode } from "../clusters/LocalCuster";
export interface IRootNode {
- devSpaces: IDevSpaceInfo[];
+ serviceAccount?: IServiceAccountInfo;
applications: IV2ApplicationInfo[];
clusterSource?: ClusterSource;
- accountClusterService?: AccountClusterService;
+ // accountClusterService?: AccountClusterService;
id?: string;
createTime?: number;
clusterName?: string;
- userInfo?: IUserInfo;
kubeConfigPath: string;
state: ClustersState;
+
+ clusterInfo?: AccountClusterNode | LocalClusterNode;
}
diff --git a/src/main/domain/IServiceAccountInfo.ts b/src/main/domain/IServiceAccountInfo.ts
index 4741d0c3..b99ef3f0 100644
--- a/src/main/domain/IServiceAccountInfo.ts
+++ b/src/main/domain/IServiceAccountInfo.ts
@@ -4,10 +4,21 @@ 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;
spacename: string;
spaceOwnType?: "Viewer" | "Owner" | string;
+ isAsleep?: boolean;
+ sleepStatus?: "wakeup" | "asleep";
}>;
}
diff --git a/src/main/extension.ts b/src/main/extension.ts
index 5d85df57..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";
@@ -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);
}
@@ -144,7 +146,7 @@ export async function activate(context: vscode.ExtensionContext) {
true
);
- await state.refreshTree();
+ state.refreshTree(false);
launchDevSpace();
@@ -159,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 e713f030..0b97f4af 100644
--- a/src/main/host.ts
+++ b/src/main/host.ts
@@ -15,7 +15,7 @@ export class Host implements vscode.Disposable {
100
);
- private devspaceDisposesMap = new Map<
+ private devSpaceDisposesMap = new Map<
string,
Map<
string,
@@ -46,12 +46,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) {
@@ -87,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;
}
@@ -105,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;
}
@@ -115,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;
}
@@ -145,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) {
@@ -315,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/DevSpaceNode.ts b/src/main/nodes/DevSpaceNode.ts
index e7ead2fd..de34ecde 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";
@@ -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);
}
@@ -259,7 +264,9 @@ export class DevSpaceNode extends NocalhostFolderNode implements RefreshData {
treeItem.iconPath = resolveVSCodeUri("loading.gif");
} else {
const iconName =
- this.info.spaceOwnType === "Viewer"
+ this.info.isAsleep === "asleep"
+ ? "namespace_sleep.svg"
+ : this.info.spaceOwnType === "Viewer"
? "devspace_viewer.svg"
: "devspace.svg";
treeItem.iconPath = resolveVSCodeUri(iconName);
@@ -267,10 +274,27 @@ 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 === "asleep"
+ ? "server-sleeping"
+ : this.info.isAsleep === "wakeup"
+ ? "server-unsleeping"
+ : "server"
}`
);
+ 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);
}
diff --git a/src/main/nodes/KubeConfigNode.ts b/src/main/nodes/KubeConfigNode.ts
index e06784bd..61374702 100644
--- a/src/main/nodes/KubeConfigNode.ts
+++ b/src/main/nodes/KubeConfigNode.ts
@@ -1,25 +1,30 @@
import * as vscode from "vscode";
-import { orderBy } from "lodash";
-import { HELM_NH_CONFIG_DIR, NOCALHOST } from "../constants";
+import { difference, orderBy } from "lodash";
+import * as fs from "fs";
+import * as yaml from "yaml";
+
+import { NOCALHOST } from "../constants";
import state from "../state";
-import AccountClusterService from "../clusters/AccountCluster";
+import AccountClusterService, {
+ AccountClusterNode,
+} 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 { resolveVSCodeUri, writeFileLock } from "../utils/fileUtil";
-import { DevSpaceNode } from "./DevSpaceNode";
-import { IUserInfo, IDevSpaceInfo, IV2ApplicationInfo } from "../domain";
+import { NocalhostRootNode } from "./NocalhostRootNode";
+import { isExistSync, resolveVSCodeUri } from "../utils/fileUtil";
+import { IV2ApplicationInfo, IRootNode, IDevSpaceInfo } from "../domain";
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;
- parent: BaseNocalhostNode = null;
public type = "KUBECONFIG";
- public devSpaceInfos: IDevSpaceInfo[];
- public userInfo: IUserInfo;
public clusterSource: ClusterSource;
public applications: Array;
public installedApps: {
@@ -28,47 +33,157 @@ export class KubeConfigNode extends NocalhostFolderNode {
}[] = [];
public id: string;
public kubeConfigPath: string;
- public accountClusterService: AccountClusterService;
private state: ClustersState;
- constructor(props: {
- id: string;
- label: string;
- devSpaceInfos: IDevSpaceInfo[];
- applications: Array;
- clusterSource: ClusterSource;
- kubeConfigPath: string;
- userInfo: IUserInfo;
- accountClusterService?: AccountClusterService;
- state: ClustersState;
- }) {
+ constructor(
+ id: string,
+ public parent: BaseNocalhostNode,
+ label: string,
+ public rootNode: IRootNode
+ ) {
super();
- const {
- id,
- label,
- devSpaceInfos,
- applications,
- clusterSource,
- kubeConfigPath,
- userInfo,
- accountClusterService,
- } = props;
+
+ const { applications, clusterSource, kubeConfigPath } = rootNode;
+
this.id = id;
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 [];
+
+ getClusterNode() {
+ return this.rootNode.clusterInfo as T;
+ }
+
+ get accountClusterService() {
+ if (this.clusterSource === ClusterSource.local) {
+ return null;
+ }
+
+ return new AccountClusterService(
+ this.rootNode.clusterInfo as AccountClusterNode
+ );
+ }
+ 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;
+ dev.isAsleep = ns?.sleepStatus;
+
+ 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,
+ clusterId: sa.clusterId,
+ storageClass: sa.storageClass,
+ spaceOwnType: ns.spaceOwnType,
+ isAsleep: ns.sleepStatus,
+ devStartAppendCommand: [
+ "--priority-class",
+ "nocalhost-container-critical",
+ ],
+ };
+ devSpaces.push(devInfo);
+ }
+ }
+ }
+
+ this.cleanDiffDevSpace(devSpaces);
+
+ state.setData(this.getNodeStateId(), 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() {
@@ -76,43 +191,30 @@ 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"];
- }
+ let devSpaces = Array.of();
- 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
- );
- devs.push(
- Object.assign(node, {
- order: d.spaceOwnType !== "Viewer",
- isSpace: d.spaceId > 0,
- })
- );
+ if (this.state.code !== 200) {
+ return [];
}
+ devSpaces = state.getData>(this.getNodeStateId());
+
+ if (!devSpaces) {
+ devSpaces = await this.updateData();
+ }
+
+ const devSpace = devSpaces.map((devSpace) => {
+ return Object.assign(
+ new DevSpaceNode(this, devSpace, this.applications, this.clusterSource),
+ {
+ order: devSpace.spaceOwnType !== "Viewer",
+ isSpace: devSpace.spaceId > 0,
+ }
+ );
+ });
+
return orderBy(
- devs,
+ devSpace,
["order", "isSpace", "label"],
["desc", "desc", "asc"]
);
@@ -131,16 +233,20 @@ export class KubeConfigNode extends NocalhostFolderNode {
}`;
if (this.clusterSource === ClusterSource.server) {
- const { username, baseUrl } = this.accountClusterService.loginInfo;
+ const { username, baseUrl } = (this.rootNode
+ .clusterInfo as AccountClusterNode).loginInfo;
treeItem.tooltip = `${username} [${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.iconPath = resolveVSCodeUri("cluster_warning.svg");
+ treeItem.tooltip = this.state.info;
+ treeItem.iconPath = resolveVSCodeUri(`${clusterType}_warning.svg`);
if (this.state.info !== "No clusters") {
treeItem.tooltip = this.state.info;
@@ -154,6 +260,16 @@ export class KubeConfigNode extends NocalhostFolderNode {
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}${NOCALHOST}${ID_SPLIT}${this.label}`;
}
diff --git a/src/main/nodes/NocalhostRootNode.ts b/src/main/nodes/NocalhostRootNode.ts
index e21e8e1f..8dce7c5c 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,
@@ -24,7 +29,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";
}
@@ -60,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
@@ -80,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,
+ });
});
});
@@ -135,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);
});
@@ -177,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;
@@ -186,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 (
@@ -200,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 (
@@ -225,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);
@@ -251,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") {
@@ -264,59 +234,25 @@ 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.clusterInfo as AccountClusterNode).loginInfo;
return !(username === info.username && baseUrl === info.baseUrl);
});
}
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) {
@@ -330,17 +266,7 @@ export class NocalhostRootNode implements BaseNocalhostNode {
async getKubeConfigNode(res: IRootNode) {
const clusterName = await getClusterName(res);
- return new KubeConfigNode({
- id: res.id,
- label: clusterName,
- 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
@@ -368,18 +294,11 @@ 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({
- id: res.id,
- label: res.clusterName,
- 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,
diff --git a/src/main/nodes/types/nodeType.ts b/src/main/nodes/types/nodeType.ts
index 196e21e9..89a88356 100644
--- a/src/main/nodes/types/nodeType.ts
+++ b/src/main/nodes/types/nodeType.ts
@@ -1,6 +1,5 @@
-import AccountClusterService from "./../../clusters/AccountCluster";
import * as vscode from "vscode";
-
+import { IRootNode } from "../../domain";
export interface AppInfo {
name: string;
releasename: string;
@@ -60,13 +59,14 @@ export interface SvcProfile {
export interface BaseNocalhostNode {
label: string;
type: string;
- accountClusterService?: AccountClusterService;
hasInit?: boolean;
resourceType?: string;
parent: BaseNocalhostNode | undefined | null;
+ rootNode?: IRootNode;
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 5929f5fa..ac092663 100644
--- a/src/main/nodes/workloads/KubernetesResourceDevMode.ts
+++ b/src/main/nodes/workloads/KubernetesResourceDevMode.ts
@@ -40,8 +40,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[] =
@@ -60,7 +62,11 @@ export const kubernetesResourceDevMode = (resourceNode: any) => (
const obj = {
resource: list,
};
- 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 b9bbeba0..8d7b1bfd 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";
@@ -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;
@@ -87,11 +87,13 @@ class State {
const refresh = async () => {
const { token } = action;
- let time = Date.now();
try {
const rootNode = this.getNode("Nocalhost") as BaseNocalhostNode;
if (rootNode) {
+ if (rootNode.hasInit === false) {
+ return;
+ }
await rootNode.updateData(null, token).catch(() => {});
}
@@ -101,7 +103,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();
@@ -120,10 +122,6 @@ class State {
await this.startAutoRefresh();
}, 10 * 1000);
}
-
- logger.info(
- `refresh size:${this.refreshFolderMap.size} time:${Date.now() - time}`
- );
};
this.cancellationToken = action;
@@ -259,18 +257,25 @@ 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);
}
}
+ for (let key of this.dataMap.keys()) {
+ if (key.startsWith(stateId)) {
+ loggerDebug.debug("dataMap", key);
+ this.dataMap.delete(key);
+ }
+ }
+
if (!deleteRefresh) {
return;
}
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 };
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"