Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@
"title": "Run Setup Wizard",
"category": "LocalStack"
},
{
"command": "localstack.refreshStatusBar",
"title": "Refresh Status Bar",
"category": "LocalStack",
"enablement": "false"
},
{
"command": "localstack.showCommands",
"title": "Show Commands",
Expand Down
10 changes: 8 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@ export const GLOBAL_CLI_INSTALLATION_DIRNAME = join(
);

const CLI_UNIX_PATHS = [
// The local installation path takes precedence.
join(LOCAL_CLI_INSTALLATION_DIRNAME, "localstack"),
// Check if it's in the PATH.
"localstack",
// Common installation paths.
join("/", "usr", "bin", "localstack"),
join("/", "usr", "local", "bin", "localstack"),
join("/", "opt", "homebrew", "bin", "localstack"),
join("/", "home", "linuxbrew", ".linuxbrew", "bin", "localstack"),
join(homedir(), ".linuxbrew", "bin", "localstack"),
join(homedir(), ".local", "bin", "localstack"),
join(LOCAL_CLI_INSTALLATION_DIRNAME, "localstack"),
];

const CLI_WINDOWS_PATHS = [
// The local installation path takes precedence.
join(LOCAL_CLI_INSTALLATION_DIRNAME, "localstack.exe"),
// Check if it's in the PATH.
"localstack.exe",
// Common installation paths.
join(GLOBAL_CLI_INSTALLATION_DIRNAME, "localstack", "localstack.exe"),
join(LOCAL_CLI_INSTALLATION_DIRNAME, "localstack.exe"),
];

export const CLI_PATHS =
Expand Down
56 changes: 28 additions & 28 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import manage from "./plugins/manage.ts";
import setup from "./plugins/setup.ts";
import statusBar from "./plugins/status-bar.ts";
import { PluginManager } from "./plugins.ts";
import { createContainerStatusTracker } from "./utils/container-status.ts";
import { createLocalStackStatusTracker } from "./utils/localstack-status.ts";
import { createCliStatusTracker } from "./utils/cli.ts";
import { createLocalStackContainerStatusTracker } from "./utils/localstack-container.ts";
import {
createHealthStatusTracker,
createLocalStackInstanceStatusTracker,
} from "./utils/localstack-instance.ts";
import { getOrCreateExtensionSessionId } from "./utils/manage.ts";
import { createSetupStatusTracker } from "./utils/setup-status.ts";
import { createSetupStatusTracker } from "./utils/setup.ts";
import { createTelemetry } from "./utils/telemetry.ts";
import { createTimeTracker } from "./utils/time-tracker.ts";

Expand All @@ -29,6 +33,9 @@ export async function activate(context: ExtensionContext) {
});
context.subscriptions.push(outputChannel);

const cliStatusTracker = createCliStatusTracker(outputChannel);
context.subscriptions.push(cliStatusTracker);

const timeTracker = createTimeTracker({ outputChannel });

