* Adding auto sync support

* Updated delete and push to support login

* Fixes based on feedback.
This commit is contained in:
Richard Guthrie 2018-01-23 17:13:24 -08:00 коммит произвёл Sheng Chen
Родитель c539ead5cf
Коммит e7a9226ea3
4 изменённых файлов: 149 добавлений и 106 удалений

Просмотреть файл

@ -73,6 +73,7 @@ This extension requires:
- [Terraform](https://www.terraform.io/downloads.html) - [Terraform](https://www.terraform.io/downloads.html)
- [Docker](http://www.docker.io) if you are running the execute test feature locally. - [Docker](http://www.docker.io) if you are running the execute test feature locally.
- [GraphViz dot](http://www.graphviz.org) if you are using the visualize feature. - [GraphViz dot](http://www.graphviz.org) if you are using the visualize feature.
- NOTE: On Windows after installing the graphViz msi/zip, you will most likely need to add your PATH env variable `(Ex. c:\Program Files(x86)\GraphViz2.38\bin)` in order to use dot from the command line. - NOTE: On Windows after installing the graphViz msi/zip, you will most likely need to add your PATH env variable `(Ex. c:\Program Files(x86)\GraphViz2.38\bin)` in order to use dot from the command line.
## Supported Environments ## Supported Environments

Просмотреть файл

@ -16,6 +16,7 @@
"Other" "Other"
], ],
"activationEvents": [ "activationEvents": [
"workspaceContains:**/*.tf",
"onCommand:vscode-terraform-azure.init", "onCommand:vscode-terraform-azure.init",
"onCommand:vscode-terraform-azure.plan", "onCommand:vscode-terraform-azure.plan",
"onCommand:vscode-terraform-azure.apply", "onCommand:vscode-terraform-azure.apply",
@ -41,6 +42,11 @@
"default": "**/*.{tf,txt,yml,tfvars,rb}", "default": "**/*.{tf,txt,yml,tfvars,rb}",
"description": "Indicates the files that should be synchronized to Azure cloudshell using the glob pattern string, for example: **/*.{tf,txt,yml,tfvars,rb}" "description": "Indicates the files that should be synchronized to Azure cloudshell using the glob pattern string, for example: **/*.{tf,txt,yml,tfvars,rb}"
}, },
"tf-azure.syncEnabled": {
"type": "boolean",
"default": true,
"description": "When terminal is set to `cloudshell`, indicates whether changes to files that match tf-azure.files setting glob pattern should automatically sync to cloudshell."
},
"tf-azure.test-container": { "tf-azure.test-container": {
"type": "string", "type": "string",
"default": "microsoft/terraform-test", "default": "microsoft/terraform-test",

Просмотреть файл

@ -4,7 +4,7 @@ import { BaseShell } from "./baseShell";
import { openCloudConsole, OSes } from "./cloudConsole"; import { openCloudConsole, OSes } from "./cloudConsole";
import { delay } from "./cloudConsoleLauncher"; import { delay } from "./cloudConsoleLauncher";
import { aciConfig, Constants, exportContainerCmd, exportTestScript } from "./Constants"; import { aciConfig, Constants, exportContainerCmd, exportTestScript } from "./Constants";
import { azFilePush, escapeFile, TerminalType, TFTerminal } from "./shared"; import { azFileDelete, azFilePush, escapeFile, TerminalType, TFTerminal } from "./shared";
import { CSTerminal } from "./utilities"; import { CSTerminal } from "./utilities";
@ -32,57 +32,38 @@ export class CloudShell extends BaseShell {
TerminalType.CloudShell, TerminalType.CloudShell,
Constants.TerraformTerminalName); Constants.TerraformTerminalName);
public async pushFiles(files: vscode.Uri[]) { public async pushFiles(files: vscode.Uri[], syncAllFiles: boolean) {
this.outputChannel.appendLine("Attempting to upload files to CloudShell"); this.outputChannel.appendLine("Attempting to upload files to CloudShell");
const RETRY_INTERVAL = 500; const RETRY_INTERVAL = 500;
const RETRY_TIMES = 30; const RETRY_TIMES = 30;
// Checking if the terminal has been created // Checking if the terminal has been created and user is logged in.
if (( this.csTerm.terminal != null) && (this.csTerm.storageAccountKey != null)) { await this.checkInitTerm();
for (let i = 0; i < RETRY_TIMES; i++ ) {
if (this.csTerm.ws.readyState !== ws.OPEN ) { for (let i = 0; i < RETRY_TIMES; i++) {
// wait for valid ws connection if (this.csTerm.ws.readyState !== ws.OPEN) {
await delay (RETRY_INTERVAL); // wait for valid ws connection
} else { await delay(RETRY_INTERVAL);
for (const file of files.map( (a) => a.fsPath)) { } else {
try { for (const file of files.map((a) => a.fsPath)) {
if (await fsExtra.pathExists(file)) { try {
this.outputChannel.appendLine(`Uploading file ${file} to cloud shell`); if (await fsExtra.pathExists(file)) {
await azFilePush(this.csTerm.storageAccountName, this.outputChannel.appendLine(`Uploading file ${file} to cloud shell`);
this.csTerm.storageAccountKey, await azFilePush(this.csTerm.storageAccountName,
this.csTerm.fileShareName, file); this.csTerm.storageAccountKey,
} this.csTerm.fileShareName, file);
} catch (err) {
this.outputChannel.appendLine(err);
} }
} catch (err) {
this.outputChannel.appendLine(err);
} }
}
if (syncAllFiles) {
vscode.window.showInformationMessage( vscode.window.showInformationMessage(
"Uploaded all the text files in the current workspace to CloudShell"); "Synced all matched files in the current workspace to CloudShell");
break;
} }
break;
} }
} else {
const message = "Do you want to open CloudShell?";
const ok: MessageItem = { title : "Yes" };
const cancel: MessageItem = { title : "No", isCloseAffordance: true };
vscode.window.showWarningMessage(message, ok, cancel).then( (response) => {
if ( response === ok ) {
this.startCloudShell().then((terminal) => {
this.csTerm.terminal = terminal[0];
this.csTerm.ws = terminal[1];
this.csTerm.storageAccountName = terminal[2];
this.csTerm.storageAccountKey = terminal[3];
this.csTerm.fileShareName = terminal[4];
this.csTerm.ResourceGroup = terminal[5];
this.outputChannel.appendLine(`Obtained cloudshell terminal, retrying push files.\n`);
this.pushFiles(files);
return;
});
} else {
console.log("Push to cloud shell cancelled by user.");
}
});
// TODO THIS LINE SEEMS UNNECESSARY console.log("Cloudshell terminal not opened when trying to transfer files");
} }
} }
@ -91,7 +72,29 @@ export class CloudShell extends BaseShell {
} }
public async deleteFiles(files: vscode.Uri[]) { public async deleteFiles(files: vscode.Uri[]) {
return; const RETRY_INTERVAL = 500;
const RETRY_TIMES = 3;
// Checking if the terminal has been created and user is logged in.
await this.checkInitTerm();
for (let i = 0; i < RETRY_TIMES; i++) {
if (this.csTerm.ws.readyState !== ws.OPEN) {
// wait for valid ws connection
await delay(RETRY_INTERVAL);
} else {
for (const file of files.map((a) => a.fsPath)) {
try {
this.outputChannel.appendLine(`Deleting file ${file} from cloud shell`);
await azFileDelete(this.csTerm.storageAccountName,
this.csTerm.storageAccountKey,
this.csTerm.fileShareName, file);
} catch (err) {
this.outputChannel.appendLine(err);
}
}
}
}
} }
protected runTerraformInternal(TFCommand: string, WorkDir: string) { protected runTerraformInternal(TFCommand: string, WorkDir: string) {
@ -286,56 +289,42 @@ export class CloudShell extends BaseShell {
} }
protected async uploadTFFiles(TFFiles) {
console.log("Uploading files to CloudShell");
const RETRY_INTERVAL = 500;
const RETRY_TIMES = 30;
// Checking if the terminal has been created
if ( (this.csTerm.terminal != null) && (this.csTerm.storageAccountKey != null) ) {
for (let i = 0; i < RETRY_TIMES; i++ ) {
if (this.csTerm.ws.readyState !== ws.OPEN ) {
await delay (RETRY_INTERVAL);
} else {
for (const file of TFFiles.map( (a) => a.fsPath)) {
try {
if (fsExtra.existsSync(file)) {
console.log(`Uploading file ${file} to cloud shell`);
azFilePush(this.csTerm.storageAccountName, this.csTerm.storageAccountKey, this.csTerm.fileShareName, file);
}
} catch (err) {
console.log(err);
}
}
vscode.window.showInformationMessage("Uploaded all the text files in the current workspace to CloudShell");
break;
}
}
} else {
const message = "Do you want to open CloudShell?";
const ok: MessageItem = { title : "Yes" };
const cancel: MessageItem = { title : "No", isCloseAffordance: true };
vscode.window.showWarningMessage(message, ok, cancel).then( (response) => {
if ( response === ok ) {
this.startCloudShell().then((terminal) => {
this.csTerm.terminal = terminal[0];
this.csTerm.ws = terminal[1];
this.csTerm.storageAccountName = terminal[2];
this.csTerm.storageAccountKey = terminal[3];
this.csTerm.fileShareName = terminal[4];
this.csTerm.ResourceGroup = terminal[5];
console.log(`Obtained terminal and fileshare data\n`);
this.uploadTFFiles(TFFiles);
return;
});
}
});
console.log("Terminal not opened when trying to transfer files");
}
}
protected stop(interval: NodeJS.Timer): void { protected stop(interval: NodeJS.Timer): void {
clearInterval(interval); clearInterval(interval);
} }
private async delayWrap(ms: number) {
await delay(500);
}
private checkInitTerm(): Promise<void> {
return new Promise<void> ((resolve, reject) => {
if ((this.csTerm.terminal !== null) && (this.csTerm.storageAccountKey !== undefined)) {
resolve();
} else {
const message = "Do you want to open CloudShell?";
const ok: MessageItem = { title: "Yes" };
const cancel: MessageItem = { title: "No", isCloseAffordance: true };
vscode.window.showWarningMessage(message, ok, cancel).then((response) => {
if (response === ok) {
this.startCloudShell().then((terminal) => {
this.csTerm.terminal = terminal[0];
this.csTerm.ws = terminal[1];
this.csTerm.storageAccountName = terminal[2];
this.csTerm.storageAccountKey = terminal[3];
this.csTerm.fileShareName = terminal[4];
this.csTerm.ResourceGroup = terminal[5];
this.outputChannel.appendLine(`Obtained cloudshell terminal, retrying push files.\n`);
this.delayWrap(500);
resolve();
}).catch((error) => {
reject(error);
});
} else {
console.log("Push to cloud shell cancelled by user.");
}
});
}
});
}
} }

