Skip to content

Commit

Permalink
Merge pull request #81 from Noahnc/feat/handle_internet_not_available
Browse files Browse the repository at this point in the history
Feat/handle internet not available
  • Loading branch information
Noahnc authored Dec 13, 2023
2 parents 25cf570 + 224bc6b commit fea1164
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 60 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/commands/base_command.ts
Original file line number Diff line number Diff line change
@@ -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<any>;
checkInternetConnection?: boolean;
}

export abstract class BaseCommand {
Expand All @@ -25,6 +27,10 @@ export abstract class BaseCommand {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async run(...args: any[]): Promise<void> {
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);
Expand Down
131 changes: 72 additions & 59 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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, () => {
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);
Expand Down
3 changes: 3 additions & 0 deletions src/models/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ export class Settings {
get excludedGlobPatterns(): string[] {
return this.getVSCodeSettingKey("tftoolbox.excludeGlobPatterns");
}
get showSpacectlNotAuthenticatedWarningOnStartup(): boolean {
return this.getVSCodeSettingKey("tftoolbox.spacelift.showLoginNotificationOnStartup");
}
}
13 changes: 13 additions & 0 deletions src/utils/helper_functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<boolean> {
return new Promise((resolve) => {
dns.lookup("google.com", function (err: any) {
if (err && err.code == "ENOTFOUND") {
resolve(false);
} else {
resolve(true);
}
});
});
}
9 changes: 9 additions & 0 deletions src/view/statusbar/base_statusbar_item.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,6 +10,7 @@ export interface IvscodeStatusBarItemSettings {
updateOnDidChangeTextEditorSelection?: boolean;
refreshIntervalSeconds?: number;
tooltip: string;
checkInternetConnection?: boolean;
}

export abstract class BaseStatusBarItem {
Expand Down Expand Up @@ -40,6 +42,13 @@ export abstract class BaseStatusBarItem {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async refresh(...args: any[]): Promise<void> {
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);
});
Expand Down

0 comments on commit fea1164

Please sign in to comment.