Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion extensions/vscode-containers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,17 @@
},
{
"command": "vscode-containers.containers.start",
"when": "view == vscode-containers.views.containers && viewItem =~ /^(created|dead|exited|paused|terminated)Container$/i",
"when": "view == vscode-containers.views.containers && viewItem =~ /^(created|dead|exited|terminated)Container$/i",
"group": "containers_2_active@5"
},
{
"command": "vscode-containers.containers.unpause",
"when": "view == vscode-containers.views.containers && viewItem =~ /^pausedContainer$/i",
"group": "containers_2_active@5"
},
{
"command": "vscode-containers.containers.pause",
"when": "view == vscode-containers.views.containers && viewItem =~ /^runningContainer$/i",
"group": "containers_2_active@5"
},
{
Expand Down Expand Up @@ -2723,6 +2733,11 @@
"title": "%vscode-containers.commands.containers.group.remove%",
"category": "%vscode-containers.commands.category.containers%"
},
{
"command": "vscode-containers.containers.pause",
"title": "%vscode-containers.commands.containers.pause%",
"category": "%vscode-containers.commands.category.containers%"
},
{
"command": "vscode-containers.containers.restart",
"title": "%vscode-containers.commands.containers.restart%",
Expand All @@ -2743,6 +2758,11 @@
"title": "%vscode-containers.commands.containers.stop%",
"category": "%vscode-containers.commands.category.containers%"
},
{
"command": "vscode-containers.containers.unpause",
"title": "%vscode-containers.commands.containers.unpause%",
"category": "%vscode-containers.commands.category.containers%"
},
Comment thread
LE0-Lin marked this conversation as resolved.
{
"command": "vscode-containers.containers.stats",
"title": "%vscode-containers.commands.containers.stats%",
Expand Down
2 changes: 2 additions & 0 deletions extensions/vscode-containers/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@
"vscode-containers.commands.containers.downloadFile": "Download...",
"vscode-containers.commands.containers.inspect": "Inspect",
"vscode-containers.commands.containers.openFile": "Open",
"vscode-containers.commands.containers.pause": "Pause",
"vscode-containers.commands.containers.prune": "Prune...",
"vscode-containers.commands.containers.refresh": "Refresh",
"vscode-containers.commands.containers.remove": "Remove...",
Expand All @@ -248,6 +249,7 @@
"vscode-containers.commands.containers.select": "Select container",
"vscode-containers.commands.containers.start": "Start",
"vscode-containers.commands.containers.stop": "Stop",
"vscode-containers.commands.containers.unpause": "Unpause",
"vscode-containers.commands.containers.stats": "Stats",
"vscode-containers.commands.containers.viewLogs": "View Logs",
"vscode-containers.commands.containers.composeGroup.logs": "Compose Logs",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ext } from '../../extensionVariables';
import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem';
import { multiSelectNodes } from '../../utils/multiSelectNodes';

