Skip to content

Commit

Permalink
feat: monitor contexts only if subscribers
Browse files Browse the repository at this point in the history
Signed-off-by: Philippe Martin <[email protected]>
  • Loading branch information
feloy committed Dec 9, 2024
1 parent 1985194 commit a08591f
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 146 deletions.
338 changes: 199 additions & 139 deletions packages/main/src/plugin/kubernetes/contexts-dispatcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import type { Cluster, Context, User } from '@kubernetes/client-node';
import { KubeConfig } from '@kubernetes/client-node';
import { beforeEach, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { ContextsDispatcher } from './contexts-dispatcher.js';
import { KubeConfigSingleContext } from './kubeconfig-single-context.js';
Expand Down Expand Up @@ -74,160 +74,220 @@ beforeEach(() => {
dispatcher.onDelete(onDeleteMock);
});

test('first call to update calls onAdd for each context', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);
expect(onUpdateMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onAddMock).toHaveBeenCalledTimes(2);
const kc1 = new KubeConfig();
kc1.loadFromOptions({
contexts: [contexts[0]],
users: [users[0]],
clusters: [clusters[0]],
currentContext: 'context1',
describe('there are subscribers for current and non current contexts', () => {
beforeEach(() => {
dispatcher.subscribeCurrentContext();
dispatcher.subscribeNonCurrentContexts();
});
expect(onAddMock).toHaveBeenCalledWith({
contextName: 'context1',
config: new KubeConfigSingleContext(kc1, contexts[0]!),
test('first call to update calls onAdd for each context', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);
expect(onUpdateMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onAddMock).toHaveBeenCalledTimes(2);
const kc1 = new KubeConfig();
kc1.loadFromOptions({
contexts: [contexts[0]],
users: [users[0]],
clusters: [clusters[0]],
currentContext: 'context1',
});
expect(onAddMock).toHaveBeenCalledWith({
contextName: 'context1',
config: new KubeConfigSingleContext(kc1, contexts[0]!),
isCurrent: false,
});
const kc2 = new KubeConfig();
kc2.loadFromOptions({
contexts: [contexts[1]],
users: [users[1]],
clusters: [clusters[1]],
currentContext: 'context2',
});
expect(onAddMock).toHaveBeenCalledWith({
contextName: 'context2',
config: new KubeConfigSingleContext(kc2, contexts[1]!),
isCurrent: false,
});
});
const kc2 = new KubeConfig();
kc2.loadFromOptions({
contexts: [contexts[1]],
users: [users[1]],
clusters: [clusters[1]],
currentContext: 'context2',
});
expect(onAddMock).toHaveBeenCalledWith({
contextName: 'context2',
config: new KubeConfigSingleContext(kc2, contexts[1]!),
});
});

test('call update again with same kubeconfig calls nothing', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);
test('call update again with same kubeconfig calls nothing', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();
onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

dispatcher.update(kc);
dispatcher.update(kc);

expect(onUpdateMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onAddMock).not.toHaveBeenCalled();
});
expect(onUpdateMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onAddMock).not.toHaveBeenCalled();
});

test('call update with a missing context calls onDelete', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);
test('call update with a missing context calls onDelete', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();
onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

kc.loadFromOptions({
contexts: [contexts[0]],
users: [users[0]],
clusters: [clusters[0]],
});
dispatcher.update(kc);

expect(onUpdateMock).not.toHaveBeenCalled();
expect(onAddMock).not.toHaveBeenCalled();
expect(onDeleteMock).toHaveBeenCalledOnce();
const kcDeleted = new KubeConfig();
kcDeleted.loadFromOptions({
contexts: [contexts[1]],
users: [users[1]],
clusters: [clusters[1]],
currentContext: 'context2',
});
expect(onDeleteMock).toHaveBeenCalledWith({
contextName: 'context2',
config: new KubeConfigSingleContext(kcDeleted, contexts[1]!),
});
});
kc.loadFromOptions({
contexts: [contexts[0]],
users: [users[0]],
clusters: [clusters[0]],
});
dispatcher.update(kc);