const {
Expand All @@ -46,45 +53,37 @@ export async function activate(context: ExtensionContext) {
statusBarItem.text = "$(loading~spin) LocalStack";
statusBarItem.show();

const containerStatusTracker = await createContainerStatusTracker(
const containerStatusTracker = createLocalStackContainerStatusTracker(
"localstack-main",
outputChannel,
timeTracker,
);
context.subscriptions.push(containerStatusTracker);

const localStackStatusTracker = createLocalStackStatusTracker(
const healthCheckStatusTracker = createHealthStatusTracker(timeTracker);
const localStackStatusTracker = createLocalStackInstanceStatusTracker(
containerStatusTracker,
healthCheckStatusTracker,
outputChannel,
timeTracker,
);
context.subscriptions.push(localStackStatusTracker);

outputChannel.trace(`[setup-status]: Starting...`);
const startStatusTracker = Date.now();
const setupStatusTracker = await createSetupStatusTracker(
outputChannel,
timeTracker,
const setupStatusTracker = await timeTracker.run(
"setup-status",
async () => {
return await createSetupStatusTracker(
outputChannel,
timeTracker,
cliStatusTracker,
);
},
);
context.subscriptions.push(setupStatusTracker);
const endStatusTracker = Date.now();
outputChannel.trace(
`[setup-status]: Completed in ${ms(
endStatusTracker - startStatusTracker,
{ long: true },
)}`,
);

const startTelemetry = Date.now();
outputChannel.trace(`[telemetry]: Starting...`);
const sessionId = await getOrCreateExtensionSessionId(context);
const telemetry = createTelemetry(outputChannel, sessionId);
const endTelemetry = Date.now();
outputChannel.trace(
`[telemetry]: Completed in ${ms(endTelemetry - startTelemetry, {
long: true,
})}`,
);
const telemetry = await timeTracker.run("telemetry", async () => {
const sessionId = await getOrCreateExtensionSessionId(context);
return createTelemetry(outputChannel, sessionId);
});

return {
statusBarItem,
Expand All @@ -100,6 +99,7 @@ export async function activate(context: ExtensionContext) {
context,
outputChannel,
statusBarItem,
cliStatusTracker,
containerStatusTracker,
localStackStatusTracker,
setupStatusTracker,
Expand Down
12 changes: 7 additions & 5 deletions src/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import ms from "ms";
import type { ExtensionContext, LogOutputChannel, StatusBarItem } from "vscode";

import type { ContainerStatusTracker } from "./utils/container-status.ts";
import type { LocalStackStatusTracker } from "./utils/localstack-status.ts";
import type { SetupStatusTracker } from "./utils/setup-status.ts";
import type { CliStatusTracker } from "./utils/cli.ts";
import type { LocalStackContainerStatusTracker } from "./utils/localstack-container.ts";
import type { LocalStackInstanceStatusTracker } from "./utils/localstack-instance.ts";
import type { SetupStatusTracker } from "./utils/setup.ts";
import type { Telemetry } from "./utils/telemetry.ts";
import type { TimeTracker } from "./utils/time-tracker.ts";

Expand All @@ -13,8 +14,9 @@ export interface PluginOptions {
context: ExtensionContext;
outputChannel: LogOutputChannel;
statusBarItem: StatusBarItem;
containerStatusTracker: ContainerStatusTracker;
localStackStatusTracker: LocalStackStatusTracker;
cliStatusTracker: CliStatusTracker;
containerStatusTracker: LocalStackContainerStatusTracker;
localStackStatusTracker: LocalStackInstanceStatusTracker;
setupStatusTracker: SetupStatusTracker;
telemetry: Telemetry;
timeTracker: TimeTracker;
Expand Down
28 changes: 25 additions & 3 deletions src/plugins/manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,30 @@

export default createPlugin(
"manage",
({ context, outputChannel, telemetry, localStackStatusTracker }) => {
({
context,
outputChannel,
telemetry,
localStackStatusTracker,
cliStatusTracker,
}) => {
context.subscriptions.push(
commands.registerCommand("localstack.start", async () => {
const cliPath = cliStatusTracker.cliPath();
if (!cliPath) {
void window.showInformationMessage(
"LocalStack CLI could not be found. Please, run the setup wizard.",
);
return;
}

if (localStackStatusTracker.status() !== "stopped") {
window.showInformationMessage("LocalStack is already running.");

Check warning on line 30 in src/plugins/manage.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
return;
}
localStackStatusTracker.forceContainerStatus("running");
try {
await startLocalStack(outputChannel, telemetry);
await startLocalStack(cliPath, outputChannel, telemetry);
} catch {
localStackStatusTracker.forceContainerStatus("stopped");
}
Expand All @@ -27,12 +41,20 @@

context.subscriptions.push(
commands.registerCommand("localstack.stop", () => {
const cliPath = cliStatusTracker.cliPath();
if (!cliPath) {
void window.showInformationMessage(
"LocalStack CLI could not be found. Please, run the setup wizard.",
);
return;
}

if (localStackStatusTracker.status() !== "running") {
window.showInformationMessage("LocalStack is not running.");

Check warning on line 53 in src/plugins/manage.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
return;
}
localStackStatusTracker.forceContainerStatus("stopping");
void stopLocalStack(outputChannel, telemetry);
void stopLocalStack(cliPath, outputChannel, telemetry);
}),
);

Expand Down
64 changes: 57 additions & 7 deletions src/plugins/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,34 @@
saveAuthToken,
readAuthToken,
} from "../utils/authenticate.ts";
import { findLocalStack } from "../utils/cli.ts";
import { configureAwsProfiles } from "../utils/configure-aws.ts";
import { runInstallProcess } from "../utils/install.ts";
import {
activateLicense,
checkIsLicenseValid,
activateLicenseUntilValid,
} from "../utils/license.ts";
import { minDelay } from "../utils/promises.ts";
import { minDelay } from "../utils/min-delay.ts";
import { updateDockerImage } from "../utils/setup.ts";
import { get_setup_ended } from "../utils/telemetry.ts";

async function getValidCliPath() {
const cli = await findLocalStack();
if (!cli.cliPath || !cli.executable || !cli.found || !cli.upToDate) {
return;
}
return cli.cliPath;
}

export default createPlugin(
"setup",
({
context,
outputChannel,
setupStatusTracker,
localStackStatusTracker,
cliStatusTracker,
telemetry,
}) => {
context.subscriptions.push(
Expand All @@ -43,7 +53,7 @@
},
});

window.withProgress(
void window.withProgress(
{
location: ProgressLocation.Notification,
title: "Setup LocalStack",
Expand All @@ -55,13 +65,14 @@
let authenticationStatus: "COMPLETED" | "SKIPPED" = "COMPLETED";
{
const installationStartedAt = new Date().toISOString();
const { cancelled, skipped } = await runInstallProcess(
const { cancelled, skipped } = await runInstallProcess({
cliPath: cliStatusTracker.cliPath(),
progress,
cancellationToken,
outputChannel,
telemetry,
origin_trigger,
);
origin: origin_trigger,
});
cliStatus = skipped === true ? "SKIPPED" : "COMPLETED";
if (cancelled || cancellationToken.isCancellationRequested) {
telemetry.track({
Expand Down Expand Up @@ -221,14 +232,52 @@
/////////////////////////////////////////////////////////////////////
progress.report({ message: "Checking LocalStack license..." });

// If the CLI status tracker doesn't have a valid CLI path yet,
// we must find it manually. This may occur when installing the
// CLI as part of the setup process: the CLI status tracker will
// detect the CLI path the next tick.
const cliPath =
cliStatusTracker.cliPath() ?? (await getValidCliPath());
if (!cliPath) {
telemetry.track(
get_setup_ended(
cliStatus,
authenticationStatus,
"CANCELLED",
"CANCELLED",
"FAILED",
origin_trigger,
await readAuthToken(),
),
);
void window
.showErrorMessage(
"Could not access the LocalStack CLI.",
{
title: "Restart Setup",
command: "localstack.setup",
},
{
title: "View Logs",
command: "localstack.viewLogs",
},
)
.then((selection) => {
if (selection) {
void commands.executeCommand(selection.command);
}
});
return;
}

// If an auth token has just been obtained or LocalStack has never been started,
// then there will be no license info to be reported by `localstack license info`.
// Also, an expired license could be cached.
// Activating the license pre-emptively to know its state during the setup process.
const licenseCheckStartedAt = new Date().toISOString();
const licenseIsValid = await minDelay(
activateLicense(outputChannel).then(() =>
checkIsLicenseValid(outputChannel),
activateLicense(cliPath, outputChannel).then(() =>
checkIsLicenseValid(cliPath, outputChannel),
),
);
if (!licenseIsValid) {
Expand All @@ -240,6 +289,7 @@
await commands.executeCommand("localstack.openLicensePage");

await activateLicenseUntilValid(
cliPath,
outputChannel,
cancellationToken,
);
Expand Down Expand Up @@ -328,25 +378,25 @@

/////////////////////////////////////////////////////////////////////
if (localStackStatusTracker.status() === "running") {
window

Check warning on line 381 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
.showInformationMessage("LocalStack is running.", {
title: "View Logs",
command: "localstack.viewLogs",
})
.then((selection) => {
if (selection) {
commands.executeCommand(selection.command);

Check warning on line 388 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
});
} else {
window

Check warning on line 392 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
.showInformationMessage("LocalStack is ready to start.", {
title: "Start LocalStack",
command: "localstack.start",
})
.then((selection) => {
if (selection) {
commands.executeCommand(selection.command);

Check warning on line 399 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
});
}
Expand Down
Loading