From 4bc103563c1f41687d0767b67e44ff7773550fe6 Mon Sep 17 00:00:00 2001 From: Jeff MAURY Date: Wed, 14 Feb 2024 18:12:55 +0100 Subject: [PATCH] feat: add HealthCheck parameter when creating container (#5981) Fixes #5841 Signed-off-by: Jeff MAURY --- packages/extension-api/src/extension-api.d.ts | 38 +++++++++++++++++ .../main/src/plugin/api/container-info.ts | 34 +++++++++++++++ .../src/plugin/container-registry.spec.ts | 41 +++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/packages/extension-api/src/extension-api.d.ts b/packages/extension-api/src/extension-api.d.ts index 75cf0f4521f..8f943ee8606 100644 --- a/packages/extension-api/src/extension-api.d.ts +++ b/packages/extension-api/src/extension-api.d.ts @@ -2223,6 +2223,39 @@ declare module '@podman-desktop/api' { errorDetails?: { message?: string }; } + interface HealthConfig { + /** + * The test to perform. Possible values are: + * + * - ```[]``` inherit healthcheck from image or parent image + * - ```["NONE"]``` disable healthcheck + * - ```["CMD", args...]``` exec arguments directly + * - ```["CMD-SHELL", command]``` run command with system's default shell + */ + Test?: string[]; + + /** + * The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + Interval?: number; + + /** + * The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + Timeout?: number; + + /** + * Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should + * be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + StartPeriod?: number; + + /** + * The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit. + */ + Retries?: number; + } + export interface PodmanContainerCreateOptions { command?: string[]; entrypoint?: string | string[]; @@ -2340,6 +2373,11 @@ declare module '@podman-desktop/api' { * Start the container immediately (default true) */ start?: boolean; + + /** + * A test to perform to check that the container is healthy. + */ + HealthCheck?: HealthConfig; } /** diff --git a/packages/main/src/plugin/api/container-info.ts b/packages/main/src/plugin/api/container-info.ts index e342125a3c5..c5ce9f9a971 100644 --- a/packages/main/src/plugin/api/container-info.ts +++ b/packages/main/src/plugin/api/container-info.ts @@ -71,6 +71,39 @@ export interface HostConfig { NetworkMode?: string; } +export interface HealthConfig { + /** + * The test to perform. Possible values are: + * + * - ```[]``` inherit healthcheck from image or parent image + * - ```["NONE"]``` disable healthcheck + * - ```["CMD", args...]``` exec arguments directly + * - ```["CMD-SHELL", command]``` run command with system's default shell + */ + Test?: string[]; + + /** + * The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + Interval?: number; + + /** + * The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + Timeout?: number; + + /** + * Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should + * be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + StartPeriod?: number; + + /** + * The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit. + */ + Retries?: number; +} + export interface ContainerCreateOptions { name?: string; Hostname?: string; @@ -96,6 +129,7 @@ export interface ContainerCreateOptions { StdinOnce?: boolean; Detach?: boolean; start?: boolean; + HealthCheck?: HealthConfig; } export interface NetworkCreateOptions { diff --git a/packages/main/src/plugin/container-registry.spec.ts b/packages/main/src/plugin/container-registry.spec.ts index 502d43d9fa7..95b9956d92c 100644 --- a/packages/main/src/plugin/container-registry.spec.ts +++ b/packages/main/src/plugin/container-registry.spec.ts @@ -2391,6 +2391,47 @@ describe('createContainer', () => { expect(createContainerMock).toHaveBeenCalledWith(expect.not.objectContaining({ EnvFiles: ['file1', 'file2'] })); }); + test('test create and start Container with healthcheck', async () => { + const createdId = '1234'; + + const startMock = vi.fn(); + const inspectMock = vi.fn(); + const createContainerMock = vi + .fn() + .mockResolvedValue({ id: createdId, start: startMock, inspect: inspectMock } as unknown as Dockerode.Container); + + inspectMock.mockResolvedValue({ + Config: { + Tty: false, + OpenStdin: false, + }, + }); + + const fakeDockerode = { + createContainer: createContainerMock, + } as unknown as Dockerode; + + containerRegistry.addInternalProvider('podman1', { + name: 'podman1', + id: 'podman1', + connection: { + type: 'podman', + }, + api: fakeDockerode, + } as InternalContainerProvider); + + const container = await containerRegistry.createContainer('podman1', { HealthCheck: { Test: ['cmd', 'arg1'] } }); + + expect(container.id).toBe(createdId); + expect(createContainerMock).toHaveBeenCalled(); + expect(startMock).toHaveBeenCalled(); + + // expect healthcheck to be set + expect(createContainerMock).toHaveBeenCalledWith( + expect.objectContaining({ HealthCheck: { Test: ['cmd', 'arg1'] } }), + ); + }); + test('test container is created but not started', async () => { const createdId = '1234';