@@ -13,8 +13,10 @@ import { exists } from './utils'
1313
1414const debug = Debug ( 'dynamodb-local' )
1515const JARNAME = 'DynamoDBLocal.jar'
16+ const DOCKER_IMAGE = 'amazon/dynamodb-local:latest'
1617
1718export const runningProcesses : { [ port : number ] : Subprocess } = { }
19+ export const runningContainers : { [ port : number ] : string } = { }
1820
1921export const dynamoDb = {
2022 async launch ( options ?: LaunchOptions ) : Promise < Subprocess | undefined > {
@@ -26,11 +28,17 @@ export const dynamoDb = {
2628 verbose = false ,
2729 detached = config . local . detached ,
2830 javaOpts = config . local . javaOpts ,
31+ useDocker = false ,
2932 } = options ?? { }
3033
3134 if ( runningProcesses [ port ] )
3235 return runningProcesses [ port ]
3336
37+ // Use Docker if requested
38+ if ( useDocker ) {
39+ return this . launchDocker ( { port, dbPath, verbose, detached } )
40+ }
41+
3442 const args = [
3543 '-Xrs' ,
3644 '-Djava.library.path=./DynamoDBLocal_lib' ,
@@ -73,10 +81,108 @@ export const dynamoDb = {
7381 }
7482 } ,
7583
84+ /**
85+ * Launch DynamoDB Local using Docker
86+ */
87+ async launchDocker ( options : {
88+ port ?: number
89+ dbPath ?: string | null
90+ verbose ?: boolean
91+ detached ?: boolean
92+ } ) : Promise < Subprocess | undefined > {
93+ const config = await getConfig ( )
94+ const {
95+ port = config . local . port ,
96+ dbPath = config . local . dbPath ,
97+ verbose = false ,
98+ detached = config . local . detached ,
99+ } = options
100+
101+ const containerName = `dynamodb-local-${ port } `
102+
103+ // Check if container already exists and is running
104+ const checkResult = Bun . spawnSync ( [ 'docker' , 'ps' , '-q' , '-f' , `name=${ containerName } ` ] )
105+ const existingContainer = checkResult . stdout . toString ( ) . trim ( )
106+ if ( existingContainer ) {
107+ debug ( `DynamoDB Local container already running on port ${ port } ` )
108+ // Return a fake subprocess that represents the running container
109+ if ( runningProcesses [ port ] )
110+ return runningProcesses [ port ]
111+ }
112+
113+ // Stop and remove any existing container with same name
114+ Bun . spawnSync ( [ 'docker' , 'rm' , '-f' , containerName ] , { stdout : 'ignore' , stderr : 'ignore' } )
115+
116+ const dockerArgs = [
117+ 'run' ,
118+ '--rm' ,
119+ '-d' ,
120+ '--name' , containerName ,
121+ '-p' , `${ port } :8000` ,
122+ ]
123+
124+ // Add volume mount if dbPath is specified
125+ if ( dbPath ) {
126+ const absoluteDbPath = path . resolve ( dbPath )
127+ await fs . promises . mkdir ( absoluteDbPath , { recursive : true } )
128+ dockerArgs . push ( '-v' , `${ absoluteDbPath } :/home/dynamodblocal/data` )
129+ dockerArgs . push ( DOCKER_IMAGE , '-jar' , 'DynamoDBLocal.jar' , '-dbPath' , '/home/dynamodblocal/data' )
130+ }
131+ else {
132+ dockerArgs . push ( DOCKER_IMAGE )
133+ }
134+
135+ debug ( 'Launching DynamoDB Local with Docker:' , dockerArgs )
136+
137+ try {
138+ const child = Bun . spawn ( [ 'docker' , ...dockerArgs ] , {
139+ onExit : ( proc , exitCode , signalCode , error ) => {
140+ if ( exitCode !== 0 && verbose )
141+ debug ( 'Docker container exit code:' , exitCode )
142+ if ( error )
143+ debug ( 'Docker container error:' , error )
144+ } ,
145+ } )
146+
147+ // Wait a moment for container to start
148+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
149+
150+ // Verify container is running
151+ const verifyResult = Bun . spawnSync ( [ 'docker' , 'ps' , '-q' , '-f' , `name=${ containerName } ` ] )
152+ const runningContainer = verifyResult . stdout . toString ( ) . trim ( )
153+ if ( ! runningContainer ) {
154+ throw new Error ( 'Failed to start DynamoDB Local Docker container' )
155+ }
156+
157+ runningContainers [ port ] = containerName
158+
159+ if ( ! detached ) {
160+ process . on ( 'exit' , ( ) => {
161+ Bun . spawnSync ( [ 'docker' , 'stop' , containerName ] , { stdout : 'ignore' , stderr : 'ignore' } )
162+ } )
163+ }
164+
165+ runningProcesses [ port ] = child
166+ return child
167+ }
168+ catch ( error ) {
169+ debug ( 'Error launching DynamoDB Local via Docker:' , error )
170+ throw error
171+ }
172+ } ,
173+
76174 stop ( port : number ) : void {
77- const process = runningProcesses [ port ]
78- if ( process ) {
79- process . kill ( 'SIGKILL' )
175+ const proc = runningProcesses [ port ]
176+ const containerName = runningContainers [ port ]
177+
178+ if ( containerName ) {
179+ // Stop Docker container
180+ Bun . spawnSync ( [ 'docker' , 'stop' , containerName ] , { stdout : 'ignore' , stderr : 'ignore' } )
181+ delete runningContainers [ port ]
182+ }
183+
184+ if ( proc ) {
185+ proc . kill ( 'SIGKILL' )
80186 delete runningProcesses [ port ]
81187 }
82188 } ,
0 commit comments