Skip to content

Commit

Permalink
feat: add open exposed url to pod details
Browse files Browse the repository at this point in the history
Signed-off-by: Vladyslav Zhukovskyi <vzhukovs@redhat.com>
  • Loading branch information
vzhukovs committed Oct 18, 2023
1 parent f873785 commit 51a621b
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 6 deletions.
6 changes: 6 additions & 0 deletions packages/renderer/src/lib/container/container-utils.ts
Expand Up @@ -113,6 +113,12 @@ export class ContainerUtils {
}
}

getOpeningUrls(containerInfo: ContainerInfo): string[] {
return containerInfo.Ports?.filter(port => port.PublicPort)
.map(port => port.PublicPort)
.map(port => `http://localhost:${port}`);
}

getEngineId(containerInfo: ContainerInfo): string {
return containerInfo.engineId;
}
Expand Down
22 changes: 19 additions & 3 deletions packages/renderer/src/lib/pod/PodActions.spec.ts
Expand Up @@ -17,28 +17,44 @@
***********************************************************************/

import '@testing-library/jest-dom/vitest';
import { test, expect, vi, beforeEach } from 'vitest';
import { test, expect, vi, beforeEach, afterEach } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/svelte';
import PodActions from './PodActions.svelte';
import type { PodInfoUI } from './PodInfoUI';
import type { ContainerInfo, Port } from '@podman-desktop/api';

const pod: PodInfoUI = {
id: 'pod',
containers: [{ Id: 'pod' }],
status: 'RUNNING',
kind: 'podman',
} as PodInfoUI;

const errorCallback = vi.fn();
const listContainersMock = vi.fn();
const getContributedMenusMock = vi.fn();

beforeEach(() => {
(window as any).kubernetesDeletePod = vi.fn();
vi.resetAllMocks();
vi.clearAllMocks();
(window as any).listContainers = listContainersMock;
(window as any).removePod = vi.fn();

listContainersMock.mockResolvedValue([
{ Id: 'pod', Ports: [{ PublicPort: 8080 } as Port] as Port[] } as ContainerInfo,
]);

(window as any).getContributedMenus = getContributedMenusMock;
getContributedMenusMock.mockImplementation(() => Promise.resolve([]));
});

afterEach(() => {
vi.resetAllMocks();
vi.clearAllMocks();
});

