Skip to content

Commit 39e76a4

Browse files
committed
Handle settings and lint readiness in initialization handler
1 parent 3a383c9 commit 39e76a4

25 files changed

Lines changed: 118 additions & 1167 deletions

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
"test:integration": "cross-env NODE_ENV=test vitest run --config vitest.integration.config.ts",
2828
"test:unit": "cross-env NODE_ENV=test vitest run --config vitest.unit.config.ts",
2929
"test:leaks": "cross-env NODE_ENV=test vitest run --pool=forks --logHeapUsage",
30-
"test:long-running": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=2048\" LONG_RUNNING_DURATION=${LONG_RUNNING_DURATION:-4h} vitest run --config vitest.long-running.config.ts",
31-
"test:long-running-standalone": "tsx tools/long-running/run-long-running-test.ts",
32-
"test:long-running-local": "npm run bundle:prod && STANDALONE_PATH=./bundle/production/cfn-lsp-server-standalone.js npm run test:long-running-standalone",
3330
"lint": "eslint --cache --cache-location node_modules/.cache/eslint --max-warnings 0 .",
3431
"lint:fix": "npm run lint -- --fix",
3532
"build:go:dev": "GOPROXY=direct go build -C cfn-init/cmd -v -o ../../bundle/development/bin/cfn-init",

