From 20d626f3ce0f8cac4aa88913f0dbeb45d5df1daf Mon Sep 17 00:00:00 2001 From: Noahnc Date: Tue, 12 Dec 2023 10:22:35 +0100 Subject: [PATCH 1/6] feat(helper): Add helper function to check internet connection. --- src/utils/helper_functions.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/utils/helper_functions.ts b/src/utils/helper_functions.ts index 34fddf1..3c69457 100644 --- a/src/utils/helper_functions.ts +++ b/src/utils/helper_functions.ts @@ -2,6 +2,7 @@ import * as path from "path"; import * as vscode from "vscode"; import { getLogger } from "./logger"; +import * as dns from "dns"; export function checkIfOpenTextEditorIsTerraform(): boolean { const activeDocument = vscode.window.activeTextEditor?.document; @@ -103,3 +104,15 @@ export function showError(message: string, silent = false) { export function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } + +export function checkInternetConnection(): Promise { + return new Promise((resolve) => { + dns.lookup("google.com", function (err: any) { + if (err && err.code == "ENOTFOUND") { + resolve(false); + } else { + resolve(true); + } + }); + }); +} From 8b87f5e4959c6711c291e70c37ed8ba0e383fdd7 Mon Sep 17 00:00:00 2001 From: Noahnc Date: Tue, 12 Dec 2023 10:23:08 +0100 Subject: [PATCH 2/6] feat(base): Add attribute to check for internet in command and status items. --- src/commands/base_command.ts | 6 ++++++ src/view/statusbar/base_statusbar_item.ts | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/commands/base_command.ts b/src/commands/base_command.ts index 6fb6b94..5176d2a 100644 --- a/src/commands/base_command.ts +++ b/src/commands/base_command.ts @@ -1,11 +1,13 @@ import * as vscode from "vscode"; import { UserShownError } from "../custom_errors"; import { getLogger } from "../utils/logger"; +import * as helper from "../utils/helper_functions"; export interface IvscodeCommandSettings { command: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any successCallback?: (...arg: any) => Promise; + checkInternetConnection?: boolean; } export abstract class BaseCommand { @@ -25,6 +27,10 @@ export abstract class BaseCommand { // eslint-disable-next-line @typescript-eslint/no-explicit-any async run(...args: any[]): Promise { getLogger().debug("Running command " + this._settings.command); + if (this._settings.checkInternetConnection && (await helper.checkInternetConnection()) === false) { + helper.showWarning("No internet connection, command " + this._settings.command + " will not be executed"); + return; + } await this.init(...args) .then(() => { getLogger().debug("Successfully ran command " + this._settings.command); diff --git a/src/view/statusbar/base_statusbar_item.ts b/src/view/statusbar/base_statusbar_item.ts index b205c51..fa3f454 100644 --- a/src/view/statusbar/base_statusbar_item.ts +++ b/src/view/statusbar/base_statusbar_item.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode"; import { UserShownError } from "../../custom_errors"; import { getLogger } from "../../utils/logger"; +import * as helper from "../../utils/helper_functions"; export interface IvscodeStatusBarItemSettings { alignment: vscode.StatusBarAlignment; @@ -9,6 +10,7 @@ export interface IvscodeStatusBarItemSettings { updateOnDidChangeTextEditorSelection?: boolean; refreshIntervalSeconds?: number; tooltip: string; + checkInternetConnection?: boolean; } export abstract class BaseStatusBarItem { @@ -40,6 +42,13 @@ export abstract class BaseStatusBarItem { } // eslint-disable-next-line @typescript-eslint/no-explicit-any async refresh(...args: any[]): Promise { + if (this._settings.checkInternetConnection) { + if (await helper.checkInternetConnection() === false) { + getLogger().debug("No internet connection, hiding status bar item") + this._statusBarItem.hide(); + return; + } + } await this.run(...args).catch((error) => { handleError(error); }); From 37727302add0adf5293fb2b825e5108ce73c5bcf Mon Sep 17 00:00:00 2001 From: Noahnc Date: Tue, 12 Dec 2023 10:23:42 +0100 Subject: [PATCH 3/6] feat(spacelift_settings): Add new setting to notify spacectl login on start. --- package.json | 5 +++++ src/models/settings.ts | 3 +++ 2 files changed, 8 insertions(+) diff --git a/package.json b/package.json index 293159f..16c0e67 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,11 @@ "default": null, "description": "Spacectl profile name to select when running this extension. The currently active Profile will be used when this is not set." }, + "tftoolbox.spacelift.showLoginNotificationOnStartup": { + "type": "boolean", + "default": false, + "description": "If enabled, a notification will be shown regarding spacectl login if spacectl is not authenticated." + }, "tftoolbox.terraform.autoSelectVersion": { "type": "boolean", "default": false, diff --git a/src/models/settings.ts b/src/models/settings.ts index 3f9b511..5c6d5ab 100644 --- a/src/models/settings.ts +++ b/src/models/settings.ts @@ -39,4 +39,7 @@ export class Settings { get excludedGlobPatterns(): string[] { return this.getVSCodeSettingKey("tftoolbox.excludeGlobPatterns"); } + get showSpacectlNotAuthenticatedWarningOnStartup(): boolean { + return this.getVSCodeSettingKey("tftoolbox.spacelift.showLoginNotificationOnStartup"); + } } From 07b325035b40e84d5a40dbb4ef7c4c0f6dd82fce Mon Sep 17 00:00:00 2001 From: Noahnc Date: Tue, 12 Dec 2023 10:24:06 +0100 Subject: [PATCH 4/6] feat(extension): Set internet required for some commands and status items. --- src/extension.ts | 131 ++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 59 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 13dc9ec..ae1c71c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -64,71 +64,84 @@ export async function activate(context: vscode.ExtensionContext) { ); // Init spacelift commands if spacelift is configured - spacectlInit(settings) - .then(([spaceliftClient, spacectlInstance, tenantID, authenticationHandler]) => { - new RunSpacectlLocalPreviewCurrentStackCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW_CURRENT_STACK }, spaceliftClient, spacectlInstance); - new RunSpacectlLocalPreviewCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW }, spaceliftClient, spacectlInstance); - const openSpaceliftWebPortalCommand = "openSpaceliftWebPortal"; - context.subscriptions.push( - vscode.commands.registerCommand(openSpaceliftWebPortalCommand, () => { - vscode.env.openExternal(vscode.Uri.parse("https://" + tenantID + cst.SPACELIFT_BASE_DOMAIN)); - }) - ); - new SpaceliftPenStackConfCount( - context, - { - alignment: vscode.StatusBarAlignment.Left, - priority: 99, - refreshIntervalSeconds: settings.spaceliftStatusBarItemRefreshIntervalSeconds, - tooltip: "Count of Spacelift Stacks pending confirmation", - onClickCommand: openSpaceliftWebPortalCommand, - }, - spaceliftClient - ).refresh(); - const spaceliftAuthStatusItem = new SpaceliftApiAuthenticationStatus( - context, - { - alignment: vscode.StatusBarAlignment.Left, - priority: 100, - refreshIntervalSeconds: settings.spaceliftStatusBarItemRefreshIntervalSeconds, - tooltip: "Log-in to Spacelift with spacectl and your Web browser", - onClickCommand: cst.COMMAND_SPACELIFT_LOGIN, - }, - authenticationHandler - ); - spaceliftAuthStatusItem.refresh(); - context.subscriptions.push( - vscode.commands.registerCommand(cst.COMMAND_SPACELIFT_LOGIN, async () => { - if (await authenticationHandler.login_interactive()) { - await spaceliftAuthStatusItem.refresh(); - } - }) - ); - }) - .catch((error) => { - getLogger().error("Failed to initialize spacectl: " + error); - helpers - .showNotificationWithDecisions( - "Failed to initialize spacectl. Some features will be disabled until spacectl is configured.", - "tftoolbox.spacelift.showSpaceliftInitErrorOnStart", - "Open spacectl documentation", - "warning" - ) - .then((result) => { - if (result) { - vscode.env.openExternal(vscode.Uri.parse("https://github.com/spacelift-io/spacectl")); - } - }); - }); + spacectlInit(settings).then(([spaceliftClient, spacectlInstance, tenantID, authenticationHandler]) => { + new RunSpacectlLocalPreviewCurrentStackCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW_CURRENT_STACK }, spaceliftClient, spacectlInstance); + new RunSpacectlLocalPreviewCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW }, spaceliftClient, spacectlInstance); + const openSpaceliftWebPortalCommand = "openSpaceliftWebPortal"; + context.subscriptions.push( + vscode.commands.registerCommand(openSpaceliftWebPortalCommand, () => { + vscode.env.openExternal(vscode.Uri.parse("https://" + tenantID + cst.SPACELIFT_BASE_DOMAIN)); + }) + ); + new SpaceliftPenStackConfCount( + context, + { + alignment: vscode.StatusBarAlignment.Left, + priority: 99, + refreshIntervalSeconds: settings.spaceliftStatusBarItemRefreshIntervalSeconds, + tooltip: "Count of Spacelift Stacks pending confirmation", + onClickCommand: openSpaceliftWebPortalCommand, + checkInternetConnection: true, + }, + spaceliftClient + ).refresh(); + const spaceliftAuthStatusItem = new SpaceliftApiAuthenticationStatus( + context, + { + alignment: vscode.StatusBarAlignment.Left, + priority: 100, + refreshIntervalSeconds: settings.spaceliftStatusBarItemRefreshIntervalSeconds, + tooltip: "Log-in to Spacelift with spacectl and your Web browser", + onClickCommand: cst.COMMAND_SPACELIFT_LOGIN, + checkInternetConnection: true, + }, + authenticationHandler + ); + spaceliftAuthStatusItem.refresh(); + context.subscriptions.push( + vscode.commands.registerCommand(cst.COMMAND_SPACELIFT_LOGIN, async () => { + if (await authenticationHandler.login_interactive()) { + await spaceliftAuthStatusItem.refresh(); + } + }) + ); + authenticationHandler + .check_token_valid() + .then((valid: boolean) => { + if (!valid && settings.showSpacectlNotAuthenticatedWarningOnStartup) { + getLogger().info("Spacectl token is not valid, showing notification to log-in to Spacelift"); + vscode.commands.executeCommand(cst.COMMAND_SPACELIFT_LOGIN); + } + }) + .catch((error) => { + getLogger().error("Failed to initialize spacectl: " + error); + helpers + .showNotificationWithDecisions( + "Failed to initialize spacectl. Some features will be disabled until spacectl is configured.", + "tftoolbox.spacelift.showSpaceliftInitErrorOnStart", + "Open spacectl documentation", + "warning" + ) + .then((result) => { + if (result) { + vscode.env.openExternal(vscode.Uri.parse("https://github.com/spacelift-io/spacectl")); + } + }); + }); + }); // Terraform version management commands const setTFVersionBasedOnProjectCommand = new SetTerraformVersionBasedOnProjectRequirementsCommand( context, - { command: cst.COMMAND_AUTO_SET_TERRAFORM_VERSION, successCallback: tfVersionItem.refresh.bind(tfVersionItem) }, + { command: cst.COMMAND_AUTO_SET_TERRAFORM_VERSION, successCallback: tfVersionItem.refresh.bind(tfVersionItem), checkInternetConnection: true }, tfVersionManager, tfProjectHelper ); - new ChoseAndSetTerraformVersionCommand(context, { command: cst.COMMAND_SET_TERRAFORM_VERSION, successCallback: tfVersionItem.refresh.bind(tfVersionItem) }, tfVersionManager); - new ChoseAndDeleteTerraformVersionsCommand(context, { command: cst.COMMAND_DELETE_TERRAFORM_VERSIONS }, tfVersionManager); + new ChoseAndSetTerraformVersionCommand( + context, + { command: cst.COMMAND_SET_TERRAFORM_VERSION, successCallback: tfVersionItem.refresh.bind(tfVersionItem), checkInternetConnection: true }, + tfVersionManager + ); + new ChoseAndDeleteTerraformVersionsCommand(context, { command: cst.COMMAND_DELETE_TERRAFORM_VERSIONS, checkInternetConnection: true }, tfVersionManager); // Terraform init commands const tfInitAllProjectsCommand = new TerraformInitAllProjectsCommand(context, { command: cst.COMMAND_INIT_ALL_PROJECTS }, tfProjectHelper); From 19225b435272537213f7430a3490ee89147bb8c3 Mon Sep 17 00:00:00 2001 From: Noahnc Date: Tue, 12 Dec 2023 10:58:28 +0100 Subject: [PATCH 5/6] feat(version): Bump package version and add changelog. --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f205104..7f67829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to the terraform-toolbox extension will be documented in this file. +## [0.3.1] + +- (refac): The extension now checks your internet connection before running commands that require an internet connection. Status bar items that require an internet connection will be hidden if no internet connection is available. +- (feat): Added a new setting to control if a login notification should be shown on startup, if the current spacectl token is not valid. + ## [0.3.0] - (feat): New Spacelift spacectl authentication handling. Since Spacelift has changed its token validity (only 1 token per user is now allowed to be active), the extension now uses the spacectl with web browser login to authenticate the user. If the current token provided by spacectl has expried or is revoked, a status item will be shown. Clicking on the status item will prompt you to authenticate spacectl with your browser. diff --git a/package.json b/package.json index 16c0e67..5b94f91 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/Noahnc/vscode-terraform-toolbox" }, "description": "VSCode extension adding a bunch of featurees regarding Terraform and Spacelift.", - "version": "0.3.0", + "version": "0.3.1", "icon": "Images/terraform_toolbox_icon.png", "engines": { "vscode": "^1.83.0" From 5929bc2287210514a85a532171436723e9564341 Mon Sep 17 00:00:00 2001 From: Noahnc Date: Tue, 12 Dec 2023 11:26:16 +0100 Subject: [PATCH 6/6] feat(spacelift_commands): Require internet for spacelift commands. --- src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ae1c71c..ce2f0b5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -65,8 +65,8 @@ export async function activate(context: vscode.ExtensionContext) { // Init spacelift commands if spacelift is configured spacectlInit(settings).then(([spaceliftClient, spacectlInstance, tenantID, authenticationHandler]) => { - new RunSpacectlLocalPreviewCurrentStackCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW_CURRENT_STACK }, spaceliftClient, spacectlInstance); - new RunSpacectlLocalPreviewCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW }, spaceliftClient, spacectlInstance); + new RunSpacectlLocalPreviewCurrentStackCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW_CURRENT_STACK, checkInternetConnection: true }, spaceliftClient, spacectlInstance); + new RunSpacectlLocalPreviewCommand(context, { command: cst.COMMAND_LOCAL_PREVIEW, checkInternetConnection: true }, spaceliftClient, spacectlInstance); const openSpaceliftWebPortalCommand = "openSpaceliftWebPortal"; context.subscriptions.push( vscode.commands.registerCommand(openSpaceliftWebPortalCommand, () => {