Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setting for Docker CLI executable path #2984

Merged
merged 3 commits into from
Jun 3, 2021
Merged
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
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,12 @@
"type": "boolean",
"default": true,
"description": "%vscode-docker.config.docker.showStartPage%"
},
"docker.dockerPath": {
"type": "string",
"default": "docker",
"description": "%vscode-docker.config.docker.dockerPath%",
"scope": "machine-overridable"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@
"vscode-docker.config.docker.volumes.sortBy": "The property to use to sort volumes in Docker view: CreatedTime or Label",
"vscode-docker.config.docker.imageBuildContextPath": "Build context PATH to pass to Docker build command.",
"vscode-docker.config.docker.truncateLongRegistryPaths": "Set to true to truncate long image and container registry paths in Docker view",
"vscode-docker.config.docker.truncateMaxLength": "Maximum length of a registry paths displayed in Docker view, including elipsis. The truncateLongRegistryPaths setting must be set to true for truncateMaxLength setting to be effective.",
"vscode-docker.config.docker.truncateMaxLength": "Maximum length of a registry paths displayed in Docker view, including ellipsis. The truncateLongRegistryPaths setting must be set to true for truncateMaxLength setting to be effective.",
"vscode-docker.config.docker.dockerodeOptions": "If specified, this object will be passed to the Dockerode constructor. Takes precedence over DOCKER_HOST, the Docker Host setting, and any existing Docker contexts.",
"vscode-docker.config.docker.host": "Equivalent to setting the DOCKER_HOST environment variable, for example, ssh://myuser@mymachine or tcp://1.2.3.4.",
"vscode-docker.config.docker.context": "Equivalent to setting the DOCKER_CONTEXT environment variable.",
Expand All @@ -188,6 +188,7 @@
"vscode-docker.config.docker.showRemoteWorkspaceWarning": "Set to true to prompt to switch from \"UI\" extension mode to \"Workspace\" extension mode if an operation is not supported in UI mode.",
"vscode-docker.config.docker.scaffolding.templatePath": "The path to use for scaffolding templates.",
"vscode-docker.config.docker.showStartPage": "Show the Docker extension Start Page when a new update is released.",
"vscode-docker.config.docker.dockerPath": "Absolute path to Docker client executable ('docker' command). If the path contains whitespace, it needs to be quoted appropriately.",
"vscode-docker.config.deprecated": "This setting has been deprecated and will be removed in a future release.",
"vscode-docker.commands.compose.down": "Compose Down",
"vscode-docker.commands.compose.restart": "Compose Restart",
Expand Down
3 changes: 2 additions & 1 deletion src/commands/containers/attachShellContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DockerOSType } from '../../docker/Common';
import { ext } from '../../extensionVariables';
import { localize } from '../../localize';
import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem';
import { dockerExePath } from '../../utils/dockerExePathProvider';
import { executeAsTask } from '../../utils/executeAsTask';
import { execAsync } from '../../utils/spawnAsync';
import { selectAttachCommand } from '../selectCommandTemplate';
Expand Down Expand Up @@ -46,7 +47,7 @@ export async function attachShellContainer(context: IActionContext, node?: Conta
// If so use it, otherwise use sh
try {
// If this succeeds, bash is present (exit code 0)
await execAsync(`docker exec -i ${node.containerId} sh -c "which bash"`);
await execAsync(`${dockerExePath(context)} exec -i ${node.containerId} sh -c "which bash"`);
shellCommand = 'bash';
} catch {
shellCommand = 'sh';
Expand Down
3 changes: 2 additions & 1 deletion src/commands/images/pullImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IActionContext } from 'vscode-azureextensionui';
import { ext } from '../../extensionVariables';
import { localize } from '../../localize';
import { ImageTreeItem } from '../../tree/images/ImageTreeItem';
import { dockerExePath } from '../../utils/dockerExePathProvider';
import { executeAsTask } from '../../utils/executeAsTask';
import { multiSelectNodes } from '../../utils/multiSelectNodes';

Expand All @@ -33,6 +34,6 @@ export async function pullImage(context: IActionContext, node?: ImageTreeItem, n
continue;
}

await executeAsTask(context, `docker pull ${n.fullTag}`, 'docker pull', { addDockerEnv: true });
await executeAsTask(context, `${dockerExePath(context)} pull ${n.fullTag}`, 'docker pull', { addDockerEnv: true });
}
}
3 changes: 2 additions & 1 deletion src/commands/images/pushImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { localize } from '../../localize';
import { ImageTreeItem } from '../../tree/images/ImageTreeItem';
import { registryExpectedContextValues } from '../../tree/registries/registryContextValues';
import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase';
import { dockerExePath } from '../../utils/dockerExePathProvider';
import { executeAsTask } from '../../utils/executeAsTask';
import { addImageTaggingTelemetry, tagImage } from './tagImage';