src/handlers/Initialize.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,25 @@ export function initializedHandler(workspace: LspWorkspace, components: ServerCo
1313
return components.cfnLintService.initialize();
1414
})
1515
.then(async () => {
16+
let mountingSucceeded = true;
17+
let mountingError = '';
18+
1619
// Process folders sequentially to avoid overwhelming the system
1720
for (const folder of workspace.getAllWorkspaceFolders()) {
1821
try {
1922
await components.cfnLintService.mountFolder(folder);
2023
} catch (error) {
2124
logger.error(error, `Failed to mount folder ${folder.name}`);
25+
mountingSucceeded = false;
26+
mountingError = `Failed to mount folder ${folder.name}: ${error instanceof Error ? error.message : String(error)}`;
2227
}
2328
}
29+
30+
if (mountingSucceeded) {
31+
components.cfnLintService.setReadinessStatus({ ready: true });
32+
} else {
33+
components.cfnLintService.setReadinessStatus({ ready: false, reason: mountingError });
34+
}
2435
})
2536
.catch((error: unknown) => {
2637
logger.error(error, `Failed to initialize server`);

src/handlers/SystemHandler.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@ export function getSystemStatusHandler(
88
): RequestHandler<void, GetSystemStatusResponse, void> {
99
return (): GetSystemStatusResponse => {
1010
try {
11-
// If settings are updating, everything is not ready
12-
if (components.settingsManager.isSettingsUpdateInProgress()) {
11+
const settingsStatus = components.settingsManager.getReadinessStatus();
12+
13+
if (!settingsStatus.ready) {
14+
const reason = settingsStatus.reason ?? 'Server initializing';
1315
return {
14-
schemasReady: { ready: false, reason: 'Settings updating', availableRegions: [] },
15-
cfnLintReady: { ready: false, reason: 'Settings updating' },
16-
cfnGuardReady: { ready: false, reason: 'Settings updating' },
16+
settingsReady: settingsStatus,
17+
schemasReady: { ready: false, reason, availableRegions: [] },
18+
cfnLintReady: { ready: false, reason },
19+
cfnGuardReady: { ready: false, reason },
1720
currentSettings: components.settingsManager.getCurrentSettings(),
1821
};
1922
}
2023

2124
const availableRegions = components.schemaStore.getPublicSchemaRegions();
2225
return {
26+
settingsReady: settingsStatus,
2327
schemasReady: {
2428
availableRegions: [...availableRegions],
2529
ready: availableRegions.length > 0,

src/services/cfnLint/CfnLintService.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { LspWorkspace } from '../../protocol/LspWorkspace';
88
import { CfnLspServerComponentsType } from '../../server/ServerComponents';
99
import { SettingsConfigurable, ISettingsSubscriber, SettingsSubscription } from '../../settings/ISettingsSubscriber';
1010
import { DefaultSettings, CfnLintSettings } from '../../settings/Settings';
11+
import { ReadinessStatus } from '../../system/SystemTypes';
1112
import { LoggerFactory } from '../../telemetry/LoggerFactory';
1213
import { ScopedTelemetry } from '../../telemetry/ScopedTelemetry';
1314
import { Count, Telemetry } from '../../telemetry/TelemetryDecorator';
@@ -48,6 +49,7 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
4849
private static readonly CFN_LINT_SOURCE = 'cfn-lint';
4950

5051
private status: STATUS = STATUS.Uninitialized;
52+
private readinessStatus: ReadinessStatus = { ready: false, reason: 'Not initialized' };
5153
private readonly delayer: Delayer<void>;
5254
private settings: CfnLintSettings;
5355
private settingsSubscription?: SettingsSubscription;
@@ -128,6 +130,17 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
128130
});
129131
}
130132

133+
getReadinessStatus(): ReadinessStatus {
134+
if (!this.settings.enabled) {
135+
return { ready: true }; // Disabled services are considered ready
136+
}
137+
return this.readinessStatus;
138+
}
139+
140+
setReadinessStatus(status: ReadinessStatus): void {
141+
this.readinessStatus = status;
142+
}
143+
131144
private onSettingsChanged(newSettings: CfnLintSettings): void {
132145
this.settings = newSettings;
133146
this.workerManager.updateSettings(newSettings);
@@ -190,23 +203,6 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
190203
}
191204
}
192205

193-
/**
194-
* Get the readiness status of the CfnLint service
195-
*/
196-
public getReadinessStatus(): { ready: boolean; reason?: string } {
197-
// If disabled, consider it ready (nothing to do)
198-
if (!this.settings.enabled) {
199-
return { ready: true };
200-
}
201-
202-
// If enabled, check actual readiness
203-
const ready = this.status === STATUS.Initialized;
204-
return {
205-
ready,
206-
...(ready ? {} : { reason: `Status is ${STATUS[this.status]}` }),
207-
};
208-
}
209-
210206
/**
211207
* Mount a workspace folder to the Pyodide filesystem.
212208
* This allows cfn-lint to access files in the workspace for linting.

src/services/guard/GuardService.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DocumentManager } from '../../document/DocumentManager';
66
import { ServerComponents } from '../../server/ServerComponents';
77
import { SettingsConfigurable, ISettingsSubscriber, SettingsSubscription } from '../../settings/ISettingsSubscriber';
88
import { DefaultSettings, GuardSettings } from '../../settings/Settings';
9+
import { ReadinessStatus } from '../../system/SystemTypes';
910
import { LoggerFactory } from '../../telemetry/LoggerFactory';
1011
import { ScopedTelemetry } from '../../telemetry/ScopedTelemetry';
1112
import { Count, Telemetry } from '../../telemetry/TelemetryDecorator';
@@ -96,10 +97,7 @@ export class GuardService implements SettingsConfigurable, Closeable {
9697
});
9798
}
9899

99-
/**
100-
* Get the readiness status of the Guard service
101-
*/
102-
public getReadinessStatus(): { ready: boolean; reason?: string } {
100+
public getReadinessStatus(): ReadinessStatus {
103101
// If disabled, consider it ready (nothing to do)
104102
if (!this.settings.enabled) {
105103
return { ready: true };

src/settings/SettingsManager.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { diff } from 'deep-object-diff';
22
import { DeepReadonly } from 'ts-essentials';
33
import { LspWorkspace } from '../protocol/LspWorkspace';
4+
import { ReadinessStatus } from '../system/SystemTypes';
45
import { LoggerFactory } from '../telemetry/LoggerFactory';
56
import { ScopedTelemetry } from '../telemetry/ScopedTelemetry';
67
import { Measure, Telemetry } from '../telemetry/TelemetryDecorator';
@@ -18,7 +19,7 @@ export class SettingsManager implements ISettingsSubscriber {
1819
@Telemetry() private readonly telemetry!: ScopedTelemetry;
1920
private readonly settingsState = new SettingsState();
2021
private readonly subscriptionManager = new SubscriptionManager<Settings>();
21-
private isUpdatingSettings = false;
22+
private readinessStatus: ReadinessStatus = { ready: false, reason: 'Not initialized' };
2223

2324
constructor(
2425
private readonly workspace: LspWorkspace,
@@ -34,11 +35,8 @@ export class SettingsManager implements ISettingsSubscriber {
3435
return this.settingsState.toSettings();
3536
}
3637

37-
/**
38-
* Check if settings update is in progress
39-
*/
40-
isSettingsUpdateInProgress(): boolean {
41-
return this.isUpdatingSettings;
38+
getReadinessStatus(): ReadinessStatus {
39+
return this.readinessStatus;
4240
}
4341

4442
reset() {
@@ -82,7 +80,10 @@ export class SettingsManager implements ISettingsSubscriber {
8280

8381
const settings = parseWithPrettyError(parseSettings, mergedConfig, this.getCurrentSettings());
8482
this.validateAndUpdate(settings);
83+
this.readinessStatus = { ready: true };
8584
} catch (error) {
85+
const errorMessage = error instanceof Error ? error.message : String(error);
86+
this.readinessStatus = { ready: false, reason: `Settings sync failed: ${errorMessage}` };
8687
logger.error(error, `Failed to sync configuration, keeping previous settings`);
8788
}
8889
}
@@ -166,13 +167,16 @@ export class SettingsManager implements ISettingsSubscriber {
166167
const hasChanged = Object.keys(difference).length > 0;
167168

168169
if (hasChanged) {
169-
this.isUpdatingSettings = true;
170+
this.readinessStatus = { ready: false, reason: 'Settings updating' };
170171
try {
171172
this.settingsState.update(newSettings);
172173
logger.info(`Settings updated: ${toString(difference)}`);
173174
this.subscriptionManager.notify(newSettings, oldSettings);
174-
} finally {
175-
this.isUpdatingSettings = false;
175+
this.readinessStatus = { ready: true };
176+
} catch (error) {
177+
const errorMessage = error instanceof Error ? error.message : String(error);
178+
this.readinessStatus = { ready: false, reason: `Settings update failed: ${errorMessage}` };
179+
logger.error(error, 'Failed to update settings');
176180
}
177181
}
178182
}

src/system/SystemTypes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type ReadinessStatus = {
77
};
88

99
export type GetSystemStatusResponse = {
10+
settingsReady: ReadinessStatus;
1011
schemasReady: ReadinessStatus & {
1112
availableRegions: string[];
1213
};
@@ -15,4 +16,4 @@ export type GetSystemStatusResponse = {
1516
currentSettings: Settings;
1617
};
1718

18-
export const GetSystemStatusRequestType = new RequestType<void, GetSystemStatusResponse, void>('aws/cfn/system/status');
19+
export const GetSystemStatusRequestType = new RequestType<void, GetSystemStatusResponse, void>('aws/system/status');

tools/long-running/Config.ts

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)