Skip to content

Commit

Permalink
fix(mis): 系统初始化时可用集群报错 (#1345)
Browse files Browse the repository at this point in the history
### 问题

因初始化时无法通过token或auth鉴权,导致系统初始化时的可用集群查询结果为空

![image](https://github.com/PKUHPC/SCOW/assets/43978285/8b5fa308-6044-4622-9e81-15e055d7fa01)

### 修改

在管理系统的web端原有获取集群配置信息接口以外增加返回简单集群信息的web接口
`getSimpleClustersInfoFromConfigFiles`
返回信息仅包含 `clusterId, displayName,` 及用于优先级排序的 `priority`

在`getSimpleClustersInfoFromConfigFiles` 及获取当前集群在线信息接口中`
getClustersRuntimeInfo` 增加是否已初始化判断,未初始化时允许所有人获取简单集群在线信息

### 修改后

系统初始化时获取集群信息正常,各操作正常,初始化完成后登录正常


![集群停用初始化](https://github.com/PKUHPC/SCOW/assets/43978285/3149453e-f0b0-495b-8c40-8edf391211ed)
<img width="677" alt="faa3afca5e2e2cc4219edc077f2ef6f"
src="https://github.com/PKUHPC/SCOW/assets/43978285/59f8b8a9-222d-492f-bb33-89bdc404d54e">
  • Loading branch information
piccaSun authored Jul 8, 2024
1 parent 1a096de commit 0275a9e
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 43 deletions.
8 changes: 8 additions & 0 deletions .changeset/chilled-schools-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@scow/mis-web": patch
"@scow/config": patch
"@scow/lib-ssh": patch
"@scow/lib-web": patch
---

修复系统初始化时因无法通过鉴权可用集群为空的问题
10 changes: 10 additions & 0 deletions apps/mis-web/src/apis/api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,16 @@ export const mockApi: MockApi<typeof api> = {
},
} }),

getSimpleClustersInfoFromConfigFiles: async () => ({
clustersInfo: {
hpc01: {
displayName: "hpc01Name",
priority: 1,
clusterId: "hpc01",
},
},
}),

