rhdh-e2e-test-utils 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -293,7 +293,7 @@ export class KeycloakHelper {
293
293
  /**
294
294
  * Wait for Keycloak to be ready
295
295
  */
296
- async waitUntilReady(timeout = 300) {
296
+ async waitUntilReady(timeout = 500) {
297
297
  this._log(`Waiting for Keycloak to be ready...`);
298
298
  const labelSelector = `app.kubernetes.io/instance=${this.deploymentConfig.releaseName}`;
299
299
  await this.k8sClient.waitForPodsWithFailureDetection(this.deploymentConfig.namespace, labelSelector, timeout);
@@ -343,14 +343,14 @@ spec:
343
343
  }
344
344
  async _waitForKeycloak() {
345
345
  this._log("Waiting for Keycloak API to be ready...");
346
- const timeout = 300;
346
+ const timeout = 500;
347
347
  const startTime = Date.now();
348
348
  while (true) {
349
349
  if (await this.isRunning()) {
350
350
  break;
351
351
  }
352
352
  if ((Date.now() - startTime) / 1000 >= timeout) {
353
- throw new Error("Keycloak API not ready after 5 minutes");
353
+ throw new Error(`Keycloak API not ready after ${timeout} seconds`);
354
354
  }
355
355
  await new Promise((resolve) => setTimeout(resolve, 5000));
356
356
  this._log(" Waiting for Keycloak API to be ready...");
@@ -21,7 +21,7 @@ export class RHDHDeployment {
21
21
  }
22
22
  async deploy() {
23
23
  this._log("Starting RHDH deployment...");
24
- test.setTimeout(500_000);
24
+ test.setTimeout(600_000);
25
25
  await this.k8sClient.createNamespaceIfNotExists(this.deploymentConfig.namespace);
26
26
  await this._applyAppConfig();
27
27
  await this._applySecrets();
@@ -173,7 +173,7 @@ export class RHDHDeployment {
173
173
  await $ `oc wait --for=delete pod -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub),app.kubernetes.io/name!=postgresql' -n ${namespace} --timeout=120s || true`;
174
174
  await $ `oc scale deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' --replicas=1 -n ${namespace}`;
175
175
  }
176
- async waitUntilReady(timeout = 300) {
176
+ async waitUntilReady(timeout = 500) {
177
177
  this._log(`Waiting for RHDH deployment to be ready in namespace ${this.deploymentConfig.namespace}...`);
178
178
  const labelSelector = "app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)";
179
179
  try {
@@ -7,7 +7,7 @@ import { resolve } from "path";
7
7
  export const baseConfig = {
8
8
  testDir: "./tests",
9
9
  forbidOnly: !!process.env.CI,
10
- retries: process.env.CI ? 2 : 0,
10
+ retries: process.env.CI ? 1 : 0,
11
11
  workers: "50%",
12
12
  outputDir: "node_modules/.cache/e2e-test-results",
13
13
  timeout: 90_000,
@@ -37,9 +37,18 @@ declare class KubernetesClientHelper {
37
37
  stringData?: Record<string, string>;
38
38
  }, namespace: string): Promise<void>;
39
39
  /**
40
- * Delete a namespace
40
+ * Delete a namespace and wait for it to be fully terminated
41
41
  */
42
- deleteNamespace(namespace: string): Promise<void>;
42
+ deleteNamespace(namespace: string, waitForDeletion?: boolean, timeoutSeconds?: number): Promise<void>;
43
+ /**
44
+ * Wait for a namespace to be fully deleted
45
+ */
46
+ private _waitForNamespaceDeletion;
47
+ /**
48
+ * Check if an error is a "not found" (404) error.
49
+ * Handles different error formats from various k8s client versions.
50
+ */
51
+ private _isNotFoundError;
43
52
  /**
44
53
  * Check if a StatefulSet is ready (all replicas are available)
45
54
  */
@@ -1 +1 @@
1
- {"version":3,"file":"kubernetes-client.d.ts","sourceRoot":"","sources":["../../src/utils/kubernetes-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAO/C;;GAEG;AACH,cAAM,sBAAsB;IAC1B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,iBAAiB,CAAuB;;IAoChD;;OAEG;IACG,uBAAuB,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA8C3B;;OAEG;IACG,0BAA0B,CAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA+B3B;;UAEM;IAmBN;;OAEG;IAsBH;;OAEG;YACW,YAAY;IA8B1B;;OAEG;IACG,wBAAwB,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IA0ChB;;OAEG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BvD;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc3E;;OAEG;IACG,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC;IAsBhD;;;;;;OAMG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBxE;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAQnC;IAEH;;;;;;;;OAQG;IACG,+BAA+B,CACnC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,IAAI,CAAC;IAiEhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA8BzB;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"kubernetes-client.d.ts","sourceRoot":"","sources":["../../src/utils/kubernetes-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAO/C;;GAEG;AACH,cAAM,sBAAsB;IAC1B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,iBAAiB,CAAuB;;IAoChD;;OAEG;IACG,uBAAuB,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA8C3B;;OAEG;IACG,0BAA0B,CAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA+B3B;;UAEM;IAmBN;;OAEG;IAsBH;;OAEG;YACW,YAAY;IA8B1B;;OAEG;IACG,wBAAwB,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IA0ChB;;OAEG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,eAAe,CACnB,SAAS,EAAE,MAAM,EACjB,eAAe,GAAE,OAAc,EAC/B,cAAc,GAAE,MAAY,GAC3B,OAAO,CAAC,IAAI,CAAC;IAyBhB;;OAEG;YACW,yBAAyB;IAsCvC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc3E;;OAEG;IACG,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC;IAsBhD;;;;;;OAMG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBxE;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAQnC;IAEH;;;;;;;;OAQG;IACG,+BAA+B,CACnC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,IAAI,CAAC;IA8FhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA8BzB;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
@@ -265,26 +265,80 @@ class KubernetesClientHelper {
265
265
  }
266
266
  }
267
267
  /**
268
- * Delete a namespace
268
+ * Delete a namespace and wait for it to be fully terminated
269
269
  */
270
- async deleteNamespace(namespace) {
270
+ async deleteNamespace(namespace, waitForDeletion = true, timeoutSeconds = 180) {
271
271
  try {
272
272
  await this._k8sApi.deleteNamespace({ name: namespace });
273
- console.log(`✓ Deleted namespace ${namespace}`);
273
+ console.log(`[K8sHelper] Deleting namespace ${namespace}...`);
274
274
  }
275
275
  catch (error) {
276
276
  // Ignore if namespace doesn't exist (already deleted), but throw other errors
277
- const err = error;
278
- if (err.body?.code === 404 ||
279
- err.response?.statusCode === 404 ||
280
- err.statusCode === 404) {
277
+ if (this._isNotFoundError(error)) {
281
278
  console.log(`✓ Namespace ${namespace} already deleted or doesn't exist`);
279
+ return;
282
280
  }
283
281
  else {
284
282
  console.error(`✗ Failed to delete namespace ${namespace}:`, error instanceof Error ? error.message : error);
285
283
  throw error;
286
284
  }
287
285
  }
286
+ if (waitForDeletion) {
287
+ await this._waitForNamespaceDeletion(namespace, timeoutSeconds);
288
+ }
289
+ }
290
+ /**
291
+ * Wait for a namespace to be fully deleted
292
+ */
293
+ async _waitForNamespaceDeletion(namespace, timeoutSeconds = 180) {
294
+ const startTime = Date.now();
295
+ const timeoutMs = timeoutSeconds * 1000;
296
+ const pollIntervalMs = 3000;
297
+ while (Date.now() - startTime < timeoutMs) {
298
+ try {
299
+ const ns = await this._k8sApi.readNamespace({ name: namespace });
300
+ const phase = ns.status?.phase;
301
+ // Namespace still exists, wait and retry
302
+ if (phase === "Terminating") {
303
+ // Only log occasionally to avoid spam
304
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
305
+ if (elapsed % 10 === 0) {
306
+ console.log(`[K8sHelper] Namespace ${namespace} still terminating (${elapsed}s)...`);
307
+ }
308
+ }
309
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
310
+ }
311
+ catch (error) {
312
+ // Check for 404 in various error formats from different k8s client versions
313
+ if (this._isNotFoundError(error)) {
314
+ console.log(`✓ Namespace ${namespace} fully deleted`);
315
+ return;
316
+ }
317
+ throw error;
318
+ }
319
+ }
320
+ throw new Error(`Timeout waiting for namespace ${namespace} to be deleted after ${timeoutSeconds}s`);
321
+ }
322
+ /**
323
+ * Check if an error is a "not found" (404) error.
324
+ * Handles different error formats from various k8s client versions.
325
+ */
326
+ _isNotFoundError(error) {
327
+ if (!error)
328
+ return false;
329
+ // Check error message for "404" or "not found"
330
+ if (error instanceof Error) {
331
+ const msg = error.message.toLowerCase();
332
+ if (msg.includes("404") || msg.includes("not found")) {
333
+ return true;
334
+ }
335
+ }
336
+ // Check various object properties for 404 status codes
337
+ const err = error;
338
+ return (err.body?.code === 404 ||
339
+ err.response?.statusCode === 404 ||
340
+ err.statusCode === 404 ||
341
+ err.code === 404);
288
342
  }
289
343
  /**
290
344
  * Check if a StatefulSet is ready (all replicas are available)
@@ -397,7 +451,7 @@ class KubernetesClientHelper {
397
451
  * @param timeoutSeconds - Maximum time to wait (default: 300)
398
452
  * @param pollIntervalMs - How often to check pod status (default: 5000)
399
453
  */
400
- async waitForPodsWithFailureDetection(namespace, labelSelector, timeoutSeconds = 300, pollIntervalMs = 5000) {
454
+ async waitForPodsWithFailureDetection(namespace, labelSelector, timeoutSeconds = 500, pollIntervalMs = 5000) {
401
455
  const startTime = Date.now();
402
456
  const timeoutMs = timeoutSeconds * 1000;
403
457
  console.log(`[K8sHelper] Waiting for pods (${labelSelector}) in ${namespace}...`);
@@ -443,8 +497,34 @@ class KubernetesClientHelper {
443
497
  console.log(`[K8sHelper] All ${pods.length} pod(s) ready in ${namespace}`);
444
498
  return;
445
499
  }
500
+ // Log pod status every 20 seconds
501
+ const elapsedSec = Math.floor((Date.now() - startTime) / 1000);
502
+ if (elapsedSec > 0 && elapsedSec % 20 === 0) {
503
+ try {
504
+ await $ `oc get pods -n ${namespace} -l ${labelSelector}`;
505
+ }
506
+ catch {
507
+ // Ignore errors
508
+ }
509
+ }
446
510
  await new Promise((r) => setTimeout(r, pollIntervalMs));
447
511
  }
512
+ // Timeout reached - collect diagnostic info before throwing
513
+ console.log(`\n[K8sHelper] ═══ Pod Diagnostics (timeout reached) ═══`);
514
+ try {
515
+ console.log(`\n[K8sHelper] ─── Pod Status ───`);
516
+ await $ `oc get pods -n ${namespace} -l ${labelSelector} -o wide`;
517
+ console.log(`\n[K8sHelper] ─── Pod Details ───`);
518
+ await $ `oc describe pods -n ${namespace} -l ${labelSelector}`;
519
+ console.log(`\n[K8sHelper] ─── Namespace Events ───`);
520
+ await $ `oc get events -n ${namespace} --sort-by='.lastTimestamp'`;
521
+ console.log(`\n[K8sHelper] ─── Pod Logs ───`);
522
+ await $ `oc logs -n ${namespace} -l ${labelSelector} --all-containers --tail=100 2>&1 || true`;
523
+ }
524
+ catch {
525
+ // Ignore errors from diagnostic commands
526
+ }
527
+ console.log(`\n[K8sHelper] ═══ End Pod Diagnostics ═══\n`);
448
528
  throw new Error(`Timeout waiting for pods (${labelSelector}) after ${timeoutSeconds}s`);
449
529
  }
450
530
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhdh-e2e-test-utils",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "Test utilities for RHDH E2E tests",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",