Expand Down Expand Up @@ -68,7 +69,7 @@ export async function pushImage(context: IActionContext, node: ImageTreeItem | u
addImageTaggingTelemetry(context, finalTag, '');

// Finally push the image
await executeAsTask(context, `docker push ${finalTag}`, finalTag, { addDockerEnv: true });
await executeAsTask(context, `${dockerExePath(context)} push ${finalTag}`, finalTag, { addDockerEnv: true });
}

async function tryGetConnectedRegistryForPath(context: IActionContext, baseImagePath: string): Promise<RegistryTreeItemBase | undefined> {
Expand Down
3 changes: 2 additions & 1 deletion src/commands/images/runAzureCliImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as os from 'os';
import * as vscode from 'vscode';
import { DialogResponses, IActionContext } from 'vscode-azureextensionui';
import { localize } from '../../localize';
import { dockerExePath } from '../../utils/dockerExePathProvider';
import { executeAsTask } from '../../utils/executeAsTask';
import { getDockerOSType } from '../../utils/osUtils';

Expand Down Expand Up @@ -44,6 +45,6 @@ export async function runAzureCliImage(context: IActionContext): Promise<void> {
vol += ` -v ${workspaceFolder.uri.fsPath}:/workspace`;
}

await executeAsTask(context, `docker run ${option} ${vol.trim()} -it --rm mcr.microsoft.com/azure-cli:latest`, 'Azure CLI', { addDockerEnv: true, focus: true });
await executeAsTask(context, `${dockerExePath(context)} run ${option} ${vol.trim()} -it --rm mcr.microsoft.com/azure-cli:latest`, 'Azure CLI', { addDockerEnv: true, focus: true });
}
}
13 changes: 7 additions & 6 deletions src/commands/registries/azure/deployImageToAci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { registryExpectedContextValues } from '../../../tree/registries/registry
import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem';
import { executeAsTask } from '../../../utils/executeAsTask';
import { execAsync } from '../../../utils/spawnAsync';
import { dockerExePath } from '../../../utils/dockerExePathProvider';
import { addImageTaggingTelemetry } from '../../images/tagImage';

