feat(cli): add native gVisor (runsc) sandboxing support (#21062)

Co-authored-by: Zheyuan <zlin252@emory.edu>
Co-authored-by: Kartik Angiras <angiraskartik@gmail.com>
This commit is contained in:
Zheyuan Lin
2026-03-05 13:39:57 -05:00
committed by GitHub
parent c7e2dbe0cf
commit 291639633f
6 changed files with 235 additions and 27 deletions
+90 -2
View File
@@ -97,7 +97,7 @@ describe('loadSandboxConfig', () => {
it('should throw if GEMINI_SANDBOX is an invalid command', async () => {
process.env['GEMINI_SANDBOX'] = 'invalid-command';
await expect(loadSandboxConfig({}, {})).rejects.toThrow(
"Invalid sandbox command 'invalid-command'. Must be one of docker, podman, sandbox-exec, lxc",
"Invalid sandbox command 'invalid-command'. Must be one of docker, podman, sandbox-exec, runsc, lxc",
);
});
@@ -194,7 +194,7 @@ describe('loadSandboxConfig', () => {
await expect(
loadSandboxConfig({}, { sandbox: 'invalid-command' }),
).rejects.toThrow(
"Invalid sandbox command 'invalid-command'. Must be one of docker, podman, sandbox-exec",
"Invalid sandbox command 'invalid-command'. Must be one of docker, podman, sandbox-exec, runsc, lxc",
);
});
});
@@ -247,4 +247,92 @@ describe('loadSandboxConfig', () => {
},
);
});
describe('with sandbox: runsc (gVisor)', () => {
beforeEach(() => {
mockedOsPlatform.mockReturnValue('linux');
mockedCommandExistsSync.mockReturnValue(true);
});
it('should use runsc via CLI argument on Linux', async () => {
const config = await loadSandboxConfig({}, { sandbox: 'runsc' });
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('runsc');
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
it('should use runsc via GEMINI_SANDBOX environment variable', async () => {
process.env['GEMINI_SANDBOX'] = 'runsc';
const config = await loadSandboxConfig({}, {});
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('runsc');
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
it('should use runsc via settings file', async () => {
const config = await loadSandboxConfig(
{ tools: { sandbox: 'runsc' } },
{},
);
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('runsc');
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
it('should prioritize GEMINI_SANDBOX over CLI and settings', async () => {
process.env['GEMINI_SANDBOX'] = 'runsc';
const config = await loadSandboxConfig(
{ tools: { sandbox: 'docker' } },
{ sandbox: 'podman' },
);
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
});
it('should reject runsc on macOS (Linux-only)', async () => {
mockedOsPlatform.mockReturnValue('darwin');
await expect(loadSandboxConfig({}, { sandbox: 'runsc' })).rejects.toThrow(
'gVisor (runsc) sandboxing is only supported on Linux',
);
});
it('should reject runsc on Windows (Linux-only)', async () => {
mockedOsPlatform.mockReturnValue('win32');
await expect(loadSandboxConfig({}, { sandbox: 'runsc' })).rejects.toThrow(
'gVisor (runsc) sandboxing is only supported on Linux',
);
});
it('should throw if runsc binary not found', async () => {
mockedCommandExistsSync.mockReturnValue(false);
await expect(loadSandboxConfig({}, { sandbox: 'runsc' })).rejects.toThrow(
"Missing sandbox command 'runsc' (from GEMINI_SANDBOX)",
);
});
it('should throw if Docker not available (runsc requires Docker)', async () => {
mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'runsc');
await expect(loadSandboxConfig({}, { sandbox: 'runsc' })).rejects.toThrow(
"runsc (gVisor) requires Docker. Install Docker, or use sandbox: 'docker'.",
);
});
it('should NOT auto-detect runsc when both runsc and docker available', async () => {
mockedCommandExistsSync.mockImplementation(
(cmd) => cmd === 'runsc' || cmd === 'docker',
);
const config = await loadSandboxConfig({}, { sandbox: true });
expect(config?.command).toBe('docker');
expect(config?.command).not.toBe('runsc');
});
});
});