From 91b0d82f3c85dbc765d8d75318da54fab6710161 Mon Sep 17 00:00:00 2001 From: Karol Zadora-Przylecki Date: Mon, 30 Mar 2020 09:50:52 -0700 Subject: [PATCH] Docker context support (#1790) Docker context changes are detected and UI/commands are refreshed accordingly Also cleans up warning suppressions for async Dockerode calls --- extension.bundle.ts | 2 +- package.json | 5 + package.nls.json | 1 + src/commands/containers/browseContainer.ts | 10 +- src/commands/containers/inspectContainer.ts | 7 +- src/commands/containers/pruneContainers.ts | 5 +- src/commands/containers/restartContainer.ts | 7 +- src/commands/containers/startContainer.ts | 7 +- src/commands/containers/stopContainer.ts | 8 +- src/commands/images/inspectImage.ts | 7 +- src/commands/images/pruneImages.ts | 5 +- src/commands/images/runImage.ts | 3 +- src/commands/images/tagImage.ts | 7 +- src/commands/networks/createNetwork.ts | 7 +- src/commands/networks/inspectNetwork.ts | 8 +- src/commands/networks/pruneNetworks.ts | 5 +- src/commands/pruneSystem.ts | 12 +- src/commands/volumes/inspectVolume.ts | 7 +- src/commands/volumes/pruneVolumes.ts | 5 +- src/configureWorkspace/configureDotNetCore.ts | 2 +- src/debugging/DockerServerReadyAction.ts | 14 +- .../coreclr/CommandLineDotNetClient.ts | 2 +- .../coreclr/LocalAspNetCoreSslManager.ts | 13 +- src/debugging/coreclr/debuggerClient.ts | 2 +- src/debugging/coreclr/dockerManager.ts | 2 +- ...dockerNetCoreDebugConfigurationProvider.ts | 2 +- src/debugging/coreclr/prereqManager.ts | 2 +- .../registerDebugConfigurationProvider.ts | 2 +- src/debugging/coreclr/tempFileProvider.ts | 2 +- src/debugging/coreclr/vsdbgClient.ts | 2 +- src/debugging/netcore/NetCoreDebugHelper.ts | 2 +- src/extensionVariables.ts | 1 + src/tasks/DockerTaskProvider.ts | 3 +- src/tasks/netcore/updateBlazorManifest.ts | 2 +- src/tree/containers/ContainerTreeItem.ts | 11 +- src/tree/containers/ContainersTreeItem.ts | 3 +- src/tree/images/ImageTreeItem.ts | 11 +- src/tree/images/ImagesTreeItem.ts | 3 +- src/tree/networks/NetworkTreeItem.ts | 11 +- src/tree/networks/NetworksTreeItem.ts | 3 +- src/tree/volumes/VolumeTreeItem.ts | 11 +- src/tree/volumes/VolumesTreeItem.ts | 3 +- .../coreclr => utils}/LocalOSProvider.ts | 4 +- src/utils/azureUtils.ts | 2 +- src/utils/callDockerode.ts | 53 +++++ src/utils/callDockerodeWithErrorHandling.ts | 25 --- src/utils/dockerContextManager.ts | 188 ++++++++++++++++++ src/utils/osUtils.ts | 5 +- src/utils/refreshDockerode.ts | 86 +++----- src/utils/timeUtils.ts | 30 +++ test/global.test.ts | 1 + test/tree/validateTree.ts | 1 + 52 files changed, 417 insertions(+), 205 deletions(-) rename src/{debugging/coreclr => utils}/LocalOSProvider.ts (93%) create mode 100644 src/utils/callDockerode.ts delete mode 100644 src/utils/callDockerodeWithErrorHandling.ts create mode 100644 src/utils/dockerContextManager.ts create mode 100644 src/utils/timeUtils.ts diff --git a/extension.bundle.ts b/extension.bundle.ts index 79ab7eb70c..bb93c227a9 100644 --- a/extension.bundle.ts +++ b/extension.bundle.ts @@ -28,7 +28,7 @@ export { DotNetClient } from './src/debugging/coreclr/CommandLineDotNetClient'; export { compareBuildImageOptions, LaunchOptions } from './src/debugging/coreclr/dockerManager'; export { FileSystemProvider } from './src/debugging/coreclr/fsProvider'; export { LineSplitter } from './src/debugging/coreclr/lineSplitter'; -export { OSProvider } from './src/debugging/coreclr/LocalOSProvider'; +export { OSProvider } from './src/utils/LocalOSProvider'; export { DockerDaemonIsLinuxPrerequisite, DockerfileExistsPrerequisite, DotNetSdkInstalledPrerequisite, LinuxUserInDockerGroupPrerequisite, MacNuGetFallbackFolderSharedPrerequisite } from './src/debugging/coreclr/prereqManager'; export { ext } from './src/extensionVariables'; export { globAsync } from './src/utils/globAsync'; diff --git a/package.json b/package.json index 3f96389ca1..8f4d5197b2 100644 --- a/package.json +++ b/package.json @@ -1329,6 +1329,11 @@ "default": 2000, "description": "%vscode-docker.config.docker.explorerRefreshInterval%" }, + "docker.contextRefreshInterval": { + "type": "number", + "default": 20, + "description": "%vscode-docker.config.docker.contextRefreshInterval%" + }, "docker.commands.build": { "oneOf": [ { diff --git a/package.nls.json b/package.nls.json index fe6f01e876..5583db44a6 100644 --- a/package.nls.json +++ b/package.nls.json @@ -124,6 +124,7 @@ "vscode-docker.config.template.composeDown.match": "The regular expression for choosing the right template. Checked against docker-compose YAML files, folder name, etc.", "vscode-docker.config.template.composeDown.description": "Command templates for `docker-compose down` commands.", "vscode-docker.config.docker.explorerRefreshInterval": "Docker view refresh interval (milliseconds)", + "vscode-docker.config.docker.contextRefreshInterval": "How often Docker context is checked for changes (seconds)", "vscode-docker.config.docker.containers.groupBy": "The property to use to group containers in Docker view: ContainerId, ContainerName, CreatedTime, FullTag, ImageId, Networks, Ports, Registry, Repository, RepositoryName, RepositoryNameAndTag, State, Status, Tag, or None", "vscode-docker.config.docker.containers.description": "Any secondary properties to display for a container (an array). Possible elements include: ContainerId, ContainerName, CreatedTime, FullTag, ImageId, Networks, Ports, Registry, Repository, RepositoryName, RepositoryNameAndTag, State, Status, and Tag", "vscode-docker.config.docker.containers.label": "The primary property to display for a container: ContainerId, ContainerName, CreatedTime, FullTag, ImageId, Networks, Ports, Registry, Repository, RepositoryName, RepositoryNameAndTag, State, Status, or Tag", diff --git a/src/commands/containers/browseContainer.ts b/src/commands/containers/browseContainer.ts index 2a3b94d707..e58e61017f 100644 --- a/src/commands/containers/browseContainer.ts +++ b/src/commands/containers/browseContainer.ts @@ -9,7 +9,7 @@ import { IActionContext, TelemetryProperties } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; import { ContainerTreeItem } from "../../tree/containers/ContainerTreeItem"; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { captureCancelStep } from '../../utils/captureCancelStep'; type BrowseTelemetryProperties = TelemetryProperties & { possiblePorts?: number[], selectedPort?: number }; @@ -59,17 +59,15 @@ export async function browseContainer(context: IActionContext, node?: ContainerT const telemetryProperties = context.telemetry.properties; if (!node) { - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - node = await captureBrowseCancelStep('node', telemetryProperties, () => + node = await captureBrowseCancelStep('node', telemetryProperties, async () => ext.containersTree.showTreeItemPicker(ContainerTreeItem.runningContainerRegExp, { ...context, noItemFoundErrorMessage: localize('vscode-docker.commands.containers.browseContainer.noContainers', 'No running containers are available to open in a browser') })); } - const container = node.getContainer(); - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - const inspectInfo: ContainerInspectInfo = await callDockerodeWithErrorHandling(() => container.inspect(), context); + const container = await node.getContainer(); + const inspectInfo: ContainerInspectInfo = await callDockerodeWithErrorHandling(async () => container.inspect(), context); const ports = inspectInfo && inspectInfo.NetworkSettings && inspectInfo.NetworkSettings.Ports || {}; const possiblePorts = diff --git a/src/commands/containers/inspectContainer.ts b/src/commands/containers/inspectContainer.ts index fac62272b1..6508421415 100644 --- a/src/commands/containers/inspectContainer.ts +++ b/src/commands/containers/inspectContainer.ts @@ -8,7 +8,7 @@ import { IActionContext, openReadOnlyJson } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; import { ContainerTreeItem } from "../../tree/containers/ContainerTreeItem"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerodeWithErrorHandling } from "../../utils/callDockerode"; export async function inspectContainer(context: IActionContext, node?: ContainerTreeItem): Promise { if (!node) { @@ -18,8 +18,7 @@ export async function inspectContainer(context: IActionContext, node?: Container }); } - const container = node.getContainer(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - const inspectInfo: ContainerInspectInfo = await callDockerodeWithErrorHandling(() => container.inspect(), context); + const container = await node.getContainer(); + const inspectInfo: ContainerInspectInfo = await callDockerodeWithErrorHandling(async () => container.inspect(), context); await openReadOnlyJson(node, inspectInfo); } diff --git a/src/commands/containers/pruneContainers.ts b/src/commands/containers/pruneContainers.ts index 0f22a3aadc..8196e5c007 100644 --- a/src/commands/containers/pruneContainers.ts +++ b/src/commands/containers/pruneContainers.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { convertToMB } from '../../utils/convertToMB'; export async function pruneContainers(context: IActionContext): Promise { @@ -18,8 +18,7 @@ export async function pruneContainers(context: IActionContext): Promise { await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.containers.pruning', 'Pruning containers...') }, async () => { - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - const result = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneContainers(), context); + const result = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneContainers(), context); const numDeleted = (result.ContainersDeleted || []).length; const mbReclaimed = convertToMB(result.SpaceReclaimed); diff --git a/src/commands/containers/restartContainer.ts b/src/commands/containers/restartContainer.ts index b366e3f0fa..d63957da0e 100644 --- a/src/commands/containers/restartContainer.ts +++ b/src/commands/containers/restartContainer.ts @@ -9,7 +9,7 @@ import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { multiSelectNodes } from '../../utils/multiSelectNodes'; export async function restartContainer(context: IActionContext, node?: ContainerTreeItem, nodes?: ContainerTreeItem[]): Promise { @@ -23,9 +23,8 @@ export async function restartContainer(context: IActionContext, node?: Container await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.containers.restart.restarting', 'Restarting Container(s)...') }, async () => { await Promise.all(nodes.map(async n => { - const container: Container = n.getContainer(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => container.restart(), context); + const container: Container = await n.getContainer(); + await callDockerodeWithErrorHandling(async () => container.restart(), context); })); }); } diff --git a/src/commands/containers/startContainer.ts b/src/commands/containers/startContainer.ts index 21975ad565..4204b70a58 100644 --- a/src/commands/containers/startContainer.ts +++ b/src/commands/containers/startContainer.ts @@ -9,7 +9,7 @@ import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { multiSelectNodes } from '../../utils/multiSelectNodes'; export async function startContainer(context: IActionContext, node?: ContainerTreeItem, nodes?: ContainerTreeItem[]): Promise { @@ -23,9 +23,8 @@ export async function startContainer(context: IActionContext, node?: ContainerTr await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.containers.start.starting', 'Starting Container(s)...') }, async () => { await Promise.all(nodes.map(async n => { - const container: Container = n.getContainer(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => container.start(), context); + const container: Container = await n.getContainer(); + await callDockerodeWithErrorHandling(async () => container.start(), context); })); }); } diff --git a/src/commands/containers/stopContainer.ts b/src/commands/containers/stopContainer.ts index 164ff992ea..7416295402 100644 --- a/src/commands/containers/stopContainer.ts +++ b/src/commands/containers/stopContainer.ts @@ -9,7 +9,7 @@ import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { multiSelectNodes } from '../../utils/multiSelectNodes'; export async function stopContainer(context: IActionContext, node?: ContainerTreeItem, nodes?: ContainerTreeItem[]): Promise { @@ -23,10 +23,8 @@ export async function stopContainer(context: IActionContext, node?: ContainerTre await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.containers.stop.stopping', 'Stopping Container(s)...') }, async () => { await Promise.all(nodes.map(async n => { - const container: Container = n.getContainer(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => container.stop(), context); - + const container: Container = await n.getContainer(); + await callDockerodeWithErrorHandling(async () => container.stop(), context); })); }); } diff --git a/src/commands/images/inspectImage.ts b/src/commands/images/inspectImage.ts index 5fec60082d..5420c2a17a 100644 --- a/src/commands/images/inspectImage.ts +++ b/src/commands/images/inspectImage.ts @@ -8,7 +8,7 @@ import { IActionContext, openReadOnlyJson } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; import { ImageTreeItem } from "../../tree/images/ImageTreeItem"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerodeWithErrorHandling } from "../../utils/callDockerode"; export async function inspectImage(context: IActionContext, node?: ImageTreeItem): Promise { if (!node) { @@ -18,8 +18,7 @@ export async function inspectImage(context: IActionContext, node?: ImageTreeItem }); } - const image: Image = node.getImage(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - const inspectInfo: ImageInspectInfo = await callDockerodeWithErrorHandling(() => image.inspect(), context); + const image: Image = await node.getImage(); + const inspectInfo: ImageInspectInfo = await callDockerodeWithErrorHandling(async () => image.inspect(), context); await openReadOnlyJson(node, inspectInfo); } diff --git a/src/commands/images/pruneImages.ts b/src/commands/images/pruneImages.ts index 856cff800e..24035ab5fa 100644 --- a/src/commands/images/pruneImages.ts +++ b/src/commands/images/pruneImages.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { convertToMB } from '../../utils/convertToMB'; export async function pruneImages(context: IActionContext): Promise { @@ -18,8 +18,7 @@ export async function pruneImages(context: IActionContext): Promise { await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.images.pruning', 'Pruning images...') }, async () => { - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - const result = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneImages(), context); + const result = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneImages(), context); const numDeleted = (result.ImagesDeleted || []).length; const mbReclaimed = convertToMB(result.SpaceReclaimed); diff --git a/src/commands/images/runImage.ts b/src/commands/images/runImage.ts index c6fd26dbbe..1c84b8419e 100644 --- a/src/commands/images/runImage.ts +++ b/src/commands/images/runImage.ts @@ -25,7 +25,8 @@ async function runImageCore(context: IActionContext, node: ImageTreeItem | undef }); } - const inspectInfo = await node.getImage().inspect(); + const image = await node.getImage(); + const inspectInfo = await image.inspect(); const terminalCommand = await selectRunCommand( context, diff --git a/src/commands/images/tagImage.ts b/src/commands/images/tagImage.ts index 2119d6ca15..b01c384f99 100644 --- a/src/commands/images/tagImage.ts +++ b/src/commands/images/tagImage.ts @@ -10,7 +10,7 @@ import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { extractRegExGroups } from '../../utils/extractRegExGroups'; export async function tagImage(context: IActionContext, node?: ImageTreeItem, registry?: RegistryTreeItemBase): Promise { @@ -33,9 +33,8 @@ export async function tagImage(context: IActionContext, node?: ImageTreeItem, re tag = newTaggedName.slice(newTaggedName.lastIndexOf(':') + 1); } - const image: Image = node.getImage(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => image.tag({ repo: repo, tag: tag }), context); + const image: Image = await node.getImage(); + await callDockerodeWithErrorHandling(async () => image.tag({ repo: repo, tag: tag }), context); return newTaggedName; } diff --git a/src/commands/networks/createNetwork.ts b/src/commands/networks/createNetwork.ts index 8e5db5ee87..b47dc9ea4e 100644 --- a/src/commands/networks/createNetwork.ts +++ b/src/commands/networks/createNetwork.ts @@ -7,7 +7,7 @@ import { window } from 'vscode'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeAsync, callDockerodeWithErrorHandling } from '../../utils/callDockerode'; export async function createNetwork(context: IActionContext): Promise { @@ -16,7 +16,7 @@ export async function createNetwork(context: IActionContext): Promise { prompt: localize('vscode-docker.commands.networks.create.promptName', 'Name of the network') }); - const engineVersion = await ext.dockerode.version(); + const engineVersion = await callDockerodeAsync(async () => ext.dockerode.version()); const drivers = engineVersion.Os === 'windows' ? [ { label: 'nat' }, @@ -36,8 +36,7 @@ export async function createNetwork(context: IActionContext): Promise { } ); - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - const result = <{ id: string }>await callDockerodeWithErrorHandling(() => ext.dockerode.createNetwork({ Name: name, Driver: driverSelection.label }), context); + const result = <{ id: string }>await callDockerodeWithErrorHandling(async () => ext.dockerode.createNetwork({ Name: name, Driver: driverSelection.label }), context); /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ window.showInformationMessage(localize('vscode-docker.commands.networks.create.created', 'Network Created with ID {0}', result.id.substr(0, 12))); diff --git a/src/commands/networks/inspectNetwork.ts b/src/commands/networks/inspectNetwork.ts index c306164941..af77c270f4 100644 --- a/src/commands/networks/inspectNetwork.ts +++ b/src/commands/networks/inspectNetwork.ts @@ -8,7 +8,7 @@ import { IActionContext, openReadOnlyJson } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; import { NetworkTreeItem } from "../../tree/networks/NetworkTreeItem"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerodeWithErrorHandling } from "../../utils/callDockerode"; export async function inspectNetwork(context: IActionContext, node?: NetworkTreeItem): Promise { if (!node) { @@ -18,8 +18,8 @@ export async function inspectNetwork(context: IActionContext, node?: NetworkTree }); } - const network: Network = node.getNetwork() - // eslint-disable-next-line @typescript-eslint/promise-function-async, @typescript-eslint/tslint/config - const inspectInfo: {} = await callDockerodeWithErrorHandling(() => network.inspect(), context); // inspect is missing type in @types/dockerode + const network: Network = await node.getNetwork() + // eslint-disable-next-line @typescript-eslint/tslint/config + const inspectInfo: {} = await callDockerodeWithErrorHandling(async () => network.inspect(), context); // inspect is missing type in @types/dockerode await openReadOnlyJson(node, inspectInfo); } diff --git a/src/commands/networks/pruneNetworks.ts b/src/commands/networks/pruneNetworks.ts index d8755323ce..7b83bf9ef6 100644 --- a/src/commands/networks/pruneNetworks.ts +++ b/src/commands/networks/pruneNetworks.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; export async function pruneNetworks(context: IActionContext): Promise { const confirmPrune: string = localize('vscode-docker.commands.networks.prune.confirm', 'Are you sure you want to remove all unused networks?'); @@ -17,8 +17,7 @@ export async function pruneNetworks(context: IActionContext): Promise { await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.networks.pruning', 'Pruning networks...') }, async () => { - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - const result = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneNetworks(), context); + const result = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneNetworks(), context); const numDeleted = (result.NetworksDeleted || []).length; let message = localize('vscode-docker.commands.networks.prune.removed', 'Removed {0} networks(s).', numDeleted); diff --git a/src/commands/pruneSystem.ts b/src/commands/pruneSystem.ts index cf8ce13b9b..50eb038b76 100644 --- a/src/commands/pruneSystem.ts +++ b/src/commands/pruneSystem.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../extensionVariables'; import { localize } from '../localize'; -import { callDockerodeWithErrorHandling } from '../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../utils/callDockerode'; import { convertToMB } from '../utils/convertToMB'; export async function pruneSystem(context: IActionContext): Promise { @@ -18,12 +18,10 @@ export async function pruneSystem(context: IActionContext): Promise { await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.pruneSystem.pruning', 'Pruning system...') }, async () => { - /* eslint-disable @typescript-eslint/promise-function-async */ - const containersResult = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneContainers(), context); - const imagesResult = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneImages(), context); - const networksResult = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneNetworks(), context); - const volumesResult = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneVolumes(), context); - /* eslint-enable @typescript-eslint/promise-function-async */ + const containersResult = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneContainers(), context); + const imagesResult = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneImages(), context); + const networksResult = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneNetworks(), context); + const volumesResult = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneVolumes(), context); const numContainers = (containersResult.ContainersDeleted || []).length; const numImages = (imagesResult.ImagesDeleted || []).length; diff --git a/src/commands/volumes/inspectVolume.ts b/src/commands/volumes/inspectVolume.ts index b69ef7a8a1..3f7dc46615 100644 --- a/src/commands/volumes/inspectVolume.ts +++ b/src/commands/volumes/inspectVolume.ts @@ -8,15 +8,14 @@ import { IActionContext, openReadOnlyJson } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; import { localize } from "../../localize"; import { VolumeTreeItem } from "../../tree/volumes/VolumeTreeItem"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerodeWithErrorHandling } from "../../utils/callDockerode"; export async function inspectVolume(context: IActionContext, node?: VolumeTreeItem): Promise { if (!node) { node = await ext.volumesTree.showTreeItemPicker(VolumeTreeItem.contextValue, { ...context, noItemFoundErrorMessage: localize('vscode-docker.commands.volumes.inspect.noVolumes', 'No volumes are available to inspect') }); } - const volume: Volume = node.getVolume(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - const inspectInfo = await callDockerodeWithErrorHandling(() => volume.inspect(), context); + const volume: Volume = await node.getVolume(); + const inspectInfo = await callDockerodeWithErrorHandling(async () => volume.inspect(), context); await openReadOnlyJson(node, inspectInfo); } diff --git a/src/commands/volumes/pruneVolumes.ts b/src/commands/volumes/pruneVolumes.ts index 89a7c7b71a..487c9b74c3 100644 --- a/src/commands/volumes/pruneVolumes.ts +++ b/src/commands/volumes/pruneVolumes.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { localize } from "../../localize"; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { convertToMB } from '../../utils/convertToMB'; export async function pruneVolumes(context: IActionContext): Promise { @@ -18,8 +18,7 @@ export async function pruneVolumes(context: IActionContext): Promise { await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: localize('vscode-docker.commands.volumes.pruning', 'Pruning volumes...') }, async () => { - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - const result = await callDockerodeWithErrorHandling(() => ext.dockerode.pruneVolumes(), context); + const result = await callDockerodeWithErrorHandling(async () => ext.dockerode.pruneVolumes(), context); const numDeleted = (result.VolumesDeleted || []).length; const mbReclaimed = convertToMB(result.SpaceReclaimed); diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index 57f1116685..5eef23a87b 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -13,7 +13,6 @@ import { IActionContext } from 'vscode-azureextensionui'; import ChildProcessProvider from '../debugging/coreclr/ChildProcessProvider'; import CommandLineDotNetClient from '../debugging/coreclr/CommandLineDotNetClient'; import { LocalFileSystemProvider } from '../debugging/coreclr/fsProvider'; -import LocalOSProvider from '../debugging/coreclr/LocalOSProvider'; import { MsBuildNetCoreProjectProvider, NetCoreProjectProvider } from '../debugging/coreclr/netCoreProjectProvider'; import { OSTempFileProvider } from '../debugging/coreclr/tempFileProvider'; import { DockerDebugScaffoldContext } from '../debugging/DebugHelper'; @@ -24,6 +23,7 @@ import { hasTask } from '../tasks/TaskHelper'; import { extractRegExGroups } from '../utils/extractRegExGroups'; import { getValidImageName } from '../utils/getValidImageName'; import { globAsync } from '../utils/globAsync'; +import LocalOSProvider from '../utils/LocalOSProvider'; import { isWindows, isWindows1019H1OrNewer, isWindows1019H2OrNewer, isWindows10RS3OrNewer, isWindows10RS4OrNewer, isWindows10RS5OrNewer } from '../utils/osUtils'; import { Platform, PlatformOS } from '../utils/platform'; import { generateNonConflictFileName } from '../utils/uniqueNameUtils'; diff --git a/src/debugging/DockerServerReadyAction.ts b/src/debugging/DockerServerReadyAction.ts index d497599ef4..549bc08524 100644 --- a/src/debugging/DockerServerReadyAction.ts +++ b/src/debugging/DockerServerReadyAction.ts @@ -11,6 +11,7 @@ import * as util from 'util'; import * as vscode from 'vscode'; import { ext } from '../extensionVariables'; import { localize } from '../localize'; +import { callDockerode } from '../utils/callDockerode'; import ChildProcessProvider from './coreclr/ChildProcessProvider'; import CliDockerClient from './coreclr/CliDockerClient'; import { ResolvedDebugConfiguration } from './DebugHelper'; @@ -191,7 +192,7 @@ type LogStream = NodeJS.ReadableStream & { destroy(): void; }; class DockerLogsTracker extends vscode.Disposable { private logStream: LogStream; - public constructor(containerName: string, detector: DockerServerReadyDetector) { + public constructor(private readonly containerName: string, private readonly detector: DockerServerReadyDetector) { super( () => { if (this.logStream) { @@ -199,12 +200,17 @@ class DockerLogsTracker extends vscode.Disposable { } }); - if (!detector) { + if (!this.detector) { return; } + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + this.startListening(); + } + + private async startListening(): Promise { try { - const container = ext.dockerode.getContainer(containerName); + const container = await callDockerode(() => ext.dockerode.getContainer(this.containerName)); container.logs( { @@ -219,7 +225,7 @@ class DockerLogsTracker extends vscode.Disposable { this.logStream = stream; this.logStream.on('data', (data) => { // tslint:disable-next-line:no-unsafe-any - detector.detectPattern(data.toString()); + this.detector.detectPattern(data.toString()); }); }); } catch { } diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 1ffc30f564..e90ee0c86c 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -4,10 +4,10 @@ import * as semver from 'semver'; import { parseError } from 'vscode-azureextensionui'; +import { OSProvider } from "../../utils/LocalOSProvider"; import { randomUtils } from '../../utils/randomUtils'; import { ProcessProvider } from "./ChildProcessProvider"; import { FileSystemProvider } from "./fsProvider"; -import { OSProvider } from "./LocalOSProvider"; export type MSBuildExecOptions = { target?: string; diff --git a/src/debugging/coreclr/LocalAspNetCoreSslManager.ts b/src/debugging/coreclr/LocalAspNetCoreSslManager.ts index a7c6450234..31084f3378 100644 --- a/src/debugging/coreclr/LocalAspNetCoreSslManager.ts +++ b/src/debugging/coreclr/LocalAspNetCoreSslManager.ts @@ -8,12 +8,12 @@ import * as process from 'process'; import { MessageItem } from 'vscode'; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; +import { OSProvider } from '../../utils/LocalOSProvider'; import { PlatformOS } from '../../utils/platform'; import { quickPickProjectFileItem } from '../../utils/quickPickFile'; import { quickPickWorkspaceFolder } from '../../utils/quickPickWorkspaceFolder'; import { ProcessProvider } from './ChildProcessProvider'; import { DotNetClient, TrustState } from './CommandLineDotNetClient'; -import { OSProvider } from './LocalOSProvider'; import { NetCoreProjectProvider } from './netCoreProjectProvider'; export type SecretsFolders = { @@ -60,11 +60,12 @@ export class LocalAspNetCoreSslManager implements AspNetCoreSslManager { message, { modal: false, learnMoreLink: 'https://aka.ms/vscode-docker-dev-certs' }, trust).then(async selection => { - if (selection === trust) { - const trustCommand = `dotnet dev-certs https --trust`; - await this.processProvider.exec(trustCommand, {}); - LocalAspNetCoreSslManager._KnownConfiguredProjects.clear(); // Clear the cache so future F5's will not use an untrusted cert - }}); + if (selection === trust) { + const trustCommand = `dotnet dev-certs https --trust`; + await this.processProvider.exec(trustCommand, {}); + LocalAspNetCoreSslManager._KnownConfiguredProjects.clear(); // Clear the cache so future F5's will not use an untrusted cert + } + }); } else if (this.osProvider.isMac) { const message = localize('vscode-docker.debug.coreclr.sslManager.notTrustedRunManual', 'The ASP.NET Core HTTPS development certificate is not trusted. To trust the certificate, run \`dotnet dev-certs https --trust\`.'); diff --git a/src/debugging/coreclr/debuggerClient.ts b/src/debugging/coreclr/debuggerClient.ts index 7b47b1dc57..9e3278bbc4 100644 --- a/src/debugging/coreclr/debuggerClient.ts +++ b/src/debugging/coreclr/debuggerClient.ts @@ -2,9 +2,9 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import { OSProvider } from '../../utils/LocalOSProvider'; import { PlatformOS } from '../../utils/platform'; import { DockerClient } from './CliDockerClient'; -import { OSProvider } from './LocalOSProvider'; import { VsDbgClient } from './vsdbgClient'; export interface DebuggerClient { diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index 25bb083a0f..f02094161e 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -7,6 +7,7 @@ import { Memento } from 'vscode'; import { parseError } from 'vscode-azureextensionui'; import { localize } from '../../localize'; import { Lazy } from '../../utils/lazy'; +import { OSProvider } from '../../utils/LocalOSProvider'; import { PlatformOS } from '../../utils/platform'; import { AppStorageProvider } from './appStorage'; import { ProcessProvider } from './ChildProcessProvider'; @@ -14,7 +15,6 @@ import { DockerBuildImageOptions, DockerClient, DockerContainerVolume, DockerRun import { DebuggerClient } from './debuggerClient'; import { FileSystemProvider } from './fsProvider'; import { AspNetCoreSslManager, LocalAspNetCoreSslManager } from './LocalAspNetCoreSslManager'; -import { OSProvider } from './LocalOSProvider'; import { OutputManager } from './outputManager'; export type DockerManagerBuildImageOptions diff --git a/src/debugging/coreclr/dockerNetCoreDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerNetCoreDebugConfigurationProvider.ts index 16ade16c92..94ec6299f1 100644 --- a/src/debugging/coreclr/dockerNetCoreDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerNetCoreDebugConfigurationProvider.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import { CancellationToken, commands, DebugConfiguration, DebugConfigurationProvider, MessageItem, ProviderResult, window, WorkspaceFolder } from 'vscode'; import { callWithTelemetryAndErrorHandling } from 'vscode-azureextensionui'; import { localize } from '../../localize'; +import { OSProvider } from '../../utils/LocalOSProvider'; import { PlatformOS } from '../../utils/platform'; import { resolveVariables } from '../../utils/resolveVariables'; import { DockerContainerExtraHost, DockerContainerPort, DockerContainerVolume } from './CliDockerClient'; @@ -14,7 +15,6 @@ import { UserSecretsRegex } from './CommandLineDotNetClient'; import { DebugSessionManager } from './debugSessionManager'; import { DockerManager, LaunchBuildOptions, LaunchResult, LaunchRunOptions } from './dockerManager'; import { FileSystemProvider } from './fsProvider'; -import { OSProvider } from './LocalOSProvider'; import { NetCoreProjectProvider } from './netCoreProjectProvider'; import { Prerequisite } from './prereqManager'; diff --git a/src/debugging/coreclr/prereqManager.ts b/src/debugging/coreclr/prereqManager.ts index cd738e464d..5ac8117b01 100644 --- a/src/debugging/coreclr/prereqManager.ts +++ b/src/debugging/coreclr/prereqManager.ts @@ -5,12 +5,12 @@ import * as path from 'path'; import * as vscode from 'vscode'; import { localize } from '../../localize'; +import { OSProvider } from '../../utils/LocalOSProvider'; import { ProcessProvider } from './ChildProcessProvider'; import { DockerClient } from './CliDockerClient'; import { DotNetClient } from './CommandLineDotNetClient'; import { LaunchOptions, MacNuGetPackageFallbackFolderPath } from './dockerManager'; import { FileSystemProvider } from './fsProvider'; -import { OSProvider } from './LocalOSProvider'; import { BrowserClient } from './OpnBrowserClient'; export interface Prerequisite { diff --git a/src/debugging/coreclr/registerDebugConfigurationProvider.ts b/src/debugging/coreclr/registerDebugConfigurationProvider.ts index 1d0f1d4b87..c696103472 100644 --- a/src/debugging/coreclr/registerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/registerDebugConfigurationProvider.ts @@ -4,6 +4,7 @@ import * as vscode from 'vscode'; import { ext } from '../../extensionVariables'; +import LocalOSProvider from '../../utils/LocalOSProvider'; import { DefaultAppStorageProvider } from './appStorage'; import ChildProcessProvider from './ChildProcessProvider'; import CliDockerClient from './CliDockerClient'; @@ -14,7 +15,6 @@ import { DefaultDockerManager } from './dockerManager'; import DockerNetCoreDebugConfigurationProvider from './dockerNetCoreDebugConfigurationProvider'; import { LocalFileSystemProvider } from './fsProvider'; import { LocalAspNetCoreSslManager } from './LocalAspNetCoreSslManager'; -import LocalOSProvider from './LocalOSProvider'; import { MsBuildNetCoreProjectProvider } from './netCoreProjectProvider'; import OpnBrowserClient from './OpnBrowserClient'; import { DefaultOutputManager } from './outputManager'; diff --git a/src/debugging/coreclr/tempFileProvider.ts b/src/debugging/coreclr/tempFileProvider.ts index 0514f997ba..fc6241de38 100644 --- a/src/debugging/coreclr/tempFileProvider.ts +++ b/src/debugging/coreclr/tempFileProvider.ts @@ -3,8 +3,8 @@ *--------------------------------------------------------*/ import * as path from 'path'; +import { OSProvider } from "../../utils/LocalOSProvider"; import { ProcessProvider } from './ChildProcessProvider'; -import { OSProvider } from "./LocalOSProvider"; export interface TempFileProvider { getTempFilename(prefix?: string): string; diff --git a/src/debugging/coreclr/vsdbgClient.ts b/src/debugging/coreclr/vsdbgClient.ts index f5f7220e0c..d6a4a98c86 100644 --- a/src/debugging/coreclr/vsdbgClient.ts +++ b/src/debugging/coreclr/vsdbgClient.ts @@ -7,10 +7,10 @@ import * as process from 'process'; import * as request from 'request-promise-native'; import { Memento } from 'vscode'; import { localize } from '../../localize'; +import { OSProvider } from '../../utils/LocalOSProvider'; import { NetCoreDebugHelper } from '../netcore/NetCoreDebugHelper'; import { ProcessProvider } from './ChildProcessProvider'; import { FileSystemProvider } from './fsProvider'; -import { OSProvider } from './LocalOSProvider'; import { OutputManager } from './outputManager'; export interface VsDbgClient { diff --git a/src/debugging/netcore/NetCoreDebugHelper.ts b/src/debugging/netcore/NetCoreDebugHelper.ts index e5f7a69217..31a5ce5273 100644 --- a/src/debugging/netcore/NetCoreDebugHelper.ts +++ b/src/debugging/netcore/NetCoreDebugHelper.ts @@ -12,6 +12,7 @@ import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; import { NetCoreTaskHelper, NetCoreTaskOptions } from '../../tasks/netcore/NetCoreTaskHelper'; import { ContainerTreeItem } from '../../tree/containers/ContainerTreeItem'; +import { LocalOSProvider } from '../../utils/LocalOSProvider'; import { pathNormalize } from '../../utils/pathNormalize'; import { PlatformOS } from '../../utils/platform'; import { unresolveWorkspaceFolder } from '../../utils/resolveVariables'; @@ -20,7 +21,6 @@ import CliDockerClient, { DockerExecOptions } from '../coreclr/CliDockerClient'; import { CommandLineDotNetClient } from '../coreclr/CommandLineDotNetClient'; import { LocalFileSystemProvider } from '../coreclr/fsProvider'; import { AspNetCoreSslManager, LocalAspNetCoreSslManager } from '../coreclr/LocalAspNetCoreSslManager'; -import { LocalOSProvider } from '../coreclr/LocalOSProvider'; import { MsBuildNetCoreProjectProvider, NetCoreProjectProvider } from '../coreclr/netCoreProjectProvider'; import { DefaultOutputManager } from '../coreclr/outputManager'; import { OSTempFileProvider } from '../coreclr/tempFileProvider'; diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts index a9f4b47cd5..721437ff99 100644 --- a/src/extensionVariables.ts +++ b/src/extensionVariables.ts @@ -59,4 +59,5 @@ export namespace ext { platform: osNode.platform(), release: osNode.release() }; + export let runningTests: boolean = false; } diff --git a/src/tasks/DockerTaskProvider.ts b/src/tasks/DockerTaskProvider.ts index d1fb2d9c4a..4ab9df4295 100644 --- a/src/tasks/DockerTaskProvider.ts +++ b/src/tasks/DockerTaskProvider.ts @@ -28,8 +28,7 @@ export abstract class DockerTaskProvider implements TaskProvider { task.scope, task.name, task.source, - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - new CustomExecution(() => Promise.resolve(new DockerPseudoterminal(this, task))), + new CustomExecution(async () => Promise.resolve(new DockerPseudoterminal(this, task))), task.problemMatchers ); } diff --git a/src/tasks/netcore/updateBlazorManifest.ts b/src/tasks/netcore/updateBlazorManifest.ts index 4adfd6f927..3a7cdc6a74 100644 --- a/src/tasks/netcore/updateBlazorManifest.ts +++ b/src/tasks/netcore/updateBlazorManifest.ts @@ -8,10 +8,10 @@ import * as path from 'path'; import * as xml2js from 'xml2js'; import ChildProcessProvider from "../../debugging/coreclr/ChildProcessProvider"; import { DockerContainerVolume } from '../../debugging/coreclr/CliDockerClient'; -import LocalOSProvider from "../../debugging/coreclr/LocalOSProvider"; import { OSTempFileProvider } from "../../debugging/coreclr/tempFileProvider"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; +import LocalOSProvider from "../../utils/LocalOSProvider"; import { pathNormalize } from '../../utils/pathNormalize'; import { PlatformOS } from '../../utils/platform'; import { execAsync } from '../../utils/spawnAsync'; diff --git a/src/tree/containers/ContainerTreeItem.ts b/src/tree/containers/ContainerTreeItem.ts index 4e45d147b6..11db9cf31c 100644 --- a/src/tree/containers/ContainerTreeItem.ts +++ b/src/tree/containers/ContainerTreeItem.ts @@ -6,7 +6,7 @@ import { Container } from "dockerode"; import { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerode, callDockerodeWithErrorHandling } from "../../utils/callDockerode"; import { getThemedIconPath, IconPath } from '../IconPath'; import { getContainerStateIcon } from "./ContainerProperties"; import { LocalContainerInfo } from "./LocalContainerInfo"; @@ -70,13 +70,12 @@ export class ContainerTreeItem extends AzExtTreeItem { } } - public getContainer(): Container { - return ext.dockerode.getContainer(this.containerId); + public async getContainer(): Promise { + return callDockerode(() => ext.dockerode.getContainer(this.containerId)); } public async deleteTreeItemImpl(context: IActionContext): Promise { - const container: Container = this.getContainer(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => container.remove({ force: true }), context); + const container: Container = await this.getContainer(); + await callDockerodeWithErrorHandling(async () => container.remove({ force: true }), context); } } diff --git a/src/tree/containers/ContainersTreeItem.ts b/src/tree/containers/ContainersTreeItem.ts index d880a7a2d7..4495595804 100644 --- a/src/tree/containers/ContainersTreeItem.ts +++ b/src/tree/containers/ContainersTreeItem.ts @@ -5,6 +5,7 @@ import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; +import { callDockerodeAsync } from "../../utils/callDockerode"; import { getImagePropertyValue } from "../images/ImageProperties"; import { LocalChildGroupType, LocalChildType, LocalRootTreeItemBase } from "../LocalRootTreeItemBase"; import { CommonGroupBy, groupByNoneProperty } from "../settings/CommonProperties"; @@ -48,7 +49,7 @@ export class ContainersTreeItem extends LocalRootTreeItemBase ext.dockerode.listContainers(options)) || []; return items.map(c => new LocalContainerInfo(c)); } diff --git a/src/tree/images/ImageTreeItem.ts b/src/tree/images/ImageTreeItem.ts index 60425ada5b..6620f892a9 100644 --- a/src/tree/images/ImageTreeItem.ts +++ b/src/tree/images/ImageTreeItem.ts @@ -8,7 +8,7 @@ import { window } from 'vscode'; import { AzExtParentTreeItem, AzExtTreeItem, IActionContext, IParsedError, parseError } from "vscode-azureextensionui"; import { ext } from '../../extensionVariables'; import { localize } from '../../localize'; -import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling'; +import { callDockerode, callDockerodeWithErrorHandling } from '../../utils/callDockerode'; import { getThemedIconPath, IconPath } from '../IconPath'; import { ILocalImageInfo } from './LocalImageInfo'; @@ -58,15 +58,14 @@ export class ImageTreeItem extends AzExtTreeItem { return getThemedIconPath(icon); } - public getImage(): Image { - return ext.dockerode.getImage(this.imageId); + public async getImage(): Promise { + return callDockerode(() => ext.dockerode.getImage(this.imageId)); } public async deleteTreeItemImpl(context: IActionContext): Promise { - const image: Image = this.getImage(); + const image: Image = await this.getImage(); try { - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => image.remove({ force: true }), context); + await callDockerodeWithErrorHandling(async () => image.remove({ force: true }), context); } catch (error) { const parsedError: IParsedError = parseError(error); diff --git a/src/tree/images/ImagesTreeItem.ts b/src/tree/images/ImagesTreeItem.ts index cc96eed55a..b468e0f9a0 100644 --- a/src/tree/images/ImagesTreeItem.ts +++ b/src/tree/images/ImagesTreeItem.ts @@ -6,6 +6,7 @@ import { ImageInfo } from "dockerode"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; +import { callDockerodeAsync } from "../../utils/callDockerode"; import { LocalChildGroupType, LocalChildType, LocalRootTreeItemBase } from "../LocalRootTreeItemBase"; import { CommonGroupBy, groupByNoneProperty } from "../settings/CommonProperties"; import { ITreeArraySettingInfo, ITreeSettingInfo } from "../settings/ITreeSettingInfo"; @@ -48,7 +49,7 @@ export class ImagesTreeItem extends LocalRootTreeItemBase ext.dockerode.listImages(options)) || []; let result: ILocalImageInfo[] = []; for (const image of images) { if (!image.RepoTags) { diff --git a/src/tree/networks/NetworkTreeItem.ts b/src/tree/networks/NetworkTreeItem.ts index 68bbcfd582..a40dd26022 100644 --- a/src/tree/networks/NetworkTreeItem.ts +++ b/src/tree/networks/NetworkTreeItem.ts @@ -7,7 +7,7 @@ import { Network } from "dockerode"; import { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "vscode-azureextensionui"; import { builtInNetworks } from "../../constants"; import { ext } from "../../extensionVariables"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerode, callDockerodeWithErrorHandling } from "../../utils/callDockerode"; import { getThemedIconPath, IconPath } from '../IconPath'; import { LocalNetworkInfo } from "./LocalNetworkInfo"; @@ -54,13 +54,12 @@ export class NetworkTreeItem extends AzExtTreeItem { return getThemedIconPath('network'); } - public getNetwork(): Network { - return ext.dockerode.getNetwork(this.networkId); + public async getNetwork(): Promise { + return callDockerode(() => ext.dockerode.getNetwork(this.networkId)); } public async deleteTreeItemImpl(context: IActionContext): Promise { - const network: Network = this.getNetwork(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => network.remove({ force: true }), context); + const network: Network = await this.getNetwork(); + await callDockerodeWithErrorHandling(async () => network.remove({ force: true }), context); } } diff --git a/src/tree/networks/NetworksTreeItem.ts b/src/tree/networks/NetworksTreeItem.ts index 763fc54bf4..0eb9e4c047 100644 --- a/src/tree/networks/NetworksTreeItem.ts +++ b/src/tree/networks/NetworksTreeItem.ts @@ -8,6 +8,7 @@ import { workspace } from "vscode"; import { builtInNetworks, configPrefix } from "../../constants"; import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; +import { callDockerodeAsync } from "../../utils/callDockerode"; import { LocalChildGroupType, LocalChildType, LocalRootTreeItemBase } from "../LocalRootTreeItemBase"; import { CommonGroupBy, getCommonPropertyValue, groupByNoneProperty } from "../settings/CommonProperties"; import { ITreeArraySettingInfo, ITreeSettingInfo } from "../settings/ITreeSettingInfo"; @@ -46,7 +47,7 @@ export class NetworksTreeItem extends LocalRootTreeItemBase('networks.showBuiltInNetworks'); - let networks = await ext.dockerode.listNetworks() || []; + let networks = await callDockerodeAsync(async () => ext.dockerode.listNetworks()) || []; if (!showBuiltInNetworks) { networks = networks.filter(network => !builtInNetworks.includes(network.Name)); diff --git a/src/tree/volumes/VolumeTreeItem.ts b/src/tree/volumes/VolumeTreeItem.ts index 59ad51228d..3efd3ad513 100644 --- a/src/tree/volumes/VolumeTreeItem.ts +++ b/src/tree/volumes/VolumeTreeItem.ts @@ -6,7 +6,7 @@ import { Volume } from "dockerode"; import { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "vscode-azureextensionui"; import { ext } from "../../extensionVariables"; -import { callDockerodeWithErrorHandling } from "../../utils/callDockerodeWithErrorHandling"; +import { callDockerode, callDockerodeWithErrorHandling } from "../../utils/callDockerode"; import { getThemedIconPath, IconPath } from "../IconPath"; import { LocalVolumeInfo } from "./LocalVolumeInfo"; @@ -44,13 +44,12 @@ export class VolumeTreeItem extends AzExtTreeItem { return getThemedIconPath('volume'); } - public getVolume(): Volume { - return ext.dockerode.getVolume(this.volumeName); + public async getVolume(): Promise { + return callDockerode(() => ext.dockerode.getVolume(this.volumeName)); } public async deleteTreeItemImpl(context: IActionContext): Promise { - const volume: Volume = this.getVolume(); - // eslint-disable-next-line @typescript-eslint/promise-function-async - await callDockerodeWithErrorHandling(() => volume.remove({ force: true }), context); + const volume: Volume = await this.getVolume(); + await callDockerodeWithErrorHandling(async () => volume.remove({ force: true }), context); } } diff --git a/src/tree/volumes/VolumesTreeItem.ts b/src/tree/volumes/VolumesTreeItem.ts index ab7dfe9433..9a0a66bdbd 100644 --- a/src/tree/volumes/VolumesTreeItem.ts +++ b/src/tree/volumes/VolumesTreeItem.ts @@ -5,6 +5,7 @@ import { ext } from "../../extensionVariables"; import { localize } from '../../localize'; +import { callDockerodeAsync } from "../../utils/callDockerode"; import { LocalChildGroupType, LocalChildType, LocalRootTreeItemBase } from "../LocalRootTreeItemBase"; import { CommonGroupBy, getCommonPropertyValue, groupByNoneProperty } from "../settings/CommonProperties"; import { ITreeArraySettingInfo, ITreeSettingInfo } from "../settings/ITreeSettingInfo"; @@ -40,7 +41,7 @@ export class VolumesTreeItem extends LocalRootTreeItemBase { - const result = await ext.dockerode.listVolumes(); + const result = await callDockerodeAsync(async () => ext.dockerode.listVolumes()); const volumes = (result && result.Volumes) || []; return volumes.map(v => new LocalVolumeInfo(v)); } diff --git a/src/debugging/coreclr/LocalOSProvider.ts b/src/utils/LocalOSProvider.ts similarity index 93% rename from src/debugging/coreclr/LocalOSProvider.ts rename to src/utils/LocalOSProvider.ts index db83acdbc4..d8e63a11ce 100644 --- a/src/debugging/coreclr/LocalOSProvider.ts +++ b/src/utils/LocalOSProvider.ts @@ -6,8 +6,8 @@ import * as os from 'os'; import * as path from 'path'; -import { pathNormalize } from '../../utils/pathNormalize'; -import { PlatformOS } from '../../utils/platform'; +import { pathNormalize } from './pathNormalize'; +import { PlatformOS } from './platform'; export interface OSProvider { homedir: string; diff --git a/src/utils/azureUtils.ts b/src/utils/azureUtils.ts index a230d526ca..39a986fe8a 100644 --- a/src/utils/azureUtils.ts +++ b/src/utils/azureUtils.ts @@ -151,7 +151,7 @@ export async function streamLogs(node: AzureRegistryTreeItem, run: AcrModels.Run // Makes sure that if item fails it does so due to network/azure errors not lack of new content if (available > start) { text = await getBlobToText(blobInfo, blob, start); - let utf8encoded = (new Buffer(text, 'ascii')).toString('utf8'); + let utf8encoded = (Buffer.from(text, 'ascii')).toString('utf8'); utf8encoded = removeAnsiEscapeSequences(utf8encoded); start += text.length; ext.outputChannel.append(utf8encoded); diff --git a/src/utils/callDockerode.ts b/src/utils/callDockerode.ts new file mode 100644 index 0000000000..b97dc78175 --- /dev/null +++ b/src/utils/callDockerode.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* eslint-disable unicorn/filename-case */ + +import { IActionContext, parseError } from "vscode-azureextensionui"; +import { ext } from '../extensionVariables'; +import { localize } from '../localize'; +import { dockerContextManager } from './dockerContextManager'; +import { refreshDockerode } from './refreshDockerode'; + +export async function callDockerode(dockerodeCallback: () => T): Promise { + const p = new Promise((resolve, reject) => { + try { + const result: T = dockerodeCallback(); + resolve(result); + } catch (err) { + reject(err); + } + }); + + return callDockerodeAsync(async () => p); +} + +export async function callDockerodeAsync(dockerodeAsyncCallback: () => Promise): Promise { + // If running tests, don't refresh Dockerode (some tests override Dockerode) + if (!ext.runningTests) { + const { Changed: contextChanged } = await dockerContextManager.getCurrentContext(); + if (contextChanged) { + await refreshDockerode(); + } + } + + return await dockerodeAsyncCallback(); +} + +export async function callDockerodeWithErrorHandling(dockerodeCallback: () => Promise, context: IActionContext): Promise { + try { + return await callDockerodeAsync(dockerodeCallback); + } catch (err) { + context.errorHandling.suppressReportIssue = true; + + const error = parseError(err); + + if (error?.errorType === 'ENOENT') { + throw new Error(localize('vscode-docker.utils.dockerode.failedToConnect', 'Failed to connect. Is Docker installed and running? Error: {0}', error.message)); + } + + throw err; + } +} diff --git a/src/utils/callDockerodeWithErrorHandling.ts b/src/utils/callDockerodeWithErrorHandling.ts deleted file mode 100644 index fa0729eb45..0000000000 --- a/src/utils/callDockerodeWithErrorHandling.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/* eslint-disable unicorn/filename-case */ - -import { IActionContext, parseError } from "vscode-azureextensionui"; -import { localize } from '../localize'; - -export async function callDockerodeWithErrorHandling(dockerodeCallback: () => Promise, context: IActionContext): Promise { - try { - return await dockerodeCallback(); - } catch (err) { - context.errorHandling.suppressReportIssue = true; - - const error = parseError(err); - - if (error && error.errorType === 'ENOENT') { - throw new Error(localize('vscode-docker.utils.dockerode.failedToConnect', 'Failed to connect. Is Docker installed and running? Error: {0}', error.message)); - } - - throw err; - } -} diff --git a/src/utils/dockerContextManager.ts b/src/utils/dockerContextManager.ts new file mode 100644 index 0000000000..4b5ff3589e --- /dev/null +++ b/src/utils/dockerContextManager.ts @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExecOptions } from 'child_process'; +import * as crypto from 'crypto'; +import * as fs from 'fs'; +import * as url from 'url'; +import { workspace, WorkspaceConfiguration } from 'vscode'; +import { parseError } from "vscode-azureextensionui"; +import { ext } from '../extensionVariables'; +import { localize } from '../localize'; +import { LocalOSProvider } from './LocalOSProvider'; +import { execAsync } from './spawnAsync'; +import { timeUtils } from './timeUtils'; + +const ContextInspectExecOptions: ExecOptions = { timeout: 5000 } +const DOCKER_CONTEXT: string = 'DOCKER_CONTEXT'; +const DefaultRefreshIntervalSec: number = 20; +const osp = new LocalOSProvider(); +const DockerConfigFilePath: string = osp.pathJoin(osp.os, osp.homedir, '.docker', 'config.json'); + + +export interface IDockerEndpoint { + Host: string, + SkipTLSVerify?: boolean +} +export interface IDockerEndpoints { + [name: string]: IDockerEndpoint +} + +export interface IDockerTlsMaterial { + // The convention seems to be that up to 3 files constitute "TLS material" + // If one file is present, it is the CA certificate to verify Docker endpoint identity (public/system CA pool is subsituted by custom CA) + // If two files are present, it is the client certificate and client key to authenticate the client + // If three files are present, it is the CA certificate for Docker endpoint verification, client certificate and client key, in that order + // See https://docs.docker.com/engine/security/https/ for more details + // This is all quite complicated and VS Code Docker extension does not support these settings + [name: string]: string[] +} + +export interface IDockerContextMetadata { + [name: string]: unknown +} + +export interface IDockerContextStorage { + MetadataPath?: string, + TLSPath?: string +} + +export interface IDockerContext { + Name: string, + Metadata: IDockerContextMetadata, + Endpoints: IDockerEndpoints, + TLSMaterial: IDockerTlsMaterial, + Storage: IDockerContextStorage, + FullSpec: string, // Useful for checking if the context didn't change + HostProtocol?: string // http, https, ssh, unix, npipe, ... +} + +export interface IDockerContextCheckResult { + Context: IDockerContext, + Changed: boolean +} + +export class DockerContextManager { + private readonly contextRefreshIntervalMs: number; + private lastContextCheckTimestamp: number; + private cachedContext: IDockerContext; + private lastDockerConfigDigest: string; + + public constructor() { + this.lastContextCheckTimestamp = 0; + const configOptions: WorkspaceConfiguration = workspace.getConfiguration('docker'); + this.contextRefreshIntervalMs = configOptions.get('contextRefreshInterval', DefaultRefreshIntervalSec) * 1000; + } + + public async getCurrentContext(): Promise { + let contextChanged: boolean = false; + + if (!this.cachedContext || (Date.now() - this.lastContextCheckTimestamp > this.contextRefreshIntervalMs)) { + try { + if (!this.cachedContext) { + // First-time check + this.lastDockerConfigDigest = await this.getDockerConfigDigest(); + contextChanged = await this.refreshCachedDockerContext(); + } else { + const dockerConfigDigest: string = await this.getDockerConfigDigest(); + + if (!dockerConfigDigest || dockerConfigDigest !== this.lastDockerConfigDigest) { + this.lastDockerConfigDigest = dockerConfigDigest; + // Config file will change when Docker context is changed, but opposite is not necessarily true + // (i.e. config file might change for other reasons). + contextChanged = await this.refreshCachedDockerContext(); + } + } + } finally { + // Start counting time _after_ we are done with all the I/O + this.lastContextCheckTimestamp = Date.now(); + } + } + + return { + Context: this.cachedContext, + Changed: contextChanged + }; + } + + private async getDockerConfigDigest(): Promise { + // Note: computing Docker config file digest may fail, typically because Docker is not installed, + // and there is no config file. We use falsy value (empty string) as a way to indicate that, + // as opposed to rejecting the promise with the captured error. + // This is a clue to the caller that they should go ahead and try to inspect the actual Docker context. + + return new Promise((resolve, reject) => { + try { + // tslint:disable-next-line: non-literal-fs-path + const dockerConfig = fs.createReadStream(DockerConfigFilePath); + let hash = crypto.createHash('sha256'); + hash = dockerConfig.pipe(hash, { end: false }); + + dockerConfig.on('end', () => { + try { + const digest = hash.digest('hex'); + resolve(digest); + } catch (ex) { + resolve(''); + } + }); + + dockerConfig.on('error', _ => { + resolve(''); + }) + } catch (ex) { + resolve(''); + } + }); + } + + private async refreshCachedDockerContext(): Promise { + const { Result: currentContext, DurationMs: duration } = await timeUtils.timeIt(async () => this.inspectCurrentContext()); + + const contextChanged = !this.cachedContext || currentContext.FullSpec !== this.cachedContext.FullSpec; + + if (contextChanged) { + const previousContext = this.cachedContext; + this.cachedContext = currentContext; + this.sendDockerContextEvent(currentContext, previousContext, duration); + } + + return contextChanged; + } + + private async inspectCurrentContext(): Promise { + let execResult: { + stdout: string; + }; + const inspectCmd = `docker context inspect ${process.env[DOCKER_CONTEXT] || ''}`.trim(); + + try { + execResult = await execAsync(inspectCmd, ContextInspectExecOptions); + } catch (err) { + const error = parseError(err); + throw new Error(localize('vscode-docker.dockerContext.inspectCurrentFailed', 'Could not determine the current Docker context. Is Docker installed? Error: {0}', error.message)); + } + + const dockerContexts = JSON.parse(execResult.stdout); + if (!dockerContexts?.[0]) { + throw new Error(localize('vscode-docker.dockerContext.contextCouldNotBeParsed', 'Docker context could not be parsed: {0}', execResult.stdout)); + } + + let currentContext = dockerContexts[0]; + currentContext.FullSpec = execResult.stdout; + try { + currentContext.HostProtocol = url.parse(currentContext.Endpoints.docker.Host).protocol?.replace(':', '') + } catch { } + return currentContext; + } + + private sendDockerContextEvent(currentContext: IDockerContext, previousContext: IDockerContext, contextRetrievalTimeMs: number): void { + const eventName: string = previousContext ? 'docker-context.change' : 'docker-context.initialize'; + ext.reporter.sendTelemetryEvent(eventName, { hostProtocol: currentContext.HostProtocol }, { contextRetrievalTimeMs: contextRetrievalTimeMs }); + } +} + +export const dockerContextManager = new DockerContextManager(); + diff --git a/src/utils/osUtils.ts b/src/utils/osUtils.ts index 15fccd62a1..cba25e06b5 100644 --- a/src/utils/osUtils.ts +++ b/src/utils/osUtils.ts @@ -6,7 +6,7 @@ import * as semver from 'semver'; import { IActionContext } from 'vscode-azureextensionui'; import { ext } from '../extensionVariables'; -import { callDockerodeWithErrorHandling } from './callDockerodeWithErrorHandling'; +import { callDockerodeWithErrorHandling } from './callDockerode'; // Minimum Windows RS3 version number const windows10RS3MinVersion = '10.0.16299'; @@ -83,8 +83,7 @@ export async function getDockerOSType(context: IActionContext): Promise ext.dockerode.info(), context); + const info = await callDockerodeWithErrorHandling(async () => ext.dockerode.info(), context); // eslint-disable-next-line @typescript-eslint/tslint/config return info.OSType; } diff --git a/src/utils/refreshDockerode.ts b/src/utils/refreshDockerode.ts index 392d90fcd3..b8f5a0f056 100644 --- a/src/utils/refreshDockerode.ts +++ b/src/utils/refreshDockerode.ts @@ -4,32 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import Dockerode = require('dockerode'); -import { DockerOptions } from 'dockerode'; import { Socket } from 'net'; import { CancellationTokenSource } from 'vscode'; +import { parseError } from 'vscode-azureextensionui'; import { ext } from '../extensionVariables'; import { localize } from '../localize'; import { addDockerSettingsToEnv } from './addDockerSettingsToEnv'; import { cloneObject } from './cloneObject'; import { delay } from './delay'; +import { dockerContextManager, IDockerContext } from './dockerContextManager'; import { isWindows } from './osUtils'; -import { execAsync } from './spawnAsync'; - -const unix = 'unix://'; -const npipe = 'npipe://'; const SSH_URL_REGEX = /ssh:\/\//i; -// Not exhaustive--only the properties we're interested in -interface IDockerEndpoint { - Host?: string; -} - -// Also not exhaustive--only the properties we're interested in -interface IDockerContext { - Endpoints: { [key: string]: IDockerEndpoint } -} - /** * Dockerode parses and handles the well-known `DOCKER_*` environment variables, but it doesn't let us pass those values as-is to the constructor * Thus we will temporarily update `process.env` and pass nothing to the constructor @@ -39,13 +26,12 @@ export async function refreshDockerode(): Promise { const oldEnv = process.env; const newEnv: NodeJS.ProcessEnv = cloneObject(process.env); // make a clone before we change anything addDockerSettingsToEnv(newEnv, oldEnv); - - const dockerodeOptions = await getDockerodeOptions(newEnv); + await addDockerHostToEnv(newEnv); ext.dockerodeInitError = undefined; process.env = newEnv; try { - ext.dockerode = new Dockerode(dockerodeOptions); + ext.dockerode = new Dockerode(); } finally { process.env = oldEnv; } @@ -55,29 +41,34 @@ export async function refreshDockerode(): Promise { } } -async function getDockerodeOptions(newEnv: NodeJS.ProcessEnv): Promise { - // By this point any DOCKER_HOST from VSCode settings is already copied to process.env, so we can use it directly +async function addDockerHostToEnv(newEnv: NodeJS.ProcessEnv): Promise { + let dockerContext: IDockerContext; try { - if (newEnv.DOCKER_HOST && - SSH_URL_REGEX.test(newEnv.DOCKER_HOST)) { - // If DOCKER_HOST is an SSH URL, we need to configure / validate SSH_AUTH_SOCK for Dockerode - // Other than that, we use default settings, so return undefined - if (!await validateSshAuthSock(newEnv)) { - // Don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - ext.ui.showWarningMessage(localize('vscode-docker.utils.dockerode.sshAgent', 'In order to use an SSH DOCKER_HOST, you must configure an ssh-agent.'), { learnMoreLink: 'https://aka.ms/AA7assy' }); - } + ({ Context: dockerContext } = await dockerContextManager.getCurrentContext()); + + if (!newEnv.DOCKER_HOST) { + newEnv.DOCKER_HOST = dockerContext.Endpoints.docker.Host; + } - return undefined; - } else if (!newEnv.DOCKER_HOST) { - // If DOCKER_HOST is unset, try to get default Docker context--this helps support WSL - return await getDefaultDockerContext(); + if (!newEnv.DOCKER_TLS_VERIFY && dockerContext.Endpoints.docker.SkipTLSVerify) { + // https://docs.docker.com/compose/reference/envvars/#docker_tls_verify + newEnv.DOCKER_TLS_VERIFY = ""; } - } catch { } // Best effort only + } catch (error) { + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + ext.ui.showWarningMessage(localize('vscode-docker.utils.dockerode.dockerContextUnobtainable', 'Docker context could not be retrieved.') + ' ' + parseError(error).message); + } - // Use default options - return undefined; + if (newEnv.DOCKER_HOST && SSH_URL_REGEX.test(newEnv.DOCKER_HOST)) { + // If DOCKER_HOST is an SSH URL, we need to configure / validate SSH_AUTH_SOCK for Dockerode + // Other than that, we use default settings, so return undefined + if (!await validateSshAuthSock(newEnv)) { + // Don't wait + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + ext.ui.showWarningMessage(localize('vscode-docker.utils.dockerode.sshAgent', 'In order to use an SSH DOCKER_HOST, you must configure an ssh-agent.'), { learnMoreLink: 'https://aka.ms/AA7assy' }); + } + } } async function validateSshAuthSock(newEnv: NodeJS.ProcessEnv): Promise { @@ -114,26 +105,3 @@ async function validateSshAuthSock(newEnv: NodeJS.ProcessEnv): Promise cts.dispose(); }); } - -async function getDefaultDockerContext(): Promise { - const { stdout } = await execAsync('docker context inspect', { timeout: 5000 }); - const dockerContexts = JSON.parse(stdout); - const defaultHost: string = - dockerContexts && - dockerContexts.length > 0 && - dockerContexts[0].Endpoints && - dockerContexts[0].Endpoints.docker && - dockerContexts[0].Endpoints.docker.Host; - - if (defaultHost.indexOf(unix) === 0) { - return { - socketPath: defaultHost.substring(unix.length), // Everything after the unix:// (expecting unix:///var/run/docker.sock) - }; - } else if (defaultHost.indexOf(npipe) === 0) { - return { - socketPath: defaultHost.substring(npipe.length), // Everything after the npipe:// (expecting npipe:////./pipe/docker_engine or npipe:////./pipe/docker_wsl) - }; - } else { - return undefined; - } -} diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts new file mode 100644 index 0000000000..4891f5a8f6 --- /dev/null +++ b/src/utils/timeUtils.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as process from 'process'; + +const NANOS_TO_MILLIS: bigint = BigInt(1e6); + +// Maximum exponent that results in a safe integer +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER +const MAX_SAFE_INTEGER_EXPONENT: number = 53; + +export namespace timeUtils { + export interface ITimedResult { + Result: T, + DurationMs: number + } + + export async function timeIt(doWork: () => Promise): Promise> { + const start = process.hrtime.bigint(); + const result = await doWork(); + const stop = process.hrtime.bigint(); + const duration: number = Number(BigInt.asUintN(MAX_SAFE_INTEGER_EXPONENT, ((stop - start) / NANOS_TO_MILLIS))); + return { + Result: result, + DurationMs: duration + }; + } +} diff --git a/test/global.test.ts b/test/global.test.ts index 6ea3b03226..17cba3f018 100644 --- a/test/global.test.ts +++ b/test/global.test.ts @@ -101,6 +101,7 @@ suiteSetup(async function (this: mocha.IHookCallbackContext): Promise { this.timeout(60 * 1000); console.log('global.test.ts: suiteSetup'); + ext.runningTests = true; // Otherwise the app can blocking asking for keychain access ext.keytar = new TestKeytar(); diff --git a/test/tree/validateTree.ts b/test/tree/validateTree.ts index 90bf775783..7db290dcc3 100644 --- a/test/tree/validateTree.ts +++ b/test/tree/validateTree.ts @@ -71,6 +71,7 @@ interface ITestDockerodeOptions { async function runWithDockerode(options: ITestDockerodeOptions, callback: () => Promise): Promise { const oldDockerode = ext.dockerode; + try { ext.dockerode = { listContainers: async () => options.containers,