Skip to content

Commit f839264

Browse files
committed
Fix and enrich pods limit error message
1 parent 81b66b7 commit f839264

File tree

3 files changed

+67
-32
lines changed

3 files changed

+67
-32
lines changed

packages/adapters/src/kubernetes-client-adapter.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class KubernetesClientAdapter {
137137
return response.body.status?.containerStatuses?.[0].state?.terminated?.reason;
138138
}
139139

140-
async isPodsLimitReached(quotaName: string) {
140+
async getPodsLimit(quotaName: string) {
141141
const kubeApi = this.config.makeApiClient(k8s.CoreV1Api);
142142

143143
try {
@@ -152,13 +152,19 @@ class KubernetesClientAdapter {
152152

153153
this.logger.info("Pods limit quota", used, hard);
154154

155-
return used >= hard;
155+
return { used, hard };
156156
}
157157
} catch (e) {
158158
this.logger.warn("Can't get quota object. ");
159159
}
160160

161-
return false;
161+
return { used: 0, hard: 0 }; // quota failed - no pods!
162+
}
163+
164+
async isPodsLimitReached(quotaName: string) {
165+
const { used, hard } = await this.getPodsLimit(quotaName);
166+
167+
return used >= hard;
162168
}
163169

164170
private async runWithRetries(retries: number, name: string, callback: any) {

packages/adapters/src/kubernetes-instance-adapter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ IComponent {
8888
};
8989
}
9090

91+
async getPodsLimit() {
92+
if (this.adapterConfig.quotaName) {
93+
return this.kubeClient.getPodsLimit(this.adapterConfig.quotaName);
94+
}
95+
96+
this.logger.warn("Quota name not provided");
97+
98+
return undefined;
99+
}
100+
91101
async run(config: InstanceConfig, instancesServerPort: number, instanceId: string): Promise<ExitCode> {
92102
if (config.type !== "kubernetes") {
93103
throw new Error(`Invalid config type for kubernetes adapter: ${config.type}`);

packages/host/src/lib/csi-controller.ts

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import { EventEmitter, once } from "events";
4040
import { ServerResponse } from "http";
4141
import { DuplexStream, getRouter } from "@scramjet/api-server";
4242

43-
import { getInstanceAdapter } from "@scramjet/adapters";
43+
import { getInstanceAdapter, KubernetesInstanceAdapter } from "@scramjet/adapters";
4444
import { cancellableDefer, CancellablePromise, defer, promiseTimeout, TypedEmitter } from "@scramjet/utility";
4545
import { ObjLogger } from "@scramjet/obj-logger";
4646
import { ReasonPhrases } from "http-status-codes";
@@ -330,71 +330,90 @@ export class CSIController extends TypedEmitter<Events> {
330330
});
331331
}
332332

333-
private mapRunnerExitCode(exitcode: number): Promise<
333+
async handlePodsLimitError(): Promise<never> {
334+
let msg = "Instance limit reached";
335+
336+
if (this.instanceAdapter instanceof KubernetesInstanceAdapter) {
337+
const limit = await this.instanceAdapter.getPodsLimit();
338+
339+
msg += limit ? ` (${limit.used}/${limit.hard})` : "";
340+
341+
return Promise.reject({
342+
message: msg,
343+
exitcode: RunnerExitCode.PODS_LIMIT_REACHED,
344+
status: InstanceStatus.ERRORED
345+
});
346+
}
347+
348+
this.logger.warn("Incorrect Adapter exitcode");
349+
350+
return Promise.reject({
351+
message: msg,
352+
exitcode: RunnerExitCode.PODS_LIMIT_REACHED,
353+
status: InstanceStatus.ERRORED
354+
});
355+
}
356+
357+
// eslint-disable-next-line complexity
358+
private async mapRunnerExitCode(exitcode: number): Promise<
334359
{ message: string, exitcode: number, reason: TerminateReason}
335360
> {
336-
// eslint-disable-next-line default-case
337361
switch (exitcode) {
338-
case RunnerExitCode.INVALID_ENV_VARS: {
362+
case RunnerExitCode.SUCCESS:
363+
return Promise.resolve({ message: "Instance completed", exitcode, reason: TerminateReason.COMPLETED, status: InstanceStatus.COMPLETED });
364+
365+
case RunnerExitCode.INVALID_ENV_VARS:
339366
return Promise.reject({
340367
message: "Runner was started with invalid configuration. This is probably a bug in STH.",
341368
exitcode: RunnerExitCode.INVALID_ENV_VARS,
342369
status: InstanceStatus.ERRORED
343370
});
344-
}
345-
case RunnerExitCode.PODS_LIMIT_REACHED: {
346-
return Promise.reject({
347-
message: "Instance limit reached",
348-
exitcode: RunnerExitCode.PODS_LIMIT_REACHED,
349-
status: InstanceStatus.ERRORED
350-
});
351-
}
352-
case RunnerExitCode.INVALID_SEQUENCE_PATH: {
371+
372+
case RunnerExitCode.PODS_LIMIT_REACHED:
373+
return this.handlePodsLimitError();
374+
375+
case RunnerExitCode.INVALID_SEQUENCE_PATH:
353376
return Promise.reject({
354377
message: `Sequence entrypoint path ${this.sequence.config.entrypointPath} is invalid. ` +
355378
"Check `main` field in Sequence package.json",
356379
exitcode: RunnerExitCode.INVALID_SEQUENCE_PATH,
357380
status: InstanceStatus.ERRORED
358381
});
359-
}
360-
case RunnerExitCode.SEQUENCE_FAILED_ON_START: {
382+
383+
case RunnerExitCode.SEQUENCE_FAILED_ON_START:
361384
return Promise.reject({
362385
message: "Sequence failed on start",
363386
exitcode: RunnerExitCode.SEQUENCE_FAILED_ON_START,
364387
status: InstanceStatus.ERRORED
365388
});
366-
}
367-
case RunnerExitCode.SEQUENCE_FAILED_DURING_EXECUTION: {
389+
390+
case RunnerExitCode.SEQUENCE_FAILED_DURING_EXECUTION:
368391
return Promise.reject({
369392
message: "Sequence failed during execution",
370393
exitcode: RunnerExitCode.SEQUENCE_FAILED_DURING_EXECUTION,
371394
status: InstanceStatus.ERRORED
372395
});
373-
}
374-
case RunnerExitCode.SEQUENCE_UNPACK_FAILED: {
396+
397+
case RunnerExitCode.SEQUENCE_UNPACK_FAILED:
375398
return Promise.reject({
376399
message: "Sequence unpack failed",
377400
exitcode: RunnerExitCode.SEQUENCE_UNPACK_FAILED,
378401
status: InstanceStatus.ERRORED
379402
});
380-
}
381-
case RunnerExitCode.KILLED: {
403+
404+
case RunnerExitCode.KILLED:
382405
return Promise.resolve({
383406
message: "Instance killed", exitcode: RunnerExitCode.KILLED, reason: TerminateReason.KILLED, status: InstanceStatus.COMPLETED
384407
});
385-
}
386-
case RunnerExitCode.STOPPED: {
408+
409+
case RunnerExitCode.STOPPED:
387410
return Promise.resolve({
388411
message: "Instance stopped", exitcode: RunnerExitCode.STOPPED, reason: TerminateReason.STOPPED, status: InstanceStatus.COMPLETED
389412
});
390-
}
391-
}
392413

393-
if (exitcode > 0) {
394-
return Promise.reject({ message: "Runner failed", exitcode, reason: TerminateReason.ERRORED, status: InstanceStatus.ERRORED });
414+
default:
415+
return Promise.reject({ message: "Runner failed", exitcode, reason: TerminateReason.ERRORED, status: InstanceStatus.ERRORED });
395416
}
396-
397-
return Promise.resolve({ message: "Instance completed", exitcode, reason: TerminateReason.COMPLETED, status: InstanceStatus.COMPLETED });
398417
}
399418

400419
async cleanup() {

0 commit comments

Comments
 (0)