@@ -230,6 +230,25 @@ ${cacheFromOverrideContent}
230
230
args . push ( '-f' , composeOverrideFile ) ;
231
231
}
232
232
233
+ const hasGpuRequirement = config . hostRequirements ?. gpu ;
234
+ const addGpuCapability = hasGpuRequirement && await checkDockerSupportForGPU ( params ) ;
235
+ if ( addGpuCapability ) {
236
+ const composeOverrideContent = `services:
237
+ ${ config . service } :
238
+ deploy:
239
+ resources:
240
+ reservations:
241
+ devices:
242
+ - capabilities: [gpu]
243
+ ` ;
244
+ const composeFolder = cliHost . path . join ( overrideFilePath , 'docker-compose' ) ;
245
+ await cliHost . mkdirp ( composeFolder ) ;
246
+ const composeOverrideFile = cliHost . path . join ( composeFolder , `${ overrideFilePrefix } -gpu-${ Date . now ( ) } .yml` ) ;
247
+ await cliHost . writeFile ( composeOverrideFile , Buffer . from ( composeOverrideContent ) ) ;
248
+ additionalComposeOverrideFiles . push ( composeOverrideFile ) ;
249
+ args . push ( '-f' , composeOverrideFile ) ;
250
+ }
251
+
233
252
if ( ! noBuild ) {
234
253
args . push ( 'build' ) ;
235
254
if ( noCache ) {
@@ -291,6 +310,13 @@ async function checkForPersistedFile(cliHost: CLIHost, output: Log, files: strin
291
310
foundLabel : false
292
311
} ;
293
312
}
313
+
314
+ async function checkDockerSupportForGPU ( params : DockerCLIParameters | DockerResolverParameters ) : Promise < Boolean > {
315
+ const result = await dockerCLI ( params , 'info' , '-f' , '{{.Runtimes.nvidia}}' ) ;
316
+ const runtimeFound = result . stdout . includes ( 'nvidia-container-runtime' ) ;
317
+ return runtimeFound ;
318
+ }
319
+
294
320
async function startContainer ( params : DockerResolverParameters , buildParams : DockerCLIParameters , config : DevContainerFromDockerComposeConfig , projectName : string , composeFiles : string [ ] , envFile : string | undefined , container : ContainerDetails | undefined , idLabels : string [ ] ) {
295
321
const { common } = params ;
296
322
const { persistedFolder, output } = common ;
@@ -300,15 +326,14 @@ async function startContainer(params: DockerResolverParameters, buildParams: Doc
300
326
301
327
common . progress ( ResolverProgress . StartingContainer ) ;
302
328
303
- const localComposeFiles = composeFiles ;
304
329
// If dockerComposeFile is an array, add -f <file> in order. https://docs.docker.com/compose/extends/#multiple-compose-files
305
- const composeGlobalArgs = ( [ ] as string [ ] ) . concat ( ...localComposeFiles . map ( composeFile => [ '-f' , composeFile ] ) ) ;
330
+ const composeGlobalArgs = ( [ ] as string [ ] ) . concat ( ...composeFiles . map ( composeFile => [ '-f' , composeFile ] ) ) ;
306
331
if ( envFile ) {
307
332
composeGlobalArgs . push ( '--env-file' , envFile ) ;
308
333
}
309
334
310
335
const infoOutput = makeLog ( buildParams . output , LogLevel . Info ) ;
311
- const composeConfig = await readDockerComposeConfig ( buildParams , localComposeFiles , envFile ) ;
336
+ const composeConfig = await readDockerComposeConfig ( buildParams , composeFiles , envFile ) ;
312
337
const services = Object . keys ( composeConfig . services || { } ) ;
313
338
if ( services . indexOf ( config . service ) === - 1 ) {
314
339
throw new ContainerError ( { description : `Service '${ config . service } ' configured in devcontainer.json not found in Docker Compose configuration.` , data : { fileWithError : composeFiles [ 0 ] } } ) ;
@@ -355,7 +380,7 @@ async function startContainer(params: DockerResolverParameters, buildParams: Doc
355
380
const noBuild = ! ! container ; //if we have an existing container, just recreate override files but skip the build
356
381
357
382
const infoParams = { ...params , common : { ...params . common , output : infoOutput } } ;
358
- const { collapsedFeaturesConfig, additionalComposeOverrideFiles } = await buildAndExtendDockerCompose ( config , projectName , infoParams , localComposeFiles , envFile , composeGlobalArgs , config . runServices ?? [ ] , params . buildNoCache ?? false , persistedFolder , featuresBuildOverrideFilePrefix , params . additionalCacheFroms , noBuild ) ;
383
+ const { collapsedFeaturesConfig, additionalComposeOverrideFiles } = await buildAndExtendDockerCompose ( config , projectName , infoParams , composeFiles , envFile , composeGlobalArgs , config . runServices ?? [ ] , params . buildNoCache ?? false , persistedFolder , featuresBuildOverrideFilePrefix , params . additionalCacheFroms , noBuild ) ;
359
384
additionalComposeOverrideFiles . forEach ( overrideFilePath => composeGlobalArgs . push ( '-f' , overrideFilePath ) ) ;
360
385
361
386
let cache : Promise < ImageDetails > | undefined ;
@@ -391,7 +416,7 @@ async function startContainer(params: DockerResolverParameters, buildParams: Doc
391
416
}
392
417
} catch ( err ) {
393
418
cancel ! ( ) ;
394
- throw new ContainerError ( { description : 'An error occurred starting Docker Compose up.' , originalError : err , data : { fileWithError : localComposeFiles [ 0 ] } } ) ;
419
+ throw new ContainerError ( { description : 'An error occurred starting Docker Compose up.' , originalError : err , data : { fileWithError : composeFiles [ 0 ] } } ) ;
395
420
}
396
421
397
422
await started ;
0 commit comments