Skip to content

Commit 1ea13d9

Browse files
committed
Fix and enrich pods limit error message
1 parent 79bfbab commit 1ea13d9

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";
@@ -324,71 +324,90 @@ export class CSIController extends TypedEmitter<Events> {
324324
});
325325
}
326326

327-
private mapRunnerExitCode(exitcode: number): Promise<
327+
async handlePodsLimitError(): Promise<never> {
328+
let msg = "Instances limit reached";
329+
330+
if (this.instanceAdapter instanceof KubernetesInstanceAdapter) {
331+
const limit = await this.instanceAdapter.getPodsLimit();
332+
333+
msg = limit ? `Instances limit reached ${limit.used}/${limit.hard}` : msg;
334+
335+
return Promise.reject({
336+
message: msg,
337+
exitcode: RunnerExitCode.PODS_LIMIT_REACHED,
338+
status: InstanceStatus.ERRORED
339+
});
340+
}
341+
342+
this.logger.warn("Incorrect Adapter exitcode");
343+
344+
return Promise.reject({
345+
message: msg,
346+
exitcode: RunnerExitCode.PODS_LIMIT_REACHED,
347+
status: InstanceStatus.ERRORED
348+
});
349+
}
350+
351+
// eslint-disable-next-line complexity
352+
private async mapRunnerExitCode(exitcode: number): Promise<
328353
{ message: string, exitcode: number, reason: TerminateReason}
329354
> {
330-
// eslint-disable-next-line default-case
331355
switch (exitcode) {
332-
case RunnerExitCode.INVALID_ENV_VARS: {
356+
case RunnerExitCode.SUCCESS:
357+
return Promise.resolve({ message: "Instance completed", exitcode, reason: TerminateReason.COMPLETED, status: InstanceStatus.COMPLETED });
358+
359+
case RunnerExitCode.INVALID_ENV_VARS:
333360
return Promise.reject({
334361
message: "Runner was started with invalid configuration. This is probably a bug in STH.",
335362
exitcode: RunnerExitCode.INVALID_ENV_VARS,
336363
status: InstanceStatus.ERRORED
337364
});
338-
}
339-
case RunnerExitCode.PODS_LIMIT_REACHED: {
340-
return Promise.reject({
341-
message: "Pods limit reached",
342-
exitcode: RunnerExitCode.PODS_LIMIT_REACHED,
343-
status: InstanceStatus.ERRORED
344-
});
345-
}
346-
case RunnerExitCode.INVALID_SEQUENCE_PATH: {
365+
366+
case RunnerExitCode.PODS_LIMIT_REACHED:
367+
return this.handlePodsLimitError();
368+
369+
case RunnerExitCode.INVALID_SEQUENCE_PATH:
347370
return Promise.reject({
348371
message: `Sequence entrypoint path ${this.sequence.config.entrypointPath} is invalid. ` +
349372
"Check `main` field in Sequence package.json",
350373
exitcode: RunnerExitCode.INVALID_SEQUENCE_PATH,
351374
status: InstanceStatus.ERRORED
352375
});
353-
}
354-
case RunnerExitCode.SEQUENCE_FAILED_ON_START: {
376+
377+
case RunnerExitCode.SEQUENCE_FAILED_ON_START:
355378
return Promise.reject({
356379
message: "Sequence failed on start",
357380
exitcode: RunnerExitCode.SEQUENCE_FAILED_ON_START,
358381
status: InstanceStatus.ERRORED
359382
});
360-
}
361-
case RunnerExitCode.SEQUENCE_FAILED_DURING_EXECUTION: {
383+
384+
case RunnerExitCode.SEQUENCE_FAILED_DURING_EXECUTION:
362385
return Promise.reject({
363386
message: "Sequence failed during execution",
364387
exitcode: RunnerExitCode.SEQUENCE_FAILED_DURING_EXECUTION,
365388
status: InstanceStatus.ERRORED
366389
});
367-
}
368-
case RunnerExitCode.SEQUENCE_UNPACK_FAILED: {
390+
391+
case RunnerExitCode.SEQUENCE_UNPACK_FAILED:
369392
return Promise.reject({
370393
message: "Sequence unpack failed",
371394
exitcode: RunnerExitCode.SEQUENCE_UNPACK_FAILED,
372395
status: InstanceStatus.ERRORED
373396
});
374-
}
375-
case RunnerExitCode.KILLED: {
397+
398+
case RunnerExitCode.KILLED:
376399
return Promise.resolve({
377400
message: "Instance killed", exitcode: RunnerExitCode.KILLED, reason: TerminateReason.KILLED, status: InstanceStatus.COMPLETED
378401
});
379-
}
380-
case RunnerExitCode.STOPPED: {
402+
403+
case RunnerExitCode.STOPPED:
381404
return Promise.resolve({
382405
message: "Instance stopped", exitcode: RunnerExitCode.STOPPED, reason: TerminateReason.STOPPED, status: InstanceStatus.COMPLETED
383406
});
384-
}
385-
}
386407

387-
if (exitcode > 0) {
388-
return Promise.reject({ message: "Runner failed", exitcode, reason: TerminateReason.ERRORED, status: InstanceStatus.ERRORED });
408+
default:
409+
return Promise.reject({ message: "Runner failed", exitcode, reason: TerminateReason.ERRORED, status: InstanceStatus.ERRORED });
389410
}
390-
391-
return Promise.resolve({ message: "Instance completed", exitcode, reason: TerminateReason.COMPLETED, status: InstanceStatus.COMPLETED });
392411
}
393412

394413
async cleanup() {

0 commit comments

Comments
 (0)