item);
+ }
+ }),
+ input.onDidChangeSelection((items) => resolve(items[0])),
+ input.onDidHide(() => {
+ (async () => {
+ reject(
+ shouldResume && (await shouldResume())
+ ? InputFlowAction.resume
+ : InputFlowAction.cancel
+ );
+ })().catch(reject);
+ })
+ );
+ if (this.current) {
+ this.current.dispose();
+ }
+ this.current = input;
+ this.current.show();
+ });
+ } finally {
+ disposables.forEach((d) => d.dispose());
+ }
+ }
+
+ async showInputBox({
+ title,
+ step,
+ totalSteps,
+ value,
+ prompt,
+ placeholder,
+ buttons,
+ validate,
+ }: P) {
+ const disposables: Disposable[] = [];
+ try {
+ return await new Promise<
+ string | (P extends { buttons: (infer I)[] } ? I : never)
+ >((resolve, reject) => {
+ const input = window.createInputBox();
+ input.title = title;
+ input.step = step;
+ input.totalSteps = totalSteps;
+ input.value = value || "";
+ input.prompt = prompt || "";
+ input.placeholder = placeholder || "";
+ (input.ignoreFocusOut = true),
+ (input.buttons = [
+ ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
+ ...(buttons || []),
+ ]);
+
+ let passed = false;
+
+ disposables.push(
+ input.onDidTriggerButton((item) => {
+ if (item === QuickInputButtons.Back) {
+ reject(InputFlowAction.back);
+ } else {
+ resolve(item);
+ }
+ }),
+ input.onDidAccept(async () => {
+ if (!passed) {
+ return;
+ }
+
+ const value = input.value;
+ input.enabled = false;
+ input.busy = true;
+ if (!(await validate(value))) {
+ resolve(value);
+ }
+ input.enabled = true;
+ input.busy = false;
+ }),
+ input.onDidChangeValue(async (text) => {
+ validate(text)
+ .then(() => {
+ passed = true;
+ input.validationMessage = undefined;
+ })
+ .catch((err) => {
+ passed = false;
+ input.validationMessage = err;
+ });
+ }),
+ input.onDidHide(() => {
+ (async () => {
+ reject(
+ shouldResume && (await shouldResume())
+ ? InputFlowAction.resume
+ : InputFlowAction.cancel
+ );
+ })().catch(reject);
+ })
+ );
+ if (this.current) {
+ this.current.dispose();
+ }
+ this.current = input;
+ this.current.show();
+ });
+ } finally {
+ disposables.forEach((d) => d.dispose());
+ }
+ }
+}
diff --git a/src/commons/tencent/commands.ts b/src/commons/tencent/commands.ts
new file mode 100644
index 0000000..f9bf6b4
--- /dev/null
+++ b/src/commons/tencent/commands.ts
@@ -0,0 +1,13 @@
+import { commands } from "vscode";
+
+export function registerCommands() {
+// commands.registerCommand(command.TENCENT_LOGIN, user.login);
+
+// commands.registerCommand(command.TENCENT_LOGINOUT, user.loginOut);
+}
+
+export namespace command {
+ export const TENCENT_LOGIN = "toolkit.tencent.login";
+ /** 退出登录 */
+ export const TENCENT_LOGINOUT = "toolkit.tencent.loginout";
+}
diff --git a/src/commons/tencent/index.ts b/src/commons/tencent/index.ts
new file mode 100644
index 0000000..02a8386
--- /dev/null
+++ b/src/commons/tencent/index.ts
@@ -0,0 +1,27 @@
+import { commands } from "vscode";
+import { registerCommands, command as _command } from "./commands";
+
+// import "./api.localfile";
+// import './api';
+// import _user from "./user";
+import _tree from "./treeDataProvider";
+
+export async function registerTencent() {
+ registerCommands();
+
+ await initialization();
+}
+
+async function initialization() {
+// commands.executeCommand(
+// "setContext",
+// "tencent.login",
+// !!(await _user.getInfo())
+// );
+}
+
+export namespace tencent {
+// export import user = _user;
+ export import tree = _tree;
+ export import command = _command;
+}
diff --git a/src/commons/tencent/sdkApi.ts b/src/commons/tencent/sdkApi.ts
new file mode 100644
index 0000000..f69fd72
--- /dev/null
+++ b/src/commons/tencent/sdkApi.ts
@@ -0,0 +1,7 @@
+import * as tencentcloud from "tencentcloud-sdk-nodejs";
+
+export interface ITencentCloudAPI {
+ describeInstances(params?: any): Promise;
+ getConfig(params?: any): Promise;
+}
+
diff --git a/src/commons/tencent/treeDataProvider.ts b/src/commons/tencent/treeDataProvider.ts
new file mode 100644
index 0000000..26b4335
--- /dev/null
+++ b/src/commons/tencent/treeDataProvider.ts
@@ -0,0 +1,61 @@
+import { injectable } from "inversify";
+import {
+ Event,
+ EventEmitter,
+ TreeDataProvider as BaseTreeDataProvider,
+ TreeItem as BaseTreeItem,
+} from "vscode";
+import { container } from "../container";
+
+export namespace tree {
+ export const TencentTreeProvider = Symbol("TencentTreeProvider");
+
+ export function refreshTreeData() {
+ const treeDataProvider =
+ container.getAll(TencentTreeProvider);
+
+ treeDataProvider.map((item) => item.refresh());
+ }
+
+ // @ts-ignore
+ @injectable()
+ export abstract class TreeDataProvider
+ implements BaseTreeDataProvider
+ {
+ private _onDidChangeTreeData: EventEmitter =
+ new EventEmitter();
+
+ readonly onDidChangeTreeData: Event =
+ this._onDidChangeTreeData.event;
+
+ refresh(): void {
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ getTreeItem(element: TreeItem): TreeItem | Thenable {
+ return element;
+ }
+
+ async getChildren(element?: TreeItem | undefined): Promise {
+ // if (!element && (await user.getInfo())) {
+ if (!element) {
+ return [new TreeItem("暂无数据")];
+ }
+ return [];
+ }
+ }
+
+ export type TreeItemType = Omit;
+
+ export class TreeItem extends BaseTreeItem {
+ constructor(public readonly label: string, params: TreeItemType = {}) {
+ super(label);
+
+ Object.entries(params).forEach(([k, v]) => {
+ // @ts-ignore
+ this[k] = v;
+ });
+ }
+ }
+}
+export default tree;
diff --git a/src/commons/tencent/types/api.ts b/src/commons/tencent/types/api.ts
new file mode 100644
index 0000000..a743111
--- /dev/null
+++ b/src/commons/tencent/types/api.ts
@@ -0,0 +1,142 @@
+export namespace res {
+ export interface ProductSets {
+ ProductSet: ProductSet[];
+ }
+
+ export interface ProductSet {
+ product: string;
+ productDocCatID?: number;
+ productName: string;
+ aPIBrief?: string;
+ productCNDocCatID?: number;
+ productVersion?: string;
+ productCNDocIntroUrl?: string;
+ }
+ export interface ActionSet {
+ actionName: string;
+ product: string;
+ description: string;
+ isSubscribed?: boolean;
+ actionCNDocUrl: string;
+ productName: string;
+ productVersion: string;
+ action: string;
+ categoryName: string;
+ icon?: string;
+ }
+
+ export interface ActionSets {
+ ActionSet: ActionSet[];
+ }
+
+ export interface SDKInfo {
+ SDKInfo: KeyValue[];
+ }
+
+ export interface KeyValue {
+ Value: string;
+ Key: string;
+ }
+
+ export interface SdkDemos {
+ SdkDemoSet: SdkDemoSet[];
+ }
+
+ export interface SdkDemoSet {
+ DemoJsonCode: string;
+ TypeMapSet: TypeMapSet[];
+ DemoCode: string;
+ Language: string;
+ }
+
+ interface TypeMapSet {
+ Postfix: string;
+ OriginalType: string;
+ SdkType: string;
+ DestinationType: string;
+ }
+
+ interface SignDemoSet {
+ SignCode: string;
+ Language: string;
+ ParameterCode: string;
+ }
+
+ export interface Parameters {
+ ActionRegions: string[];
+ OutputParameterSet: any[];
+ ParameterSet: ParameterSet[];
+ RequestId: string;
+ ObjectSet: ObjectSet[];
+ }
+
+ interface ParameterSet {
+ IsArray: boolean;
+ IsPublic: boolean;
+ Name: string;
+ Default: string;
+ Required: boolean;
+ Visibility: number;
+ Type: string;
+ Example: string;
+ ValueAllowedNull: boolean;
+ Desc: string;
+ }
+
+ interface ObjectSet {
+ ObjectId: number;
+ Name: string;
+ Members: Member[];
+ Description: string;
+ }
+
+ interface Member {
+ IsArray: boolean;
+ IsPublic: boolean;
+ Name: string;
+ Default: string;
+ Required: boolean;
+ Visibility: number;
+ Type: string;
+ Example: string;
+ Desc: string;
+ }
+
+ export interface Action {
+ Document: string;
+ ActionRegions: string[];
+ ParameterSet: ParameterSet[];
+ RequestId: string;
+ ObjectSet: ObjectSet[];
+ }
+}
+export namespace req {
+ interface BaseParmeter {
+ Version: string;
+ ProductName: string;
+ }
+ export interface ProductParmeter extends BaseParmeter {
+ ProductVersion: string;
+ }
+ export interface SDKInfoParmeter extends BaseParmeter {
+ SDKType: string;
+ }
+ export interface Parmeter extends ProductParmeter {
+ ProductAction: string;
+ }
+ export interface SdkParmeter extends BaseParmeter {
+ SDKType: string;
+ }
+}
+
+export interface IApiDoc {
+ describeAllProducts(): Promise;
+ describeProductActions(
+ parame: Partial
+ ): Promise;
+ describeAllActions(): Promise;
+ describeSdkDemos(parame: req.Parmeter): Promise;
+ describeSDKInfo(parame: req.SDKInfoParmeter): Promise;
+ // describeAction(): Promise;
+ // describeParameters(parame?: req.Parmeter): Promise;
+}
diff --git a/src/commons/tencent/types/index.ts b/src/commons/tencent/types/index.ts
new file mode 100644
index 0000000..308f5ae
--- /dev/null
+++ b/src/commons/tencent/types/index.ts
@@ -0,0 +1 @@
+export * from './api';
\ No newline at end of file
diff --git a/src/connectivity/client.ts b/src/connectivity/client.ts
new file mode 100644
index 0000000..8dbdccb
--- /dev/null
+++ b/src/connectivity/client.ts
@@ -0,0 +1,64 @@
+"use strict";
+
+import * as vscode from "vscode";
+// import * as tencentcloud from "tencentcloud-sdk-nodejs-cvm";
+import { Client as CvmClient } from "tencentcloud-sdk-nodejs-cvm/tencentcloud/services/cvm/v20170312/cvm_client";
+import { Client as TkeClient } from "tencentcloud-sdk-nodejs-tke/tencentcloud/services/tke/v20180525/tke_client";
+
+const tkeClient = TkeClient;
+const cvmClient = CvmClient;
+
+export async function getTkeClient(): Promise {
+ const secretId = process.env.TENCENTCLOUD_SECRET_ID;
+ const secretKey = process.env.TENCENTCLOUD_SECRET_KEY;
+
+ if (secretId === undefined || secretKey === undefined || secretId === null || secretKey === null) {
+ vscode.window.showErrorMessage("Cannot find TENCENTCLOUD_SECRET_ID and TENCENTCLOUD_SECRET_KEY, please set them first!");
+ return null;
+ }
+
+ return new tkeClient({
+ credential: {
+ secretId: process.env.TENCENTCLOUD_SECRET_ID,
+ secretKey: process.env.TENCENTCLOUD_SECRET_KEY,
+ },
+ region: (process.env.TENCENTCLOUD_REGION === undefined) ?
+ "ap-guangzhou" : process.env.TENCENTCLOUD_REGION,
+ profile: {
+ signMethod: "TC3-HMAC-SHA256", // 签名方法
+ httpProfile: {
+ reqMethod: "POST", // 请求方法
+ reqTimeout: 30, // 请求超时时间,默认60s
+ },
+ },
+ });
+}
+
+export async function getCvmClient(region?: string): Promise {
+ const secretId = process.env.TENCENTCLOUD_SECRET_ID;
+ const secretKey = process.env.TENCENTCLOUD_SECRET_KEY;
+
+ if (secretId === undefined || secretKey === undefined || secretId === null || secretKey === null) {
+ vscode.window.showErrorMessage("Cannot find TENCENTCLOUD_SECRET_ID and TENCENTCLOUD_SECRET_KEY, please set them first!");
+ return null;
+ }
+
+ return new CvmClient({
+ credential: {
+ secretId: secretId,
+ secretKey: secretKey,
+ },
+ // 产品地域
+ region: (process.env.TENCENTCLOUD_REGION === undefined) ?
+ "ap-guangzhou" : process.env.TENCENTCLOUD_REGION,
+ // 可选配置实例
+ profile: {
+ // signMethod: "TC3-HMAC-SHA256", // 签名方法
+ httpProfile: {
+ reqMethod: "POST", // 请求方法
+ // reqTimeout: 60, // 请求超时时间,默认60s
+ endpoint: "cvm.tencentcloudapi.com",
+ },
+ },
+ })
+}
diff --git a/src/extension.ts b/src/extension.ts
index b58acad..9f7948f 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,101 +1,126 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
-import { TerraformCommand } from './common';
+import * as settingUtils from "./utils/settingUtils";
+import { init } from "vscode-nls-i18n";
+import { TerraformCommand } from "./commons/commands";
import { terraformShellManager } from "./terraformShellManager";
-import * as settingUtils from './utils/settingUtils';
import { DialogOption } from "./utils/uiUtils";
import { TerraformCompletionProvider } from './autocomplete/TerraformCompletionProvider';
import { TerraformDefinitionProvider } from './autocomplete/TerraformDefinitionProvider';
+import { registerCommon } from './commons';
+import { registerView } from './views';
+import { TerraformRunner } from './utils/terraformRunner';
+import { TerraformerRunner } from './utils/terraformerRunner';
const TF_MODE: vscode.DocumentFilter = { language: 'terraform', scheme: 'file' };
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export async function activate(context: vscode.ExtensionContext) {
- console.log('Congratulations, your extension "TencentCloud Terraform" is now active!');
+ console.log('Congratulations, your extension "TencentCloud Terraform" is now active!');
- await settingUtils.checkTerraformInstalled();
- await settingUtils.checkTCCLIInstalled();
+ await TerraformRunner.getInstance().checkInstalled();
+ await TerraformerRunner.getInstance().checkInstalled();
- let disposableLogin = vscode.commands.registerCommand('tcTerraform.login', async () => {
- // to-do
- // wait for cloudshell and tccli implement ready
- let accessKey = settingUtils.getSecretIdFromUI()
- let secretKey = settingUtils.getSecretKeyFromUI()
+ let disposableLogin = vscode.commands.registerCommand('tcTerraform.login', async () => {
+ // to-do
+ // wait for cloudshell and tccli implement ready
+ let accessKey = settingUtils.getSecretIdFromUI();
+ let secretKey = settingUtils.getSecretKeyFromUI();
- // process.env.TENCENTCLOUD_SECRET_ID=accessKey;
- // process.env.TENCENTCLOUD_SECRET_KEY=secretKey;
+ // process.env.TENCENTCLOUD_SECRET_ID=accessKey;
+ // process.env.TENCENTCLOUD_SECRET_KEY=secretKey;
- // console.log("TENCENTCLOUD_SECRET_ID:", process.env.TENCENTCLOUD_SECRET_ID);
- // console.log("TENCENTCLOUD_SECRET_KEY:", process.env.TENCENTCLOUD_SECRET_KEY);
+ // console.log("TENCENTCLOUD_SECRET_ID:", process.env.TENCENTCLOUD_SECRET_ID);
+ // console.log("TENCENTCLOUD_SECRET_KEY:", process.env.TENCENTCLOUD_SECRET_KEY);
- terraformShellManager.getShell().runNormalCmd("export TENCENTCLOUD_SECRET_ID=" + accessKey);
- terraformShellManager.getShell().runNormalCmd("export TENCENTCLOUD_SECRET_KEY=" + secretKey);
- });
+ terraformShellManager.getShell().runNormalCmd("export TENCENTCLOUD_SECRET_ID=" + accessKey);
+ terraformShellManager.getShell().runNormalCmd("export TENCENTCLOUD_SECRET_KEY=" + secretKey);
+ });
- context.subscriptions.push(disposableLogin);
+ context.subscriptions.push(disposableLogin);
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.init', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Init);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.init', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Init);
+ }));
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.plan', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Plan);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.plan', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Plan);
+ }));
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.apply', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Apply);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.apply', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Apply);
+ }));
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.import', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Import);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.import', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Import);
+ }));
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.validate', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Validate);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.validate', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Validate);
+ }));
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.refresh', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Refresh);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.refresh', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Refresh);
+ }));
- context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.destroy', () => {
- terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Destroy);
- }));
+ context.subscriptions.push(vscode.commands.registerCommand('tcTerraform.destroy', () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Destroy);
+ }));
- let disposableGraph = vscode.commands.registerCommand('tcTerraform.visualize', async () => {
- if (settingUtils.isTerminalSetToCloudShell()) {
- const choice: vscode.MessageItem = await vscode.window.showInformationMessage(
- "Visualization only works locally. Would you like to run it in the integrated terminal?",
- DialogOption.ok,
- DialogOption.cancel,
- );
- if (choice === DialogOption.cancel) {
- return;
- }
- }
- await terraformShellManager.getIntegratedShell().visualize();
- });
+ let disposableGraph = vscode.commands.registerCommand('tcTerraform.visualize', async () => {
+ if (settingUtils.isTerminalSetToCloudShell()) {
+ const choice: vscode.MessageItem = await vscode.window.showInformationMessage(
+ "Visualization only works locally. Would you like to run it in the integrated terminal?",
+ DialogOption.ok,
+ DialogOption.cancel,
+ );
+ if (choice === DialogOption.cancel) {
+ return;
+ }
+ }
+ await terraformShellManager.getIntegratedShell().visualize();
+ });
- context.subscriptions.push(disposableGraph);
+ context.subscriptions.push(disposableGraph);
- let disposableTest = vscode.commands.registerCommand('tcTerraform.test', async () => {
- // to-do
- });
+ let disposableTest = vscode.commands.registerCommand('tcTerraform.test', async () => {
+ // to-do
+ });
- context.subscriptions.push(disposableTest);
+ context.subscriptions.push(disposableTest);
- let disposablePush = vscode.commands.registerCommand('tcTerraform.push', async () => {
- // to-do
- // wait for cloudshell implement ready
- });
+ let disposablePush = vscode.commands.registerCommand('tcTerraform.push', async () => {
+ // to-do
+ // wait for cloudshell implement ready
+ });
- context.subscriptions.push(disposablePush);
+ context.subscriptions.push(disposablePush);
+ // terraformer
+ let disposableTferImport = vscode.commands.registerCommand('tcTerraformer.import', async () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Destroy);
+ });
+
+ context.subscriptions.push(disposableTferImport);
+
+ let disposableTferPlan = vscode.commands.registerCommand('tcTerraformer.plan', async () => {
+ terraformShellManager.getShell().runTerraformCmd(TerraformCommand.Destroy);
+ });
+
+ context.subscriptions.push(disposableTferPlan);
+
+ // auto-complete
console.log('activate the auto complete(snippets and lint) feature');
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(TF_MODE, new TerraformCompletionProvider(), '.'));
context.subscriptions.push(vscode.languages.registerDefinitionProvider(TF_MODE, new TerraformDefinitionProvider()));
+
+ // import
+ console.log('activate the import feature');
+ init(context.extensionPath);
+ registerCommon();
+ registerView();
}
// This method is called when your extension is deactivated
diff --git a/src/import/cvm.ts b/src/import/cvm.ts
new file mode 100644
index 0000000..f5d09bc
--- /dev/null
+++ b/src/import/cvm.ts
@@ -0,0 +1,58 @@
+"use strict";
+
+import * as vscode from "vscode";
+import * as client from "../connectivity/client";
+import { Instance } from "tencentcloud-sdk-nodejs-cvm/tencentcloud/services/cvm/v20170312/cvm_models";
+import { ITencentCloudAPI } from "../commons/tencent/sdkApi";
+import { error } from "console";
+
+export class CvmService implements ITencentCloudAPI {
+ async getConfig(params?: any): Promise {
+ return {
+ 'product': 'cvm',
+ 'resource': {
+ 'name': "tencentcloud_instance",
+ //'xxx': "yyy"
+ },
+ 'import': {
+ 'file': 'cvm.tf'
+ }
+ };
+ }
+
+ async describeInstances(params?: any): Promise {
+ const res = await (await client.getCvmClient()).DescribeInstances({
+ // find all instances
+ }).then(
+ (result) => {
+ // console.debug('[DEBUG]--------------------------------result:', result);
+ if (result.TotalCount === 0) {
+ throw new Error('[Warn] DescribeInstances result.TotalCount is 0.');
+ }
+ return result.InstanceSet;
+ },
+ (err) => {
+ console.error('[Error] DescribeInstances got a error from SDK.', err.message);
+ return err;
+ }
+ );
+
+ return res;
+ }
+}
+
+// export async function describeInstances(params:any): Promise {
+// const res = await (await client.getCvmClient()).DescribeInstances({}).then(result => {
+// console.warn('--------------------------------result:', result);
+// if (result.TotalCount === 0) {
+// throw new Error('[Warn] DescribeInstances result.TotalCount is 0.');
+// }
+
+// return result.InstanceSet;
+// }).catch((error) => {
+// console.error(error);
+// return error;
+// });
+
+// return res;
+// }
diff --git a/src/import/mysql.ts b/src/import/mysql.ts
new file mode 100644
index 0000000..6a773c5
--- /dev/null
+++ b/src/import/mysql.ts
@@ -0,0 +1,11 @@
+import { ITencentCloudAPI } from "@/commons/tencent/sdkApi";
+
+export class MysqlService implements ITencentCloudAPI {
+ describeInstances(params?: any): Promise {
+ throw new Error("Method not implemented.");
+ }
+ getConfig(params?: any): Promise {
+ throw new Error("Method not implemented.");
+ }
+
+}
\ No newline at end of file
diff --git a/src/import/tke.ts b/src/import/tke.ts
new file mode 100644
index 0000000..b49eec4
--- /dev/null
+++ b/src/import/tke.ts
@@ -0,0 +1,10 @@
+import { ITencentCloudAPI } from "@/commons/tencent/sdkApi";
+
+export class TkeService implements ITencentCloudAPI {
+ describeInstances(params?: any): Promise {
+ throw new Error("Method not implemented.");
+ }
+ getConfig(params?: any): Promise {
+ throw new Error("Method not implemented.");
+ }
+}
\ No newline at end of file
diff --git a/src/integratedShell.ts b/src/integratedShell.ts
index bbea64f..a42bd85 100644
--- a/src/integratedShell.ts
+++ b/src/integratedShell.ts
@@ -13,13 +13,20 @@ import { commands, Uri, ViewColumn } from "vscode";
import * as TelemetryWrapper from "vscode-extension-telemetry-wrapper";
import { BaseShell } from "./baseShell";
import { Constants } from "./constants";
-// import { TestOption } from "./shared";
import { executeCommand } from "./utils/cpUtils";
-// import { isDockerInstalled, runCustomCommandInDocker, runE2EInDocker, runLintInDocker } from "./utils/dockerUtils";
import { drawGraph } from "./utils/dotUtils";
import { isDotInstalled } from "./utils/dotUtils";
-// import * as settingUtils from "./utils/settingUtils";
import { selectWorkspaceFolder } from "./utils/workspaceUtils";
+import { TerraformCommand } from "./commons/commands";
+import * as helper from "./utils/helper";
+import { command } from "./commons/tencent/commands";
+import { promisify } from "util";
+import { ChildProcess } from "child_process";
+import * as cp from "child_process";
+import { TerraformerRunner, CommandType, FlagType, FlagsMap, defaultProduct } from "./utils/terraformerRunner";
+import { values } from "lodash";
+
+// import stripAnsi from 'strip-ansi';
export class IntegratedShell extends BaseShell {
private static readonly GRAPH_FILE_NAME = "graph.png";
@@ -59,13 +66,81 @@ export class IntegratedShell extends BaseShell {
await commands.executeCommand("vscode.open", Uri.file(path.join(cwd, IntegratedShell.GRAPH_FILE_NAME)), ViewColumn.Two);
}
- public runTerraformCmd(tfCommand: string) {
+ public async import(params: any, file?: string): Promise {
+
+ const runner = TerraformerRunner.getInstance();// terraform or terraformer
+
+ await runner.checkInstalled();
+
+ const cwd: string = await selectWorkspaceFolder();
+ if (!cwd) {
+ TelemetryWrapper.sendError(Error("noWorkspaceSelected"));
+ return;
+ }
+
+ const preRet = await runner.preImport(cwd);
+ console.debug("[DEBUG]#### Executed pre-import. result:[%s]", preRet);
+
+ const resource = defaultProduct;
+ if (!defaultProduct.includes(params.product)) {
+ resource.push(params.product);
+ }
+
+ const cmd = CommandType.Import;
+ const flags: FlagsMap[] = [
+ {
+ flag: FlagType.Resources,
+ value: resource.join(",")
+ },
+ {
+ flag: FlagType.Filter,
+ value: [params.resource.type, params.resource.id].join("=")
+ },
+ {
+ flag: FlagType.Regions,
+ value: "ap-guangzhou"
+ },
+ {
+ flag: FlagType.Redirect,
+ value: "terraformer_default_result"
+ },
+ ];
+
+ const importRet = await runner.executeImport(cwd, "", cmd, flags);
+ console.debug("[DEBUG]#### Executed import command. result:[%s]", importRet);
+
+ // terraform state replace-provider registry.terraform.io/-/tencentcloud tencentcloudstack/tencentcloud
+ const args = "";
+ const postRet = await runner.postImport(cwd, args);
+
+ const content: string = await runner.executeShow(cwd);
+
+ const tfFile: string = importRet;
+
+ vscode.window.showInformationMessage(`The resource:[${params.resource.type}] has been imported successfully, generated tf file:[${tfFile}].`);
+
+ await commands.executeCommand("vscode.open", Uri.file(tfFile), ViewColumn.Active || ViewColumn.One);
+ }
+
+
+ public async runTerraformCmdWithoutTerminal(tfCommand: string, args?: string[]) {
+ const cmd = [tfCommand, ...(args || [])].join(' ');
+ const { stdout, stderr } = await promisify(cp.exec)(cmd);
+ return { stdout, stderr };
+ }
+
+ public async runTerraformCmd(tfCommand: string, args?: string[]) {
this.checkCreateTerminal();
this.terminal.show();
- this.terminal.sendText(tfCommand);
+
+ // const cmd= [tfCommand, args.values].join(' ');
+ let tmp: string[] = [tfCommand];
+ args.forEach((arg) => tmp.push(arg));
+ const cmd = tmp.join(' ');
+ this.terminal.sendText(cmd);
}
- public runNormalCmd(tfCommand: string, newLine=true) {
+ public async runNormalCmd(tfCommand: string, newLine = true) {
this.checkCreateTerminal();
this.terminal.show();
this.terminal.sendText(tfCommand, newLine);
@@ -86,6 +161,13 @@ export class IntegratedShell extends BaseShell {
}
}
+ private async deleteFile(cwd, file: string): Promise {
+ const filePath: string = path.join(cwd, file);
+ if (await fse.pathExists(filePath)) {
+ await fse.remove(filePath);
+ }
+ }
+
private checkCreateTerminal(): void {
if (!this.terminal) {
this.terminal = vscode.window.createTerminal(Constants.TerraformTerminalName);
diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts
index 7029e38..6671ba3 100644
--- a/src/test/suite/index.ts
+++ b/src/test/suite/index.ts
@@ -1,38 +1,40 @@
import * as path from 'path';
-import * as Mocha from 'mocha';
-import * as glob from 'glob';
+// import * as Mocha from 'mocha';
+import mocha from "mocha";
+// import * as glob from 'glob';
+import glob from 'glob';
export function run(): Promise {
- // Create the mocha test
- const mocha = new Mocha({
- ui: 'tdd',
- color: true
- });
+ // Create the mocha test
+ const mocha = new Mocha({
+ ui: 'tdd',
+ color: true
+ });
- const testsRoot = path.resolve(__dirname, '..');
+ const testsRoot = path.resolve(__dirname, '..');
- return new Promise((c, e) => {
- glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
- if (err) {
- return e(err);
- }
+ return new Promise((c, e) => {
+ glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
+ if (err) {
+ return e(err);
+ }
- // Add files to the test suite
- files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
+ // Add files to the test suite
+ files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
- try {
- // Run the mocha test
- mocha.run(failures => {
- if (failures > 0) {
- e(new Error(`${failures} tests failed.`));
- } else {
- c();
- }
- });
- } catch (err) {
- console.error(err);
- e(err);
- }
- });
- });
+ try {
+ // Run the mocha test
+ mocha.run(failures => {
+ if (failures > 0) {
+ e(new Error(`${failures} tests failed.`));
+ } else {
+ c();
+ }
+ });
+ } catch (err) {
+ console.error(err);
+ e(err);
+ }
+ });
+ });
}
diff --git a/src/utils/baseRunner.ts b/src/utils/baseRunner.ts
new file mode 100644
index 0000000..4dea3b0
--- /dev/null
+++ b/src/utils/baseRunner.ts
@@ -0,0 +1,50 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Tencent Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+"use strict";
+
+export abstract class BaseRunner {
+
+ public tfExecutor: any;
+
+ constructor() {
+ this.init();
+ }
+
+ public abstract init(): void;
+ /**
+ * execute this command to prepare the terraform import.
+ * @param cwd
+ * @returns
+ */
+ public abstract preImport(cwd: string, args?: any, path?: string): Promise;
+
+ /**
+ * execute this command to import the existing resource from tencentcloud
+ * @param cwd
+ * @param args
+ * @returns
+ */
+ public abstract executeImport(cwd: string, args?: string): Promise;
+
+ /**
+ * execute this command to handle post of the terraform import.
+ * @param cwd
+ * @param executor Choose who will execute this command? terraform or terraformer
+ * @returns
+ */
+ public abstract postImport(cwd: string, executor?:string, args?: string): Promise;
+
+ /**
+ * check binary whether ready or not
+ */
+ public abstract checkInstalled(): Promise;
+
+ /**
+ * execute this command to display/output the terraform state.
+ * @param cwd
+ * @returns
+ */
+ public abstract executeShow(cwd: string, args?: string): Promise;
+}
diff --git a/src/utils/cpUtils.ts b/src/utils/cpUtils.ts
index 5013250..752fe60 100644
--- a/src/utils/cpUtils.ts
+++ b/src/utils/cpUtils.ts
@@ -11,17 +11,28 @@ import { terraformChannel } from "../terraformChannel";
export async function executeCommand(command: string, args: string[], options: cp.SpawnOptions): Promise {
return new Promise((resolve: (res: string) => void, reject: (e: Error) => void): void => {
let result: string = "";
+ const stripAnsi = require('strip-ansi');
const childProc: cp.ChildProcess = cp.spawn(command, args, options);
- childProc.stdout.on("data", (data: string | Buffer) => {
- data = data.toString();
+ childProc.stdout.on("data", (raw: string | Buffer) => {
+ const data = stripAnsi(raw.toString());
+ console.debug("[DEBUG]#### executeCommand received data:[%s]", data);
+
result = result.concat(data);
terraformChannel.append(data);
});
- childProc.stderr.on("data", (data: string | Buffer) => terraformChannel.append(data.toString()));
+ childProc.stderr.on("data", (raw: string | Buffer) => {
+ const data = stripAnsi(raw.toString());
+ console.error("Error found in stderr.on: %s", data);
+ terraformChannel.append(data);
+ });
+
+ childProc.on("error", (err: any) => {
+ console.error("Error found in childProc.on error: %s", err);
+ // reject(err);
+ });
- childProc.on("error", reject);
childProc.on("close", (code: number) => {
if (code !== 0) {
reject(new Error(`Command "${command} ${args.toString()}" failed with exit code "${code}".`));
diff --git a/src/utils/helper.ts b/src/utils/helper.ts
new file mode 100644
index 0000000..2f7c258
--- /dev/null
+++ b/src/utils/helper.ts
@@ -0,0 +1,51 @@
+'use strict';
+
+import { Function } from "lodash";
+import { Func } from "mocha";
+
+export async function retryF(func: () => Promise<{ stdout: string, stderr: string }>): Promise {
+ const command = "terraform state rm tencentcloud_instance.foo";
+ const maxRetry = 3;
+ let retryCount = 1;
+
+ while (true) {
+ try {
+ const result = await func();
+ console.log(result.stdout);
+
+ // retry "Error acquiring the state lock"
+ if (/Error acquiring the state lock/.test(result.stderr)) {
+ if (retryCount < maxRetry) {
+ retryCount++;
+ console.log(`Retrying (${retryCount}/${maxRetry})...`);
+ await new Promise(resolve => setTimeout(resolve, 500)); // wait for 5 seconds before retrying
+ continue;
+ } else {
+ console.warn("[WARN]#### Max retry count reached.");
+ return;
+ }
+ }
+ if (/Invalid target address/.test(result.stderr) || /read-only file system/.test(result.stderr)) {
+ console.log(`Invalid target address is accepted: ${result.stderr}`);
+ return;
+ }
+ } catch (error) {
+ if (/Error acquiring the state lock/.test(error.message)) {
+ if (retryCount < maxRetry) {
+ retryCount++;
+ console.log(`An error occurred executing "${command}": ${error.message}. Retrying...`);
+ await new Promise(resolve => setTimeout(resolve, 500)); // wait for 5 seconds before retrying
+ continue;
+ } else {
+ console.warn("[WARN]#### Max retry count reached.");
+ return;
+ }
+ } else if (/Invalid target address/.test(error.message) || /read-only file system/.test(error.message)) {
+ return;
+ } else {
+ console.error(error);
+ return;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/utils/icons.ts b/src/utils/icons.ts
new file mode 100644
index 0000000..ddeeaf3
--- /dev/null
+++ b/src/utils/icons.ts
@@ -0,0 +1,10 @@
+// @ts-ignore
+import { ThemeIcon } from 'vscode';
+
+export class Icons {
+ public static getIcon(id:string): ThemeIcon {
+ // @ts-ignore
+ return Icons.getIcon(id);
+ }
+}
+
diff --git a/src/utils/settingUtils.ts b/src/utils/settingUtils.ts
index f12a787..5aff7b1 100644
--- a/src/utils/settingUtils.ts
+++ b/src/utils/settingUtils.ts
@@ -53,30 +53,7 @@ export async function checkTCCLIInstalled(): Promise {
uiUtils.openUrlHintOrNotShowAgain("TCCLI is not installed, please install it before use extension.",
"https://www.tencentcloud.com/document/product/1013/33464",
() => {
- setCheckTerraformCmd(false);
- });
- }
-}
-
-export function getCheckTerraformCmd(): boolean {
- return vscode.workspace.getConfiguration().get("tcTerraform.checkTerraformCmd");
-}
-
-export function setCheckTerraformCmd(checked: boolean): void {
- vscode.workspace.getConfiguration().update("tcTerraform.checkTerraformCmd", checked);
-}
-
-export async function checkTerraformInstalled(): Promise {
- if (isTerminalSetToCloudShell() || !getCheckTerraformCmd()) {
- return;
- }
- try {
- await cpUtils.executeCommand("terraform", ["-v"], { shell: true });
- } catch (error) {
- uiUtils.openUrlHintOrNotShowAgain("Terraform is not installed, please make sure Terraform is in the PATH environment variable.",
- "https://cloud.tencent.com/document/product/1653/82868",
- () => {
- setCheckTerraformCmd(false);
+ // setCheckTerraformCmd(false);
});
}
}
diff --git a/src/utils/terraformRunner.ts b/src/utils/terraformRunner.ts
new file mode 100644
index 0000000..5ad5388
--- /dev/null
+++ b/src/utils/terraformRunner.ts
@@ -0,0 +1,123 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Tencent Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+"use strict";
+
+import * as path from "path";
+import * as fse from "fs-extra";
+import * as vscode from "vscode";
+import { executeCommand } from "./cpUtils";
+import { BaseRunner } from "./baseRunner";
+import { TerraformCommand } from "../commons/commands";
+import { terraformShellManager } from "../terraformShellManager";
+import * as settingUtils from "./settingUtils";
+import { openUrlHintOrNotShowAgain } from "./uiUtils";
+
+
+export class TerraformRunner extends BaseRunner {
+ private constructor() {
+ super();
+ }
+
+ private static ins: BaseRunner;
+
+ public static getInstance(): BaseRunner {
+ if (!TerraformRunner.ins) {
+ TerraformRunner.ins = new TerraformRunner();
+ }
+ return TerraformRunner.ins;
+ }
+
+ public init(): void {
+ // throw new Error("Method not implemented.");
+ }
+
+ public async executeShow(cwd: string, args?: string): Promise {
+ return await executeCommand(
+ "terraform",
+ ["show"],
+ {
+ shell: true,
+ cwd,
+ }
+ );
+ }
+
+ public async executeImport(cwd: string, args?: string): Promise {
+ return await executeCommand(
+ "terraform",
+ [args],
+ {
+ shell: true,
+ cwd,
+ }
+ );
+ }
+
+ public async preImport(cwd: string, params: any, file: string): Promise<{ importArgs: string, tfFile: string }> {
+ const fileName = (file === undefined) ? params.resource.type + '.tf' : file;
+
+ const defaultContents = `resource "${params.resource.type}" "${params.resource.name}" {}`;
+ const resAddress = `${params.resource.type}.${params.resource.name}`;
+
+ const tfFile: string = path.join(cwd, fileName);
+
+ // reset file
+ await this.resetFileContent(tfFile, defaultContents);
+ // reset state
+ await this.resetTFState(resAddress);
+
+ const importArgs = ['import ', params.resource.type, '.', params.resource.name, ' ', params.resource.id].join('');
+ console.debug("[DEBUG]#### import cmd: args=[%s], defaultContents=[%s]", importArgs, defaultContents);
+ return { importArgs, tfFile };
+ }
+
+
+ private async resetFileContent(tfFile: string, defaultContents: string) {
+ if (!fse.existsSync(tfFile)) {
+ fse.writeFileSync(tfFile, defaultContents);
+ } else {
+ await fse.writeFile(tfFile, defaultContents);
+ }
+ }
+
+ private async resetTFState(resAddress: string) {
+ await terraformShellManager.getIntegratedShell().runTerraformCmd(TerraformCommand.State, ['rm', '-lock=false', resAddress]);
+ }
+
+ /**
+ * todo: implement
+ * @param cwd
+ * @param args
+ */
+ public async postImport(cwd: string, args?: string): Promise {
+ console.debug("[DEBUG]#### terraform postImport TODO.");
+ }
+
+ public async checkInstalled(): Promise {
+ if (settingUtils.isTerminalSetToCloudShell() || !getCheckTerraformCmd()) {
+ return;
+ }
+ try {
+ await executeCommand("terraform", ["-v"], { shell: true });
+ } catch (error) {
+ openUrlHintOrNotShowAgain("Terraform is not installed, please make sure Terraform is in the PATH environment variable.",
+ "https://aka.ms/azTerraform-requirement",
+ () => {
+ setCheckTerraformCmd(false);
+ });
+ }
+ return;
+ }
+}
+
+export function getCheckTerraformCmd(): boolean {
+ return vscode.workspace.getConfiguration().get("tcTerraform.checkTerraformCmd");
+}
+
+export function setCheckTerraformCmd(checked: boolean): void {
+ vscode.workspace.getConfiguration().update("tcTerraform.checkTerraformCmd", checked);
+}
+
+
diff --git a/src/utils/terraformerRunner.ts b/src/utils/terraformerRunner.ts
new file mode 100644
index 0000000..99f2c7f
--- /dev/null
+++ b/src/utils/terraformerRunner.ts
@@ -0,0 +1,143 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Tencent Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+"use strict";
+import * as vscode from "vscode";
+import * as settingUtils from "./settingUtils";
+import { executeCommand } from "./cpUtils";
+import { BaseRunner } from "./baseRunner";
+import { openUrlHintOrNotShowAgain } from "./uiUtils";
+
+export const defaultProduct = ["vpc", "subnet", "security_group"];
+
+export enum CommandType {
+ Import = "import tencentcloud",
+ Plan = "plan tencentcloud",
+ Version = "version"
+}
+
+export enum FlagType {
+ Resources = "-r",
+ Filter = "-f",
+ Bucket = "-b",
+ Excludes = "-x",
+ Output = "-O",
+ Regions = "--regions",
+ State = "-s",
+ RetryNum = "-n",
+ RetrySleep = "-m",
+ Redirect = ">"
+}
+
+export interface FlagsMap {
+ flag: FlagType;
+ value: string;
+}
+
+export class TerraformerRunner extends BaseRunner {
+ static defaultProduct: any;
+ private constructor() {
+ super();
+ }
+
+ private static ins: BaseRunner;
+
+ public static getInstance(): TerraformerRunner {
+ if (!TerraformerRunner.ins) {
+ TerraformerRunner.ins = new TerraformerRunner();
+ }
+ return TerraformerRunner.ins;
+ }
+
+ public init(): void {
+ // throw new Error("Method not implemented.");
+ }
+
+
+ public async preImport(cwd: string, args?: any, path?: string): Promise {
+ console.debug("[DEBUG]#### TerraformerRunner.preImport begin, cwd:[%s], args:[%s], path:[%s]", cwd, args, path);
+ return await executeCommand(
+ "terraform",
+ ["init", "-upgrade"],
+ {
+ shell: true,
+ cwd,
+ },
+ );
+ }
+
+ public async executeImport(cwd: string, args?: string, cmd?: CommandType, flags?: FlagsMap[]): Promise {
+ console.debug("[DEBUG]#### TerraformerRunner.executeImport begin, cwd:[%s], args:[%s], cmd:[%s], flags:[%s]", cwd, args, cmd.toString(), flags.toString());
+ const exeArgs: string[] = [];
+ if (args) {
+ exeArgs.push(args);
+ }
+
+ if (cmd !== null) {
+ exeArgs.push(cmd.toString());
+ }
+
+ if (flags) {
+ flags.forEach((vv) => {
+ exeArgs.push(vv.flag.toString(), vv.value);
+ });
+ }
+
+ const opExeArgs: string = exeArgs.join(" ");
+
+ console.debug("[DEBUG]#### import exeArgs:[%s]", opExeArgs);
+
+ return await executeCommand(
+ "terraformer",
+ exeArgs,
+ {
+ shell: true,
+ cwd,
+ }
+ );
+ }
+
+ public async postImport(cwd: string, args?: string): Promise {
+ console.debug("[DEBUG]#### TerraformerRunner.postImport begin, cwd:[%s], args:[%s]", cwd, args);
+ const exeArgs = args.split(",");
+
+ return await executeCommand(
+ "terraformer",
+ exeArgs,
+ {
+ shell: true,
+ cwd,
+ }
+ );
+ }
+
+ public async executeShow(cwd: string, args?: string): Promise {
+ console.debug("[DEBUG]#### TerraformerRunner not need this step, skip it.");
+ return "";
+ }
+
+ public async checkInstalled(): Promise {
+ if (settingUtils.isTerminalSetToCloudShell() || !getCheckTerraformerCmd()) {
+ return;
+ }
+ try {
+ await executeCommand("terraformer", ["-v"], { shell: true });
+ } catch (error) {
+ openUrlHintOrNotShowAgain("Terraformer is not installed, please make sure Terraformer is in the PATH environment variable.",
+ "https://github.com/GoogleCloudPlatform/terraformer",
+ () => {
+ setCheckTerraformerCmd(false);
+ });
+ }
+ return;
+ }
+}
+
+export function getCheckTerraformerCmd(): boolean {
+ return vscode.workspace.getConfiguration().get("tcTerraform.checkTerraformerCmd");
+}
+
+export function setCheckTerraformerCmd(checked: boolean): void {
+ vscode.workspace.getConfiguration().update("tcTerraform.checkTerraformerCmd", checked);
+}
diff --git a/src/utils/uiUtils.ts b/src/utils/uiUtils.ts
index 9818578..f5441cb 100644
--- a/src/utils/uiUtils.ts
+++ b/src/utils/uiUtils.ts
@@ -5,7 +5,8 @@
"use strict";
-import * as opn from "opn";
+// import * as opn from "opn";
+import opn from "opn";
import * as vscode from "vscode";
import { terraformChannel } from "../terraformChannel";
diff --git a/src/views/help/helpExplorer.ts b/src/views/help/helpExplorer.ts
new file mode 100644
index 0000000..fa907f2
--- /dev/null
+++ b/src/views/help/helpExplorer.ts
@@ -0,0 +1,59 @@
+import { ThemeIcon } from "vscode";
+import { localize } from "vscode-nls-i18n";
+
+import { container, tencent, cmds} from "../../commons";
+import { Icons } from "../../utils/icons";
+
+const { tree } = tencent;
+
+const { TreeDataProvider, TreeItem } = tree;
+
+export class HelpProvider extends TreeDataProvider {
+ async getChildren(element?: tencent.tree.TreeItem | undefined): Promise {
+ if (!element) {
+ const elements = [
+ new TreeItem(localize("TcTerraform.view.help.explorer.provider"), {
+ // iconPath: Icons.getIcon("tools"),
+ command: {
+ command: cmds.openURL,
+ title: "",
+ arguments: ["https://registry.terraform.io/providers/tencentcloudstack/tencentcloud/latest"],
+ },
+ }),
+ new TreeItem(localize("TcTerraform.view.help.explorer.doc"), {
+ // iconPath: Icons.getIcon("book"),
+ command: {
+ command: cmds.openURL,
+ title: "",
+ arguments: ["https://cloud.tencent.com/product/tiat"],
+ },
+ }),
+ new TreeItem(localize("TcTerraform.view.help.explorer.repo"), {
+ // iconPath: Icons.getIcon("github"),
+ command: {
+ command: cmds.openURL,
+ title: "",
+ arguments: ["https://github.com/tencentcloudstack/terraform-provider-tencentcloud"],
+ },
+ }),
+ ];
+
+ // const info = await user.getInfo();
+ // if (info) {
+ // elements.push(
+ // new TreeItem(localize("tencent.loginout", info.uin), {
+ // iconPath: Icons.getIcon("account"),
+ // command: { command: tencent.command.TENCENT_LOGINOUT, title: "" },
+ // })
+ // );
+ // }
+
+ return elements;
+ }
+ return [];
+ }
+}
+
+container.bind(HelpProvider).toSelf().inSingletonScope();
+
+container.bind(tencent.tree.TencentTreeProvider).toService(HelpProvider);
diff --git a/src/views/help/index.ts b/src/views/help/index.ts
new file mode 100644
index 0000000..2d95c8c
--- /dev/null
+++ b/src/views/help/index.ts
@@ -0,0 +1,8 @@
+import { window } from "vscode";
+
+import { container } from "../../commons/container";
+import { HelpProvider } from "./helpExplorer";
+
+export function registerHelp() {
+ window.registerTreeDataProvider("tcTerraform.helpExplorer", container.get(HelpProvider));
+}
\ No newline at end of file
diff --git a/src/views/index.ts b/src/views/index.ts
new file mode 100644
index 0000000..5216556
--- /dev/null
+++ b/src/views/index.ts
@@ -0,0 +1,8 @@
+import { registerHelp } from "./help";
+import { registerResources } from "./resources";
+
+export function registerView() {
+ registerHelp();
+
+ registerResources();
+}
diff --git a/src/views/resources/index.ts b/src/views/resources/index.ts
new file mode 100644
index 0000000..5419113
--- /dev/null
+++ b/src/views/resources/index.ts
@@ -0,0 +1,10 @@
+import { window } from "vscode";
+
+import { container } from "../../commons/container";
+import { CvmResProvider, TkeResProvider } from "./resExplorer";
+
+export function registerResources() {
+ // window.registerTreeDataProvider("tcTerraform.resourcesExplorer", container.get(CvmResProvider));
+ window.registerTreeDataProvider("tcTerraform.resourcesExplorer.cvm", container.get(CvmResProvider));
+ // window.registerTreeDataProvider("tcTerraform.resourcesExplorer.tke", container.get(TkeResProvider));
+}
\ No newline at end of file
diff --git a/src/views/resources/resExplorer.ts b/src/views/resources/resExplorer.ts
new file mode 100644
index 0000000..e9a8c11
--- /dev/null
+++ b/src/views/resources/resExplorer.ts
@@ -0,0 +1,106 @@
+import { inject } from "inversify";
+import { ThemeIcon, TreeItem, TreeItemCollapsibleState, window } from "vscode";
+import { readFile } from "fs/promises";
+import { join } from "path";
+import * as vscode from "vscode";
+
+import { container, tencent, cmds } from "../../commons";
+import { res, IApiDoc } from "../../commons/tencent/types";
+import { Icons } from "../../utils/icons";
+import { CvmService } from "../../import/cvm";
+
+export const defaultResourceName = "foo";
+
+export class CvmResProvider extends tencent.tree.TreeDataProvider {
+ private icons: { [key: string]: any };
+
+ async getChildren(element?: tencent.tree.TreeItem | undefined): Promise {
+ if (!element) {
+ try {
+ // TODO: replace with specified resource type
+ const service = await new CvmService();
+ const instances = await service.describeInstances();
+ const config = await service.getConfig();
+
+ const items: tencent.tree.TreeItem[] = Array.isArray(instances)
+ ? instances.map(instance => ({
+ label: `${instance.InstanceName}(${instance.InstanceId})`,
+ id: instance.InstanceId,
+ // iconPath: Icons.getIcon("book"),
+ command: {
+ command: cmds.executeImport,
+ title: "",
+ arguments: [{
+ resource: {
+ type: config.resource.name,
+ name: defaultResourceName,
+ id: instance.InstanceId
+ },
+ product: config.product,
+ fileName: config.import.file,
+ this: config
+ }],
+ },
+ }))
+ : [];
+
+ return items;
+ } catch (error) {
+ console.error('[Error]#### getChildren got a error:[%s] from CvmService. stack:%s', error.message, error.stack);
+ return error;
+ }
+
+ } else {
+ if ("getChildren" in element) {
+ return (element as ParentItem).getChildren(element);
+ }
+ }
+
+ return [];
+ }
+
+ private async getFontIcons() {
+ return readFile(join(__dirname, "../package.json")).then((buff) => {
+ const { icons } = JSON.parse(buff.toString()).contributes;
+ this.icons = icons;
+ });
+ }
+
+ private async getIcon(name: string): Promise {
+ const iconName = `${name}-icon`;
+
+ return Icons.getIcon(this.icons[iconName] ? iconName : "default-icon");
+ }
+}
+
+export class TkeResProvider extends tencent.tree.TreeDataProvider {
+ private icons: { [key: string]: any };
+ // vscode.window.showInformationMessage(`TkeResProvider is called`);
+
+ async getChildren(element?: tencent.tree.TreeItem | undefined): Promise {
+ vscode.window.showInformationMessage(`TkeResProvider is called`);
+
+ return [];
+ }
+}
+
+class ResourcesItem extends tencent.tree.TreeItem {
+ constructor(private action: res.ActionSet, _iconPath: ThemeIcon) {
+ // action.icon = iconPath.id;
+ super(action.actionName, {
+ command: {
+ command: "OPEN_API_DOC",
+ arguments: [action],
+ title: "",
+ },
+ });
+ }
+}
+
+interface ParentItem {
+ getChildren(element: tencent.tree.TreeItem): Promise;
+}
+
+container.bind(CvmResProvider).toSelf().inSingletonScope();
+
+container.bind(tencent.tree.TencentTreeProvider).toService(CvmResProvider);
diff --git a/terraform.log b/terraform.log
deleted file mode 100644
index 7a82c1f..0000000
--- a/terraform.log
+++ /dev/null
@@ -1,34 +0,0 @@
-2023-03-02T17:31:56.349+0800 [INFO] Terraform version: 1.3.4
-2023-03-02T17:31:56.350+0800 [DEBUG] using github.com/hashicorp/go-tfe v1.9.0
-2023-03-02T17:31:56.350+0800 [DEBUG] using github.com/hashicorp/hcl/v2 v2.14.1
-2023-03-02T17:31:56.350+0800 [DEBUG] using github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2
-2023-03-02T17:31:56.350+0800 [DEBUG] using github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734
-2023-03-02T17:31:56.350+0800 [DEBUG] using github.com/zclconf/go-cty v1.12.0
-2023-03-02T17:31:56.350+0800 [INFO] Go runtime version: go1.19.3
-2023-03-02T17:31:56.350+0800 [INFO] CLI args: []string{"terraform", "init"}
-2023-03-02T17:31:56.350+0800 [TRACE] Stdout is a terminal of width 139
-2023-03-02T17:31:56.350+0800 [TRACE] Stderr is a terminal of width 139
-2023-03-02T17:31:56.350+0800 [TRACE] Stdin is a terminal
-2023-03-02T17:31:56.350+0800 [DEBUG] Attempting to open CLI config file: /Users/luoyin/dev.tfrc
-2023-03-02T17:31:56.350+0800 [INFO] Loading CLI configuration from /Users/luoyin/dev.tfrc
-2023-03-02T17:31:56.352+0800 [DEBUG] Not reading CLI config directory because config location is overridden by environment variable
-2023-03-02T17:31:56.353+0800 [DEBUG] Explicit provider installation configuration is set
-2023-03-02T17:31:56.353+0800 [TRACE] Selected provider installation method cliconfig.ProviderInstallationDirect with includes [] and excludes []
-2023-03-02T17:31:56.355+0800 [INFO] CLI command args: []string{"init"}
-2023-03-22T22:14:19.169+0800 [INFO] Terraform version: 1.3.4
-2023-03-22T22:14:19.170+0800 [DEBUG] using github.com/hashicorp/go-tfe v1.9.0
-2023-03-22T22:14:19.170+0800 [DEBUG] using github.com/hashicorp/hcl/v2 v2.14.1
-2023-03-22T22:14:19.170+0800 [DEBUG] using github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2
-2023-03-22T22:14:19.170+0800 [DEBUG] using github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734
-2023-03-22T22:14:19.170+0800 [DEBUG] using github.com/zclconf/go-cty v1.12.0
-2023-03-22T22:14:19.170+0800 [INFO] Go runtime version: go1.19.3
-2023-03-22T22:14:19.170+0800 [INFO] CLI args: []string{"terraform", "init"}
-2023-03-22T22:14:19.170+0800 [TRACE] Stdout is a terminal of width 142
-2023-03-22T22:14:19.170+0800 [TRACE] Stderr is a terminal of width 142
-2023-03-22T22:14:19.170+0800 [TRACE] Stdin is a terminal
-2023-03-22T22:14:19.170+0800 [DEBUG] Attempting to open CLI config file: /Users/luoyin/dev.tfrc
-2023-03-22T22:14:19.170+0800 [INFO] Loading CLI configuration from /Users/luoyin/dev.tfrc
-2023-03-22T22:14:19.170+0800 [DEBUG] Not reading CLI config directory because config location is overridden by environment variable
-2023-03-22T22:14:19.170+0800 [DEBUG] Explicit provider installation configuration is set
-2023-03-22T22:14:19.170+0800 [TRACE] Selected provider installation method cliconfig.ProviderInstallationDirect with includes [] and excludes []
-2023-03-22T22:14:19.171+0800 [INFO] CLI command args: []string{"init"}
diff --git a/tsconfig.json b/tsconfig.json
index cd9b3a9..5729f13 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,17 +1,37 @@
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es6",
- "outDir": "out",
- "lib": [
- "es6"
- ],
- "sourceMap": true,
- "rootDir": "src",
- // "strict": true /* enable all strict type-checking options */
- /* Additional Checks */
- "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
- "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
- // "noUnusedParameters": true, /* Report errors on unused parameters. */
- }
-}
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ // "module": "commonjs",
+ "module": "NodeNext",
+ "moduleResolution": "node",
+ "target": "ES2020",
+ "outDir": "./out",
+ "resolveJsonModule": true,
+ "allowSyntheticDefaultImports" :true,
+ "lib": [
+ "ES2020"
+ ],
+ "sourceMap": true,
+ "types": [
+ "reflect-metadata",
+ "node"
+ ],
+ "baseUrl": "./",
+ "paths": {
+ "@/*": [
+ "src/*"
+ ]
+ },
+ // "rootDir": "src",
+ // "strict": true /* enable all strict type-checking options */
+ /* Additional Checks */
+ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+ // "noUnusedParameters": true, /* Report errors on unused parameters. */
+ },
+ "ts-node": {
+ "require": [
+ "tsconfig-paths/register"
+ ]
+ }
+}
\ No newline at end of file