Add complete object from contexts informers (#6116)

* refactor: use index signature for resources counts
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* refactor: send complete objects instead of counts
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* test: fix badge tests
Signed-off-by: Philippe Martin <phmartin@redhat.com>
This commit is contained in:
Philippe Martin 2024-02-22 21:15:49 +01:00 committed by GitHub
parent c93c53656c
commit 1ec4bd4305
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 84 additions and 40 deletions

View file

@ -88,14 +88,14 @@ function fakeMakeInformer(
case '/api/v1/namespaces/ns2/pods':
return new FakeInformer(2, connectResult);
case '/api/v1/namespaces/default/pods':
return new FakeInformer(9, connectResult);
return new FakeInformer(3, connectResult);
case '/apis/apps/v1/namespaces/ns1/deployments':
return new FakeInformer(11, connectResult);
return new FakeInformer(4, connectResult);
case '/apis/apps/v1/namespaces/ns2/deployments':
return new FakeInformer(12, connectResult);
return new FakeInformer(5, connectResult);
case '/apis/apps/v1/namespaces/default/deployments':
return new FakeInformer(19, connectResult);
return new FakeInformer(6, connectResult);
}
return new FakeInformer(0, connectResult);
}
@ -167,26 +167,34 @@ test('should send info of resources in all reachable contexts and nothing in non
expectedMap.set('context1', {
reachable: false,
error: 'Error: connection error',
podsCount: 0,
deploymentsCount: 0,
resources: {
pods: [],
deployments: [],
},
} as ContextState);
expectedMap.set('context2', {
reachable: true,
error: undefined,
podsCount: 9,
deploymentsCount: 19,
resources: {
pods: [{}, {}, {}],
deployments: [{}, {}, {}, {}, {}, {}],
},
} as ContextState);
expectedMap.set('context2-1', {
reachable: true,
error: undefined,
podsCount: 1,
deploymentsCount: 11,
resources: {
pods: [{}],
deployments: [{}, {}, {}, {}],
},
} as ContextState);
expectedMap.set('context2-2', {
reachable: true,
error: undefined,
podsCount: 2,
deploymentsCount: 12,
resources: {
pods: [{}, {}],
deployments: [{}, {}, {}, {}, {}],
},
} as ContextState);
await new Promise(resolve => setTimeout(resolve, 1200));
expect(apiSenderSendMock).toHaveBeenCalledWith('kubernetes-contexts-state-update', expectedMap);
@ -232,13 +240,17 @@ test('should send info of resources in all reachable contexts and nothing in non
expectedMap = new Map<string, ContextState>();
expectedMap.set('context2', {
reachable: true,
podsCount: 9,
deploymentsCount: 19,
resources: {
pods: [{}, {}, {}],
deployments: [{}, {}, {}, {}, {}, {}],
},
} as ContextState);
expectedMap.set('context2-1', {
reachable: true,
podsCount: 1,
deploymentsCount: 11,
resources: {
pods: [{}],
deployments: [{}, {}, {}, {}],
},
} as ContextState);
await new Promise(resolve => setTimeout(resolve, 1200));
expect(apiSenderSendMock).toHaveBeenLastCalledWith('kubernetes-contexts-state-update', expectedMap);

View file

@ -39,10 +39,15 @@ interface ContextInternalState {
export interface ContextState {
error?: string;
reachable: boolean;
podsCount: number;
deploymentsCount: number;
resources: ContextStateResources;
}
type ResourceName = 'pods' | 'deployments';
export type ContextStateResources = {
[resourceName in ResourceName]: KubernetesObject[];
};
interface CreateInformerOptions<T> {
checkReachable?: boolean;
onAdd?: (obj: T) => void;
@ -107,8 +112,10 @@ class ContextsStates {
this.published.set(name, {
error: undefined,
reachable: false,
podsCount: 0,
deploymentsCount: 0,
resources: {
pods: [],
deployments: [],
},
});
}
const val = this.published.get(name);
@ -193,8 +200,12 @@ export class ContextsManager {
return this.createInformer<V1Pod>(kc, context, path, listFn, {
timer: this.podTimer,
backoff: new Backoff(1000, 60_000, 300),
onAdd: _obj => this.setStateAndDispatch(context.name, state => state.podsCount++),
onDelete: _obj => this.setStateAndDispatch(context.name, state => state.podsCount--),
onAdd: obj => this.setStateAndDispatch(context.name, state => state.resources.pods.push(obj)),
onDelete: obj =>
this.setStateAndDispatch(
context.name,
state => (state.resources.pods = state.resources.pods.filter(d => d.metadata?.uid !== obj.metadata?.uid)),
),
onReachable: reachable =>
this.setStateAndDispatch(context.name, state => {
state.reachable = reachable;
@ -215,8 +226,15 @@ export class ContextsManager {
return this.createInformer<V1Deployment>(kc, context, path, listFn, {
timer: this.deploymentTimer,
backoff: new Backoff(1000, 60_000, 300),
onAdd: _obj => this.setStateAndDispatch(context.name, state => state.deploymentsCount++),
onDelete: _obj => this.setStateAndDispatch(context.name, state => state.deploymentsCount--),
onAdd: obj => this.setStateAndDispatch(context.name, state => state.resources.deployments.push(obj)),
onDelete: obj =>
this.setStateAndDispatch(
context.name,
state =>
(state.resources.deployments = state.resources.deployments.filter(
d => d.metadata?.uid !== obj.metadata?.uid,
)),
),
});
}

View file

@ -152,13 +152,17 @@ test('state and resources counts are displayed in contexts', () => {
const state: Map<string, ContextState> = new Map();
state.set('context-name', {
reachable: true,
podsCount: 1,
deploymentsCount: 2,
resources: {
pods: [{}],
deployments: [{}, {}],
},
});
state.set('context-name2', {
reachable: false,
podsCount: 0,
deploymentsCount: 0,
resources: {
pods: [],
deployments: [],
},
});
vi.mocked(kubernetesContextsState).kubernetesContextsState = readable<Map<string, ContextState>>(state);
render(PreferencesKubernetesContextsRendering, {});

View file

@ -108,13 +108,13 @@ async function handleDeleteContext(contextName: string) {
<div class="text-center">
<div class="font-bold text-[9px] text-gray-800">PODS</div>
<div class="text-[16px] text-white" aria-label="context-pods-count">
{$kubernetesContextsState.get(context.name)?.podsCount}
{$kubernetesContextsState.get(context.name)?.resources.pods.length}
</div>
</div>
<div class="text-center">
<div class="font-bold text-[9px] text-gray-800">DEPLOYMENTS</div>
<div class="text-[16px] text-white" aria-label="context-deployments-count">
{$kubernetesContextsState.get(context.name)?.deploymentsCount}
{$kubernetesContextsState.get(context.name)?.resources.deployments.length}
</div>
</div>
</div>

View file

@ -62,8 +62,10 @@ test('expect badges to show as there is a context', async () => {
mocks.getCurrentKubeContextState.mockReturnValue({
error: undefined,
reachable: true,
deploymentsCount: 0,
podsCount: 0,
resources: {
pods: [],
deployments: [],
},
} as ContextState); // no current ContextState
render(KubernetesCurrentContextConnectionBadge);
@ -76,8 +78,10 @@ test('expect badges to be green when reachable', async () => {
mocks.getCurrentKubeContextState.mockReturnValue({
error: undefined,
reachable: true,
deploymentsCount: 0,
podsCount: 0,
resources: {
pods: [],
deployments: [],
},
} as ContextState); // no current ContextState
render(KubernetesCurrentContextConnectionBadge);
@ -90,8 +94,10 @@ test('expect badges to be gray when not reachable', async () => {
mocks.getCurrentKubeContextState.mockReturnValue({
error: undefined,
reachable: false,
deploymentsCount: 0,
podsCount: 0,
resources: {
pods: [],
deployments: [],
},
} as ContextState); // no current ContextState
render(KubernetesCurrentContextConnectionBadge);
@ -104,8 +110,10 @@ test('expect no tooltip when no error', async () => {
mocks.getCurrentKubeContextState.mockReturnValue({
error: undefined,
reachable: false,
deploymentsCount: 0,
podsCount: 0,
resources: {
pods: [],
deployments: [],
},
} as ContextState); // no current ContextState
render(KubernetesCurrentContextConnectionBadge);
@ -118,8 +126,10 @@ test('expect tooltip when error', async () => {
mocks.getCurrentKubeContextState.mockReturnValue({
error: 'error message',
reachable: false,
deploymentsCount: 0,
podsCount: 0,
resources: {
pods: [],
deployments: [],
},
} as ContextState); // no current ContextState
render(KubernetesCurrentContextConnectionBadge);