Skip to content

Commit ec341da

Browse files
committed
Add option for GPU availability (microsoft/vscode-remote-release#9385)
1 parent cbfbdb1 commit ec341da

File tree

4 files changed

+18
-4
lines changed

4 files changed

+18
-4
lines changed

src/spec-node/devContainers.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as crypto from 'crypto';
88
import * as os from 'os';
99

1010
import { mapNodeOSToGOOS, mapNodeArchitectureToGOARCH } from '../spec-configuration/containerCollectionsOCI';
11-
import { DockerResolverParameters, DevContainerAuthority, UpdateRemoteUserUIDDefault, BindMountConsistency, getCacheFolder } from './utils';
11+
import { DockerResolverParameters, DevContainerAuthority, UpdateRemoteUserUIDDefault, BindMountConsistency, getCacheFolder, GPUAvailability } from './utils';
1212
import { createNullLifecycleHook, finishBackgroundTasks, ResolverParameters, UserEnvProbe } from '../spec-common/injectHeadless';
1313
import { GoARCH, GoOS, getCLIHost, loadNativeModule } from '../spec-common/commonUtils';
1414
import { resolve } from './configContainer';
@@ -28,6 +28,7 @@ export interface ProvisionOptions {
2828
containerSystemDataFolder: string | undefined;
2929
workspaceFolder: string | undefined;
3030
workspaceMountConsistency?: BindMountConsistency;
31+
gpuAvailability?: GPUAvailability;
3132
mountWorkspaceGitRoot: boolean;
3233
configFile: URI | undefined;
3334
overrideConfigFile: URI | undefined;
@@ -101,7 +102,7 @@ export async function launch(options: ProvisionOptions, providedIdLabels: string
101102
}
102103

103104
export async function createDockerParams(options: ProvisionOptions, disposables: (() => Promise<unknown> | undefined)[]): Promise<DockerResolverParameters> {
104-
const { persistedFolder, additionalMounts, updateRemoteUserUIDDefault, containerDataFolder, containerSystemDataFolder, workspaceMountConsistency, mountWorkspaceGitRoot, remoteEnv, experimentalLockfile, experimentalFrozenLockfile, omitLoggerHeader, secretsP } = options;
105+
const { persistedFolder, additionalMounts, updateRemoteUserUIDDefault, containerDataFolder, containerSystemDataFolder, workspaceMountConsistency, gpuAvailability, mountWorkspaceGitRoot, remoteEnv, experimentalLockfile, experimentalFrozenLockfile, omitLoggerHeader, secretsP } = options;
105106
let parsedAuthority: DevContainerAuthority | undefined;
106107
if (options.workspaceFolder) {
107108
parsedAuthority = { hostPath: options.workspaceFolder } as DevContainerAuthority;
@@ -212,6 +213,7 @@ export async function createDockerParams(options: ProvisionOptions, disposables:
212213
dockerComposeCLI: dockerComposeCLI,
213214
dockerEnv: cliHost.env,
214215
workspaceMountConsistencyDefault: workspaceMountConsistency,
216+
gpuAvailability: gpuAvailability || 'detect',
215217
mountWorkspaceGitRoot,
216218
updateRemoteUserUIDOnMacOS: false,
217219
cacheMount: 'bind',

src/spec-node/devContainersSpecCLI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ function provisionOptions(y: Argv) {
105105
'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' },
106106
'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' },
107107
'workspace-mount-consistency': { choices: ['consistent' as 'consistent', 'cached' as 'cached', 'delegated' as 'delegated'], default: 'cached' as 'cached', description: 'Workspace mount consistency.' },
108+
'gpu-availability': { choices: ['all' as 'all', 'detect' as 'detect', 'none' as 'none'], default: 'detect' as 'detect', description: 'Availability of GPUs in case the dev container requires any. `all` expects a GPU to be available.' },
108109
'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' },
109110
'id-label': { type: 'string', description: 'Id label(s) of the format name=value. These will be set on the container and used to query for an existing container. If no --id-label is given, one will be inferred from the --workspace-folder path.' },
110111
'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' },
@@ -179,6 +180,7 @@ async function provision({
179180
'container-system-data-folder': containerSystemDataFolder,
180181
'workspace-folder': workspaceFolderArg,
181182
'workspace-mount-consistency': workspaceMountConsistency,
183+
'gpu-availability': gpuAvailability,
182184
'mount-workspace-git-root': mountWorkspaceGitRoot,
183185
'id-label': idLabel,
184186
config,
@@ -233,6 +235,7 @@ async function provision({
233235
containerSystemDataFolder,
234236
workspaceFolder,
235237
workspaceMountConsistency,
238+
gpuAvailability,
236239
mountWorkspaceGitRoot,
237240
configFile: config ? URI.file(path.resolve(process.cwd(), config)) : undefined,
238241
overrideConfigFile: overrideConfig ? URI.file(path.resolve(process.cwd(), overrideConfig)) : undefined,

src/spec-node/singleContainer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ export async function findDevContainer(params: DockerCLIParameters | DockerResol
329329
return details.filter(container => container.State.Status !== 'removing')[0];
330330
}
331331

332-
export async function extraRunArgs(common: ResolverParameters, params: DockerCLIParameters | DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig) {
332+
export async function extraRunArgs(common: ResolverParameters, params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig) {
333333
const extraArguments: string[] = [];
334334
if (config.hostRequirements?.gpu) {
335335
if (await checkDockerSupportForGPU(params)) {

src/spec-node/utils.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export { uriToFsPath, parentURI } from '../spec-configuration/configurationCommo
3535

3636
export type BindMountConsistency = 'consistent' | 'cached' | 'delegated' | undefined;
3737

38+
export type GPUAvailability = 'all' | 'detect' | 'none';
39+
3840
// Generic retry function
3941
export async function retry<T>(fn: () => Promise<T>, options: { retryIntervalMilliseconds: number; maxRetries: number; output: Log }): Promise<T> {
4042
const { retryIntervalMilliseconds, maxRetries, output } = options;
@@ -100,6 +102,7 @@ export interface DockerResolverParameters {
100102
dockerComposeCLI: () => Promise<DockerComposeCLI>;
101103
dockerEnv: NodeJS.ProcessEnv;
102104
workspaceMountConsistencyDefault: BindMountConsistency;
105+
gpuAvailability: GPUAvailability;
103106
mountWorkspaceGitRoot: boolean;
104107
updateRemoteUserUIDOnMacOS: boolean;
105108
cacheMount: 'volume' | 'bind' | 'none';
@@ -202,7 +205,13 @@ async function hasLabels(params: DockerResolverParameters, info: any, expectedLa
202205
.every(name => actualLabels[name] === expectedLabels[name]);
203206
}
204207

205-
export async function checkDockerSupportForGPU(params: DockerCLIParameters | DockerResolverParameters): Promise<Boolean> {
208+
export async function checkDockerSupportForGPU(params: DockerResolverParameters): Promise<Boolean> {
209+
if (params.gpuAvailability === 'all') {
210+
return true;
211+
}
212+
if (params.gpuAvailability === 'none') {
213+
return false;
214+
}
206215
const result = await dockerCLI(params, 'info', '-f', '{{.Runtimes.nvidia}}');
207216
const runtimeFound = result.stdout.includes('nvidia-container-runtime');
208217
return runtimeFound;

0 commit comments

Comments
 (0)