getClustersConnectionInfo: async () => ({ results: [{
clusterId: "hpc01",
schedulerName: "hpc",
Expand Down
6 changes: 4 additions & 2 deletions apps/mis-web/src/apis/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
/* eslint-disable max-len */

import { apiClient } from "src/apis/client";
import type { getClusterConfigFilesSchema } from "src/pages/api//clusterConfigsInfo";
import type { GetClusterConfigFilesSchema } from "src/pages/api//clusterConfigsInfo";
import type { ActivateClusterSchema } from "src/pages/api/admin/activateCluster";
import type { ChangeJobPriceSchema } from "src/pages/api/admin/changeJobPrice";
import type { ChangePasswordAsPlatformAdminSchema } from "src/pages/api/admin/changePassword";
Expand Down Expand Up @@ -93,6 +93,7 @@ import type { GetOperationLogsSchema } from "src/pages/api/log/getOperationLog";
import type { ChangeEmailSchema } from "src/pages/api/profile/changeEmail";
import type { ChangePasswordSchema } from "src/pages/api/profile/changePassword";
import type { CheckPasswordSchema } from "src/pages/api/profile/checkPassword";
import { GetSimpleClustersInfoFromConfigFilesSchema } from "src/pages/api/simpleClustersInfo";
import type { DewhitelistAccountSchema } from "src/pages/api/tenant/accountWhitelist/dewhitelistAccount";
import type { GetWhitelistedAccountsSchema } from "src/pages/api/tenant/accountWhitelist/getWhitelistedAccounts";
import type { WhitelistAccountSchema } from "src/pages/api/tenant/accountWhitelist/whitelistAccount";
Expand Down Expand Up @@ -167,7 +168,7 @@ export const api = {
authCallback: apiClient.fromTypeboxRoute<typeof AuthCallbackSchema>("GET", "/api/auth/callback"),
logout: apiClient.fromTypeboxRoute<typeof LogoutSchema>("DELETE", "/api/auth/logout"),
validateToken: apiClient.fromTypeboxRoute<typeof ValidateTokenSchema>("GET", "/api/auth/validateToken"),
getClusterConfigFiles: apiClient.fromTypeboxRoute<typeof getClusterConfigFilesSchema>("GET", "/api//clusterConfigsInfo"),
getClusterConfigFiles: apiClient.fromTypeboxRoute<typeof GetClusterConfigFilesSchema>("GET", "/api//clusterConfigsInfo"),
getUserStatus: apiClient.fromTypeboxRoute<typeof GetUserStatusSchema>("GET", "/api/dashboard/status"),
exportAccount: apiClient.fromTypeboxRoute<typeof ExportAccountSchema>("GET", "/api/file/exportAccount"),
exportChargeRecord: apiClient.fromTypeboxRoute<typeof ExportChargeRecordSchema>("GET", "/api/file/exportChargeRecord"),
Expand Down Expand Up @@ -226,4 +227,5 @@ export const api = {
queryStorageUsage: apiClient.fromTypeboxRoute<typeof QueryStorageUsageSchema>("GET", "/api/users/storageUsage"),
unblockUserInAccount: apiClient.fromTypeboxRoute<typeof UnblockUserInAccountSchema>("PUT", "/api/users/unblockInAccount"),
unsetAdmin: apiClient.fromTypeboxRoute<typeof UnsetAdminSchema>("PUT", "/api/users/unsetAdmin"),
getSimpleClustersInfoFromConfigFiles: apiClient.fromTypeboxRoute<typeof GetSimpleClustersInfoFromConfigFilesSchema>("GET", "/api//simpleClustersInfo"),
};
44 changes: 27 additions & 17 deletions apps/mis-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import "nprogress/nprogress.css";
import "antd/dist/reset.css";

import { failEvent } from "@ddadaal/next-typed-api-routes-runtime/lib/client";
import { ClusterConfigSchema } from "@scow/config/build/cluster";
import { ClusterConfigSchema, SimpleClusterSchema } from "@scow/config/build/cluster";
import { UiExtensionStore } from "@scow/lib-web/build/extensions/UiExtensionStore";
import { DarkModeCookie, DarkModeProvider, getDarkModeCookieValue } from "@scow/lib-web/build/layouts/darkMode";
import { GlobalStyle } from "@scow/lib-web/build/layouts/globalStyle";
Expand Down Expand Up @@ -46,6 +46,7 @@ import {
} from "src/stores/UserStore";
import { Cluster, getPublicConfigClusters } from "src/utils/cluster";
import { publicConfig, runtimeConfig } from "src/utils/config";
import { queryIfInitialized } from "src/utils/init";

const languagesMap = {
"zh_cn": zh_cn,
Expand Down Expand Up @@ -137,8 +138,9 @@ interface ExtraProps {
footerText: string;
darkModeCookieValue: DarkModeCookie | undefined;
initialLanguage: string;
clusterConfigs: { [clusterId: string]: ClusterConfigSchema; };
initialActivatedClusters: {[clusterId: string]: Cluster};
clusterConfigs: Record<string, ClusterConfigSchema>;
initialActivatedClusters: Record<string, Cluster>;
initialSimpleClustersInfo: Record<string, SimpleClusterSchema>;
}

type Props = AppProps & { extra: ExtraProps };
Expand All @@ -153,7 +155,7 @@ function MyApp({ Component, pageProps, extra }: Props) {
});

const clusterInfoStore = useConstant(() => {
return createStore(ClusterInfoStore, extra.clusterConfigs, extra.initialActivatedClusters);
return createStore(ClusterInfoStore, extra.clusterConfigs, extra.initialActivatedClusters, extra.initialSimpleClustersInfo);
});

const uiExtensionStore = useConstant(() => createStore(UiExtensionStore, publicConfig.UI_EXTENSION));
Expand Down Expand Up @@ -216,6 +218,7 @@ MyApp.getInitialProps = async (appContext: AppContext) => {
initialLanguage: "",
clusterConfigs: {},
initialActivatedClusters: {},
initialSimpleClustersInfo: {},
};

// This is called on server on first load, and on client on every page transition
Expand Down Expand Up @@ -246,24 +249,31 @@ MyApp.getInitialProps = async (appContext: AppContext) => {

const clusterConfigs = data?.clusterConfigs;
if (clusterConfigs && Object.keys(clusterConfigs).length > 0) {

extra.clusterConfigs = clusterConfigs;
const publicConfigClusters
= getPublicConfigClusters(clusterConfigs);
// get initial activated clusters
const clustersRuntimeInfo =
await api.getClustersRuntimeInfo({ query: { token } }).then((x) => x, () => undefined);

const activatedClusters
= formatActivatedClusters({
clustersRuntimeInfo: clustersRuntimeInfo?.results,
misConfigClusters: publicConfigClusters });
extra.initialActivatedClusters = activatedClusters.misActivatedClusters ?? {};

}
}
}

const clustersRuntimeInfo = token ?
await api.getClustersRuntimeInfo({ query: { token } }).then((x) => x, () => undefined)
: await api.getClustersRuntimeInfo({ query: { } }).then((x) => x, () => undefined);

// get deployed clusters' simple info (only clusterId, displayName and priority)
const simpleClustersInfo
= await api.getSimpleClustersInfoFromConfigFiles({}).then((x) => x, () => ({ clustersInfo: {} }));

extra.initialSimpleClustersInfo = simpleClustersInfo?.clustersInfo;

const publicConfigClusters = Object.keys(extra.clusterConfigs).length > 0 ?
getPublicConfigClusters(extra.clusterConfigs) : getPublicConfigClusters(extra.initialSimpleClustersInfo) ?? {};

const activatedClusters
= formatActivatedClusters({
clustersRuntimeInfo: clustersRuntimeInfo?.results,
misConfigClusters: publicConfigClusters });

extra.initialActivatedClusters = activatedClusters.misActivatedClusters ?? {};

const hostname = getHostname(appContext.ctx.req);

extra.primaryColor = (hostname && runtimeConfig.UI_CONFIG?.primaryColor?.hostnameMap?.[hostname])
Expand Down
14 changes: 9 additions & 5 deletions apps/mis-web/src/pages/api/admin/getClustersRuntimeInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { authenticate } from "src/auth/server";
import { validateToken } from "src/auth/token";
import { getClusterConfigFiles } from "src/server/clusterConfig";
import { getClient } from "src/utils/client";
import { queryIfInitialized } from "src/utils/init";
import { route } from "src/utils/route";

export const GetClustersRuntimeInfoSchema = typeboxRouteSchema({
Expand All @@ -43,11 +44,14 @@ const auth = authenticate(() => true);
export default route(GetClustersRuntimeInfoSchema,
async (req, res) => {

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 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; }
}

const client = getClient(ConfigServiceClient);
const result = await asyncClientCall(client, "getClustersRuntimeInfo", {});
Expand Down
5 changes: 3 additions & 2 deletions apps/mis-web/src/pages/api/clusterConfigsInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ import { Type } from "@sinclair/typebox";
import { authenticate } from "src/auth/server";
import { validateToken } from "src/auth/token";
import { getClusterConfigFiles } from "src/server/clusterConfig";
import { queryIfInitialized } from "src/utils/init";
import { route } from "src/utils/route";


export const getClusterConfigFilesSchema = typeboxRouteSchema({
export const GetClusterConfigFilesSchema = typeboxRouteSchema({
method: "GET",

// only set the query value when firstly used in getInitialProps
Expand All @@ -48,7 +49,7 @@ export const getClusterConfigFilesSchema = typeboxRouteSchema({

const auth = authenticate(() => true);

export default route(getClusterConfigFilesSchema,
export default route(GetClusterConfigFilesSchema,
async (req, res) => {

const { token } = req.query;
Expand Down
68 changes: 68 additions & 0 deletions apps/mis-web/src/pages/api/simpleClustersInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy
* SCOW is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

/**
* Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy
* SCOW is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

import { typeboxRouteSchema } from "@ddadaal/next-typed-api-routes-runtime";
import { getClusterConfigFiles } from "src/server/clusterConfig";
import { ClusterConfigSchema, SimpleClusterSchema } from "@scow/config/build/cluster";
import { route } from "src/utils/route";
import { Type } from "@sinclair/typebox";
import { queryIfInitialized } from "src/utils/init";
import { authenticate } from "src/auth/server";

export const GetSimpleClustersInfoFromConfigFilesSchema = typeboxRouteSchema({
method: "GET",

responses: {

200: Type.Object({
clustersInfo: Type.Record(Type.String(), SimpleClusterSchema) }),
},
});

const auth = authenticate(() => true);
export default route(GetSimpleClustersInfoFromConfigFilesSchema,
async (req, res) => {

// 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; }
}

const clustersFullInfo: Record<string, ClusterConfigSchema> = await getClusterConfigFiles();

const clustersInfo: Record<string, SimpleClusterSchema> = {};

Object.keys(clustersFullInfo).forEach(key => {
clustersInfo[key] = {
clusterId: key,
displayName: clustersFullInfo[key].displayName,
priority: clustersFullInfo[key].priority,
};
});

return {
200: { clustersInfo },
};
});
13 changes: 7 additions & 6 deletions apps/mis-web/src/stores/ClusterInfoStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@
* See the Mulan PSL v2 for more details.
*/

import { ClusterConfigSchema } from "@scow/config/build/cluster";
import { ClusterConfigSchema, SimpleClusterSchema } from "@scow/config/build/cluster";
import { getSortedClusterIds } from "@scow/lib-web/build/utils/cluster";
import { useEffect, useState } from "react";
import { Cluster, getPublicConfigClusters } from "src/utils/cluster";

// export function ClusterInfoStore(
export function ClusterInfoStore(
clusterConfigs: {[clusterId: string]: ClusterConfigSchema},
initialActivatedClusters: {[clusterId: string]: Cluster},
clusterConfigs: Record<string, ClusterConfigSchema>,
initialActivatedClusters: Record<string, Cluster>,
initialSimpleClusters: Record<string, SimpleClusterSchema>,
) {

const publicConfigClusters = getPublicConfigClusters(clusterConfigs);
const publicConfigClusters = getPublicConfigClusters(clusterConfigs) ?? getPublicConfigClusters(initialSimpleClusters) ?? {};

const clusterSortedIdList = getSortedClusterIds(clusterConfigs);
const clusterSortedIdList = getSortedClusterIds(clusterConfigs) ?? getSortedClusterIds(initialSimpleClusters) ?? [];

const [activatedClusters, setActivatedClusters]
= useState<{[clusterId: string]: Cluster}>(initialActivatedClusters);
= useState<Record<string, Cluster>>(initialActivatedClusters);

const initialDefaultClusterId = clusterSortedIdList.find((x) => {
return Object.keys(initialActivatedClusters).find((c) => c === x);
Expand Down
15 changes: 8 additions & 7 deletions apps/mis-web/src/utils/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* See the Mulan PSL v2 for more details.
*/

import { ClusterConfigSchema } from "@scow/config/build/cluster";
import { SimpleClusterSchema } from "@scow/config/build/cluster";
import { I18nStringType } from "@scow/config/build/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";

Expand All @@ -19,13 +19,13 @@ export type Cluster = { id: string; name: I18nStringType; }
export const getClusterName = (
clusterId: string,
languageId: string,
publicConfigClusters: { [clusterId: string]: Cluster },
publicConfigClusters: Record<string, Cluster>,
) => {
return getI18nConfigCurrentText(publicConfigClusters[clusterId]?.name, languageId) || clusterId;
};

export const getSortedClusterValues =
(publicConfigClusters: { [clusterId: string]: Cluster },
(publicConfigClusters: Record<string, Cluster>,
clusterSortedIdList: string[],
): Cluster[] => {

Expand All @@ -39,18 +39,19 @@ export const getSortedClusterValues =


export const getPublicConfigClusters =
(configClusters: Record<string, ClusterConfigSchema>):
{ [clusterId: string]: Cluster } => {
(configClusters: Record<string, Partial<SimpleClusterSchema>>):
Record<string, Cluster> => {

const publicConfigClusters: { [clusterId: string]: Cluster; } = {};
const publicConfigClusters: Record<string, Cluster> = {};

Object.keys(configClusters).forEach((clusterId) => {
const cluster = {
id: clusterId,
name: configClusters[clusterId].displayName,
name: configClusters[clusterId].displayName!,
};
publicConfigClusters[clusterId] = cluster;
});

return publicConfigClusters;
};

7 changes: 7 additions & 0 deletions libs/config/src/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ import { Logger } from "ts-log";

const CLUSTER_CONFIG_BASE_PATH = "clusters";

export const SimpleClusterSchema = Type.Object({
clusterId: Type.String(),
displayName: createI18nStringSchema({ description: "集群名称" }),
priority: Type.Number(),
});
export type SimpleClusterSchema = Static<typeof SimpleClusterSchema>;

export enum k8sRuntime {
docker = "docker",
containerd = "containerd",
Expand Down
1 change: 0 additions & 1 deletion libs/ssh/src/ssh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ export async function executeAsUser(
*/
export async function testRootUserSshLogin(host: string, keyPair: KeyPair, logger: Logger) {
return await sshConnect(host, "root", keyPair, logger, async () => undefined).catch((e) => e);

}

/**
Expand Down
7 changes: 4 additions & 3 deletions libs/web/src/utils/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
* See the Mulan PSL v2 for more details.
*/

import { Cluster as ClusterWithConfig, ClusterConfigSchema } from "@scow/config/build/cluster";
import { Cluster as ClusterWithConfig, ClusterConfigSchema, SimpleClusterSchema } from "@scow/config/build/cluster";

export const getSortedClusterIds = (clusters: Record<string, ClusterConfigSchema>): string[] => {
export const getSortedClusterIds = (clusters: Record<string, Partial<SimpleClusterSchema>>): string[] => {
return Object.keys(clusters)
.sort(
(a, b) => {
return clusters[a].priority - clusters[b].priority;
return (clusters[a].priority ?? Number.MAX_SAFE_INTEGER)
- (clusters[b].priority ?? Number.MIN_SAFE_INTEGER);
},
);
};
Expand Down

0 comments on commit 0275a9e

Please sign in to comment.