* Creates the isolated volume and populates it with files. * * @param files - Map of filename to content (string or Buffer) * @returns The volume name for use in bind mounts * * @example * ```typescript * const volume = new IsolatedContainerVolume(tenantId, runId); * await vo
(files: Record<string, string | Buffer>)
| 66 | * ``` |
| 67 | */ |
| 68 | async initialize(files: Record<string, string | Buffer>): Promise<string> { |
| 69 | if (this.isInitialized) { |
| 70 | throw new ConfigurationError('Volume already initialized', { |
| 71 | details: { volumeName: this.volumeName, tenantId: this.tenantId, runId: this.runId }, |
| 72 | }); |
| 73 | } |
| 74 | |
| 75 | // Create unique volume name with timestamp to prevent collisions |
| 76 | // when parallel nodes in the same run each create their own volume. |
| 77 | const timestamp = Date.now(); |
| 78 | this.volumeName = `tenant-${this.tenantId}-run-${this.runId}-${timestamp}`; |
| 79 | |
| 80 | try { |
| 81 | // Create the volume with labels for tracking |
| 82 | await this.executeDockerCommand('volume', 'create', [ |
| 83 | '--label', |
| 84 | `studio.tenant=${this.tenantId}`, |
| 85 | '--label', |
| 86 | `studio.run=${this.runId}`, |
| 87 | '--label', |
| 88 | `studio.created=${new Date().toISOString()}`, |
| 89 | '--label', |
| 90 | 'studio.managed=true', |
| 91 | this.volumeName, |
| 92 | ]); |
| 93 | |
| 94 | // Populate files if provided |
| 95 | if (Object.keys(files).length > 0) { |
| 96 | await this.writeFiles(files); |
| 97 | } |
| 98 | |
| 99 | this.isInitialized = true; |
| 100 | return this.volumeName; |
| 101 | } catch (error) { |
| 102 | // Clean up on failure |
| 103 | if (this.volumeName) { |
| 104 | await this.cleanup().catch(() => { |
| 105 | // Ignore cleanup errors during initialization failure |
| 106 | }); |
| 107 | } |
| 108 | throw new ContainerError( |
| 109 | `Failed to initialize isolated volume: ${error instanceof Error ? error.message : String(error)}`, |
| 110 | { |
| 111 | cause: error instanceof Error ? error : undefined, |
| 112 | details: { tenantId: this.tenantId, runId: this.runId }, |
| 113 | }, |
| 114 | ); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * Validates filename to prevent path traversal and shell injection. |