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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: only delete selected pods #3378

Merged
merged 2 commits into from Aug 10, 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
168 changes: 166 additions & 2 deletions packages/renderer/src/lib/ContainerList.spec.ts
Expand Up @@ -28,8 +28,10 @@ import { providerInfos } from '../stores/providers';

const listContainersMock = vi.fn();
const getProviderInfosMock = vi.fn();
const listViewsMock = vi.fn();

const deleteContainerMock = vi.fn();
const removePodMock = vi.fn();
const listPodsMock = vi.fn();

const kubernetesListPodsMock = vi.fn();
Expand All @@ -41,11 +43,13 @@ beforeAll(() => {
onDidUpdateProviderStatusMock.mockImplementation(() => Promise.resolve());
listPodsMock.mockImplementation(() => Promise.resolve([]));
kubernetesListPodsMock.mockImplementation(() => Promise.resolve([]));
listViewsMock.mockImplementation(() => Promise.resolve([]));
(window as any).listViewsContributions = listViewsMock;
(window as any).listContainers = listContainersMock;
(window as any).listPods = listPodsMock;
(window as any).kubernetesListPods = kubernetesListPodsMock;
(window as any).getProviderInfos = getProviderInfosMock;
(window as any).removePod = vi.fn();
(window as any).removePod = removePodMock;
(window as any).deleteContainer = deleteContainerMock;

(window.events as unknown) = {
Expand Down Expand Up @@ -156,7 +160,7 @@ test('Try to delete a pod that has containers', async () => {
await fireEvent.click(deleteButton);

// expect that we call to delete the pod first (as it's a group of containers)
expect((window as any).removePod).toHaveBeenCalledWith('podman', podId);
expect(removePodMock).toHaveBeenCalledWith('podman', podId);

// wait deleteContainerMock is called
while (deleteContainerMock.mock.calls.length === 0) {
Expand All @@ -167,3 +171,163 @@ test('Try to delete a pod that has containers', async () => {
expect(deleteContainerMock).toBeCalledWith('podman', singleContainer.Id);
expect(deleteContainerMock).toBeCalledTimes(1);
});

test('Try to delete a container without deleting pods', async () => {
removePodMock.mockClear();
deleteContainerMock.mockClear();
listContainersMock.mockResolvedValue([]);

window.dispatchEvent(new CustomEvent('extensions-already-started'));
window.dispatchEvent(new CustomEvent('provider-lifecycle-change'));
window.dispatchEvent(new CustomEvent('tray:update-provider'));

// wait for the store to be cleared
while (get(containersInfos).length !== 0) {
await new Promise(resolve => setTimeout(resolve, 250));
}

const podId = 'pod-id2';

const singleContainer = {
Id: 'sha256:21234567890123',
Image: 'sha256:123',
Names: ['foo'],
Status: 'Running',
engineId: 'podman',
engineName: 'podman',
};

// one single container and a container as part of a pod
const mockedContainers = [
singleContainer,
{
Id: 'sha256:7897891234567890123',
Image: 'sha256:345',
Names: ['container-in-pod'],
Status: 'Running',
pod: {
name: 'my-pod2',
id: podId,
status: 'Running',
},
engineId: 'podman',
engineName: 'podman',
},
];

listContainersMock.mockResolvedValue(mockedContainers);

window.dispatchEvent(new CustomEvent('extensions-already-started'));
window.dispatchEvent(new CustomEvent('provider-lifecycle-change'));
window.dispatchEvent(new CustomEvent('tray:update-provider'));

// wait until the store is populated
while (get(containersInfos).length === 0) {
await new Promise(resolve => setTimeout(resolve, 250));
}

await waitRender({});

// select the standalone container checkbox
const containerCheckbox = screen.getAllByRole('checkbox', { name: 'Toggle container' });
expect(containerCheckbox[0]).toBeInTheDocument();
await fireEvent.click(containerCheckbox[0]);

// click on the delete button
const deleteButton = screen.getByRole('button', { name: 'Delete selected containers and pods' });
expect(deleteButton).toBeInTheDocument();
await fireEvent.click(deleteButton);

// wait until deleteContainerMock is called
while (deleteContainerMock.mock.calls.length === 0) {
await new Promise(resolve => setTimeout(resolve, 100));
}

// expect that the container has been deleted
expect(deleteContainerMock).toBeCalledWith('podman', singleContainer.Id);

// but not the other container
expect(deleteContainerMock).toHaveBeenCalledOnce();

// and not the pod
expect(removePodMock).not.toBeCalled();
});

test('Try to delete a pod without deleting container', async () => {
removePodMock.mockClear();
deleteContainerMock.mockClear();
listContainersMock.mockResolvedValue([]);

window.dispatchEvent(new CustomEvent('extensions-already-started'));
window.dispatchEvent(new CustomEvent('provider-lifecycle-change'));
window.dispatchEvent(new CustomEvent('tray:update-provider'));

// wait for the store to be cleared
while (get(containersInfos).length !== 0) {
await new Promise(resolve => setTimeout(resolve, 250));
}

const podId = 'pod-id3';

const singleContainer = {
Id: 'sha256:56789012345',
Image: 'sha256:567',
Names: ['foo'],
Status: 'Running',
engineId: 'podman',
engineName: 'podman',
};

// one single container and a container as part of a pod
const mockedContainers = [
singleContainer,
{
Id: 'sha256:7897891234567890123',
Image: 'sha256:345',
Names: ['container-in-pod'],
Status: 'Running',
pod: {
name: 'my-pod3',
id: podId,
status: 'Running',
},
engineId: 'podman',
engineName: 'podman',
},
];

listContainersMock.mockResolvedValue(mockedContainers);

window.dispatchEvent(new CustomEvent('extensions-already-started'));
window.dispatchEvent(new CustomEvent('provider-lifecycle-change'));
window.dispatchEvent(new CustomEvent('tray:update-provider'));

// wait until the store is populated
while (get(containersInfos).length === 0) {
await new Promise(resolve => setTimeout(resolve, 250));
}

await waitRender({});

// select the pod checkbox
const podCheckbox = screen.getByRole('checkbox', { name: 'Toggle pod' });
expect(podCheckbox).toBeInTheDocument();
await fireEvent.click(podCheckbox);

// click on the delete button
const deleteButton = screen.getByRole('button', { name: 'Delete selected containers and pods' });
expect(deleteButton).toBeInTheDocument();
await fireEvent.click(deleteButton);

// wait until removePodMock is called
while (removePodMock.mock.calls.length === 0) {
await new Promise(resolve => setTimeout(resolve, 100));
}

// expect that the pod has been removed
expect(removePodMock).toHaveBeenCalledWith('podman', podId);
expect(removePodMock).toHaveBeenCalledOnce();

// and the standalone container has not been deleted
expect(deleteContainerMock).not.toHaveBeenCalled();
});
6 changes: 4 additions & 2 deletions packages/renderer/src/lib/ContainerList.svelte
Expand Up @@ -151,7 +151,9 @@ function toggleCheckboxContainerGroup(checked: boolean, containerGroup: Containe
let bulkDeleteInProgress = false;
async function deleteSelectedContainers() {
// delete pods first if any
const podGroups = containerGroups.filter(group => group.type === ContainerGroupInfoTypeUI.POD);
const podGroups = containerGroups
.filter(group => group.type === ContainerGroupInfoTypeUI.POD)
.filter(pod => pod.selected);
if (podGroups.length > 0) {
await Promise.all(
podGroups.map(async podGroup => {
Expand All @@ -163,7 +165,7 @@ async function deleteSelectedContainers() {
}),
);
}
// then containers (that are no inside a pod)
// then containers (that are not inside a pod)
const selectedContainers = containerGroups
.filter(group => group.type !== ContainerGroupInfoTypeUI.POD)
.map(group => group.containers)
Expand Down