Skip to content

Commit e422209

Browse files
authored
feat: kubernetes providers state (#376)
Signed-off-by: Philippe Martin <[email protected]>
1 parent 0cad95e commit e422209

File tree

15 files changed

+395
-0
lines changed

15 files changed

+395
-0
lines changed

__mocks__/@podman-desktop/api.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ const plugin = {
4040
onDidUpdateContainerConnection: vi.fn(),
4141
onDidRegisterContainerConnection: vi.fn(),
4242
onDidUnregisterContainerConnection: vi.fn(),
43+
onDidSetConnectionFactory: vi.fn(),
44+
onDidUnsetConnectionFactory: vi.fn(),
45+
getConnectionFactories: vi.fn(),
4346
},
4447
containerEngine: {
4548
onEvent: vi.fn(),

packages/channels/src/channels.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type { ContextsHealthsInfo } from './model/contexts-healths-info';
2929
import type { ContextsPermissionsInfo } from './model/contexts-permissions-info';
3030
import type { CurrentContextInfo } from './model/current-context-info';
3131
import type { EndpointsInfo } from './model/endpoints-info';
32+
import type { KubernetesProvidersInfo } from './model/kubernetes-providers-info';
3233
import type { PodLogsChunk } from './model/pod-logs-chunk';
3334
import type { PodTerminalChunk } from './model/pod-terminal-chunk';
3435
import type { PortForwardsInfo } from './model/port-forward-info';
@@ -57,6 +58,7 @@ export const RESOURCE_DETAILS = createRpcChannel<ResourceDetailsInfo>('ResourceD
5758
export const RESOURCE_EVENTS = createRpcChannel<ResourceEventsInfo>('ResourceEvents');
5859
export const PORT_FORWARDS = createRpcChannel<PortForwardsInfo>('PortForwards');
5960
export const ENDPOINTS = createRpcChannel<EndpointsInfo>('Endpoints');
61+
export const KUBERNETES_PROVIDERS = createRpcChannel<KubernetesProvidersInfo>('KubernetesProviders');
6062

6163
// Channels fot streams
6264
export const API_POD_LOGS = createRpcChannel<PodLogsApi>('PodLogsApi');

packages/channels/src/model/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export * from './endpoints-info';
2929
export * from './endpoints-options';
3030
export * from './kubernetes-contexts-healths';
3131
export * from './kubernetes-contexts-permissions';
32+
export * from './kubernetes-providers-info';
3233
export * from './kubernetes-resource-count';
3334
export * from './kubernetes-troubleshooting';
3435
export * from './openshift-types';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**********************************************************************
2+
* Copyright (C) 2025 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import type { ProviderImages } from '@podman-desktop/api';
20+
21+
export interface KubernetesProvider {
22+
id: string;
23+
creationDisplayName?: string;
24+
creationButtonTitle?: string;
25+
emptyConnectionMarkdownDescription?: string;
26+
images?: ProviderImages;
27+
}
28+
29+
export interface KubernetesProvidersInfo {
30+
providers: KubernetesProvider[];
31+
}

packages/extension/src/dashboard-extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { PortForwardServiceProvider } from './port-forward/port-forward-service'
4444
import { PodLogsApiImpl } from './manager/pod-logs-api-impl';
4545
import { PodTerminalsApiImpl } from './manager/pod-terminals-api-impl';
4646
import { NavigationApiImpl } from '/@/manager/navigation-api';
47+
import { KubernetesProvidersManager } from '/@/manager/kubernetes-providers';
4748

4849
export class DashboardExtension {
4950
#container: Container | undefined;
@@ -59,6 +60,7 @@ export class DashboardExtension {
5960
#podLogsApiImpl: PodLogsApiImpl;
6061
#podTerminalsApiImpl: PodTerminalsApiImpl;
6162
#navigationApiImpl: NavigationApiImpl;
63+
#kubernetesProvidersManager: KubernetesProvidersManager;
6264

6365
constructor(readonly extensionContext: ExtensionContext) {
6466
this.#extensionContext = extensionContext;
@@ -86,6 +88,9 @@ export class DashboardExtension {
8688
this.#podLogsApiImpl = await this.#container.getAsync(PodLogsApiImpl);
8789
this.#podTerminalsApiImpl = await this.#container.getAsync(PodTerminalsApiImpl);
8890
this.#navigationApiImpl = await this.#container.getAsync(NavigationApiImpl);
91+
this.#kubernetesProvidersManager = await this.#container.getAsync(KubernetesProvidersManager);
92+
93+
this.#kubernetesProvidersManager.init();
8994

9095
const afterFirst = performance.now();
9196

packages/extension/src/dispatcher/_dispatcher-module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { ResourceEventsDispatcher } from './resource-events-dispatcher';
2929
import { PortForwardsDispatcher } from './port-forwards-dispatcher';
3030
import { EndpointsDispatcher } from './endpoints-dispatcher';
3131
import { AvailableContextsDispatcher } from './available-contexts-dispatcher';
32+
import { KubernetesProvidersDispatcher } from './kubernetes-providers-dispatcher';
3233

3334
const dispatchersModule = new ContainerModule(options => {
3435
options.bind<ActiveResourcesCountDispatcher>(ActiveResourcesCountDispatcher).toSelf().inSingletonScope();
@@ -63,6 +64,9 @@ const dispatchersModule = new ContainerModule(options => {
6364

6465
options.bind<EndpointsDispatcher>(EndpointsDispatcher).toSelf().inSingletonScope();
6566
options.bind(DispatcherObject).toService(EndpointsDispatcher);
67+
68+
options.bind<KubernetesProvidersDispatcher>(KubernetesProvidersDispatcher).toSelf().inSingletonScope();
69+
options.bind(DispatcherObject).toService(KubernetesProvidersDispatcher);
6670
});
6771

6872
export { dispatchersModule };
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**********************************************************************
2+
* Copyright (C) 2025 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import { inject, injectable } from 'inversify';
20+
import type { DispatcherObject } from './util/dispatcher-object';
21+
import { AbsDispatcherObjectImpl } from './util/dispatcher-object';
22+
import { ContextsManager } from '/@/manager/contexts-manager';
23+
import { KubernetesProvidersInfo } from '@kubernetes-dashboard/channels/dist/model/kubernetes-providers-info';
24+
import { RpcExtension } from '@kubernetes-dashboard/rpc';
25+
import { KUBERNETES_PROVIDERS } from '@kubernetes-dashboard/channels';
26+
import { KubernetesProvidersManager } from '/@/manager/kubernetes-providers';
27+
28+
@injectable()
29+
export class KubernetesProvidersDispatcher
30+
extends AbsDispatcherObjectImpl<void, KubernetesProvidersInfo>
31+
implements DispatcherObject<void>
32+
{
33+
constructor(
34+
@inject(RpcExtension) rpcExtension: RpcExtension,
35+
@inject(ContextsManager) private manager: ContextsManager,
36+
@inject(KubernetesProvidersManager) private kubernetesProvidersManager: KubernetesProvidersManager,
37+
) {
38+
super(rpcExtension, KUBERNETES_PROVIDERS);
39+
}
40+
41+
getData(): KubernetesProvidersInfo {
42+
return {
43+
providers: this.kubernetesProvidersManager.getKubernetesProviders(),
44+
};
45+
}
46+
}

packages/extension/src/manager/_manager-module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { PodLogsApiImpl } from './pod-logs-api-impl';
2626
import { IDisposable } from '@kubernetes-dashboard/channels';
2727
import { PodTerminalsApiImpl } from './pod-terminals-api-impl';
2828
import { NavigationApiImpl } from '/@/manager/navigation-api';
29+
import { KubernetesProvidersManager } from '/@/manager/kubernetes-providers';
2930

3031
const managersModule = new ContainerModule(options => {
3132
options.bind<ContextsManager>(ContextsManager).toSelf().inSingletonScope();
@@ -35,10 +36,12 @@ const managersModule = new ContainerModule(options => {
3536
options.bind<PodLogsApiImpl>(PodLogsApiImpl).toSelf().inSingletonScope();
3637
options.bind<PodTerminalsApiImpl>(PodTerminalsApiImpl).toSelf().inSingletonScope();
3738
options.bind<NavigationApiImpl>(NavigationApiImpl).toSelf().inSingletonScope();
39+
options.bind<KubernetesProvidersManager>(KubernetesProvidersManager).toSelf().inSingletonScope();
3840

3941
// Bind IDisposable to services which need to clear data/stop connection/etc when the panel is left
4042
// (the onDestroy are not called from components when the panel is left, which may introduce memory leaks if not disposed here)
4143
options.bind(IDisposable).toService(PodLogsApiImpl);
44+
options.bind(IDisposable).toService(KubernetesProvidersManager);
4245
});
4346

4447
export { managersModule };

packages/extension/src/manager/contexts-states-dispatcher.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ import {
4040
CONTEXTS_PERMISSIONS,
4141
CURRENT_CONTEXT,
4242
ENDPOINTS,
43+
KUBERNETES_PROVIDERS,
4344
RESOURCE_DETAILS,
4445
RESOURCE_EVENTS,
4546
RESOURCES_COUNT,
4647
UPDATE_RESOURCE,
4748
} from '@kubernetes-dashboard/channels';
49+
import { KubernetesProvidersManager } from '/@/manager/kubernetes-providers.js';
4850

4951
let container: Container;
5052
const contextsManagerMock: ContextsManager = {
@@ -80,6 +82,10 @@ const contextsHealthsDispatcher = {
8082
const contextsPermissionsDispatcher = {
8183
dispatch: vi.fn(),
8284
} as unknown as ContextsPermissionsDispatcher;
85+
const kubernetesProvidersManagerMock = {
86+
onKubernetesProvidersChange: vi.fn(),
87+
getKubernetesProviders: vi.fn(),
88+
} as unknown as KubernetesProvidersManager;
8389

8490
beforeAll(async () => {
8591
const inversifyBinding = new InversifyBinding(rpcExtension, extensionContext, telemetryLogger);
@@ -89,6 +95,7 @@ beforeAll(async () => {
8995
(await container.rebind(ActiveResourcesCountDispatcher)).toConstantValue(activeResourcesCountDispatcher);
9096
(await container.rebind(ContextsHealthsDispatcher)).toConstantValue(contextsHealthsDispatcher);
9197
(await container.rebind(ContextsPermissionsDispatcher)).toConstantValue(contextsPermissionsDispatcher);
98+
(await container.rebind(KubernetesProvidersManager)).toConstantValue(kubernetesProvidersManagerMock);
9299
});
93100

94101
beforeEach(() => {
@@ -216,3 +223,16 @@ test('ContextsStatesDispatcher should dispatch ENDPOINTS when onEndpointsChange
216223
});
217224
expect(dispatcherSpy).toHaveBeenCalledWith(ENDPOINTS);
218225
});
226+
227+
test('ContextsStatesDispatcher should dispatch KUBERNETES_PROVIDERS when onKubernetesProvidersChange event is fired', async () => {
228+
const dispatcherSpy = vi.spyOn(dispatcher, 'dispatch').mockResolvedValue();
229+
dispatcher.init();
230+
expect(dispatcherSpy).not.toHaveBeenCalled();
231+
232+
vi.mocked(kubernetesProvidersManagerMock.onKubernetesProvidersChange).mockImplementation(f => f() as IDisposable);
233+
dispatcher.init();
234+
await vi.waitFor(() => {
235+
expect(dispatcherSpy).toHaveBeenCalledTimes(1);
236+
});
237+
expect(dispatcherSpy).toHaveBeenCalledWith(KUBERNETES_PROVIDERS);
238+
});

packages/extension/src/manager/contexts-states-dispatcher.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
RESOURCES_COUNT,
3131
UPDATE_RESOURCE,
3232
SubscribeApi,
33+
KUBERNETES_PROVIDERS,
3334
} from '@kubernetes-dashboard/channels';
3435

3536
import type { ContextHealthState } from './context-health-checker.js';
@@ -41,6 +42,7 @@ import { inject, injectable, multiInject } from 'inversify';
4142
import { DispatcherObject } from '/@/dispatcher/util/dispatcher-object.js';
4243
import { ChannelSubscriber } from '/@/types/channel-subscriber.js';
4344
import { PortForwardServiceProvider } from '/@/port-forward/port-forward-service.js';
45+
import { KubernetesProvidersManager } from '/@/manager/kubernetes-providers.js';
4446

4547
@injectable()
4648
export class ContextsStatesDispatcher extends ChannelSubscriber implements SubscribeApi {
@@ -50,6 +52,9 @@ export class ContextsStatesDispatcher extends ChannelSubscriber implements Subsc
5052
@inject(PortForwardServiceProvider)
5153
private portForwardServiceProvider: PortForwardServiceProvider;
5254

55+
@inject(KubernetesProvidersManager)
56+
private kubernetesProvidersManager: KubernetesProvidersManager;
57+
5358
#dispatchers: Map<string, DispatcherObject<unknown>> = new Map();
5459

5560
constructor(@multiInject(DispatcherObject) dispatchers: DispatcherObject<unknown>[]) {
@@ -99,6 +104,9 @@ export class ContextsStatesDispatcher extends ChannelSubscriber implements Subsc
99104
await this.dispatch(ENDPOINTS);
100105
});
101106
this.onSubscribe(channelName => this.dispatchByChannelName(channelName));
107+
this.kubernetesProvidersManager.onKubernetesProvidersChange(async () => {
108+
await this.dispatch(KUBERNETES_PROVIDERS);
109+
});
102110
}
103111

104112
// TODO replace this with an event

0 commit comments

Comments
 (0)