test('Expect no error deleting pod', async () => {
listContainersMock.mockResolvedValue([]);

render(PodActions, { pod, errorCallback });

// click on delete button
Expand Down
47 changes: 46 additions & 1 deletion packages/renderer/src/lib/pod/PodActions.svelte
@@ -1,5 +1,13 @@
<script lang="ts">
import { faFileCode, faPlay, faRocket, faStop, faArrowsRotate, faTrash } from '@fortawesome/free-solid-svg-icons';
import {
faFileCode,
faPlay,
faRocket,
faStop,
faArrowsRotate,
faTrash,
faExternalLinkSquareAlt,
} from '@fortawesome/free-solid-svg-icons';
import type { PodInfoUI } from './PodInfoUI';
import { router } from 'tinro';
import ListItemButtonIcon from '../ui/ListItemButtonIcon.svelte';
Expand All @@ -9,6 +17,7 @@ import type { Menu } from '../../../../main/src/plugin/menu-registry';
import ContributionActions from '/@/lib/actions/ContributionActions.svelte';
import { onMount } from 'svelte';
import { MenuContext } from '../../../../main/src/plugin/menu-registry';
import { ContainerUtils } from '../container/container-utils';
export let pod: PodInfoUI;
export let dropdownMenu = false;
Expand All @@ -22,6 +31,28 @@ onMount(async () => {
contributions = await window.getContributedMenus(MenuContext.DASHBOARD_POD);
});
let urls: Array<string> = [];
$: openingUrls = urls;
function extractPort(urlString: string) {
const match = urlString.match(/:(\d+)/);
return match ? parseInt(match[1], 10) : undefined;
}
onMount(async () => {
const containerUtils = new ContainerUtils();
const containerIds = pod.containers.map(podContainer => podContainer.Id);
const podContainers = (await window.listContainers()).filter(
container => containerIds.findIndex(containerInfo => containerInfo === container.Id) >= 0,
);
podContainers.forEach(container => {
const openingUrls = containerUtils.getOpeningUrls(container);
urls = urls.concat(openingUrls);
});
});
async function startPod(podInfoUI: PodInfoUI) {
inProgressCallback(true, 'STARTING');
try {
Expand Down Expand Up @@ -128,6 +159,20 @@ if (dropdownMenu) {
menu="{dropdownMenu}"
detailed="{detailed}"
icon="{faRocket}" />
{#if openingUrls && openingUrls.length > 0 && pod.status === 'RUNNING' && !dropdownMenu}
<DropdownMenu icon="{faExternalLinkSquareAlt}" shownAsMenuActionItem="{true}" ariaLabel="Open Port">
{#each openingUrls as url}
<ListItemButtonIcon
title="Open {extractPort(url)}"
onClick="{() => window.openExternal(url)}"
menu="{!dropdownMenu}"
enabled="{pod.status === 'RUNNING'}"
hidden="{!(pod.status === 'RUNNING')}"
detailed="{true}"
icon="{faExternalLinkSquareAlt}" />
{/each}
</DropdownMenu>
{/if}
<ListItemButtonIcon
title="Restart Pod"
onClick="{() => restartPod(pod)}"
Expand Down
2 changes: 2 additions & 0 deletions packages/renderer/src/lib/pod/PodDetails.spec.ts
Expand Up @@ -29,6 +29,7 @@ import { router } from 'tinro';
import { lastPage } from '/@/stores/breadcrumb';

const listPodsMock = vi.fn();
const listContainersMock = vi.fn();
const kubernetesListPodsMock = vi.fn();

const myPod: PodInfo = {
Expand All @@ -52,6 +53,7 @@ const getContributedMenusMock = vi.fn();

beforeAll(() => {
(window as any).listPods = listPodsMock;
(window as any).listContainers = listContainersMock.mockResolvedValue([]);
(window as any).kubernetesListPods = kubernetesListPodsMock;
(window as any).removePod = removePodMock;
(window as any).getContributedMenus = getContributedMenusMock;
Expand Down
2 changes: 2 additions & 0 deletions packages/renderer/src/lib/pod/PodsList.spec.ts
Expand Up @@ -30,6 +30,7 @@ import type { PodInfo } from '../../../../main/src/plugin/api/pod-info';

const getProvidersInfoMock = vi.fn();
const listPodsMock = vi.fn();
const listContainersMock = vi.fn();
const kubernetesListPodsMock = vi.fn();
const getContributedMenusMock = vi.fn();

Expand Down Expand Up @@ -127,6 +128,7 @@ const kubepod2: PodInfo = {
beforeAll(() => {
(window as any).getProviderInfos = getProvidersInfoMock;
(window as any).listPods = listPodsMock;
(window as any).listContainers = listContainersMock.mockResolvedValue([]);
(window as any).kubernetesListPods = kubernetesListPodsMock;
(window as any).onDidUpdateProviderStatus = vi.fn().mockResolvedValue(undefined);
(window.events as unknown) = {
Expand Down
1 change: 1 addition & 0 deletions packages/renderer/src/lib/ui/DropDownMenuItems.svelte
Expand Up @@ -21,6 +21,7 @@ onMount(() => {
</script>

<div
title="Drop Down Menu Items"
bind:clientHeight="{dropDownHeight}"
bind:this="{dropDownElement}"
class="origin-top-right absolute right-0 z-10 m-2 rounded-md shadow-lg bg-charcoal-600 ring-2 ring-purple-900 hover:ring-purple-700 divide-y divide-charcoal-600 focus:outline-none">
Expand Down
12 changes: 10 additions & 2 deletions packages/renderer/src/lib/ui/DropdownMenu.svelte
@@ -1,8 +1,13 @@
<script lang="ts">
import { faEllipsisVertical } from '@fortawesome/free-solid-svg-icons';
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
import DropDownMenuItems from './DropDownMenuItems.svelte';
export let icon: IconDefinition = faEllipsisVertical;
export let ariaLabel = 'drop-down-menu';
export let shownAsMenuActionItem = false;
// Show and hide the menu using clickOutside
let showMenu = false;
Expand Down Expand Up @@ -40,9 +45,12 @@ function onWindowClick(e: any) {
clientY = e.clientY;
toggleMenu();
}}"
aria-label="{ariaLabel}"
bind:this="{outsideWindow}"
class="mr-2 text-gray-400 hover:bg-charcoal-800 hover:text-purple-400 font-medium rounded-md inline-flex items-center px-2 py-2 text-center">
<Fa class="h-4 w-4" icon="{faEllipsisVertical}" />
class="text-gray-400 {shownAsMenuActionItem
? 'bg-charcoal-800 px-3'
: 'hover:bg-charcoal-800 mr-2'} hover:text-purple-400 font-medium rounded-md inline-flex items-center px-2 py-2 text-center">
<Fa class="h-4 w-4" icon="{icon}" />
</button>

<!-- Dropdown menu for all other actions -->
Expand Down

0 comments on commit 51a621b

Please sign in to comment.