export async function pauseContainer(context: IActionContext, node?: ContainerTreeItem, nodes?: ContainerTreeItem[]): Promise<void> {
nodes = await multiSelectNodes(
{ ...context, noItemFoundErrorMessage: vscode.l10n.t('No containers are available to pause') },
ext.containersTree,
/^runningContainer$/i,
node,
nodes
);

await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: vscode.l10n.t('Pausing Container(s)...') }, async () => {
await ext.runWithDefaults(client =>
client.pauseContainers({ container: nodes.map(n => n.containerId) })
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function startContainer(context: IActionContext, node?: ContainerTr
nodes = await multiSelectNodes(
{ ...context, noItemFoundErrorMessage: vscode.l10n.t('No containers are available to start') },
ext.containersTree,
/^(created|dead|exited|paused|terminated)Container$/i,
/^(created|dead|exited|terminated)Container$/i,
node,
nodes
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ext } from '../../extensionVariables';
import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem';
import { multiSelectNodes } from '../../utils/multiSelectNodes';

export async function unpauseContainer(context: IActionContext, node?: ContainerTreeItem, nodes?: ContainerTreeItem[]): Promise<void> {
nodes = await multiSelectNodes(
{ ...context, noItemFoundErrorMessage: vscode.l10n.t('No containers are available to unpause') },
ext.containersTree,
/^pausedContainer$/i,
node,
nodes
);

await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: vscode.l10n.t('Unpausing Container(s)...') }, async () => {
await ext.runWithDefaults(client =>
client.unpauseContainers({ container: nodes.map(n => n.containerId) })
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { configureContainersExplorer } from "./containers/configureContainersExp
import { downloadContainerFile } from "./containers/files/downloadContainerFile";
import { openContainerFile } from "./containers/files/openContainerFile";
import { inspectContainer } from "./containers/inspectContainer";
import { pauseContainer } from "./containers/pauseContainer";
import { pruneContainers } from "./containers/pruneContainers";
import { removeContainer } from "./containers/removeContainer";
import { removeContainerGroup } from "./containers/removeContainerGroup";
Expand All @@ -27,6 +28,7 @@ import { selectContainer } from "./containers/selectContainer";
import { startContainer } from "./containers/startContainer";
import { stats } from "./containers/stats";
import { stopContainer } from "./containers/stopContainer";
import { unpauseContainer } from "./containers/unpauseContainer";
import { viewContainerLogs } from "./containers/viewContainerLogs";
import { configureDockerContextsExplorer, dockerContextsHelp } from "./context/DockerContextsViewCommands";
import { inspectDockerContext } from "./context/inspectDockerContext";
Expand Down Expand Up @@ -134,13 +136,15 @@ export function registerCommands(): void {
registerCommand('vscode-containers.containers.filter', filterContainersTree);
registerCommand('vscode-containers.containers.clearFilter', clearContainersFilter);
registerCommand('vscode-containers.containers.openFile', openContainerFile);
registerCommand('vscode-containers.containers.pause', pauseContainer);
registerCommand('vscode-containers.containers.prune', pruneContainers);
registerCommand('vscode-containers.containers.remove', removeContainer);
registerCommand('vscode-containers.containers.group.remove', removeContainerGroup);
registerCommand('vscode-containers.containers.restart', restartContainer);
registerCommand('vscode-containers.containers.select', selectContainer);
registerCommand('vscode-containers.containers.start', startContainer);
registerCommand('vscode-containers.containers.stop', stopContainer);
registerCommand('vscode-containers.containers.unpause', unpauseContainer);
registerWorkspaceCommand('vscode-containers.containers.stats', stats);
registerWorkspaceCommand('vscode-containers.containers.viewLogs', viewContainerLogs);
registerWorkspaceCommand('vscode-containers.containers.composeGroup.logs', composeGroupLogs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import type {
LoginCommandOptions,
LogoutCommandOptions,
LogsForContainerCommandOptions,
PauseContainersCommandOptions,
PruneContainersCommandOptions,
PruneContainersItem,
PruneImagesCommandOptions,
Expand All @@ -80,6 +81,7 @@ import type {
StatPathItem,
StopContainersCommandOptions,
TagImageCommandOptions,
UnpauseContainersCommandOptions,
UseContextCommandOptions,
VersionCommandOptions,
VersionItem,
Expand Down Expand Up @@ -862,6 +864,58 @@ export abstract class DockerClientBase extends ConfigurableClient implements ICo

//#endregion

//#region PauseContainers Command

protected getPauseContainersCommandArgs(options: PauseContainersCommandOptions): CommandLineArgs {
return composeArgs(
withArg('container', 'pause'),
withArg(...toArray(options.container)),
)();
}

protected parsePauseContainersCommandOutput(
options: PauseContainersCommandOptions,
output: string,
strict: boolean,
): Promise<Array<string>> {
return Promise.resolve(asIds(output));
}

pauseContainers(options: PauseContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>> {
return this.makeCommandResponse(
this.getPauseContainersCommandArgs(options),
(output, strict) => this.parsePauseContainersCommandOutput(options, output, strict),
);
}

//#endregion

//#region UnpauseContainers Command

protected getUnpauseContainersCommandArgs(options: UnpauseContainersCommandOptions): CommandLineArgs {
return composeArgs(
withArg('container', 'unpause'),
withArg(...toArray(options.container)),
)();
}

protected parseUnpauseContainersCommandOutput(
options: UnpauseContainersCommandOptions,
output: string,
strict: boolean,
): Promise<Array<string>> {
return Promise.resolve(asIds(output));
}

unpauseContainers(options: UnpauseContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>> {
return this.makeCommandResponse(
this.getUnpauseContainersCommandArgs(options),
(output, strict) => this.parseUnpauseContainersCommandOutput(options, output, strict),
);
}

//#endregion

//#region RestartContainers Command

protected getRestartContainersCommandArgs(options: RestartContainersCommandOptions): CommandLineArgs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import type {
ListNetworksCommandOptions,
ListVolumeItem,
ListVolumesCommandOptions,
PauseContainersCommandOptions,
PruneContainersCommandOptions,
PruneImagesCommandOptions,
PruneNetworksCommandOptions,
Expand All @@ -46,6 +47,7 @@ import type {
RemoveNetworksCommandOptions,
RestartContainersCommandOptions,
RunContainerCommandOptions,
UnpauseContainersCommandOptions,
VersionCommandOptions,
VersionItem,
WriteFileCommandOptions,
Expand Down Expand Up @@ -366,6 +368,18 @@ export class WslcClient extends DockerClientBase {
return Promise.reject(new CommandNotSupportedError('wslc does not support the restart command.'));
}

// wslc has no `pause` subcommand. Reject rather than silently inheriting
// a command line that wslc would reject.
public override pauseContainers(options: PauseContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>> {
return Promise.reject(new CommandNotSupportedError('wslc does not support the pause command.'));
}

// wslc has no `unpause` subcommand. Reject rather than silently inheriting
// a command line that wslc would reject.
public override unpauseContainers(options: UnpauseContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>> {
return Promise.reject(new CommandNotSupportedError('wslc does not support the unpause command.'));
}

protected override getInspectContainersCommandArgs(options: InspectContainersCommandOptions): CommandLineArgs {
return this.getWslcInspectCommandArgs('container', options.containers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,40 @@ type StartContainersCommand = {
startContainers(options: StartContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>>;
};

// Pause Containers Command Types

export type PauseContainersCommandOptions = CommonCommandOptions & {
/**
* Containers to pause
*/
container: Array<string>;
};

type PauseContainersCommand = {
/**
* Generate a CommandResponse for pausing container(s).
* @param options Command options
*/
pauseContainers(options: PauseContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>>;
};

// Unpause Containers Command Types

export type UnpauseContainersCommandOptions = CommonCommandOptions & {
/**
* Containers to unpause
*/
container: Array<string>;
};

type UnpauseContainersCommand = {
/**
* Generate a CommandResponse for unpausing container(s).
* @param options Command options
*/
unpauseContainers(options: UnpauseContainersCommandOptions): Promise<PromiseCommandResponse<Array<string>>>;
};

// Restart Containers Command Types

export type RestartContainersCommandOptions = CommonCommandOptions & {
Expand Down Expand Up @@ -1883,6 +1917,8 @@ export interface IContainersClient extends
ExecContainerCommand,
ListContainersCommand,
StartContainersCommand,
PauseContainersCommand,
UnpauseContainersCommand,
RestartContainersCommand,
StopContainersCommand,
RemoveContainersCommand,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,32 @@ describe('(integration) ContainersClientE2E', function () {
expect(startedContainer.state.toLowerCase()).to.equal('running');
});

it('PauseAndUnpauseContainersCommands', async function () {
if (clientTypeToTest === 'wslc') {
this.skip(); // wslc has no `pause` or `unpause` subcommands
}

const pausedContainers = await defaultRunner.getCommandRunner()(
client.pauseContainers({ container: [testContainerId] })
);

expect(pausedContainers).to.be.an('array');
expect(pausedContainers).to.include(testContainerId);

const pausedContainer = (await validateContainerExists(client, defaultRunner, { containerId: testContainerId }))!;
expect(pausedContainer.state.toLowerCase()).to.equal('paused');

const unpausedContainers = await defaultRunner.getCommandRunner()(
client.unpauseContainers({ container: [testContainerId] })
);

expect(unpausedContainers).to.be.an('array');
expect(unpausedContainers).to.include(testContainerId);

const runningContainer = (await validateContainerExists(client, defaultRunner, { containerId: testContainerId }))!;
expect(runningContainer.state.toLowerCase()).to.equal('running');
});

it('RestartContainersCommand', async function () {
if (clientTypeToTest === 'wslc') {
this.skip(); // wslc has no `restart` subcommand
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,36 @@ import type { BuildImageCommandOptions, RunContainerCommandOptions } from '../..
describe('(unit) DockerClient', () => {
const client = new DockerClient();

describe('#pauseContainers()', () => {
it('generates the command and parses paused container IDs', async () => {
const commandResult = await client.pauseContainers({ container: ['abc', 'def'] });

expect(commandResult).to.have.a.property('command', 'docker');
expect(commandResult.args).to.deep.equal([
escaped('container'),
escaped('pause'),
escaped('abc'),
escaped('def'),
]);
expect(await commandResult.parse('abc\ndef\n', true)).to.deep.equal(['abc', 'def']);
});
});

describe('#unpauseContainers()', () => {
it('generates the command and parses unpaused container IDs', async () => {
const commandResult = await client.unpauseContainers({ container: ['abc', 'def'] });

expect(commandResult).to.have.a.property('command', 'docker');
expect(commandResult.args).to.deep.equal([
escaped('container'),
escaped('unpause'),
escaped('abc'),
escaped('def'),
]);
expect(await commandResult.parse('abc\ndef\n', true)).to.deep.equal(['abc', 'def']);
});
});

describe('#listImagesAsync()', () => {
it('parses date formats', async () => {
const commandResult = await client.listImages({});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ describe('(integration) WslcCanary', function () {
{ description: '`events`', args: 'events --help', unrecognizedToken: 'events', workaround: 'WslcClient.getEventStream rejects with CommandNotSupportedError' },
{ description: '`info`', args: 'info --help', unrecognizedToken: 'info', workaround: 'WslcClient.getInfoCommandArgs/parseInfoCommandOutput synthesize a linux InfoItem' },
{ description: '`container restart`', args: 'container restart --help', unrecognizedToken: 'restart', workaround: 'WslcClient.restartContainers rejects with CommandNotSupportedError' },
{ description: '`container pause`', args: 'container pause --help', unrecognizedToken: 'pause', workaround: 'WslcClient.pauseContainers rejects with CommandNotSupportedError' },
{ description: '`container unpause`', args: 'container unpause --help', unrecognizedToken: 'unpause', workaround: 'WslcClient.unpauseContainers rejects with CommandNotSupportedError' },
];

cases.forEach(({ description, args, unrecognizedToken, workaround }) => {
Expand Down
Loading