fix(SEC-ORCH-30): Add unique suffix to container names
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Add crypto.randomBytes(4) hex suffix to container name generation to prevent name collisions when multiple agents spawn simultaneously within the same millisecond. Container names now include both a timestamp and 8 random hex characters for guaranteed uniqueness. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -162,6 +162,42 @@ describe("DockerSandboxService", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should include a random suffix in container name for uniqueness", async () => {
|
||||||
|
const agentId = "agent-123";
|
||||||
|
const taskId = "task-456";
|
||||||
|
const workspacePath = "/workspace/agent-123";
|
||||||
|
|
||||||
|
await service.createContainer(agentId, taskId, workspacePath);
|
||||||
|
|
||||||
|
const callArgs = (mockDocker.createContainer as ReturnType<typeof vi.fn>).mock
|
||||||
|
.calls[0][0] as Docker.ContainerCreateOptions;
|
||||||
|
const containerName = callArgs.name as string;
|
||||||
|
|
||||||
|
// Name format: mosaic-agent-{agentId}-{timestamp}-{8 hex chars}
|
||||||
|
expect(containerName).toMatch(/^mosaic-agent-agent-123-\d+-[0-9a-f]{8}$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate unique container names across rapid successive calls", async () => {
|
||||||
|
const agentId = "agent-123";
|
||||||
|
const taskId = "task-456";
|
||||||
|
const workspacePath = "/workspace/agent-123";
|
||||||
|
const containerNames = new Set<string>();
|
||||||
|
|
||||||
|
// Spawn multiple containers rapidly to test for collisions
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
await service.createContainer(agentId, taskId, workspacePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const calls = (mockDocker.createContainer as ReturnType<typeof vi.fn>).mock.calls;
|
||||||
|
for (const call of calls) {
|
||||||
|
const args = call[0] as Docker.ContainerCreateOptions;
|
||||||
|
containerNames.add(args.name as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All 20 names must be unique (no collisions)
|
||||||
|
expect(containerNames.size).toBe(20);
|
||||||
|
});
|
||||||
|
|
||||||
it("should throw error if container creation fails", async () => {
|
it("should throw error if container creation fails", async () => {
|
||||||
const agentId = "agent-123";
|
const agentId = "agent-123";
|
||||||
const taskId = "task-456";
|
const taskId = "task-456";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { randomBytes } from "crypto";
|
||||||
import Docker from "dockerode";
|
import Docker from "dockerode";
|
||||||
import {
|
import {
|
||||||
DockerSandboxOptions,
|
DockerSandboxOptions,
|
||||||
@@ -248,8 +249,10 @@ export class DockerSandboxService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container name with timestamp to ensure uniqueness
|
// Container name with timestamp and random suffix to guarantee uniqueness
|
||||||
const containerName = `mosaic-agent-${agentId}-${Date.now().toString()}`;
|
// even when multiple agents are spawned simultaneously within the same millisecond
|
||||||
|
const uniqueSuffix = randomBytes(4).toString("hex");
|
||||||
|
const containerName = `mosaic-agent-${agentId}-${Date.now().toString()}-${uniqueSuffix}`;
|
||||||
|
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Creating container for agent ${agentId} (image: ${image}, memory: ${memoryMB.toString()}MB, cpu: ${cpuLimit.toString()})`
|
`Creating container for agent ${agentId} (image: ${image}, memory: ${memoryMB.toString()}MB, cpu: ${cpuLimit.toString()})`
|
||||||
|
|||||||
Reference in New Issue
Block a user