Skip to content

Commit

Permalink
Merge pull request #656 from Microsoft/roy/dev
Browse files Browse the repository at this point in the history
ACR Fixes
  • Loading branch information
rosanch authored Dec 7, 2018
2 parents a138e66 + b4fc179 commit 774ecba
Show file tree
Hide file tree
Showing 15 changed files with 186 additions and 174 deletions.
2 changes: 1 addition & 1 deletion commands/azureCommands/delete-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function deleteAzureRegistry(context?: AzureRegistryNode): Promise<
if (context) {
registry = context.registry;
} else {
registry = await quickPickACRRegistry(false, 'Select the registry you want to delete');
registry = await quickPickACRRegistry(false, undefined, 'Select the registry you want to delete');
}
const shouldDelete = await confirmUserIntent(`Are you sure you want to delete ${registry.name} and its associated images?`);
if (shouldDelete) {
Expand Down
105 changes: 54 additions & 51 deletions commands/azureCommands/pull-from-azure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,91 @@ import { exec } from 'child_process';
import * as fse from 'fs-extra';
import * as path from "path";
import vscode = require('vscode');
import { callWithTelemetryAndErrorHandling, IActionContext, parseError } from 'vscode-azureextensionui';
import { UserCancelledError } from '../../explorer/deploy/wizard';
import { callWithTelemetryAndErrorHandling, IActionContext } from 'vscode-azureextensionui';
import { AzureImageTagNode, AzureRepositoryNode } from '../../explorer/models/azureRegistryNodes';
import { ext } from '../../extensionVariables';
import * as acrTools from '../../utils/Azure/acrTools';
import { AzureImage } from "../../utils/Azure/models/image";
import { Repository } from "../../utils/Azure/models/repository";
import * as quickPicks from '../utils/quick-pick-azure';
import { quickPickACRImage, quickPickACRRegistry, quickPickACRRepository } from '../utils/quick-pick-azure';

export async function pullRepoFromAzure(context?: AzureRepositoryNode): Promise<void> {
await pullFromAzure(context, true);
}

export async function pullImageFromAzure(context?: AzureImageTagNode): Promise<void> {
await pullFromAzure(context, false);
}

/* Pulls an image from Azure. The context is the image node the user has right clicked on */
export async function pullFromAzure(context?: AzureImageTagNode | AzureRepositoryNode): Promise<void> {
let registryName: string;
async function pullFromAzure(context: AzureImageTagNode | AzureRepositoryNode, pullAll: boolean): Promise<void> {
let registry: Registry;
let imageName: string;
let imageRequest: string;

if (context) { // Right Click
registryName = context.registry.loginServer;
registry = context.registry;

if (context instanceof AzureImageTagNode) { // Right Click on AzureImageNode
imageName = context.label;
imageRequest = context.label;
} else if (context instanceof AzureRepositoryNode) { // Right Click on AzureRepositoryNode
imageName = `${context.label} -a`; // Pull all images in repository
imageRequest = `${context.label} -a`; // Pull all images in repository
} else {
assert.fail(`Unexpected node type`);
}

} else { // Command Palette
registry = await quickPicks.quickPickACRRegistry();
registryName = registry.loginServer;
const repository: Repository = await quickPicks.quickPickACRRepository(registry, 'Select the repository of the image you want to pull');
const image: AzureImage = await quickPicks.quickPickACRImage(repository, 'Select the image you want to pull');
imageName = `${repository.name}:${image.tag}`;
registry = await quickPickACRRegistry();
const repository: Repository = await quickPickACRRepository(registry, 'Select the repository of the image you want to pull.');
if (pullAll) {
imageRequest = `${repository.name} -a`;
} else {
const image: AzureImage = await quickPickACRImage(repository, 'Select the image you want to pull');
imageRequest = image.toString();
}
}

// Using loginCredentials function to get the username and password. This takes care of all users, even if they don't have the Azure CLI
const credentials = await acrTools.getLoginCredentials(registry);
const username = credentials.username;
const password = credentials.password;
await pullImage(registryName, imageName, username, password);
const { username, password } = await acrTools.getLoginCredentials(registry);
await pullImage(registry.loginServer, imageRequest, username, password);
}

async function pullImage(registryName: string, imageName: string, username: string, password: string): Promise<void> {
// Check if user is logged into Docker and send appropriate commands to terminal
let result = await isLoggedIntoDocker(registryName);
if (!result.loggedIn) { // If not logged in to Docker
let login: vscode.MessageItem = { title: 'Log in to Docker CLI' };
let msg = `You are not currently logged in to "${registryName}" in the Docker CLI.`;
let response = await vscode.window.showErrorMessage(msg, login)
if (response !== login) {
throw new UserCancelledError(msg);
}
async function pullImage(loginServer: string, imageRequest: string, username: string, password: string): Promise<void> {
// We can't know if the key is still active. So we login into Docker and send appropriate commands to terminal
let dockerConfigPath: string = path.join(process.env.HOMEPATH, '.docker', 'config.json');

await new Promise((resolve, reject) => {
const dockerLoginCmd = `docker login ${loginServer} --username ${username} --password-stdin`;
let childProcess = exec(dockerLoginCmd, (err, stdout, stderr) => {
ext.outputChannel.append(`${dockerLoginCmd} xxxxxx\n`);
ext.outputChannel.append(stdout);
ext.outputChannel.append(stderr);
if (err && err.message.match(/error storing credentials.*The stub received bad data/)) {
// Temporary work-around for this error- same as Azure CLI
// See https://github.com/Azure/azure-cli/issues/4843
ext.outputChannel.show();
reject(new Error(`In order to log in to the Docker CLI using tokens, you currently need to go to \n${dockerConfigPath} and remove "credsStore": "wincred" from the config.json file, then try again. \nDoing this will disable wincred and cause Docker to store credentials directly in the .docker/config.json file. All registries that are currently logged in will be effectly logged out.`));
} else if (err) {
ext.outputChannel.show();
reject(err);
} else if (stderr) {
ext.outputChannel.show();
reject(stderr);
}

await new Promise((resolve, reject) => {
let childProcess = exec(`docker login ${registryName} --username ${username} --password-stdin`, (err, stdout, stderr) => {
ext.outputChannel.append(stdout);
ext.outputChannel.append(stderr);
if (err && err.message.match(/error storing credentials.*The stub received bad data/)) {
// Temporary work-around for this error- same as Azure CLI
// See https://github.com/Azure/azure-cli/issues/4843
reject(new Error(`In order to log in to the Docker CLI using tokens, you currently need to go to \n${result.configPath} and remove "credsStore": "wincred" from the config.json file, then try again. \nDoing this will disable wincred and cause Docker to store credentials directly in the .docker/config.json file. All registries that are currently logged in will be effectly logged out.`));
} else if (err) {
reject(err);
} else if (stderr) {
reject(stderr);
}

resolve();
});

childProcess.stdin.write(password); // Prevents insecure password error
childProcess.stdin.end();
resolve();
});
}

childProcess.stdin.write(password); // Prevents insecure password error
childProcess.stdin.end();
});

const terminal: vscode.Terminal = ext.terminalProvider.createTerminal("docker pull");
terminal.show();

terminal.sendText(`docker pull ${registryName}/${imageName}`);
terminal.sendText(`docker pull ${loginServer}/${imageRequest}`);
}

async function isLoggedIntoDocker(registryName: string): Promise<{ configPath: string, loggedIn: boolean }> {
async function isLoggedIntoDocker(loginServer: string): Promise<{ configPath: string, loggedIn: boolean }> {
let home = process.env.HOMEPATH;
let configPath: string = path.join(home, '.docker', 'config.json');
let buffer: Buffer;
Expand All @@ -100,7 +103,7 @@ async function isLoggedIntoDocker(registryName: string): Promise<{ configPath: s
buffer = fse.readFileSync(configPath);
});

let index = buffer.indexOf(registryName);
let index = buffer.indexOf(loginServer);
let loggedIn = index >= 0; // Returns -1 if user is not logged into Docker
return { configPath, loggedIn }; // Returns object with configuration path and boolean indicating if user was logged in or not
}
4 changes: 2 additions & 2 deletions commands/azureCommands/run-task.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TaskRunRequest } from "azure-arm-containerregistry/lib/models";
import { Registry } from "azure-arm-containerregistry/lib/models";
import { TaskRunRequest } from "azure-arm-containerregistry/lib/models";
import { ResourceGroup } from "azure-arm-resource/lib/resource/models";
import { Subscription } from "azure-arm-resource/lib/subscription/models";
import vscode = require('vscode');
Expand Down Expand Up @@ -29,7 +29,7 @@ export async function runTask(context?: TaskNode): Promise<void> {
taskName = context.task.name;
} else { // Command Palette
subscription = await quickPickSubscription();
registry = await quickPickACRRegistry();
registry = await quickPickACRRegistry(false, subscription);
resourceGroup = await acrTools.getResourceGroup(registry, subscription);
taskName = (await quickPickTask(registry, subscription, resourceGroup)).name;
}
Expand Down
2 changes: 1 addition & 1 deletion commands/azureCommands/show-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function showTaskProperties(context?: TaskNode): Promise<void> {
task = context.task.name;
} else { // Command palette
subscription = await quickPickSubscription();
registry = await quickPickACRRegistry();
registry = await quickPickACRRegistry(false, subscription);
resourceGroup = await acrTools.getResourceGroup(registry, subscription);
task = (await quickPickTask(registry, subscription, resourceGroup)).name;
}
Expand Down
59 changes: 3 additions & 56 deletions commands/build-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,20 @@

import * as path from "path";
import * as vscode from "vscode";
import { DialogResponses, IActionContext, UserCancelledError } from "vscode-azureextensionui";
import { DOCKERFILE_GLOB_PATTERN, YAML_GLOB_PATTERN } from '../dockerExtension';
import { IActionContext } from "vscode-azureextensionui";
import { delay } from "../explorer/utils/utils";
import { ext } from "../extensionVariables";
import { addImageTaggingTelemetry, getTagFromUserInput } from "./tag-image";
import { quickPickDockerFileItem } from "./utils/quick-pick-file";
import { quickPickWorkspaceFolder } from "./utils/quickPickWorkspaceFolder";

async function getFileUris(folder: vscode.WorkspaceFolder, globPattern: string): Promise<vscode.Uri[]> {
return await vscode.workspace.findFiles(new vscode.RelativePattern(folder, globPattern), undefined, 1000, undefined);
}

export interface Item extends vscode.QuickPickItem {
relativeFilePath: string;
relativeFolderPath: string;
}

function createFileItem(rootFolder: vscode.WorkspaceFolder, uri: vscode.Uri): Item {
let relativeFilePath = path.join(".", uri.fsPath.substr(rootFolder.uri.fsPath.length));

return <Item>{
description: undefined,
relativeFilePath: relativeFilePath,
label: relativeFilePath,
relativeFolderPath: path.dirname(relativeFilePath)
};
}

export async function resolveFileItem(rootFolder: vscode.WorkspaceFolder, fileUri: vscode.Uri | undefined, globPattern: string, message: string): Promise<Item | undefined> {
if (fileUri) {
return createFileItem(rootFolder, fileUri);
}

let uris: vscode.Uri[] = await getFileUris(rootFolder, globPattern);

if (!uris || uris.length === 0) {
return undefined;
} else {
let items: Item[] = uris.map(uri => createFileItem(rootFolder, uri));
if (items.length === 1) {
return items[0];
} else {
const res: vscode.QuickPickItem = await ext.ui.showQuickPick(items, { placeHolder: message });
return <Item>res;
}
}
}

export async function buildImage(actionContext: IActionContext, dockerFileUri: vscode.Uri | undefined): Promise<void> {
const configOptions: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('docker');
const defaultContextPath = configOptions.get('imageBuildContextPath', '');
let dockerFileItem: Item | undefined;

let rootFolder: vscode.WorkspaceFolder = await quickPickWorkspaceFolder('To build Docker files you must first open a folder or workspace in VS Code.');

while (!dockerFileItem) {
let resolvedItem: Item | undefined = await resolveFileItem(rootFolder, dockerFileUri, DOCKERFILE_GLOB_PATTERN, 'Choose a Dockerfile to build the image.');
if (resolvedItem) {
dockerFileItem = resolvedItem;
} else {
let msg = "Couldn't find a Dockerfile in your workspace. Would you like to add Docker files to the workspace?";
actionContext.properties.cancelStep = msg;
await ext.ui.showWarningMessage(msg, DialogResponses.yes, DialogResponses.cancel);
actionContext.properties.cancelStep = undefined;
await vscode.commands.executeCommand('vscode-docker.configure');
// Try again
}
}
const dockerFileItem = await quickPickDockerFileItem(actionContext, dockerFileUri, rootFolder);

let contextPath: string = dockerFileItem.relativeFolderPath;
if (defaultContextPath && defaultContextPath !== '') {
Expand Down
2 changes: 1 addition & 1 deletion commands/docker-compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as path from 'path';
import * as vscode from 'vscode';
import { COMPOSE_FILE_GLOB_PATTERN } from '../dockerExtension';
import { COMPOSE_FILE_GLOB_PATTERN } from '../constants';
import { ext } from '../extensionVariables';
import { quickPickWorkspaceFolder } from './utils/quickPickWorkspaceFolder';

Expand Down
22 changes: 10 additions & 12 deletions commands/utils/SourceArchiveUtility.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,42 @@

import { ContainerRegistryManagementClient } from 'azure-arm-containerregistry/lib/containerRegistryManagementClient';
import { DockerBuildRequest, FileTaskRunRequest, Registry } from 'azure-arm-containerregistry/lib/models';
import { Subscription } from 'azure-arm-resource/lib/subscription/models';
import { BlobService, createBlobServiceWithSas } from "azure-storage";
import * as fse from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
import * as tar from 'tar';
import * as url from 'url';
import vscode = require('vscode');
import { IActionContext, IAzureQuickPickItem } from 'vscode-azureextensionui';
import { ext } from '../../extensionVariables';
import { getBlobInfo, getResourceGroupName, streamLogs } from "../../utils/Azure/acrTools";
import { AzureUtilityManager } from '../../utils/azureUtilityManager';
import { Item } from '../build-image';
import { quickPickACRRegistry, quickPickSubscription } from './quick-pick-azure';
import { quickPickDockerFileItem, quickPickImageName, quickPickYamlFileItem } from './quick-pick-image';
import { Item, quickPickDockerFileItem, quickPickYamlFileItem } from './quick-pick-file';
import { quickPickImageName } from './quick-pick-image';
import { quickPickWorkspaceFolder } from './quickPickWorkspaceFolder';

const idPrecision = 6;
const vcsIgnoreList = ['.git', '.gitignore', '.bzr', 'bzrignore', '.hg', '.hgignore', '.svn']

export type runRequestType =
| 'DockerBuildRequest'
| 'FileTaskRunRequest';

export async function scheduleRunRequest(fileUri: vscode.Uri, requestType: runRequestType, actionContext?: IActionContext): Promise<void> {
export async function scheduleRunRequest(fileUri: vscode.Uri, requestType: 'DockerBuildRequest' | 'FileTaskRunRequest', actionContext?: IActionContext): Promise<void> {
//Acquire information.
let rootFolder: vscode.WorkspaceFolder;
let fileItem: Item;
let imageName: string;
if (requestType === 'DockerBuildRequest') {
rootFolder = await quickPickWorkspaceFolder("To quick build Docker files you must first open a folder or workspace in VS Code.");
fileItem = await quickPickDockerFileItem(actionContext, fileUri, rootFolder);
imageName = await quickPickImageName(actionContext, rootFolder, fileItem);
} else if (requestType === 'FileTaskRunRequest') {
rootFolder = await quickPickWorkspaceFolder("To run a task from a .yaml file you must first open a folder or workspace in VS Code.");
fileItem = await quickPickYamlFileItem(fileUri, rootFolder);
fileItem = await quickPickYamlFileItem(fileUri, rootFolder, "To run a task from a .yaml file you must have yaml file in your VS Code workspace.");
} else {
throw new Error("Run Request Type Currently not supported.");
}
const subscription: Subscription = await quickPickSubscription();
const registry: Registry = await quickPickACRRegistry(true);
const registry: Registry = await quickPickACRRegistry(true, subscription);
const osPick = ['Linux', 'Windows'].map(item => <IAzureQuickPickItem<string>>{ label: item, data: item });
const osType: string = (await ext.ui.showQuickPick(osPick, { 'canPickMany': false, 'placeHolder': 'Select image base OS' })).data;

Expand All @@ -53,7 +52,6 @@ export async function scheduleRunRequest(fileUri: vscode.Uri, requestType: runRe

let runRequest: DockerBuildRequest | FileTaskRunRequest;
if (requestType === 'DockerBuildRequest') {
const imageName: string = await quickPickImageName(actionContext, rootFolder, fileItem);
runRequest = {
type: requestType,
imageNames: [imageName],
Expand Down Expand Up @@ -109,6 +107,6 @@ function getTempSourceArchivePath(): string {
const id: number = Math.floor(Math.random() * Math.pow(10, idPrecision));
const archive = `sourceArchive${id}.tar.gz`;
ext.outputChannel.appendLine(`Setting up temp file with '${archive}'`);
const tarFilePath: string = url.resolve(os.tmpdir(), archive);
const tarFilePath: string = path.join(os.tmpdir(), archive);
return tarFilePath;
}
Loading

0 comments on commit 774ecba

Please sign in to comment.