test('call update with a new context calls onAdd', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

const newContext = {
name: 'context3',
cluster: 'cluster3',
user: 'user3',
};
const newUser = {
name: 'user3',
};
const newCluster = {
name: 'cluster3',
};
kc.loadFromOptions({
contexts: [...contexts, newContext],
users: [...users, newUser],
clusters: [...clusters, newCluster],
});
dispatcher.update(kc);

expect(onUpdateMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onAddMock).toHaveBeenCalledOnce();
const kc3 = new KubeConfig();
kc3.loadFromOptions({
contexts: [newContext],
users: [newUser],
clusters: [newCluster],
currentContext: 'context3',
expect(onUpdateMock).not.toHaveBeenCalled();
expect(onAddMock).not.toHaveBeenCalled();
expect(onDeleteMock).toHaveBeenCalledOnce();
const kcDeleted = new KubeConfig();
kcDeleted.loadFromOptions({
contexts: [contexts[1]],
users: [users[1]],
clusters: [clusters[1]],
currentContext: 'context2',
});
expect(onDeleteMock).toHaveBeenCalledWith({
contextName: 'context2',
config: new KubeConfigSingleContext(kcDeleted, contexts[1]!),
isCurrent: false,
});
});
expect(onAddMock).toHaveBeenCalledWith({
contextName: 'context3',
config: new KubeConfigSingleContext(kc3, newContext),

test('call update with a new context calls onAdd', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

const newContext = {
name: 'context3',
cluster: 'cluster3',
user: 'user3',
};
const newUser = {
name: 'user3',
};
const newCluster = {
name: 'cluster3',
};
kc.loadFromOptions({
contexts: [...contexts, newContext],
users: [...users, newUser],
clusters: [...clusters, newCluster],
});
dispatcher.update(kc);

expect(onUpdateMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onAddMock).toHaveBeenCalledOnce();
const kc3 = new KubeConfig();
kc3.loadFromOptions({
contexts: [newContext],
users: [newUser],
clusters: [newCluster],
currentContext: 'context3',
});
expect(onAddMock).toHaveBeenCalledWith({
contextName: 'context3',
config: new KubeConfigSingleContext(kc3, newContext),
isCurrent: false,
});
});
});

test('call update with a modifed context calls onUpdate', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);
test('call update with a modifed context calls onUpdate', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();
onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

const updatedUser = {
name: 'user1',
certData: 'cert',
} as User;
kc.loadFromOptions({
contexts,
users: [updatedUser, users[1]],
clusters,
const updatedUser = {
name: 'user1',
certData: 'cert',
} as User;
kc.loadFromOptions({
contexts,
users: [updatedUser, users[1]],
clusters,
});
dispatcher.update(kc);

expect(onAddMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onUpdateMock).toHaveBeenCalledOnce();
const kc1 = new KubeConfig();
kc1.loadFromOptions({
contexts: [contexts[0]],
users: [updatedUser],
clusters: [clusters[0]],
currentContext: 'context1',
});
expect(onUpdateMock).toHaveBeenCalledWith({
contextName: 'context1',
config: new KubeConfigSingleContext(kc1, contexts[0]!),
isCurrent: false,
});
});
dispatcher.update(kc);

expect(onAddMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onUpdateMock).toHaveBeenCalledOnce();
const kc1 = new KubeConfig();
kc1.loadFromOptions({
contexts: [contexts[0]],
users: [updatedUser],
clusters: [clusters[0]],
currentContext: 'context1',

test('call update with a context set to current one calls onUpdate', () => {
const kc = new KubeConfig();
kc.loadFromOptions(kcWith2contexts);
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

kc.loadFromOptions({
contexts,
users: users,
clusters,
currentContext: 'context1',
});
dispatcher.update(kc);

expect(onAddMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onUpdateMock).toHaveBeenCalledOnce();
});
expect(onUpdateMock).toHaveBeenCalledWith({
contextName: 'context1',
config: new KubeConfigSingleContext(kc1, contexts[0]!),

test('call update with another current context calls onUpdate for both previous current and new current contexts', () => {
const kc = new KubeConfig();
kc.loadFromOptions({
contexts,
users: users,
clusters,
currentContext: 'context1',
});
dispatcher.update(kc);

onAddMock.mockReset();
onUpdateMock.mockReset();
onDeleteMock.mockReset();

kc.loadFromOptions({
contexts,
users: users,
clusters,
currentContext: 'context2',
});
dispatcher.update(kc);

expect(onAddMock).not.toHaveBeenCalled();
expect(onDeleteMock).not.toHaveBeenCalled();
expect(onUpdateMock).toHaveBeenCalledTimes(2);
});
});
Loading

0 comments on commit a08591f

Please sign in to comment.