Skip to content

Commit 2234c56

Browse files
authored
feat: enhance Kubernetes service provider handling and update dependencies (#6)
1 parent 67ebb1c commit 2234c56

File tree

3 files changed

+208
-35
lines changed

3 files changed

+208
-35
lines changed

backend/package-lock.json

Lines changed: 140 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@nestjs/core": "^10.4.6",
2929
"@nestjs/platform-express": "^10.4.6",
3030
"@nestjs/serve-static": "^4.0.0",
31-
"@openmfp/portal-server-lib": "^0.154.0",
31+
"@openmfp/portal-server-lib": "0.155.0",
3232
"axios": "^1.6.3",
3333
"class-validator": "^0.14.1",
3434
"cookie-parser": "1.4.7",

backend/src/service-providers/kubernetes-service-providers.service.ts

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,102 @@ import {
44
ServiceProviderService,
55
} from '@openmfp/portal-server-lib';
66
import { KubeConfig, CustomObjectsApi } from '@kubernetes/client-node';
7+
import { PromiseMiddlewareWrapper } from '@kubernetes/client-node/dist/gen/middleware.js';
78

89
export class KubernetesServiceProvidersService
9-
implements ServiceProviderService
10+
implements ServiceProviderService
1011
{
1112
private k8sApi: CustomObjectsApi;
13+
private baseUrl: URL;
1214

1315
constructor() {
1416
const kc = new KubeConfig();
1517
kc.loadFromDefault();
18+
this.baseUrl = new URL(kc.getCurrentCluster()?.server || '');
1619
this.k8sApi = kc.makeApiClient(CustomObjectsApi);
1720
}
1821

1922
async getServiceProviders(
20-
token: string,
21-
entities: string[],
22-
context: Record<string, any>
23+
token: string,
24+
entities: string[],
25+
context: Record<string, any>
2326
): Promise<ServiceProviderResponse> {
2427
const entity = !entities || !entities.length ? 'main' : entities[0];
2528

29+
let response;
2630
try {
27-
const response = await this.k8sApi.listClusterCustomObject({
28-
group: 'core.openmfp.io',
29-
version: 'v1alpha1',
30-
plural: 'contentconfigurations',
31-
labelSelector: `portal.openmfp.org/entity=${entity}`,
32-
});
31+
response = await this.getKubernetesResources(entity, context);
32+
} catch (error) {
33+
console.error(error);
3334

34-
if (!response.items) {
35-
return {
36-
rawServiceProviders: [],
37-
};
35+
if (error.code == 429 || error.statusCode == 429) {
36+
await new Promise((resolve) => setTimeout(resolve, 1000));
37+
console.log('Retry after 1 second reading kubernetes resources.');
38+
response = await this.getKubernetesResources(entity, context);
3839
}
40+
}
41+
42+
if (!response.items) {
43+
return {
44+
rawServiceProviders: [],
45+
};
46+
}
3947

40-
const responseItems = response.items as any[];
48+
const responseItems = response.items as any[];
4149

42-
let contentConfigurations = responseItems
50+
let contentConfigurations = responseItems
4351
.filter((item) => !!item.status.configurationResult)
4452
.map((item) => {
4553
const contentConfiguration = JSON.parse(
46-
item.status.configurationResult
54+
item.status.configurationResult
4755
) as ContentConfiguration;
4856
if (!contentConfiguration.url) {
4957
contentConfiguration.url = item.spec.remoteConfiguration?.url;
5058
}
5159
return contentConfiguration;
5260
});
5361

54-
return {
55-
rawServiceProviders: [
56-
{
57-
name: 'openmfp-system',
58-
displayName: '',
59-
creationTimestamp: '',
60-
contentConfiguration: contentConfigurations,
62+
return {
63+
rawServiceProviders: [
64+
{
65+
name: 'openmfp-system',
66+
displayName: '',
67+
creationTimestamp: '',
68+
contentConfiguration: contentConfigurations,
69+
},
70+
],
71+
};
72+
}
73+
74+
private async getKubernetesResources(
75+
entity: string,
76+
requestContext: Record<string, any>
77+
) {
78+
const gvr = {
79+
group: 'core.openmfp.io',
80+
version: 'v1alpha1',
81+
plural: 'contentconfigurations',
82+
labelSelector: `portal.openmfp.org/entity=${entity}`,
83+
};
84+
return await this.k8sApi.listClusterCustomObject(gvr, {
85+
middleware: [
86+
new PromiseMiddlewareWrapper({
87+
pre: async (context) => {
88+
const url = new URL(context.getUrl());
89+
90+
let path = `${this.baseUrl.pathname}/clusters/root:orgs:${requestContext.organization}`;
91+
if (requestContext?.account) {
92+
path += `:${requestContext.account}`; // FIXME: how are nested accounts and paths handled in the portal?
93+
}
94+
path += `/apis/${gvr.group}/${gvr.version}/${gvr.plural}`;
95+
96+
url.pathname = path;
97+
context.setUrl(url.toString());
98+
return context;
6199
},
62-
],
63-
};
64-
} catch (error) {
65-
console.error(error);
66-
}
100+
post: async (context) => context,
101+
}),
102+
],
103+
});
67104
}
68-
}
105+
}

0 commit comments

Comments
 (0)