Resolve service name conflicts when the name is computed to the same value

This commit is contained in:
Nikola Jokic
2026-01-26 12:44:11 +01:00
parent d8d2e74810
commit 04b61b9744
2 changed files with 53 additions and 9 deletions

View File

@@ -58,14 +58,30 @@ export async function prepareJob(
}
let services: k8s.V1Container[] = []
let serviceNames: string[] = []
if (args.services?.length) {
const occurrences = new Map<string, number>()
for (const s of args.services) {
const base = generateContainerName(s.image)
occurrences.set(base, (occurrences.get(base) || 0) + 1)
}
const indices = new Map<string, number>()
services = args.services.map(service => {
return createContainerSpec(
service,
generateContainerName(service.image),
false,
extension
)
const base = generateContainerName(service.image)
const total = occurrences.get(base) || 0
const idx = indices.get(base) || 0
let name: string
if (total > 1) {
name = `${base}-${idx}`
} else {
name = base
}
indices.set(base, idx + 1)
serviceNames.push(name)
return createContainerSpec(service, name, false, extension)
})
}
@@ -153,14 +169,15 @@ export async function prepareJob(
throw new Error(`failed to determine if the pod is alpine: ${message}`)
}
core.debug(`Setting isAlpine to ${isAlpine}`)
generateResponseFile(responseFile, args, createdPod, isAlpine)
generateResponseFile(responseFile, args, createdPod, isAlpine, serviceNames)
}
function generateResponseFile(
responseFile: string,
args: PrepareJobArgs,
appPod: k8s.V1Pod,
isAlpine: boolean
isAlpine: boolean,
serviceNames?: string[]
): void {
if (!appPod.metadata?.name) {
throw new Error('app pod must have metadata.name specified')
@@ -193,7 +210,9 @@ function generateResponseFile(
if (args.services?.length) {
const serviceContainerNames =
args.services?.map(s => generateContainerName(s.image)) || []
serviceNames && serviceNames.length
? serviceNames
: args.services?.map(s => generateContainerName(s.image)) || []
response.context['services'] = appPod?.spec?.containers
?.filter(c => serviceContainerNames.includes(c.name))

View File

@@ -243,4 +243,29 @@ describe('Prepare job', () => {
'ghcr.io/actions/actions-runner:latest'
)
})
it('should create unique service container names when images collide', async () => {
// make two services with the same image
const svc = JSON.parse(JSON.stringify(prepareJobData.args.services[0]))
prepareJobData.args.services = [svc, JSON.parse(JSON.stringify(svc))]
// ensure registries are null as TestHelper expects
prepareJobData.args.services.forEach((s: any) => (s.registry = null))
await expect(
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
).resolves.not.toThrow()
const content = JSON.parse(
fs.readFileSync(prepareJobOutputFilePath).toString()
)
expect(content.context.services).toBeTruthy()
expect(content.context.services.length).toBe(2)
const got = await getPodByName(content.state.jobPod)
const names = (got.spec?.containers || []).map(c => c.name)
// when images collide, names should be suffixed with -0, -1
expect(names).toEqual(expect.arrayContaining(['redis-0', 'redis-1']))
})
})