Просмотреть файл

@ -3,7 +3,7 @@
// Import the module and reference it with the alias vscode in your code below // Import the module and reference it with the alias vscode in your code below
import * as vscode from "vscode"; import * as vscode from "vscode";
import { commands, Disposable, extensions, GlobPattern, window } from "vscode"; import { commands, Disposable, extensions, GlobPattern, MessageItem, window } from "vscode";
import { AzureServiceClient, BaseResource } from "ms-rest-azure"; import { AzureServiceClient, BaseResource } from "ms-rest-azure";
import { join } from "path"; import { join } from "path";
@ -20,6 +20,9 @@ import { isDotInstalled } from "./utils/dotUtils";
let cs: CloudShell; let cs: CloudShell;
let is: IntegratedShell; let is: IntegratedShell;
let outputChannel: vscode.OutputChannel; let outputChannel: vscode.OutputChannel;
let fileWatcher: vscode.FileSystemWatcher;
let isFirstPush = true;
let _disposable: Disposable;
function getShell(): BaseShell { function getShell(): BaseShell {
let activeShell = null; let activeShell = null;
@ -35,6 +38,7 @@ function getShell(): BaseShell {
function init(): void { function init(): void {
cs = new CloudShell(outputChannel); cs = new CloudShell(outputChannel);
is = new IntegratedShell(outputChannel); is = new IntegratedShell(outputChannel);
initFileWatcher();
outputChannel.show(); outputChannel.show();
} }
@ -48,18 +52,13 @@ export function activate(ctx: vscode.ExtensionContext) {
outputChannel.appendLine("Loading extension"); outputChannel.appendLine("Loading extension");
const fileWatcher = vscode.workspace.createFileSystemWatcher(filesGlobSetting()); ctx.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration("tf-azure.files")) {
// TODO: Implement filewatcher handlers and config to automatically sync changes in workspace to cloudshell. // dispose of current file watcher and re-init
fileWatcher.onDidDelete((deletedUri) => { fileWatcher.dispose();
outputChannel.appendLine("Deleted: " + deletedUri.path); initFileWatcher();
}); }
fileWatcher.onDidCreate((createdUri) => { }));
outputChannel.appendLine("Created: " + createdUri.path);
});
fileWatcher.onDidChange((changedUri) => {
outputChannel.appendLine("Changed: " + changedUri.path);
});
ctx.subscriptions.push(vscode.commands.registerCommand("vscode-terraform-azure.init", () => { ctx.subscriptions.push(vscode.commands.registerCommand("vscode-terraform-azure.init", () => {
getShell().runTerraformCmd("terraform init", Constants.clouddrive); getShell().runTerraformCmd("terraform init", Constants.clouddrive);
@ -106,7 +105,7 @@ export function activate(ctx: vscode.ExtensionContext) {
// Create a function that will sync the files to Cloudshell // Create a function that will sync the files to Cloudshell
if (terminalSetToCloudshell()) { if (terminalSetToCloudshell()) {
vscode.workspace.findFiles(filesGlobSetting()).then((tfFiles) => { vscode.workspace.findFiles(filesGlobSetting()).then((tfFiles) => {
cs.pushFiles(tfFiles); cs.pushFiles(tfFiles, true);
}); });
} else { } else {
vscode.window.showErrorMessage("Push function only available when using cloudshell.") vscode.window.showErrorMessage("Push function only available when using cloudshell.")
@ -122,6 +121,10 @@ export function filesGlobSetting(): GlobPattern {
return vscode.workspace.getConfiguration("tf-azure").get("files") as GlobPattern; return vscode.workspace.getConfiguration("tf-azure").get("files") as GlobPattern;
} }
export function workspaceSyncEnabled(): boolean {
return vscode.workspace.getConfiguration("tf-azure").get("syncEnabled") as boolean;
}
export async function TFLogin(api: AzureAccount) { export async function TFLogin(api: AzureAccount) {
outputChannel.appendLine("Attempting - TFLogin"); outputChannel.appendLine("Attempting - TFLogin");
if (!(await api.waitForLogin())) { if (!(await api.waitForLogin())) {
@ -129,3 +132,47 @@ export async function TFLogin(api: AzureAccount) {
} }
outputChannel.appendLine("Succeeded - TFLogin"); outputChannel.appendLine("Succeeded - TFLogin");
} }
function initFileWatcher(): void {
// TODO: Implement filewatcher handlers and config to automatically sync changes in workspace to cloudshell.
const subscriptions: Disposable[] = [];
fileWatcher = vscode.workspace.createFileSystemWatcher(filesGlobSetting());
// enable file watcher to detect file changes.
fileWatcher.onDidDelete((deletedUri) => {
if (terminalSetToCloudshell() && workspaceSyncEnabled()) {
cs.deleteFiles([deletedUri]);
}
}, this, subscriptions);
fileWatcher.onDidCreate((createdUri) => {
pushHelper(createdUri);
}, this, subscriptions);
fileWatcher.onDidChange((changedUri) => {
pushHelper(changedUri);
}, this, subscriptions);
this._disposable = Disposable.from(...subscriptions);
}
function pushHelper(uri: vscode.Uri) {
if (terminalSetToCloudshell() && workspaceSyncEnabled()) {
if (isFirstPush) {
// do initial sync of workspace before enabling file watcher.
vscode.workspace.findFiles(filesGlobSetting()).then((tfFiles) => {
cs.pushFiles(tfFiles, false);
});
isFirstPush = false;
return;
}
cs.pushFiles([uri], false);
}
}
export function deactivate(): void {
this._disposable.dispose();
if (fileWatcher) {
fileWatcher.dispose();
}
}