Skip to content

Commit 9c3c994

Browse files
author
Lauren Nathan
committed
cleaned up checking if the container is ready for connections logic
1 parent 28baa6c commit 9c3c994

File tree

4 files changed

+80
-34
lines changed

4 files changed

+80
-34
lines changed

src/containerDeployment/dockerUtils.ts

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -411,21 +411,19 @@ export async function startDocker(): Promise<DockerCommandParams> {
411411
}
412412
}
413413

414-
export function restartContainer(containerName: string): Promise<boolean> {
414+
export async function restartContainer(containerName: string): Promise<boolean> {
415415
sendActionEvent(TelemetryViews.ContainerDeployment, TelemetryActions.StartContainer);
416416

417-
return startDocker().then((isDockerStarted) => {
418-
if (!isDockerStarted.success) return false;
417+
await startDocker();
418+
const isContainerRunning = await isDockerContainerRunning(containerName);
419+
if (isContainerRunning) return true; // Container is already running
420+
await execCommand(COMMANDS.START_CONTAINER(containerName));
421+
const containerReadyResult = await checkIfContainerIsReadyForConnections(containerName);
419422

420-
return isDockerContainerRunning(containerName).then((containerRunning) => {
421-
if (containerRunning) return true;
422-
423-
return execCommand(COMMANDS.START_CONTAINER(containerName))
424-
.then(() => checkIfContainerIsReadyForConnections(containerName))
425-
.then((result) => result.success)
426-
.catch(() => false);
427-
});
428-
});
423+
if (!containerReadyResult.success) {
424+
return false;
425+
}
426+
return true;
429427
}
430428

431429
/**
@@ -439,16 +437,39 @@ export async function checkIfContainerIsReadyForConnections(
439437
const intervalMs = 1000;
440438
const start = Date.now();
441439

440+
// We check the logs for the timestamp of the "Recovery is complete" message,
441+
// because when a container is stopped and started, the logs are not cleared.
442+
// Checking the timestamp ensures that the container is ready after it has been restarted,
443+
// rather than returning a false positive from the previous run.
444+
const TIMESTAMP_REGEX = /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?)/;
445+
442446
return new Promise((resolve) => {
443447
const interval = setInterval(async () => {
444448
try {
445449
const logs = await execCommand(COMMANDS.CHECK_LOGS(containerName, platform()));
446-
if (logs.includes(COMMANDS.CHECK_CONTAINER_READY)) {
447-
clearInterval(interval);
448-
return resolve({ success: true });
450+
const lines = logs.split("\n");
451+
const readyLine = lines.find((line) =>
452+
line.includes(COMMANDS.CHECK_CONTAINER_READY),
453+
);
454+
455+
if (readyLine) {
456+
const match = readyLine.match(TIMESTAMP_REGEX);
457+
if (match) {
458+
const timestampStr = match[1];
459+
460+
// Parse using Date constructor – replace space with 'T' to make it ISO-ish, adn add 'Z' for UTC
461+
const logTimestamp = new Date(timestampStr.replace(" ", "T") + "Z");
462+
463+
const ageMs = new Date().getTime() - logTimestamp.getTime();
464+
465+
if (ageMs >= 0 && ageMs <= timeoutMs) {
466+
clearInterval(interval);
467+
return resolve({ success: true });
468+
}
469+
}
449470
}
450471
} catch {
451-
// Ignore error and retry until timeout
472+
// Ignore and retry
452473
}
453474

454475
if (Date.now() - start > timeoutMs) {

src/controllers/mainController.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,7 +1209,14 @@ export default class MainController implements vscode.Disposable {
12091209
// doing it this way instead of directly calling startContainer
12101210
// allows for the object explorer item loading UI to show
12111211
this._objectExplorerProvider.deleteChildrenCache(node);
1212-
await this._objectExplorerProvider.refresh(node);
1212+
await this._objectExplorerProvider.setNodeLoading(node);
1213+
this._objectExplorerProvider.refresh(node);
1214+
await this.objectExplorerTree.reveal(node, {
1215+
select: true,
1216+
focus: true,
1217+
expand: true,
1218+
});
1219+
12131220
await this.connectionManager.connectionUI
12141221
.saveProfile(node.connectionProfile as IConnectionProfile)
12151222
.then(async () => {
@@ -1234,23 +1241,27 @@ export default class MainController implements vscode.Disposable {
12341241
Constants.cmdStopContainer,
12351242
async (node: TreeNodeInfo) => {
12361243
const containerName = node.connectionProfile.containerName;
1237-
const stoppedSuccessfully = await stopContainer(
1238-
node.connectionProfile.containerName,
1239-
);
1240-
vscode.window.showInformationMessage(
1241-
stoppedSuccessfully
1242-
? LocalizedConstants.ContainerDeployment.stoppedContainerSucessfully(
1243-
containerName,
1244-
)
1245-
: LocalizedConstants.ContainerDeployment.failStopContainer(
1246-
containerName,
1247-
),
1248-
);
1249-
if (stoppedSuccessfully) {
1250-
// Disconnect from the node
1251-
await this._objectExplorerProvider.disconnectNode(node as ConnectionNode);
1252-
return this._objectExplorerProvider.refresh(undefined);
1253-
}
1244+
1245+
await this._objectExplorerProvider.setNodeLoading(node);
1246+
this._objectExplorerProvider.refresh(node);
1247+
1248+
await stopContainer(containerName).then(async (stoppedSuccessfully) => {
1249+
if (stoppedSuccessfully) {
1250+
await this._objectExplorerProvider
1251+
.disconnectNode(node as ConnectionNode)
1252+
.then(() => this._objectExplorerProvider.refresh(undefined));
1253+
}
1254+
1255+
vscode.window.showInformationMessage(
1256+
stoppedSuccessfully
1257+
? LocalizedConstants.ContainerDeployment.stoppedContainerSucessfully(
1258+
containerName,
1259+
)
1260+
: LocalizedConstants.ContainerDeployment.failStopContainer(
1261+
containerName,
1262+
),
1263+
);
1264+
});
12541265
},
12551266
),
12561267
);

src/objectExplorer/objectExplorerProvider.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export class ObjectExplorerProvider implements vscode.TreeDataProvider<any> {
5757
}
5858
}
5959

60+
public async setNodeLoading(node: TreeNodeInfo): Promise<void> {
61+
await this._objectExplorerService.setLoadingUiForNode(node);
62+
}
63+
6064
public async createSession(
6165
connectionCredentials?: IConnectionInfo,
6266
): Promise<CreateSessionResult> {

src/objectExplorer/objectExplorerService.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,16 @@ export class ObjectExplorerService {
436436
* one blocked operation can delay the other.
437437
*/
438438
void this.getOrCreateNodeChildrenWithSession(element);
439+
return this.setLoadingUiForNode(element);
440+
}
441+
442+
/**
443+
* Sets a loading UI for the given node.
444+
* This is used to show a loading spinner while the children are being fetched/ other node operations are being performed.
445+
* @param element The node to set the loading UI for
446+
* @returns A loading node that will be displayed in the tree
447+
*/
448+
public async setLoadingUiForNode(element: TreeNodeInfo): Promise<vscode.TreeItem[]> {
439449
const loadingNode = new vscode.TreeItem(
440450
LocalizedConstants.ObjectExplorer.LoadingNodeLabel,
441451
vscode.TreeItemCollapsibleState.None,

0 commit comments

Comments
 (0)