diff --git a/examples/prepare-job.json b/examples/prepare-job.json index f7d03f5..8e122d1 100644 --- a/examples/prepare-job.json +++ b/examples/prepare-job.json @@ -73,6 +73,8 @@ "contextName": "redis", "image": "redis", "createOptions": "--cpus 1", + "entrypoint": null, + "entryPointArgs": [], "environmentVariables": {}, "userMountVolumes": [ { diff --git a/packages/k8s/src/hooks/prepare-job.ts b/packages/k8s/src/hooks/prepare-job.ts index 7d79f96..4c4a681 100644 --- a/packages/k8s/src/hooks/prepare-job.ts +++ b/packages/k8s/src/hooks/prepare-job.ts @@ -14,6 +14,7 @@ import { containerVolumes, DEFAULT_CONTAINER_ENTRY_POINT, DEFAULT_CONTAINER_ENTRY_POINT_ARGS, + generateContainerName, PodPhase } from '../k8s/utils' import { JOB_CONTAINER_NAME } from './constants' @@ -31,14 +32,14 @@ export async function prepareJob( let container: k8s.V1Container | undefined = undefined if (args.container?.image) { core.debug(`Using image '${args.container.image}' for job image`) - container = createPodSpec(args.container, JOB_CONTAINER_NAME, true) + container = createContainerSpec(args.container, JOB_CONTAINER_NAME, true) } let services: k8s.V1Container[] = [] if (args.services?.length) { services = args.services.map(service => { core.debug(`Adding service '${service.image}' to pod definition`) - return createPodSpec(service, service.image.split(':')[0]) + return createContainerSpec(service, generateContainerName(service.image)) }) } if (!container && !services?.length) { @@ -153,7 +154,7 @@ async function copyExternalsToRoot(): Promise { } } -function createPodSpec( +export function createContainerSpec( container, name: string, jobContainer = false @@ -166,14 +167,20 @@ function createPodSpec( const podContainer = { name, image: container.image, - command: [container.entryPoint], - args: container.entryPointArgs, ports: containerPorts(container) } as k8s.V1Container if (container.workingDirectory) { podContainer.workingDir = container.workingDirectory } + if (container.entryPoint) { + podContainer.command = [container.entryPoint] + } + + if (container.entryPointArgs?.length > 0) { + podContainer.args = container.entryPointArgs + } + podContainer.env = [] for (const [key, value] of Object.entries( container['environmentVariables'] diff --git a/packages/k8s/src/k8s/utils.ts b/packages/k8s/src/k8s/utils.ts index e2a23aa..bf637b6 100644 --- a/packages/k8s/src/k8s/utils.ts +++ b/packages/k8s/src/k8s/utils.ts @@ -137,6 +137,17 @@ exec ${environmentPrefix} ${entryPoint} ${ } } +export function generateContainerName(image: string): string { + const nameWithTag = image.split('/').pop() + const name = nameWithTag?.split(':').at(0) + + if (!name) { + throw new Error(`Image definition '${image}' is invalid`) + } + + return name +} + export enum PodPhase { PENDING = 'Pending', RUNNING = 'Running', diff --git a/packages/k8s/tests/k8s-utils-test.ts b/packages/k8s/tests/k8s-utils-test.ts index 4341375..919bea0 100644 --- a/packages/k8s/tests/k8s-utils-test.ts +++ b/packages/k8s/tests/k8s-utils-test.ts @@ -1,6 +1,10 @@ import * as fs from 'fs' import { containerPorts, POD_VOLUME_NAME } from '../src/k8s' -import { containerVolumes, writeEntryPointScript } from '../src/k8s/utils' +import { + containerVolumes, + generateContainerName, + writeEntryPointScript +} from '../src/k8s/utils' import { TestHelper } from './test-setup' let testHelper: TestHelper @@ -221,4 +225,32 @@ describe('k8s utils', () => { expect(() => containerPorts({ portMappings: ['1/tcp/udp'] })).toThrow() }) }) + + describe('generate container name', () => { + it('should return the container name from image string', () => { + expect( + generateContainerName('public.ecr.aws/localstack/localstack') + ).toEqual('localstack') + expect( + generateContainerName( + 'public.ecr.aws/url/with/multiple/slashes/postgres:latest' + ) + ).toEqual('postgres') + expect(generateContainerName('postgres')).toEqual('postgres') + expect(generateContainerName('postgres:latest')).toEqual('postgres') + expect(generateContainerName('localstack/localstack')).toEqual( + 'localstack' + ) + expect(generateContainerName('localstack/localstack:latest')).toEqual( + 'localstack' + ) + }) + + it('should throw on invalid image string', () => { + expect(() => + generateContainerName('localstack/localstack/:latest') + ).toThrow() + expect(() => generateContainerName(':latest')).toThrow() + }) + }) }) diff --git a/packages/k8s/tests/prepare-job-test.ts b/packages/k8s/tests/prepare-job-test.ts index 1bb6067..d8494c1 100644 --- a/packages/k8s/tests/prepare-job-test.ts +++ b/packages/k8s/tests/prepare-job-test.ts @@ -1,8 +1,10 @@ import * as fs from 'fs' import * as path from 'path' import { cleanupJob } from '../src/hooks' -import { prepareJob } from '../src/hooks/prepare-job' +import { createContainerSpec, prepareJob } from '../src/hooks/prepare-job' import { TestHelper } from './test-setup' +import { generateContainerName } from '../src/k8s/utils' +import { V1Container } from '@kubernetes/client-node' jest.useRealTimers() @@ -71,4 +73,13 @@ describe('Prepare job', () => { prepareJob(prepareJobData.args, prepareJobOutputFilePath) ).rejects.toThrow() }) + + it('should not set command + args for service container if not passed in args', async () => { + const services = prepareJobData.args.services.map(service => { + return createContainerSpec(service, generateContainerName(service.image)) + }) as [V1Container] + + expect(services[0].command).toBe(undefined) + expect(services[0].args).toBe(undefined) + }) })