Skip to content

Commit 7d945d4

Browse files
authored
Merge branch 'main' into reflect-localstack-status-immediately-after-running-start-and-stop-commands
2 parents 3c4c3da + b049377 commit 7d945d4

File tree

6 files changed

+100
-31
lines changed

6 files changed

+100
-31
lines changed

src/plugins/setup.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import os from "node:os";
2-
31
import { commands, ProgressLocation, window } from "vscode";
42

53
import { createPlugin } from "../plugins.ts";
@@ -10,6 +8,11 @@ import {
108
} from "../utils/authenticate.ts";
119
import { configureAwsProfiles } from "../utils/configure-aws.ts";
1210
import { runInstallProcess } from "../utils/install.ts";
11+
import {
12+
activateLicense,
13+
checkIsLicenseValid,
14+
activateLicenseUntilValid,
15+
} from "../utils/license.ts";
1316
import { minDelay } from "../utils/promises.ts";
1417

1518
export default createPlugin(
@@ -122,8 +125,6 @@ export default createPlugin(
122125
progress.report({
123126
message:
124127
"Waiting for authentication response from the browser...",
125-
// message: "Waiting for browser response...",
126-
// message: "Waiting for authentication response...",
127128
});
128129
const { authToken } = await minDelay(
129130
requestAuthentication(context, cancellationToken),
@@ -146,7 +147,6 @@ export default createPlugin(
146147

147148
/////////////////////////////////////////////////////////////////////
148149
progress.report({
149-
// message: "Authenticating...",
150150
message: "Authenticating to file...",
151151
});
152152
await minDelay(saveAuthToken(authToken, outputChannel));
@@ -168,6 +168,38 @@ export default createPlugin(
168168
}
169169
}
170170

171+
/////////////////////////////////////////////////////////////////////
172+
progress.report({ message: "Checking LocalStack license..." });
173+
174+
// If an auth token has just been obtained or LocalStack has never been started,
175+
// then there will be no license info to be reported by `localstack license info`.
176+
// Also, an expired license could be cached.
177+
// Activating the license pre-emptively to know its state during the setup process.
178+
const licenseIsValid = await minDelay(
179+
activateLicense(outputChannel).then(() =>
180+
checkIsLicenseValid(outputChannel),
181+
),
182+
);
183+
if (!licenseIsValid) {
184+
progress.report({
185+
message:
186+
"License is not valid or not assigned. Open License settings page to activate it.",
187+
});
188+
189+
commands.executeCommand("localstack.openLicensePage");
190+
191+
await activateLicenseUntilValid(
192+
outputChannel,
193+
cancellationToken,
194+
);
195+
}
196+
197+
if (cancellationToken.isCancellationRequested) {
198+
return;
199+
}
200+
201+
//TODO add telemetry
202+
171203
/////////////////////////////////////////////////////////////////////
172204
progress.report({
173205
message: "Configuring AWS profiles...",

src/utils/authenticate.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createHash } from "node:crypto";
21
import * as fs from "node:fs/promises";
32
import * as os from "node:os";
43
import * as path from "node:path";
@@ -11,7 +10,6 @@ import type {
1110
import { env, Uri, window } from "vscode";
1211

1312
import { assertIsError } from "./assert.ts";
14-
import { execLocalStack } from "./cli.ts";
1513

1614
/**
1715
* Registers a {@link UriHandler} that waits for an authentication token from the browser,
@@ -114,21 +112,6 @@ export async function saveAuthToken(
114112
}
115113
}
116114

117-
const LICENSE_VALIDITY_MARKER = "license validity: valid";
118-
119-
export async function checkIsLicenseValid(outputChannel: LogOutputChannel) {
120-
try {
121-
const licenseInfoResponse = await execLocalStack(["license", "info"], {
122-
outputChannel,
123-
});
124-
return licenseInfoResponse.stdout.includes(LICENSE_VALIDITY_MARKER);
125-
} catch (error) {
126-
outputChannel.error(error instanceof Error ? error : String(error));
127-
128-
return undefined;
129-
}
130-
}
131-
132115
/**
133116
* Checks if the user is authenticated by validating the stored auth token.
134117
*

src/utils/license.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { CancellationToken, LogOutputChannel } from "vscode";
2+
3+
import { execLocalStack } from "./cli.ts";
4+
5+
const LICENSE_VALIDITY_MARKER = "license validity: valid";
6+
7+
export async function checkIsLicenseValid(outputChannel: LogOutputChannel) {
8+
try {
9+
const licenseInfoResponse = await execLocalStack(["license", "info"], {
10+
outputChannel,
11+
});
12+
return licenseInfoResponse.stdout.includes(LICENSE_VALIDITY_MARKER);
13+
} catch (error) {
14+
outputChannel.error(error instanceof Error ? error : String(error));
15+
16+
return false;
17+
}
18+
}
19+
20+
export async function activateLicense(outputChannel: LogOutputChannel) {
21+
try {
22+
await execLocalStack(["license", "activate"], {
23+
outputChannel,
24+
});
25+
} catch (error) {
26+
outputChannel.error(error instanceof Error ? error : String(error));
27+
}
28+
}
29+
30+
export async function activateLicenseUntilValid(
31+
outputChannel: LogOutputChannel,
32+
cancellationToken: CancellationToken,
33+
): Promise<void> {
34+
while (true) {
35+
if (cancellationToken.isCancellationRequested) {
36+
break;
37+
}
38+
const licenseIsValid = await checkIsLicenseValid(outputChannel);
39+
if (licenseIsValid) {
40+
break;
41+
}
42+
await activateLicense(outputChannel);
43+
// Wait before trying again
44+
await new Promise((resolve) => setTimeout(resolve, 1000));
45+
}
46+
}

src/utils/manage.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { v7 as uuidv7 } from "uuid";
22
import type { ExtensionContext, LogOutputChannel, MessageItem } from "vscode";
33
import { commands, env, Uri, window } from "vscode";
44

5-
import { checkIsLicenseValid } from "./authenticate.ts";
65
import { spawnLocalStack } from "./cli.ts";
76
import { exec } from "./exec.ts";
7+
import { checkIsLicenseValid } from "./license.ts";
88
import { spawn } from "./spawn.ts";
99
import type { Telemetry } from "./telemetry.ts";
1010

@@ -199,7 +199,12 @@ export async function stopLocalStack(
199199

200200
export async function openLicensePage() {
201201
const url = new URL("https://app.localstack.cloud/settings/auth-tokens");
202-
await env.openExternal(Uri.parse(url.toString()));
202+
const openSuccessful = await env.openExternal(Uri.parse(url.toString()));
203+
if (!openSuccessful) {
204+
window.showErrorMessage(
205+
`Open LocalStack License page in browser by entering the URL manually: ${url.toString()}`,
206+
);
207+
}
203208
}
204209

205210
async function showInformationMessage(

src/utils/promises.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import pMinDelay from "p-min-delay";
22

33
/**
4-
* Setting up a minimum wait time of 1s allows users
4+
* Setting up a minimum wait time allows users
55
* to visually grasp the text before it goes away, if
6-
* the task was fast (less than 0.5s).
6+
* the task was faster than the minimum wait time.
77
*/
88
const MIN_TIME_BETWEEN_STEPS_MS = 500;
99

src/utils/setup.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ import type { LogOutputChannel } from "vscode";
33
import { checkIsAuthenticated } from "./authenticate.ts";
44
import { checkIsProfileConfigured } from "./configure-aws.ts";
55
import { checkLocalstackInstalled } from "./install.ts";
6+
import { checkIsLicenseValid } from "./license.ts";
67

78
export async function checkIsSetupRequired(
89
outputChannel: LogOutputChannel,
910
): Promise<boolean> {
10-
const [isInstalled, isAuthenticated, isProfileConfigured] = await Promise.all(
11-
[
11+
const [isInstalled, isAuthenticated, isLicenseValid, isProfileConfigured] =
12+
await Promise.all([
1213
checkLocalstackInstalled(outputChannel),
1314
checkIsAuthenticated(),
15+
checkIsLicenseValid(outputChannel),
1416
checkIsProfileConfigured(),
15-
],
16-
);
17+
]);
1718

18-
return !isInstalled || !isAuthenticated || !isProfileConfigured;
19+
return (
20+
!isInstalled || !isAuthenticated || !isLicenseValid || !isProfileConfigured
21+
);
1922
}

0 commit comments

Comments
 (0)