export async function deployImageToAci(context: IActionContext, node?: RemoteTagTreeItem): Promise<void> {
Expand All @@ -35,13 +36,13 @@ export async function deployImageToAci(context: IActionContext, node?: RemoteTag
title: localize('vscode-docker.commands.registries.deployImageToAci.gettingPorts', 'Determining ports from image...'),
};
const ports = await vscode.window.withProgress(progressOptions, async () => {
return getImagePorts(node.fullTag);
return getImagePorts(node.fullTag, context);
});
const portsArg = ports.map(port => `-p ${port}:${port}`).join(' ');

addImageTaggingTelemetry(context, node.fullTag, '');

const command = `docker --context ${aciContext.name} run -d ${portsArg} ${node.fullTag}`;
const command = `${dockerExePath(context)} --context ${aciContext.name} run -d ${portsArg} ${node.fullTag}`;
const title = localize('vscode-docker.commands.registries.deployImageToAci.deploy', 'Deploy to ACI');
const options = {
addDockerEnv: false,
Expand All @@ -51,20 +52,20 @@ export async function deployImageToAci(context: IActionContext, node?: RemoteTag
await executeAsTask(context, command, title, { ...options, rejectOnError: true });
} catch {
// If it fails, try logging in and make one more attempt
await executeAsTask(context, `docker login azure --cloud-name ${await promptForAciCloud(context)}`, title, options);
await executeAsTask(context, `${dockerExePath(context)} login azure --cloud-name ${await promptForAciCloud(context)}`, title, options);
await executeAsTask(context, command, title, options);
}
}

async function getImagePorts(fullTag: string): Promise<number[]> {
async function getImagePorts(fullTag: string, context: IActionContext): Promise<number[]> {
try {
const result: number[] = [];

// 1. Pull the image to the default context
await execAsync(`docker --context default pull ${fullTag}`);
await execAsync(`${dockerExePath(context)} --context default pull ${fullTag}`);

// 2. Inspect it in the default context to find out the ports to map
const { stdout } = await execAsync(`docker --context default inspect ${fullTag} --format="{{ json .Config.ExposedPorts }}"`);
const { stdout } = await execAsync(`${dockerExePath(context)} --context default inspect ${fullTag} --format="{{ json .Config.ExposedPorts }}"`);

try {
const portsJson = <{ [key: string]: never }>JSON.parse(stdout);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/registries/logOutOfDockerCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IActionContext } from 'vscode-azureextensionui';
import { ext } from '../../extensionVariables';
import { registryExpectedContextValues } from '../../tree/registries/registryContextValues';
import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase';
import { dockerExePath } from '../../utils/dockerExePathProvider';
import { executeAsTask } from '../../utils/executeAsTask';

export async function logOutOfDockerCli(context: IActionContext, node?: RegistryTreeItemBase): Promise<void> {
Expand All @@ -15,5 +16,5 @@ export async function logOutOfDockerCli(context: IActionContext, node?: Registry
}

const creds = await node.getDockerCliCredentials();
await executeAsTask(context, `docker logout ${creds.registryPath}`, 'Docker', { addDockerEnv: true });
await executeAsTask(context, `${dockerExePath(context)} logout ${creds.registryPath}`, 'Docker', { addDockerEnv: true });
}
13 changes: 7 additions & 6 deletions src/commands/registries/pullImages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IActionContext } from "vscode-azureextensionui";
import { IActionContext } from 'vscode-azureextensionui';
import { ext } from '../../extensionVariables';
import { registryExpectedContextValues } from "../../tree/registries/registryContextValues";
import { registryExpectedContextValues } from '../../tree/registries/registryContextValues';
import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase';
import { RemoteRepositoryTreeItemBase } from "../../tree/registries/RemoteRepositoryTreeItemBase";
import { RemoteRepositoryTreeItemBase } from '../../tree/registries/RemoteRepositoryTreeItemBase';
import { RemoteTagTreeItem } from '../../tree/registries/RemoteTagTreeItem';
import { executeAsTask } from "../../utils/executeAsTask";
import { logInToDockerCli } from "./logInToDockerCli";
import { executeAsTask } from '../../utils/executeAsTask';
import { dockerExePath } from '../../utils/dockerExePathProvider';
import { logInToDockerCli } from './logInToDockerCli';

export async function pullRepository(context: IActionContext, node?: RemoteRepositoryTreeItemBase): Promise<void> {
if (!node) {
Expand All @@ -31,5 +32,5 @@ export async function pullImageFromRepository(context: IActionContext, node?: Re
async function pullImages(context: IActionContext, node: RegistryTreeItemBase, imageRequest: string): Promise<void> {
await logInToDockerCli(context, node);

await executeAsTask(context, `docker pull ${node.baseImagePath}/${imageRequest}`, 'Docker', { addDockerEnv: true });
await executeAsTask(context, `${dockerExePath(context)} pull ${node.baseImagePath}/${imageRequest}`, 'Docker', { addDockerEnv: true });
}
12 changes: 11 additions & 1 deletion src/commands/selectCommandTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ContextType } from '../docker/Contexts';
import { ext } from '../extensionVariables';
import { localize } from '../localize';
import { resolveVariables } from '../utils/resolveVariables';
import { dockerExePath, DefaultDockerPath } from '../utils/dockerExePathProvider';

type TemplateCommand = 'build' | 'run' | 'runInteractive' | 'attach' | 'logs' | 'composeUp' | 'composeDown';

Expand Down Expand Up @@ -170,7 +171,16 @@ export async function selectCommandTemplate(
context.telemetry.properties.commandContextType = `[${selectedTemplate.contextTypes?.join(', ') ?? ''}]`;
context.telemetry.properties.currentContextType = currentContextType;

return resolveVariables(selectedTemplate.template, folder, additionalVariables);
let resolvedCommand = resolveVariables(selectedTemplate.template, folder, additionalVariables);

if (resolvedCommand.startsWith(DefaultDockerPath + ' ')) {
const dockerPath = dockerExePath(context);
if (dockerPath !== DefaultDockerPath) {
resolvedCommand = dockerPath + resolvedCommand.substring(DefaultDockerPath.length);
}
}

return resolvedCommand;
}

async function quickPickTemplate(templates: CommandTemplate[], templatePicker: TemplatePicker): Promise<CommandTemplate> {
Expand Down
11 changes: 6 additions & 5 deletions src/docker/ContextManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { localize } from '../localize';
import { AsyncLazy } from '../utils/lazy';
import { isWindows } from '../utils/osUtils';
import { execAsync, spawnAsync } from '../utils/spawnAsync';
import { dockerExePath } from '../utils/dockerExePathProvider';
import { ContextType, DockerContext, DockerContextInspection, isNewContextType } from './Contexts';

// CONSIDER
Expand Down Expand Up @@ -172,20 +173,20 @@ export class DockerContextManager implements ContextManager, Disposable {
}

public async inspect(actionContext: IActionContext, contextName: string): Promise<DockerContextInspection> {
const { stdout } = await execAsync(`docker context inspect ${contextName}`, { timeout: 10000 });
const { stdout } = await execAsync(`${dockerExePath(actionContext)} context inspect ${contextName}`, { timeout: 10000 });

// The result is an array with one entry
const result: DockerContextInspection[] = JSON.parse(stdout) as DockerContextInspection[];
return result[0];
}

public async use(actionContext: IActionContext, contextName: string): Promise<void> {
const useCmd: string = `docker context use ${contextName}`;
const useCmd: string = `${dockerExePath(actionContext)} context use ${contextName}`;
await execAsync(useCmd, ContextCmdExecOptions);
}

public async remove(actionContext: IActionContext, contextName: string): Promise<void> {
const removeCmd: string = `docker context rm ${contextName}`;
const removeCmd: string = `${dockerExePath(actionContext)} context rm ${contextName}`;
await spawnAsync(removeCmd, ContextCmdExecOptions);
}

Expand Down Expand Up @@ -261,7 +262,7 @@ export class DockerContextManager implements ContextManager, Disposable {
// return that specified context as Current = true. This way we don't need extra logic below in parsing.
// TODO: eventually change to `docker context ls --format json`
// eslint-disable-next-line @typescript-eslint/naming-convention
const { stdout } = await execAsync('docker context ls --format="{{json .}}"', { ...ContextCmdExecOptions, env: { ...process.env, DOCKER_CONTEXT: dockerContextEnv } });
const { stdout } = await execAsync(`${dockerExePath()} context ls --format="{{json .}}"`, { ...ContextCmdExecOptions, env: { ...process.env, DOCKER_CONTEXT: dockerContextEnv } });

try {
// Try parsing as-is; newer CLIs output a JSON object array
Expand Down Expand Up @@ -334,7 +335,7 @@ export class DockerContextManager implements ContextManager, Disposable {
} else {
// Otherwise we look at the output of `docker serve --help`
// TODO: this is not a very good heuristic
const { stdout } = await execAsync('docker serve --help');
const { stdout } = await execAsync(`${dockerExePath()} serve --help`);

if (/^\s*Start an api server/i.test(stdout)) {
result = true;
Expand Down
5 changes: 3 additions & 2 deletions src/tree/contexts/aci/AciContextCreateStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ext } from '../../../extensionVariables';
import { localize } from '../../../localize';
import { executeAsTask } from '../../../utils/executeAsTask';
import { execAsync } from '../../../utils/spawnAsync';
import { dockerExePath } from '../../../utils/dockerExePathProvider';
import { IAciWizardContext } from './IAciWizardContext';

export class AciContextCreateStep extends AzureWizardExecuteStep<IAciWizardContext> {
Expand All @@ -20,7 +21,7 @@ export class AciContextCreateStep extends AzureWizardExecuteStep<IAciWizardConte
ext.outputChannel.appendLine(creatingNewContext);
progress.report({ message: creatingNewContext });

const command = `docker context create aci ${wizardContext.contextName} --subscription-id ${wizardContext.subscriptionId} --resource-group ${wizardContext.resourceGroup.name}`;
const command = `${dockerExePath(wizardContext)} context create aci ${wizardContext.contextName} --subscription-id ${wizardContext.subscriptionId} --resource-group ${wizardContext.resourceGroup.name}`;

try {
await execAsync(command);
Expand All @@ -30,7 +31,7 @@ export class AciContextCreateStep extends AzureWizardExecuteStep<IAciWizardConte
if (error.errorType === '5' || /not logged in/i.test(error.message)) {
// If error is due to being not logged in, we'll go through login and try again
// Because login could involve device auth we do this step in the terminal
await executeAsTask(wizardContext, `docker login azure --cloud-name ${wizardContext.environment.name}`, localize('vscode-docker.commands.contexts.create.aci.azureLogin', 'Azure Login'), { rejectOnError: true });
await executeAsTask(wizardContext, `${dockerExePath(wizardContext)} login azure --cloud-name ${wizardContext.environment.name}`, localize('vscode-docker.commands.contexts.create.aci.azureLogin', 'Azure Login'), { rejectOnError: true });
await execAsync(command);
} else {
// Otherwise rethrow
Expand Down
8 changes: 5 additions & 3 deletions src/utils/DockerInstallStatusProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { AsyncLazy } from "./lazy";
import { execAsync } from "./spawnAsync";
import { AsyncLazy } from './lazy';
import { execAsync } from './spawnAsync';
import { dockerExePath } from './dockerExePathProvider';

export interface IDockerInstallStatusProvider {
isDockerInstalled(): Promise<boolean>
Expand Down Expand Up @@ -32,7 +33,8 @@ class DockerInstallStatusProvider implements IDockerInstallStatusProvider {

public async isDockerInstalledRealTimeCheck(): Promise<boolean> {
try {
await execAsync('docker -v');

await execAsync(`${dockerExePath()} -v`);
return true; // As long as the docker command did't throw exception, assume it is installed.
} catch (error) {
return false; // docker not installed
Expand Down
Loading