Skip to content

[STH] Instance stop and kill body params fix #673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 6, 2022
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
14 changes: 4 additions & 10 deletions packages/api-client/src/instance-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RunnerMessageCode } from "@scramjet/symbols";
import { EncodedControlMessage, STHRestAPI } from "@scramjet/types";
import { EncodedControlMessage, STHRestAPI, StopSequenceMessageData, KillMessageData } from "@scramjet/types";
import { ClientProvider, HttpClient, SendStreamOptions } from "@scramjet/client-utils";

export type InstanceInputStream = "stdin" | "input";
Expand Down Expand Up @@ -54,13 +54,7 @@ export class InstanceClient {
async stop(timeout: number, canCallKeepalive: boolean): Promise<STHRestAPI.SendStopInstanceResponse> {
return this.clientUtils.post<STHRestAPI.SendStopInstanceResponse>(
`${this.instanceURL}/_stop`,
[
RunnerMessageCode.STOP,
{
timeout,
canCallKeepalive,
},
] as EncodedControlMessage,
{ timeout, canCallKeepalive, } as StopSequenceMessageData,
{},
{ json: true, parse: "json" }
);
Expand All @@ -73,10 +67,10 @@ export class InstanceClient {
* @param {boolean} opts.removeImmediately If true, Instance lifetime extension delay will be bypassed.
* @returns {Promise<SendKillInstanceResponse>} Promise resolving to kill Instance result.
*/
async kill(opts = { removeImmediately: false }): Promise<STHRestAPI.SendKillInstanceResponse> {
async kill(opts: KillMessageData = {}): Promise<STHRestAPI.SendKillInstanceResponse> {
return this.clientUtils.post<STHRestAPI.SendKillInstanceResponse>(
`${this.instanceURL}/_kill`,
[RunnerMessageCode.KILL, { removeImmediately: opts.removeImmediately }] as EncodedControlMessage,
opts,
{},
{ json: true, parse: "json" }
);
Expand Down
57 changes: 29 additions & 28 deletions packages/host/src/lib/csi-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
InstanceStatus,
MonitoringMessageData,
InstanceStats,
OpResponse
OpResponse,
StopSequenceMessageData,
} from "@scramjet/types";
import {
AppError,
Expand All @@ -29,7 +30,6 @@ import {
HostError,
MessageUtilities,
InstanceAdapterError,
isStopSequenceMessage
} from "@scramjet/model";
import { CommunicationChannel as CC, RunnerExitCode, RunnerMessageCode } from "@scramjet/symbols";
import { PassThrough, Readable } from "stream";
Expand Down Expand Up @@ -198,7 +198,7 @@ export class CSIController extends TypedEmitter<Events> {

this.logger.debug("Constructor executed");
this.info.created = new Date();
this.status = "initializing";
this.status = InstanceStatus.INITIALIZING;
}

async start() {
Expand All @@ -209,15 +209,15 @@ export class CSIController extends TypedEmitter<Events> {

i.then(() => this.main()).catch(e => {
this.logger.info("Instance status: errored", e);
this.status = "errored";
this.status = InstanceStatus.ERRORED;
this.emit("error", e);
});

return i;
}

async main() {
this.status = "running";
this.status = InstanceStatus.RUNNING;
this.logger.trace("Instance started");

let code = 0;
Expand All @@ -236,7 +236,7 @@ export class CSIController extends TypedEmitter<Events> {
this.logger.error("Instance caused error", e.message, code);
}

this.status = !errored ? "completed" : "errored";
this.status = !errored ? InstanceStatus.COMPLETED : InstanceStatus.ERRORED;

this.info.ended = new Date();

Expand All @@ -260,7 +260,7 @@ export class CSIController extends TypedEmitter<Events> {

const instanceMain = async () => {
try {
this.status = "starting";
this.status = InstanceStatus.STARTING;

await this.instanceAdapter.init();

Expand All @@ -276,15 +276,15 @@ export class CSIController extends TypedEmitter<Events> {
const exitcode = await this.endOfSequence;

if (exitcode > 0) {
this.status = "errored";
this.status = InstanceStatus.ERRORED;
this.logger.error("Crashlog", await this.instanceAdapter.getCrashLog());
}

await this.cleanup();

return exitcode;
} catch (error: any) {
this.status = "errored";
this.status = InstanceStatus.ERRORED;
this.logger.error("Error caught", error.stack);

await this.cleanup();
Expand Down Expand Up @@ -642,17 +642,19 @@ export class CSIController extends TypedEmitter<Events> {
}

private async handleStop(req: ParsedMessage): Promise<OpResponse<STHRestAPI.SendStopInstanceResponse>> {
const message = (req.body || []) as EncodedMessage<RunnerMessageCode.STOP>;
const { body: { timeout = 7000, canCallKeepalive = false } = { timeout: 7000, canCallKeepalive: false } } = req;

if (!isStopSequenceMessage(message[1] || {})) {
return { opStatus: ReasonPhrases.BAD_REQUEST };
if (typeof timeout !== "number") {
return { opStatus: ReasonPhrases.BAD_REQUEST, error: "Invalid timeout format" };
}
if (typeof canCallKeepalive !== "boolean") {
return { opStatus: ReasonPhrases.BAD_REQUEST, error: "Invalid canCallKeepalive format" };
}
const message: StopSequenceMessageData = { timeout, canCallKeepalive };

this.status = "stopping";

await this.communicationHandler.sendControlMessage(...message);
this.status = InstanceStatus.STOPPING;

const [, { timeout }] = message;
await this.communicationHandler.sendControlMessage(RunnerMessageCode.STOP, message);

this.keepAliveRequested = false;

Expand All @@ -667,29 +669,28 @@ export class CSIController extends TypedEmitter<Events> {
}

private async handleKill(req: ParsedMessage): Promise<OpResponse<STHRestAPI.SendKillInstanceResponse>> {
if (this.status === "killing") {
const { body: { removeImmediately = false } = { removeImmediately: false } } = req;

if (typeof removeImmediately !== "boolean")
return { opStatus: ReasonPhrases.BAD_REQUEST, error: "Invalid removeImmediately format" };

if (this.status === InstanceStatus.KILLING) {
await this.instanceAdapter.remove();
}
this.status = "killing";
this.status = InstanceStatus.KILLING;

await this.communicationHandler.sendControlMessage(RunnerMessageCode.KILL, {});

// This will be resolved after HTTP response. It's not awaited on purpose
promiseTimeout(this.endOfSequence, runnerExitDelay)
.catch(() => this.instanceAdapter.remove());

try {
const message = req.body as EncodedMessage<RunnerMessageCode.KILL>;

if (message[1].removeImmediately) {
this.instanceLifetimeExtensionDelay = 0;
if (removeImmediately) {
this.instanceLifetimeExtensionDelay = 0;

if (this.finalizingPromise) {
this.finalizingPromise.cancel();
}
if (this.finalizingPromise) {
this.finalizingPromise.cancel();
}
} catch (e) {
this.logger.warn("Failed to parse KILL message", e);
}

return {
Expand Down
10 changes: 9 additions & 1 deletion packages/types/src/instance.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
export type InstanceId = string;
export type InstanceStatus = "initializing" | "starting" | "running" | "stopping" | "killing" | "completed" | "errored";
export const enum InstanceStatus {
INITIALIZING = "initializing",
STARTING = "starting",
RUNNING = "running",
STOPPING = "stopping",
KILLING = "killing",
COMPLETED ="completed",
ERRORED = "errored",
}
2 changes: 1 addition & 1 deletion packages/types/src/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export { EventMessage, EventMessageData } from "./event";
export { PangMessageData, PingMessageData, HandshakeMessage } from "./handshake";
export { HandshakeAcknowledgeMessage, HandshakeAcknowledgeMessageData } from "./handshake-acknowledge";
export { KeepAliveMessage, KeepAliveMessageData } from "./keep-alive";
export { KillSequenceMessage } from "./kill-sequence";
export { KillSequenceMessage, KillMessageData } from "./kill-sequence";
export { EmptyMessageData, Message } from "./message";
export { MonitoringRateMessage, MonitoringRateMessageData } from "./monitor-rate";
export { MonitoringMessage, MonitoringMessageData, MonitoringMessageFromRunnerData } from "./monitoring";
Expand Down