Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

ACR Fixes #656

Merged
merged 7 commits into from
Dec 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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