From b443d436e84a95868eb5513f5917917be6c898ce Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Tue, 9 Jul 2024 06:16:07 +0000 Subject: [PATCH 01/10] fix --- .changeset/twelve-olives-scream.md | 6 ++ apps/mis-server/src/bl/clustersUtils.ts | 2 +- apps/mis-server/src/plugins/clusters.ts | 16 ++-- apps/mis-server/src/services/init.ts | 4 +- apps/mis-server/src/services/misConfig.ts | 105 ++++++++++++++-------- apps/mis-server/src/services/tenant.ts | 5 +- apps/mis-server/src/services/user.ts | 5 +- apps/mis-server/src/utils/createUser.ts | 5 +- apps/portal-server/src/app.ts | 18 ++-- apps/portal-server/src/utils/proxy.ts | 5 +- apps/portal-server/src/utils/ssh.ts | 11 ++- 11 files changed, 120 insertions(+), 62 deletions(-) create mode 100644 .changeset/twelve-olives-scream.md diff --git a/.changeset/twelve-olives-scream.md b/.changeset/twelve-olives-scream.md new file mode 100644 index 0000000000..e0ad5de9e9 --- /dev/null +++ b/.changeset/twelve-olives-scream.md @@ -0,0 +1,6 @@ +--- +"@scow/portal-server": patch +"@scow/mis-server": patch +--- + +在门户和管理系统启动时只检查启用中集群登录节点的 ssh 连接,在管理系统启用集群操作中检查登录节点的 ssh 连接 diff --git a/apps/mis-server/src/bl/clustersUtils.ts b/apps/mis-server/src/bl/clustersUtils.ts index 82e7d00a73..38736c6cdf 100644 --- a/apps/mis-server/src/bl/clustersUtils.ts +++ b/apps/mis-server/src/bl/clustersUtils.ts @@ -84,7 +84,7 @@ export async function getClustersRuntimeInfo( }); const clusterDatabaseList = clustersFromDb.map((x) => { - return `(Cluster ID: ${x.clusterId}) : ${x.activationStatus}`; + return `Cluster ID: ${x.clusterId}, Current Status: ${x.activationStatus}`; }).join("; "); logger.info("Current clusters list: %s", clusterDatabaseList); diff --git a/apps/mis-server/src/plugins/clusters.ts b/apps/mis-server/src/plugins/clusters.ts index f54c73fca8..633b8c300e 100644 --- a/apps/mis-server/src/plugins/clusters.ts +++ b/apps/mis-server/src/plugins/clusters.ts @@ -17,7 +17,7 @@ import { ClusterConfigSchema, getLoginNode } from "@scow/config/build/cluster"; import { getSchedulerAdapterClient, SchedulerAdapterClient } from "@scow/lib-scheduler-adapter"; import { scowErrorMetadata } from "@scow/lib-server/build/error"; import { testRootUserSshLogin } from "@scow/lib-ssh"; -import { updateCluster } from "src/bl/clustersUtils"; +import { getActivatedClusters, updateCluster } from "src/bl/clustersUtils"; import { configClusters } from "src/config/clusters"; import { rootKeyPair } from "src/config/env"; @@ -52,8 +52,16 @@ export const ADAPTER_CALL_ON_ONE_ERROR = "ADAPTER_CALL_ON_ONE_ERROR"; export const clustersPlugin = plugin(async (f) => { + // initial clusters database + const configClusterIds = Object.keys(configClusters); + await updateCluster(f.ext.orm.em.fork(), configClusterIds, f.logger); + if (process.env.NODE_ENV === "production") { - await Promise.all(Object.values(configClusters).map(async ({ displayName, loginNodes }) => { + + // only check activated clusters' root user login when system is starting + const activatedClusters = await getActivatedClusters(f.ext.orm.em.fork(), f.logger); + + await Promise.all(Object.values(activatedClusters).map(async ({ displayName, loginNodes }) => { const loginNode = getLoginNode(loginNodes[0]); const address = loginNode.address; const node = loginNode.name; @@ -68,10 +76,6 @@ export const clustersPlugin = plugin(async (f) => { })); } - // initial clusters database - const configClusterIds = Object.keys(configClusters); - await updateCluster(f.ext.orm.em.fork(), configClusterIds, f.logger); - // adapterClient of all config clusters const adapterClientForClusters = Object.entries(configClusters).reduce((prev, [cluster, c]) => { const client = getSchedulerAdapterClient(c.adapterUrl); diff --git a/apps/mis-server/src/services/init.ts b/apps/mis-server/src/services/init.ts index 72c6bb86f8..0402748b1e 100644 --- a/apps/mis-server/src/services/init.ts +++ b/apps/mis-server/src/services/init.ts @@ -17,6 +17,7 @@ import { UniqueConstraintViolationException } from "@mikro-orm/core"; import { createUser } from "@scow/lib-auth"; import { InitServiceServer, InitServiceService } from "@scow/protos/build/server/init"; import { authUrl } from "src/config"; +import { configClusters } from "src/config/clusters"; import { SystemState } from "src/entities/SystemState"; import { PlatformRole, TenantRole, User } from "src/entities/User"; import { DEFAULT_TENANT_NAME } from "src/utils/constants"; @@ -72,7 +73,8 @@ export const initServiceServer = plugin((server) => { server.logger) .then(async () => { // 插入公钥失败也认为是创建用户成功 - await insertKeyToNewUser(userId, password, server.logger) + // 在所有集群下执行 + await insertKeyToNewUser(userId, password, server.logger, configClusters) .catch(() => null); return true; }) diff --git a/apps/mis-server/src/services/misConfig.ts b/apps/mis-server/src/services/misConfig.ts index f0be6550f8..d400fe4352 100644 --- a/apps/mis-server/src/services/misConfig.ts +++ b/apps/mis-server/src/services/misConfig.ts @@ -13,9 +13,13 @@ import { asyncClientCall } from "@ddadaal/tsgrpc-client"; import { plugin } from "@ddadaal/tsgrpc-server"; import { ServiceError, status } from "@grpc/grpc-js"; +import { getLoginNode } from "@scow/config/build/cluster"; +import { testRootUserSshLogin } from "@scow/lib-ssh"; import { ClusterRuntimeInfo_LastActivationOperation, ConfigServiceServer, ConfigServiceService } from "@scow/protos/build/server/config"; import { getActivatedClusters, getClustersRuntimeInfo } from "src/bl/clustersUtils"; +import { configClusters } from "src/config/clusters"; +import { rootKeyPair } from "src/config/env"; import { Cluster, ClusterActivationStatus } from "src/entities/Cluster"; export const misConfigServiceServer = plugin((server) => { @@ -75,50 +79,75 @@ export const misConfigServiceServer = plugin((server) => { activateCluster: async ({ request, em, logger }) => { const { clusterId, operatorId } = request; - const cluster = await em.findOne(Cluster, { clusterId }); - if (!cluster) { - throw { - code: status.NOT_FOUND, message: `Cluster( Cluster ID: ${clusterId}) is not found`, - } as ServiceError; - } + await em.transactional(async (em) => { + const cluster = await em.findOne(Cluster, { clusterId }); - // check current scheduler adapter connection state - // do not need check cluster's activation - await server.ext.clusters.callOnOne( - clusterId, - logger, - async (client) => await asyncClientCall(client.config, "getClusterConfig", {}), - ).catch((e) => { - logger.info("Cluster Connection Error ( Cluster ID : %s , Details: %s ) .", cluster, e); - throw { - code: status.FAILED_PRECONDITION, - message: `Activate cluster failed, Cluster( Cluster ID: ${clusterId}) is currently unreachable.`, - } as ServiceError; - }); + if (!cluster) { + throw { + code: status.NOT_FOUND, message: `Cluster( Cluster ID: ${clusterId}) is not found`, + } as ServiceError; + } - // when the cluster has already been activated - if (cluster.activationStatus === ClusterActivationStatus.ACTIVATED) { - logger.info("Cluster (Cluster ID: %s) has already been activated", + // check current scheduler adapter connection state + // do not need check cluster's activation + await server.ext.clusters.callOnOne( + clusterId, + logger, + async (client) => await asyncClientCall(client.config, "getClusterConfig", {}), + ).catch((e) => { + logger.info("Cluster Connection Error ( Cluster ID : %s , Details: %s ) .", cluster, e); + throw { + code: status.FAILED_PRECONDITION, + message: `Activate cluster failed, Cluster( Cluster ID: ${clusterId}) is currently unreachable.`, + } as ServiceError; + }); + + // when the cluster has already been activated + if (cluster.activationStatus === ClusterActivationStatus.ACTIVATED) { + logger.info("Cluster (Cluster ID: %s) has already been activated", + clusterId, + ); + return [{ executed: false }]; + } + + // check root user ssh login in the target cluster + const targetClusterLoginNodes = configClusters[clusterId].loginNodes; + + const loginNode = getLoginNode(targetClusterLoginNodes[0]); + const address = loginNode.address; + const node = loginNode.name; + logger.info("Checking if root can login to cluster (clusterId: %s) by login node %s", + clusterId, node); + const error = await testRootUserSshLogin(address, rootKeyPair, logger); + + if (error) { + logger.info("Root cannot login to cluster (clusterId: %s) by login node %s. err: %o", + clusterId, node, error); + throw { + code: status.FAILED_PRECONDITION, + message: `Activate cluster failed, root login check failed in Cluster( Cluster ID: ${clusterId}) .`, + } as ServiceError; + } else { + logger.info("Root can login to cluster (clusterId: %s) by login node %s", clusterId, node); + } + + cluster.activationStatus = ClusterActivationStatus.ACTIVATED; + + // save operator userId in lastActivationOperation + const lastActivationOperationMap: ClusterRuntimeInfo_LastActivationOperation = {}; + + lastActivationOperationMap.operatorId = operatorId; + cluster.lastActivationOperation = lastActivationOperationMap; + + await em.persistAndFlush(cluster); + + logger.info("Cluster (Cluster ID: %s) is successfully activated by user (User Id: %s)", clusterId, + operatorId, ); - return [{ executed: false }]; - } - - cluster.activationStatus = ClusterActivationStatus.ACTIVATED; - - // save operator userId in lastActivationOperation - const lastActivationOperationMap: ClusterRuntimeInfo_LastActivationOperation = {}; - - lastActivationOperationMap.operatorId = operatorId; - cluster.lastActivationOperation = lastActivationOperationMap; - await em.persistAndFlush(cluster); - - logger.info("Cluster (Cluster ID: %s) is successfully activated by user (User Id: %s)", - clusterId, - operatorId, - ); + }); return [{ executed: true }]; diff --git a/apps/mis-server/src/services/tenant.ts b/apps/mis-server/src/services/tenant.ts index 3e62552074..485a7d2a37 100644 --- a/apps/mis-server/src/services/tenant.ts +++ b/apps/mis-server/src/services/tenant.ts @@ -20,6 +20,7 @@ import { TenantServiceServer, TenantServiceService } from "@scow/protos/build/se import { blockAccount, unblockAccount } from "src/bl/block"; import { getActivatedClusters } from "src/bl/clustersUtils"; import { authUrl } from "src/config"; +import { configClusters } from "src/config/clusters"; import { Account } from "src/entities/Account"; import { Tenant } from "src/entities/Tenant"; import { TenantRole, User } from "src/entities/User"; @@ -147,7 +148,9 @@ export const tenantServiceServer = plugin((server) => { { identityId: user.userId, id: user.id, mail: user.email, name: user.name, password: userPassword }, logger) .then(async () => { - await insertKeyToNewUser(userId, userPassword, logger) + // 插入公钥失败也认为是创建用户成功 + // 在所有集群下执行 + await insertKeyToNewUser(userId, userPassword, logger, configClusters) .catch(() => { }); return true; }) diff --git a/apps/mis-server/src/services/user.ts b/apps/mis-server/src/services/user.ts index 490113dea7..6263b38418 100644 --- a/apps/mis-server/src/services/user.ts +++ b/apps/mis-server/src/services/user.ts @@ -34,6 +34,7 @@ import { import { blockUserInAccount, unblockUserInAccount } from "src/bl/block"; import { getActivatedClusters } from "src/bl/clustersUtils"; import { authUrl } from "src/config"; +import { configClusters } from "src/config/clusters"; import { Account } from "src/entities/Account"; import { Tenant } from "src/entities/Tenant"; import { PlatformRole, TenantRole, User } from "src/entities/User"; @@ -440,7 +441,9 @@ export const userServiceServer = plugin((server) => { server.logger) .then(async () => { // insert public key - await insertKeyToNewUser(identityId, password, server.logger) + // 插入公钥失败也认为是创建用户成功 + // 在所有集群下执行 + await insertKeyToNewUser(identityId, password, server.logger, configClusters) .catch(() => {}); return true; }) diff --git a/apps/mis-server/src/utils/createUser.ts b/apps/mis-server/src/utils/createUser.ts index 2ce8d85861..237f3fc9fc 100644 --- a/apps/mis-server/src/utils/createUser.ts +++ b/apps/mis-server/src/utils/createUser.ts @@ -15,7 +15,7 @@ import { ServiceError } from "@grpc/grpc-js"; import { Status } from "@grpc/grpc-js/build/src/constants"; import { UniqueConstraintViolationException } from "@mikro-orm/core"; import { MySqlDriver, SqlEntityManager } from "@mikro-orm/mysql"; -import { getLoginNode } from "@scow/config/build/cluster"; +import { ClusterConfigSchema, getLoginNode } from "@scow/config/build/cluster"; import { insertKeyAsUser } from "@scow/lib-ssh"; import { configClusters } from "src/config/clusters"; import { rootKeyPair } from "src/config/env"; @@ -66,11 +66,12 @@ export async function insertKeyToNewUser( userId: string, password: string, logger: Logger, + currentClusters: Record, ) { // Making an ssh Request to the login node as the user created. if (process.env.NODE_ENV === "production") { - await Promise.all(Object.values(configClusters).map(async ({ displayName, loginNodes }) => { + await Promise.all(Object.values(currentClusters).map(async ({ displayName, loginNodes }) => { const node = getLoginNode(loginNodes[0]); logger.info("Checking if user can login to %s by login node %s", displayName, node.name); diff --git a/apps/portal-server/src/app.ts b/apps/portal-server/src/app.ts index 826c502efa..55c24d3449 100644 --- a/apps/portal-server/src/app.ts +++ b/apps/portal-server/src/app.ts @@ -12,6 +12,7 @@ import { Server } from "@ddadaal/tsgrpc-server"; import { omitConfigSpec } from "@scow/lib-config"; +import { libGetCurrentActivatedClusters } from "@scow/lib-server"; import { readVersionFile } from "@scow/utils/build/version"; import { configClusters } from "src/config/clusters"; import { config } from "src/config/env"; @@ -28,6 +29,8 @@ import { setupProxyGateway } from "src/utils/proxy"; import { initShellFile } from "src/utils/shell"; import { checkClustersRootUserLogin } from "src/utils/ssh"; +import { commonConfig } from "./config/common"; + export async function createServer() { const server = new Server({ @@ -51,17 +54,20 @@ export async function createServer() { await server.register(dashboardServiceServer); await server.register(fileServiceServer); await server.register(desktopServiceServer); - if (process.env.NODE_ENV === "production") { - await checkClustersRootUserLogin(server.logger); - await Promise.all(Object.entries(configClusters).map(async ([id]) => { + const activatedClusters = await libGetCurrentActivatedClusters( + server.logger, + configClusters, + config.MIS_SERVER_URL, + commonConfig.scowApi?.auth?.token); + + await checkClustersRootUserLogin(server.logger, activatedClusters); + await Promise.all(Object.entries(activatedClusters).map(async ([id]) => { await initShellFile(id, server.logger); })); - await setupProxyGateway(server.logger); + await setupProxyGateway(server.logger, activatedClusters); } - - return server; } diff --git a/apps/portal-server/src/utils/proxy.ts b/apps/portal-server/src/utils/proxy.ts index 1e6e265df2..316bcbab3c 100644 --- a/apps/portal-server/src/utils/proxy.ts +++ b/apps/portal-server/src/utils/proxy.ts @@ -10,6 +10,7 @@ * See the Mulan PSL v2 for more details. */ +import { ClusterConfigSchema } from "@scow/config/build/cluster"; import { loggedExec, sftpWriteFile } from "@scow/lib-ssh"; import { dirname } from "path"; import { configClusters } from "src/config/clusters"; @@ -17,12 +18,12 @@ import { config } from "src/config/env"; import { sshConnect } from "src/utils/ssh"; import { Logger } from "ts-log"; -export const setupProxyGateway = async (logger: Logger) => { +export const setupProxyGateway = async (logger: Logger, activatedClusters: Record) => { let portalBasePath = config.PORTAL_BASE_PATH; if (!portalBasePath.endsWith("/")) { portalBasePath += "/"; } - for (const id of Object.keys(configClusters)) { + for (const id of Object.keys(activatedClusters)) { const proxyGatewayConfig = configClusters[id].proxyGateway; diff --git a/apps/portal-server/src/utils/ssh.ts b/apps/portal-server/src/utils/ssh.ts index 39f225d6aa..6e3156f611 100644 --- a/apps/portal-server/src/utils/ssh.ts +++ b/apps/portal-server/src/utils/ssh.ts @@ -12,7 +12,7 @@ import { ServiceError } from "@ddadaal/tsgrpc-common"; import { status } from "@grpc/grpc-js"; -import { getLoginNode } from "@scow/config/build/cluster"; +import { ClusterConfigSchema, getLoginNode } from "@scow/config/build/cluster"; import { scowErrorMetadata } from "@scow/lib-server/build/error"; import { SftpError, sshConnect as libConnect, SshConnectError, testRootUserSshLogin } from "@scow/lib-ssh"; import { NodeSSH } from "node-ssh"; @@ -34,7 +34,7 @@ export function getConfigClusterLoginNode(cluster: string): string | undefined { return loginNode?.address; } -// TODO: 不要?在线集群节点信息 +// 获取集群中各节点信息 export function getClusterLoginNode(cluster: string): string | undefined { const loginNode = getLoginNode(configClusters[cluster]?.loginNodes?.[0]); return loginNode?.address; @@ -119,8 +119,11 @@ export async function sshConnect( /** * Check whether all clusters can be logged in as root user */ -export async function checkClustersRootUserLogin(logger: Logger) { - await Promise.all(Object.values(configClusters).map(async ({ displayName, loginNodes }) => { +export async function checkClustersRootUserLogin( + logger: Logger, + activatedClusters: Record, +) { + await Promise.all(Object.values(activatedClusters).map(async ({ displayName, loginNodes }) => { const node = getLoginNode(loginNodes[0]); logger.info("Checking if root can login to %s by login node %s", displayName, node.name); const error = await testRootUserSshLogin(node.address, rootKeyPair, console); From 3b04a846b2d08a316f542ab3398069ce22cf78a8 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Tue, 9 Jul 2024 06:46:54 +0000 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E5=A4=84=E7=90=86=E8=8C=83=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mis-server/src/services/misConfig.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/mis-server/src/services/misConfig.ts b/apps/mis-server/src/services/misConfig.ts index d400fe4352..cfb6c2d240 100644 --- a/apps/mis-server/src/services/misConfig.ts +++ b/apps/mis-server/src/services/misConfig.ts @@ -79,8 +79,7 @@ export const misConfigServiceServer = plugin((server) => { activateCluster: async ({ request, em, logger }) => { const { clusterId, operatorId } = request; - - await em.transactional(async (em) => { + return await em.transactional(async (em) => { const cluster = await em.findOne(Cluster, { clusterId }); if (!cluster) { @@ -147,9 +146,10 @@ export const misConfigServiceServer = plugin((server) => { operatorId, ); - }); - return [{ executed: true }]; + return [{ executed: true }]; + + }); }, From 802ac988e294eb6fb2d852f81ce020de5e1774cd Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Tue, 9 Jul 2024 15:10:11 +0000 Subject: [PATCH 03/10] fix --- .changeset/twelve-mugs-pump.md | 7 +++++++ apps/mis-web/src/pages/_app.tsx | 6 ++---- apps/mis-web/src/stores/ClusterInfoStore.ts | 12 +++++++++--- libs/config/src/cluster.ts | 2 +- libs/web/src/utils/cluster.ts | 3 +-- 5 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 .changeset/twelve-mugs-pump.md diff --git a/.changeset/twelve-mugs-pump.md b/.changeset/twelve-mugs-pump.md new file mode 100644 index 0000000000..98540f53da --- /dev/null +++ b/.changeset/twelve-mugs-pump.md @@ -0,0 +1,7 @@ +--- +"@scow/mis-web": patch +"@scow/config": patch +"@scow/lib-web": patch +--- + +修复系统初始化时作业价格表设置页面查询参数报错问题 diff --git a/apps/mis-web/src/pages/_app.tsx b/apps/mis-web/src/pages/_app.tsx index 985c964c29..63cebfaf15 100644 --- a/apps/mis-web/src/pages/_app.tsx +++ b/apps/mis-web/src/pages/_app.tsx @@ -256,9 +256,7 @@ MyApp.getInitialProps = async (appContext: AppContext) => { } } - const clustersRuntimeInfo = token ? - await api.getClustersRuntimeInfo({ query: { token } }).then((x) => x, () => undefined) - : await api.getClustersRuntimeInfo({ query: { } }).then((x) => x, () => undefined); + const clustersRuntimeInfo = await api.getClustersRuntimeInfo({ query: { token } }).then((x) => x, () => undefined); // get deployed clusters' simple info (only clusterId, displayName and priority) const simpleClustersInfo @@ -266,7 +264,7 @@ MyApp.getInitialProps = async (appContext: AppContext) => { extra.initialSimpleClustersInfo = simpleClustersInfo?.clustersInfo; - const publicConfigClusters = Object.keys(extra.clusterConfigs).length > 0 ? + const publicConfigClusters = extra.clusterConfigs && Object.keys(extra.clusterConfigs).length > 0 ? getPublicConfigClusters(extra.clusterConfigs) : getPublicConfigClusters(extra.initialSimpleClustersInfo) ?? {}; const activatedClusters diff --git a/apps/mis-web/src/stores/ClusterInfoStore.ts b/apps/mis-web/src/stores/ClusterInfoStore.ts index e4e102911f..f06d8b1f1d 100644 --- a/apps/mis-web/src/stores/ClusterInfoStore.ts +++ b/apps/mis-web/src/stores/ClusterInfoStore.ts @@ -22,10 +22,16 @@ export function ClusterInfoStore( initialSimpleClusters: Record, ) { - const publicConfigClusters = getPublicConfigClusters(clusterConfigs) - ?? getPublicConfigClusters(initialSimpleClusters) ?? {}; + let publicConfigClusters: Record = {}; + let clusterSortedIdList: string[] = []; - const clusterSortedIdList = getSortedClusterIds(clusterConfigs) ?? getSortedClusterIds(initialSimpleClusters) ?? []; + if (Object.keys(clusterConfigs).length > 0) { + clusterSortedIdList = getSortedClusterIds(clusterConfigs); + publicConfigClusters = getPublicConfigClusters(clusterConfigs); + } else { + clusterSortedIdList = getSortedClusterIds(initialSimpleClusters ?? {}); + publicConfigClusters = getPublicConfigClusters(initialSimpleClusters ?? {}); + } const [activatedClusters, setActivatedClusters] = useState>(initialActivatedClusters); diff --git a/libs/config/src/cluster.ts b/libs/config/src/cluster.ts index 7a02e646a2..05d4a6d173 100644 --- a/libs/config/src/cluster.ts +++ b/libs/config/src/cluster.ts @@ -21,7 +21,7 @@ const CLUSTER_CONFIG_BASE_PATH = "clusters"; export const SimpleClusterSchema = Type.Object({ clusterId: Type.String(), displayName: createI18nStringSchema({ description: "集群名称" }), - priority: Type.Number(), + priority: Type.Number({ description: "集群使用的优先级, 数字越小越先展示", default: Number.MAX_SAFE_INTEGER }), }); export type SimpleClusterSchema = Static; diff --git a/libs/web/src/utils/cluster.ts b/libs/web/src/utils/cluster.ts index 40373c0486..f9c36fddd4 100644 --- a/libs/web/src/utils/cluster.ts +++ b/libs/web/src/utils/cluster.ts @@ -16,8 +16,7 @@ export const getSortedClusterIds = (clusters: Record { - return (clusters[a].priority ?? Number.MAX_SAFE_INTEGER) - - (clusters[b].priority ?? Number.MIN_SAFE_INTEGER); + return clusters[a].priority! - clusters[b].priority!; }, ); }; From 81e4c1149254babb4eed588a02be972f2f3c26ae Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Wed, 10 Jul 2024 02:58:59 +0000 Subject: [PATCH 04/10] =?UTF-8?q?fix:=20api=E8=AF=B7=E6=B1=82=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E4=BC=9A=E6=8C=82=E8=B5=B7=E5=8F=8A=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mis-web/src/pages/_app.tsx | 4 ++-- apps/mis-web/src/pages/api/admin/getClustersRuntimeInfo.ts | 5 ++++- apps/mis-web/src/pages/api/simpleClustersInfo.ts | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/mis-web/src/pages/_app.tsx b/apps/mis-web/src/pages/_app.tsx index 63cebfaf15..14688db570 100644 --- a/apps/mis-web/src/pages/_app.tsx +++ b/apps/mis-web/src/pages/_app.tsx @@ -262,14 +262,14 @@ MyApp.getInitialProps = async (appContext: AppContext) => { const simpleClustersInfo = await api.getSimpleClustersInfoFromConfigFiles({}).then((x) => x, () => ({ clustersInfo: {} })); - extra.initialSimpleClustersInfo = simpleClustersInfo?.clustersInfo; + extra.initialSimpleClustersInfo = simpleClustersInfo?.clustersInfo ?? {}; const publicConfigClusters = extra.clusterConfigs && Object.keys(extra.clusterConfigs).length > 0 ? getPublicConfigClusters(extra.clusterConfigs) : getPublicConfigClusters(extra.initialSimpleClustersInfo) ?? {}; const activatedClusters = formatActivatedClusters({ - clustersRuntimeInfo: clustersRuntimeInfo?.results, + clustersRuntimeInfo: clustersRuntimeInfo?.results ?? [], misConfigClusters: publicConfigClusters }); extra.initialActivatedClusters = activatedClusters.misActivatedClusters ?? {}; diff --git a/apps/mis-web/src/pages/api/admin/getClustersRuntimeInfo.ts b/apps/mis-web/src/pages/api/admin/getClustersRuntimeInfo.ts index 91a4f3781b..0d20135c3d 100644 --- a/apps/mis-web/src/pages/api/admin/getClustersRuntimeInfo.ts +++ b/apps/mis-web/src/pages/api/admin/getClustersRuntimeInfo.ts @@ -36,6 +36,8 @@ export const GetClustersRuntimeInfoSchema = typeboxRouteSchema({ 200: Type.Object({ results: Type.Array(ClusterRuntimeInfoSchema), }), + + 403: Type.Null(), }, }); @@ -46,11 +48,12 @@ export default route(GetClustersRuntimeInfoSchema, // if not initialized, every one can get clustersRuntimeInfo if (await queryIfInitialized()) { + const { token } = req.query; // when firstly used in getInitialProps, check the token // when logged in, use auth() const info = token ? await validateToken(token) : await auth(req, res); - if (!info) { return; } + if (!info) { return { 403: null }; } } const client = getClient(ConfigServiceClient); diff --git a/apps/mis-web/src/pages/api/simpleClustersInfo.ts b/apps/mis-web/src/pages/api/simpleClustersInfo.ts index 1df3ad4023..799ed36384 100644 --- a/apps/mis-web/src/pages/api/simpleClustersInfo.ts +++ b/apps/mis-web/src/pages/api/simpleClustersInfo.ts @@ -37,6 +37,8 @@ export const GetSimpleClustersInfoFromConfigFilesSchema = typeboxRouteSchema({ 200: Type.Object({ clustersInfo: Type.Record(Type.String(), SimpleClusterSchema) }), + + 403: Type.Null(), }, }); @@ -47,7 +49,7 @@ export default route(GetSimpleClustersInfoFromConfigFilesSchema, // if not initialized, every one can getSimpleClusterInfo which includes clusterId, displayedName and priority if (await queryIfInitialized()) { const info = await auth(req, res); - if (!info) { return; } + if (!info) { return { 403: null }; } } const clustersFullInfo: Record = await getClusterConfigFiles(); From 2b02dfcf956072c9409a7f931f2d201aed71a561 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Wed, 10 Jul 2024 05:57:29 +0000 Subject: [PATCH 05/10] fix --- apps/mis-server/src/plugins/clusters.ts | 7 ++++++- apps/mis-server/src/tasks/fetch.ts | 6 +++++- apps/mis-web/src/utils/route.ts | 2 +- apps/portal-web/src/utils/route.ts | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/mis-server/src/plugins/clusters.ts b/apps/mis-server/src/plugins/clusters.ts index 633b8c300e..b351affe02 100644 --- a/apps/mis-server/src/plugins/clusters.ts +++ b/apps/mis-server/src/plugins/clusters.ts @@ -59,7 +59,11 @@ export const clustersPlugin = plugin(async (f) => { if (process.env.NODE_ENV === "production") { // only check activated clusters' root user login when system is starting - const activatedClusters = await getActivatedClusters(f.ext.orm.em.fork(), f.logger); + const activatedClusters = await getActivatedClusters(f.ext.orm.em.fork(), f.logger).catch((e) => { + f.logger.info("!!![important] No available activated clusters.This will skip root ssh login check in cluster!!!"); + f.logger.info(e); + return {}; + }); await Promise.all(Object.values(activatedClusters).map(async ({ displayName, loginNodes }) => { const loginNode = getLoginNode(loginNodes[0]); @@ -74,6 +78,7 @@ export const clustersPlugin = plugin(async (f) => { f.logger.info("Root can login to %s by login node %s", displayName, node); } })); + } // adapterClient of all config clusters diff --git a/apps/mis-server/src/tasks/fetch.ts b/apps/mis-server/src/tasks/fetch.ts index aba6609f89..0e7b2cb6d6 100644 --- a/apps/mis-server/src/tasks/fetch.ts +++ b/apps/mis-server/src/tasks/fetch.ts @@ -80,7 +80,11 @@ export async function fetchJobs( const persistJobAndCharge = async (jobs: ({ cluster: string } & ClusterJobInfo)[]) => { const result = await em.transactional(async (em) => { - const currentActivatedClusters = await getActivatedClusters(em, logger); + const currentActivatedClusters = await getActivatedClusters(em, logger).catch((e) => { + logger.info("!!![important] No available activated clusters.This will skip fetchJobs in cluster!!!"); + logger.info(e); + return {}; + }); ; // Calculate prices for new info and persist const pricedJobs: JobInfo[] = []; diff --git a/apps/mis-web/src/utils/route.ts b/apps/mis-web/src/utils/route.ts index e6ff73d2b7..f38985416c 100644 --- a/apps/mis-web/src/utils/route.ts +++ b/apps/mis-web/src/utils/route.ts @@ -28,7 +28,7 @@ export const route: typeof typeboxRoute = (schema, handler) => { const SCOW_ERROR = e.metadata.get("IS_SCOW_ERROR"); if (!SCOW_ERROR) { throw e; } - const code = e.metadata.get("SCOW_ERROR_CODE")[0].toString(); + const code = e.metadata.get("SCOW_ERROR_CODE")?.[0]?.toString(); const details = e.details; // 如果包含集群详细错误信息 diff --git a/apps/portal-web/src/utils/route.ts b/apps/portal-web/src/utils/route.ts index f7cdba7d40..b3a747ab0e 100644 --- a/apps/portal-web/src/utils/route.ts +++ b/apps/portal-web/src/utils/route.ts @@ -30,7 +30,7 @@ export const route: typeof typeboxRoute = (schema, handler) => { const SCOW_CAUSE = (e.metadata as Metadata).get("cause"); if (SCOW_ERROR.length === 0) { throw e; } - const code = e.metadata.get("SCOW_ERROR_CODE")[0].toString(); + const code = e.metadata.get("SCOW_ERROR_CODE")?.[0]?.toString(); const details = e.details; const message = SCOW_CAUSE[0]; From 8e3dd9a7ba9478d105016409162bfc66c4dd91e6 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Wed, 10 Jul 2024 08:15:51 +0000 Subject: [PATCH 06/10] =?UTF-8?q?fix:=20createPriceMap=E4=B8=AD=E4=B9=9F?= =?UTF-8?q?=E8=B7=B3=E8=BF=87=E6=97=A0=E9=9B=86=E7=BE=A4=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mis-server/src/bl/PriceMap.ts | 11 +++++++++-- apps/mis-server/src/tasks/fetch.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/mis-server/src/bl/PriceMap.ts b/apps/mis-server/src/bl/PriceMap.ts index baa696a2d4..b37b113365 100644 --- a/apps/mis-server/src/bl/PriceMap.ts +++ b/apps/mis-server/src/bl/PriceMap.ts @@ -20,6 +20,7 @@ import { misConfig } from "src/config/mis"; import { JobPriceInfo } from "src/entities/JobInfo"; import { AmountStrategy, JobPriceItem } from "src/entities/JobPriceItem"; import { ClusterPlugin } from "src/plugins/clusters"; +import { getActivatedClusters } from "./clustersUtils"; export interface JobInfo { // cluster job id @@ -91,12 +92,18 @@ export async function createPriceMap( // partitions info for all clusters const partitionsForClusters: Record = {}; - // call for all config clusters + // call for all activated clusters + const activatedClusters = await getActivatedClusters(em, logger).catch((e) => { + logger.info("!!![important] No available activated clusters.This will skip creating price map in cluster!!!"); + logger.info(e); + return {}; + }); const reply = await clusterPlugin.callOnAll( - configClusters, + activatedClusters, logger, async (client) => await asyncClientCall(client.config, "getClusterConfig", {}), ); + reply.forEach((x) => { partitionsForClusters[x.cluster] = x.result.partitions; }); diff --git a/apps/mis-server/src/tasks/fetch.ts b/apps/mis-server/src/tasks/fetch.ts index 0e7b2cb6d6..6c16bcfa86 100644 --- a/apps/mis-server/src/tasks/fetch.ts +++ b/apps/mis-server/src/tasks/fetch.ts @@ -81,7 +81,7 @@ export async function fetchJobs( const result = await em.transactional(async (em) => { const currentActivatedClusters = await getActivatedClusters(em, logger).catch((e) => { - logger.info("!!![important] No available activated clusters.This will skip fetchJobs in cluster!!!"); + logger.info("!!![important] No available activated clusters.This will skip fetching Jobs in cluster!!!"); logger.info(e); return {}; }); ; From cc31acdd64413f4cb69b394e3e6d7108ed4d15e6 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Wed, 10 Jul 2024 09:00:43 +0000 Subject: [PATCH 07/10] =?UTF-8?q?fix=EF=BC=9Alint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mis-server/src/bl/PriceMap.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/mis-server/src/bl/PriceMap.ts b/apps/mis-server/src/bl/PriceMap.ts index b37b113365..1a59f66f87 100644 --- a/apps/mis-server/src/bl/PriceMap.ts +++ b/apps/mis-server/src/bl/PriceMap.ts @@ -20,6 +20,7 @@ import { misConfig } from "src/config/mis"; import { JobPriceInfo } from "src/entities/JobInfo"; import { AmountStrategy, JobPriceItem } from "src/entities/JobPriceItem"; import { ClusterPlugin } from "src/plugins/clusters"; + import { getActivatedClusters } from "./clustersUtils"; export interface JobInfo { From 5bd6961c6526eeacee362c49e765b47af3d95389 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Wed, 10 Jul 2024 11:02:31 +0000 Subject: [PATCH 08/10] fix --- apps/mis-server/src/bl/PriceMap.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/mis-server/src/bl/PriceMap.ts b/apps/mis-server/src/bl/PriceMap.ts index 1a59f66f87..e718dfe4c0 100644 --- a/apps/mis-server/src/bl/PriceMap.ts +++ b/apps/mis-server/src/bl/PriceMap.ts @@ -15,7 +15,6 @@ import { Logger } from "@ddadaal/tsgrpc-server"; import { MySqlDriver, SqlEntityManager } from "@mikro-orm/mysql"; import { Partition } from "@scow/scheduler-adapter-protos/build/protos/config"; import { calculateJobPrice } from "src/bl/jobPrice"; -import { configClusters } from "src/config/clusters"; import { misConfig } from "src/config/mis"; import { JobPriceInfo } from "src/entities/JobInfo"; import { AmountStrategy, JobPriceItem } from "src/entities/JobPriceItem"; @@ -117,7 +116,7 @@ export async function createPriceMap( const missingPaths = [] as string[]; - for (const cluster in configClusters) { + for (const cluster in activatedClusters) { for (const partition of partitionsForClusters[cluster]) { const path = [cluster, partition.name]; const { qos } = partition; From 6b159acaed326f619f4a340dce5f650545894748 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Mon, 15 Jul 2024 00:51:44 +0000 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20createPriceMap=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8callOnAll,=20=E9=80=82=E9=85=8D=E5=99=A8?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=BC=82=E5=B8=B8=E6=97=B6=E4=B9=9F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=90=AF=E7=94=A8=E6=8C=89=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mis-server/src/bl/PriceMap.ts | 26 ++++++++++++------- apps/mis-server/src/tasks/fetch.ts | 2 +- .../admin/ClusterManagementTable.tsx | 1 - 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/apps/mis-server/src/bl/PriceMap.ts b/apps/mis-server/src/bl/PriceMap.ts index e718dfe4c0..0259eed9fe 100644 --- a/apps/mis-server/src/bl/PriceMap.ts +++ b/apps/mis-server/src/bl/PriceMap.ts @@ -89,8 +89,7 @@ export async function createPriceMap( return price; }; - // partitions info for all clusters - const partitionsForClusters: Record = {}; + // call for all activated clusters const activatedClusters = await getActivatedClusters(em, logger).catch((e) => { @@ -98,15 +97,22 @@ export async function createPriceMap( logger.info(e); return {}; }); - const reply = await clusterPlugin.callOnAll( - activatedClusters, - logger, - async (client) => await asyncClientCall(client.config, "getClusterConfig", {}), - ); - reply.forEach((x) => { - partitionsForClusters[x.cluster] = x.result.partitions; - }); + // partitions info for all clusters + const partitionsForClusters: Record = {}; + + await Promise.allSettled(Object.keys(activatedClusters).map(async (cluster) => { + try { + const result = await clusterPlugin.callOnOne( + cluster, + logger, + async (client) => await asyncClientCall(client.config, "getClusterConfig", {}), + ); + partitionsForClusters[cluster] = result.partitions; + } catch (error) { + logger.info(`Can not get cluster's (clusterId: ${cluster}) config info from adapter.`, error); + }; + })); return { diff --git a/apps/mis-server/src/tasks/fetch.ts b/apps/mis-server/src/tasks/fetch.ts index 6c16bcfa86..2fcc541fb6 100644 --- a/apps/mis-server/src/tasks/fetch.ts +++ b/apps/mis-server/src/tasks/fetch.ts @@ -84,7 +84,7 @@ export async function fetchJobs( logger.info("!!![important] No available activated clusters.This will skip fetching Jobs in cluster!!!"); logger.info(e); return {}; - }); ; + }); // Calculate prices for new info and persist const pricedJobs: JobInfo[] = []; diff --git a/apps/mis-web/src/pageComponents/admin/ClusterManagementTable.tsx b/apps/mis-web/src/pageComponents/admin/ClusterManagementTable.tsx index 39975096db..8559271487 100644 --- a/apps/mis-web/src/pageComponents/admin/ClusterManagementTable.tsx +++ b/apps/mis-web/src/pageComponents/admin/ClusterManagementTable.tsx @@ -188,7 +188,6 @@ export const ClusterManagementTable: React.FC = ({ } { r.hpcEnabled && r.activationStatus === ClusterActivationStatus.DEACTIVATED - && r.connectionStatus === ClusterConnectionStatus.AVAILABLE && ( <> { From 9268920827f48708f5cff2a998d013bc5b3a15e0 Mon Sep 17 00:00:00 2001 From: Picca Sun Date: Mon, 15 Jul 2024 09:39:05 +0000 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0partitionsForClu?= =?UTF-8?q?sters[cluster]=E4=B8=8D=E5=AD=98=E5=9C=A8=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E5=AE=B9=E9=94=99=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mis-server/src/bl/PriceMap.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/mis-server/src/bl/PriceMap.ts b/apps/mis-server/src/bl/PriceMap.ts index 0259eed9fe..1a9c60b0fd 100644 --- a/apps/mis-server/src/bl/PriceMap.ts +++ b/apps/mis-server/src/bl/PriceMap.ts @@ -98,7 +98,7 @@ export async function createPriceMap( return {}; }); - // partitions info for all clusters + // partitions info for activated clusters const partitionsForClusters: Record = {}; await Promise.allSettled(Object.keys(activatedClusters).map(async (cluster) => { @@ -123,6 +123,13 @@ export async function createPriceMap( const missingPaths = [] as string[]; for (const cluster in activatedClusters) { + + if (!partitionsForClusters[cluster]) { + logger.info( + `Can not get missing default price items from partitions of cluster (clusterId: ${cluster}) currently.`); + continue; + } + for (const partition of partitionsForClusters[cluster]) { const path = [cluster, partition.name]; const { qos } = partition;