Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:frontend new api call && database file migration #4332

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/providers/applaunchpad/src/constants/app.ts
Expand Up @@ -80,6 +80,7 @@ export const noGpuSliderKey = 'NoGpu';
export const pauseKey = 'deploy.cloud.sealos.io/pause';
export const maxReplicasKey = 'deploy.cloud.sealos.io/maxReplicas';
export const minReplicasKey = 'deploy.cloud.sealos.io/minReplicas';
export const deployPVCResizeKey = 'deploy.cloud.sealos.io/resize';
export const appDeployKey = 'cloud.sealos.io/app-deploy-manager';
export const publicDomainKey = `cloud.sealos.io/app-deploy-manager-domain`;
export const gpuNodeSelectorKey = 'nvidia.com/gpu.product';
Expand Down
45 changes: 25 additions & 20 deletions frontend/providers/applaunchpad/src/pages/_app.tsx
Expand Up @@ -134,28 +134,33 @@ const App = ({ Component, pageProps }: AppProps) => {
}, [refresh, router.pathname]);

// InternalAppCall
useEffect(() => {
const event = async (e: MessageEvent) => {
const setupInternalAppCallListener = async () => {
try {
const envs = await getPlatformEnv();
const whitelist = [`https://${envs?.domain}`];
if (!whitelist.includes(e.origin)) {
return;
}
try {
if (e.data?.type === 'InternalAppCall' && e.data?.name) {
router.push({
pathname: '/app/detail',
query: {
name: e.data.name
}
});
const event = async (e: MessageEvent) => {
const whitelist = [`https://${envs?.domain}`];
if (!whitelist.includes(e.origin)) {
return;
}
} catch (error) {
console.log(error, 'error');
}
};
window.addEventListener('message', event);
return () => window.removeEventListener('message', event);
try {
if (e.data?.type === 'InternalAppCall' && e.data?.name) {
router.push({
pathname: '/app/detail',
query: {
name: e.data.name
}
});
}
} catch (error) {
console.log(error, 'error');
}
};
window.addEventListener('message', event);
return () => window.removeEventListener('message', event);
} catch (error) {}
};
useEffect(() => {
setupInternalAppCallListener();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Expand Down
66 changes: 37 additions & 29 deletions frontend/providers/applaunchpad/src/pages/api/getAppByAppName.ts
Expand Up @@ -11,35 +11,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
if (!appName) {
throw new Error('appName is empty');
}

const { k8sApp, k8sCore, k8sNetworkingApp, k8sAutoscaling, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});

const response = await Promise.allSettled([
k8sApp.readNamespacedDeployment(appName, namespace),
k8sApp.readNamespacedStatefulSet(appName, namespace),
k8sCore.readNamespacedService(appName, namespace),
k8sCore.readNamespacedConfigMap(appName, namespace),
k8sNetworkingApp
.listNamespacedIngress(
namespace,
undefined,
undefined,
undefined,
undefined,
`${appDeployKey}=${appName}`
)
.then((res) => ({
body: res.body.items.map((item) => ({
...item,
apiVersion: res.body.apiVersion, // item does not contain apiversion and kind
kind: 'Ingress'
}))
})),
k8sCore.readNamespacedSecret(appName, namespace),
k8sAutoscaling.readNamespacedHorizontalPodAutoscaler(appName, namespace)
]);
const response = await GetAppByAppName({ appName, req });

// Check for errors other than 404
const responseData = response
Expand All @@ -61,3 +33,39 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}

export async function GetAppByAppName({
appName,
req
}: { appName: string } & { req: NextApiRequest }) {
const { k8sApp, k8sCore, k8sNetworkingApp, k8sAutoscaling, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});

const response = await Promise.allSettled([
k8sApp.readNamespacedDeployment(appName, namespace),
k8sApp.readNamespacedStatefulSet(appName, namespace),
k8sCore.readNamespacedService(appName, namespace),
k8sCore.readNamespacedConfigMap(appName, namespace),
k8sNetworkingApp
.listNamespacedIngress(
namespace,
undefined,
undefined,
undefined,
undefined,
`${appDeployKey}=${appName}`
)
.then((res) => ({
body: res.body.items.map((item) => ({
...item,
apiVersion: res.body.apiVersion, // item does not contain apiversion and kind
kind: 'Ingress'
}))
})),
k8sCore.readNamespacedSecret(appName, namespace),
k8sAutoscaling.readNamespacedHorizontalPodAutoscaler(appName, namespace)
]);

return response;
}
15 changes: 8 additions & 7 deletions frontend/providers/applaunchpad/src/pages/api/getPodsMetrics.ts
Expand Up @@ -4,36 +4,37 @@ import { authSession } from '@/services/backend/auth';
import { getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';

type Props = {
export type GetPodMetricsProps = {
podsName: string[];
};

// get App Metrics By DeployName. compute average value
export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { podsName } = req.body as Props;
const { podsName } = req.body as GetPodMetricsProps;

if (!podsName) {
throw new Error('podsName is empty');
}

jsonRes(res, {
data: await getPodMetrics({ req, podsName })
data: await GetPodMetrics({ req, podsName })
});
} catch (err: any) {
// console.log(err, 'get metrics error')
jsonRes(res, {
code: 500,
error: err
});
}
}

export const getPodMetrics = async ({ req, podsName }: Props & { req: NextApiRequest }) => {
export const GetPodMetrics = async ({
req,
podsName
}: GetPodMetricsProps & { req: NextApiRequest }) => {
const { metricsClient, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});
// get pods metrics

const metrics = await Promise.allSettled(
podsName.map((name) => metricsClient.getPodMetrics(namespace, name))
);
Expand Down
@@ -0,0 +1,34 @@
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import type { NextApiRequest, NextApiResponse } from 'next';
import { GetAppByAppName } from '../getAppByAppName';

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { appName } = req.query as { appName: string };
if (!appName) {
throw new Error('appName is empty');
}

const response = await GetAppByAppName({ appName, req });

// Check for errors other than 404
const responseData = response
.map((item) => {
if (item.status === 'fulfilled') return item.value.body;
if (+item.reason?.body?.code === 404) return '';
throw new Error('Get APP Deployment Error');
})
.filter((item) => item)
.flat();

jsonRes(res, {
data: responseData
});
} catch (err: any) {
jsonRes(res, {
code: 500,
error: err
});
}
}
@@ -0,0 +1,42 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { ApiResp } from '@/services/kubernet';
import { authSession } from '@/services/backend/auth';
import { getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';

// get App Metrics By DeployName. compute average value
export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { name } = req.query;

if (!name) {
throw new Error('Name is empty');
}

const { k8sCore, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});

// get pods
const {
body: { items: pods }
} = await k8sCore.listNamespacedPod(
namespace,
undefined,
undefined,
undefined,
undefined,
`app=${name}`
);

jsonRes(res, {
data: pods
});
} catch (err: any) {
// console.log(err, 'get metrics error')
jsonRes(res, {
code: 500,
error: err
});
}
}
@@ -0,0 +1,25 @@
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import type { NextApiRequest, NextApiResponse } from 'next';
import { GetPodMetrics, GetPodMetricsProps } from '../getPodsMetrics';

// get App Metrics By DeployName. compute average value
export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { podsName } = req.body as GetPodMetricsProps;

if (!podsName) {
throw new Error('podsName is empty');
}

jsonRes(res, {
data: await GetPodMetrics({ req, podsName })
});
} catch (err: any) {
console.log(err, 'get metrics error');
jsonRes(res, {
code: 500,
error: err
});
}
}