@red-hat-developer-hub/e2e-test-utils 1.1.22 → 1.1.24
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.
- package/dist/deployment/rhdh/deployment.d.ts +18 -1
- package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
- package/dist/deployment/rhdh/deployment.js +135 -51
- package/dist/playwright/fixtures/test.d.ts.map +1 -1
- package/dist/playwright/fixtures/test.js +8 -0
- package/dist/playwright/helpers/common.d.ts +1 -1
- package/dist/playwright/helpers/common.d.ts.map +1 -1
- package/dist/playwright/helpers/common.js +8 -11
- package/dist/playwright/helpers/ui-helper.d.ts.map +1 -1
- package/dist/playwright/helpers/ui-helper.js +1 -0
- package/dist/playwright/teardown-reporter.d.ts +17 -5
- package/dist/playwright/teardown-reporter.d.ts.map +1 -1
- package/dist/playwright/teardown-reporter.js +55 -14
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/kubernetes-client.d.ts.map +1 -1
- package/dist/utils/kubernetes-client.js +0 -2
- package/dist/utils/plugin-metadata.d.ts +52 -96
- package/dist/utils/plugin-metadata.d.ts.map +1 -1
- package/dist/utils/plugin-metadata.js +201 -211
- package/dist/utils/workspace-paths.d.ts +43 -0
- package/dist/utils/workspace-paths.d.ts.map +1 -0
- package/dist/utils/workspace-paths.js +65 -0
- package/package.json +1 -1
|
@@ -10,9 +10,25 @@ export declare class RHDHDeployment {
|
|
|
10
10
|
}): Promise<void>;
|
|
11
11
|
private _applyAppConfig;
|
|
12
12
|
private _applySecrets;
|
|
13
|
+
/** Shared merge strategy for dynamic plugin arrays. */
|
|
14
|
+
private static readonly pluginMergeOpts;
|
|
15
|
+
/**
|
|
16
|
+
* Merges package defaults + auth config (+ optional user config) into a
|
|
17
|
+
* single dynamic plugins configuration.
|
|
18
|
+
*/
|
|
19
|
+
private _mergeBaseConfigs;
|
|
20
|
+
/**
|
|
21
|
+
* Merges a generated plugin config with the base (defaults + auth) config.
|
|
22
|
+
*/
|
|
23
|
+
private _mergeGeneratedWithBase;
|
|
13
24
|
/**
|
|
14
25
|
* Builds the merged dynamic plugins configuration.
|
|
15
|
-
*
|
|
26
|
+
*
|
|
27
|
+
* 1. Assembles raw config: user-provided OR auto-generated from metadata
|
|
28
|
+
* 2. Processes for deployment: injects metadata (PR) + resolves all packages to OCI
|
|
29
|
+
*
|
|
30
|
+
* The processing step is shared — processPluginsForDeployment handles
|
|
31
|
+
* both PR and nightly via isNightlyJob() and GIT_PR_NUMBER detection.
|
|
16
32
|
*/
|
|
17
33
|
private _buildDynamicPluginsConfig;
|
|
18
34
|
private _applyDynamicPlugins;
|
|
@@ -27,6 +43,7 @@ export declare class RHDHDeployment {
|
|
|
27
43
|
scaleDownAndRestart(): Promise<void>;
|
|
28
44
|
waitUntilReady(timeout?: number): Promise<void>;
|
|
29
45
|
teardown(): Promise<void>;
|
|
46
|
+
private _deploymentExists;
|
|
30
47
|
private _resolveChartVersion;
|
|
31
48
|
/**
|
|
32
49
|
* Resolve the semantic version from the "next" tag by looking up the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAwB1E,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAClB,SAAS,yBAAgC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;gBAE9B,SAAS,EAAE,MAAM;IAKvB,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YA0CpD,eAAe;YAmBf,aAAa;IAqB3B,uDAAuD;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAE5B;IAEX;;;OAGG;YACW,iBAAiB;IAY/B;;OAEG;YACW,uBAAuB;IAgBrC;;;;;;;;OAQG;YACW,0BAA0B;YAsC1B,oBAAoB;YAWpB,eAAe;YA6Df,mBAAmB;IAoE3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrC;;;;OAIG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,iBAAiB;YASjB,oBAAoB;IAsClC;;;OAGG;YACW,0BAA0B;IAwCxC,OAAO,CAAC,sBAAsB;IAoCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { KubernetesClientHelper } from "../../utils/kubernetes-client.js";
|
|
2
|
+
import { WorkspacePaths } from "../../utils/workspace-paths.js";
|
|
2
3
|
import { $ } from "../../utils/bash.js";
|
|
3
4
|
import yaml from "js-yaml";
|
|
4
|
-
import
|
|
5
|
+
import os from "os";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { test, request, expect } from "@playwright/test";
|
|
5
8
|
import { mergeYamlFilesIfExists, deepMerge } from "../../utils/merge-yamls.js";
|
|
6
|
-
import {
|
|
9
|
+
import { generatePluginsFromMetadata, processPluginsForDeployment, getNormalizedPluginMergeKey, disablePluginWrappers, } from "../../utils/plugin-metadata.js";
|
|
7
10
|
import { envsubst } from "../../utils/common.js";
|
|
8
11
|
import { runOnce } from "../../playwright/run-once.js";
|
|
9
12
|
import cloneDeepWith from "lodash.clonedeepwith";
|
|
@@ -31,8 +34,11 @@ export class RHDHDeployment {
|
|
|
31
34
|
await this._applyAppConfig();
|
|
32
35
|
await this._applySecrets();
|
|
33
36
|
if (this.deploymentConfig.method === "helm") {
|
|
37
|
+
const isUpgrade = await this._deploymentExists();
|
|
34
38
|
await this._deployWithHelm(this.deploymentConfig.valueFile);
|
|
35
|
-
|
|
39
|
+
if (isUpgrade) {
|
|
40
|
+
await this.scaleDownAndRestart(); // Restart as helm does not monitor config changes
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
else {
|
|
38
44
|
await this._applyDynamicPlugins();
|
|
@@ -69,51 +75,70 @@ export class RHDHDeployment {
|
|
|
69
75
|
});
|
|
70
76
|
await this.k8sClient.applySecretFromObject("rhdh-secrets", substituted, this.deploymentConfig.namespace);
|
|
71
77
|
}
|
|
78
|
+
/** Shared merge strategy for dynamic plugin arrays. */
|
|
79
|
+
static pluginMergeOpts = {
|
|
80
|
+
arrayMergeStrategy: { byKey: "package" },
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Merges package defaults + auth config (+ optional user config) into a
|
|
84
|
+
* single dynamic plugins configuration.
|
|
85
|
+
*/
|
|
86
|
+
async _mergeBaseConfigs(userConfigPath) {
|
|
87
|
+
const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
|
|
88
|
+
const paths = [
|
|
89
|
+
DEFAULT_CONFIG_PATHS.dynamicPlugins,
|
|
90
|
+
authConfig.dynamicPlugins,
|
|
91
|
+
...(userConfigPath ? [userConfigPath] : []),
|
|
92
|
+
];
|
|
93
|
+
return await mergeYamlFilesIfExists(paths, RHDHDeployment.pluginMergeOpts);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Merges a generated plugin config with the base (defaults + auth) config.
|
|
97
|
+
*/
|
|
98
|
+
async _mergeGeneratedWithBase(generatedConfig) {
|
|
99
|
+
const baseConfig = await this._mergeBaseConfigs();
|
|
100
|
+
// Use normalizeKey so OCI and local path for the same logical plugin
|
|
101
|
+
// (e.g., keycloak from metadata OCI + auth local path with -dynamic suffix)
|
|
102
|
+
// are deduplicated; generated (metadata) wins so OCI URL is kept.
|
|
103
|
+
return deepMerge(baseConfig, generatedConfig, {
|
|
104
|
+
arrayMergeStrategy: {
|
|
105
|
+
byKey: "package",
|
|
106
|
+
normalizeKey: (item) => getNormalizedPluginMergeKey(item),
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
72
110
|
/**
|
|
73
111
|
* Builds the merged dynamic plugins configuration.
|
|
74
|
-
*
|
|
112
|
+
*
|
|
113
|
+
* 1. Assembles raw config: user-provided OR auto-generated from metadata
|
|
114
|
+
* 2. Processes for deployment: injects metadata (PR) + resolves all packages to OCI
|
|
115
|
+
*
|
|
116
|
+
* The processing step is shared — processPluginsForDeployment handles
|
|
117
|
+
* both PR and nightly via isNightlyJob() and GIT_PR_NUMBER detection.
|
|
75
118
|
*/
|
|
76
119
|
async _buildDynamicPluginsConfig() {
|
|
77
120
|
const userConfigPath = this.deploymentConfig.dynamicPlugins;
|
|
78
121
|
const userConfigExists = userConfigPath && fs.existsSync(userConfigPath);
|
|
79
|
-
const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
|
|
80
122
|
const wrapperPlugins = disablePluginWrappers(this.deploymentConfig.disableWrappers);
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
this._log(`
|
|
84
|
-
|
|
85
|
-
// Merge with package defaults and auth config. Use normalized plugin key so
|
|
86
|
-
// the same logical plugin (e.g. keycloak from metadata OCI + auth local path)
|
|
87
|
-
// is deduplicated; metadata (source) wins so OCI URL is kept on PR builds.
|
|
88
|
-
const authPlugins = await mergeYamlFilesIfExists([DEFAULT_CONFIG_PATHS.dynamicPlugins, authConfig.dynamicPlugins], { arrayMergeStrategy: { byKey: "package" } });
|
|
89
|
-
const dynamicPlugins = deepMerge(authPlugins, metadataConfig, {
|
|
90
|
-
arrayMergeStrategy: {
|
|
91
|
-
byKey: "package",
|
|
92
|
-
normalizeKey: (item) => getNormalizedPluginMergeKey(item),
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
if (!process.env.GIT_PR_NUMBER) {
|
|
96
|
-
return dynamicPlugins;
|
|
97
|
-
}
|
|
98
|
-
return deepMerge(dynamicPlugins, wrapperPlugins, {
|
|
99
|
-
arrayMergeStrategy: "concat",
|
|
100
|
-
});
|
|
123
|
+
let config;
|
|
124
|
+
if (userConfigExists) {
|
|
125
|
+
this._log(`Using user config: ${userConfigPath}`);
|
|
126
|
+
config = await this._mergeBaseConfigs(userConfigPath);
|
|
101
127
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
await loadAndInjectPluginMetadata(dynamicPluginsConfig);
|
|
128
|
+
else {
|
|
129
|
+
this._log(`No user config at '${userConfigPath}', auto-generating from metadata...`);
|
|
130
|
+
const generated = await generatePluginsFromMetadata(WorkspacePaths.metadataDir);
|
|
131
|
+
config = await this._mergeGeneratedWithBase(generated);
|
|
132
|
+
}
|
|
133
|
+
// Process for deployment: inject metadata (PR only) + resolve all packages to OCI
|
|
134
|
+
let result = await processPluginsForDeployment(config, WorkspacePaths.metadataDir);
|
|
135
|
+
// Disable wrapper plugins (PR builds only)
|
|
111
136
|
if (process.env.GIT_PR_NUMBER) {
|
|
112
|
-
|
|
137
|
+
result = deepMerge(result, wrapperPlugins, {
|
|
113
138
|
arrayMergeStrategy: "concat",
|
|
114
139
|
});
|
|
115
140
|
}
|
|
116
|
-
return
|
|
141
|
+
return result;
|
|
117
142
|
}
|
|
118
143
|
async _applyDynamicPlugins() {
|
|
119
144
|
const dynamicPluginsYaml = await this._buildDynamicPluginsConfig();
|
|
@@ -133,23 +158,64 @@ export class RHDHDeployment {
|
|
|
133
158
|
valueFileObject.global = {};
|
|
134
159
|
}
|
|
135
160
|
valueFileObject.global.dynamic = await this._buildDynamicPluginsConfig();
|
|
161
|
+
// Set catalog index image if CATALOG_INDEX_IMAGE env var is provided.
|
|
162
|
+
// The catalog index provides dynamic-plugins.default.yaml with default plugin
|
|
163
|
+
// configurations and versions for the RHDH release.
|
|
164
|
+
const catalogIndexImage = process.env.CATALOG_INDEX_IMAGE;
|
|
165
|
+
if (catalogIndexImage) {
|
|
166
|
+
const [imageRef, tag] = catalogIndexImage.split(":");
|
|
167
|
+
const firstSlash = imageRef.indexOf("/");
|
|
168
|
+
valueFileObject.global.catalogIndex = {
|
|
169
|
+
image: {
|
|
170
|
+
registry: imageRef.substring(0, firstSlash),
|
|
171
|
+
repository: imageRef.substring(firstSlash + 1),
|
|
172
|
+
tag: tag || "latest",
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
this._log(`Catalog index image: ${catalogIndexImage}`);
|
|
176
|
+
}
|
|
136
177
|
this._logBoxen("Dynamic Plugins", valueFileObject.global.dynamic);
|
|
137
|
-
|
|
178
|
+
// Escape {{inherit}} for Helm's Go template engine.
|
|
179
|
+
// The RHDH chart uses `tpl` on dynamic plugin values, so {{inherit}} would be
|
|
180
|
+
// interpreted as a Go template action. Escaping to {{ "{{inherit}}" }} produces
|
|
181
|
+
// the literal string {{inherit}} after template rendering.
|
|
182
|
+
const valuesYaml = yaml
|
|
183
|
+
.dump(valueFileObject)
|
|
184
|
+
.replace(/\{\{inherit\}\}/g, '{{ "{{inherit}}" }}');
|
|
185
|
+
const valueFilePath = path.join(os.tmpdir(), `${this.deploymentConfig.namespace}-value-file.yaml`);
|
|
186
|
+
fs.writeFileSync(valueFilePath, valuesYaml);
|
|
138
187
|
await $ `
|
|
139
188
|
helm upgrade redhat-developer-hub -i "${process.env.CHART_URL || CHART_URL}" --version "${chartVersion}" \
|
|
140
|
-
-f "
|
|
189
|
+
-f "${valueFilePath}" \
|
|
141
190
|
--set global.clusterRouterBase="${process.env.K8S_CLUSTER_ROUTER_BASE}" \
|
|
142
191
|
--namespace="${this.deploymentConfig.namespace}"
|
|
143
192
|
`;
|
|
144
193
|
this._log(`Helm deployment completed successfully`);
|
|
145
194
|
}
|
|
146
195
|
async _deployWithOperator(subscription) {
|
|
147
|
-
const subscriptionObject = await mergeYamlFilesIfExists([
|
|
196
|
+
const subscriptionObject = (await mergeYamlFilesIfExists([
|
|
148
197
|
DEFAULT_CONFIG_PATHS.operator.subscription,
|
|
149
198
|
subscription,
|
|
150
|
-
]);
|
|
199
|
+
]));
|
|
200
|
+
// Set catalog index image if CATALOG_INDEX_IMAGE env var is provided.
|
|
201
|
+
const catalogIndexImage = process.env.CATALOG_INDEX_IMAGE;
|
|
202
|
+
if (catalogIndexImage) {
|
|
203
|
+
const spec = (subscriptionObject.spec ??= {});
|
|
204
|
+
const app = (spec.application ??= {});
|
|
205
|
+
const extraEnvs = (app.extraEnvs ??=
|
|
206
|
+
{});
|
|
207
|
+
const envs = (extraEnvs.envs ??=
|
|
208
|
+
[]);
|
|
209
|
+
envs.push({
|
|
210
|
+
name: "CATALOG_INDEX_IMAGE",
|
|
211
|
+
value: catalogIndexImage,
|
|
212
|
+
containers: ["install-dynamic-plugins"],
|
|
213
|
+
});
|
|
214
|
+
this._log(`Catalog index image: ${catalogIndexImage}`);
|
|
215
|
+
}
|
|
151
216
|
this._logBoxen("Subscription", subscriptionObject);
|
|
152
|
-
|
|
217
|
+
const subscriptionFilePath = path.join(os.tmpdir(), `${this.deploymentConfig.namespace}-subscription.yaml`);
|
|
218
|
+
fs.writeFileSync(subscriptionFilePath, yaml.dump(subscriptionObject));
|
|
153
219
|
const version = this.deploymentConfig.version;
|
|
154
220
|
const isSemanticVersion = /^\d+(\.\d+)?$/.test(version);
|
|
155
221
|
// Use main branch for non-semantic versions (e.g., "next", "latest")
|
|
@@ -178,7 +244,7 @@ export class RHDHDeployment {
|
|
|
178
244
|
echo "Backstage CRD is created."
|
|
179
245
|
' || echo "Error: Timed out waiting for Backstage CRD creation."
|
|
180
246
|
|
|
181
|
-
oc apply -f "
|
|
247
|
+
oc apply -f "${subscriptionFilePath}" -n "${this.deploymentConfig.namespace}"
|
|
182
248
|
`;
|
|
183
249
|
this._log("Operator deployment executed successfully.");
|
|
184
250
|
}
|
|
@@ -200,19 +266,37 @@ export class RHDHDeployment {
|
|
|
200
266
|
await $ `oc scale deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' --replicas=1 -n ${namespace}`;
|
|
201
267
|
}
|
|
202
268
|
async waitUntilReady(timeout = 500) {
|
|
203
|
-
|
|
269
|
+
const namespace = this.deploymentConfig.namespace;
|
|
204
270
|
const labelSelector = "app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)";
|
|
271
|
+
const startTime = Date.now();
|
|
205
272
|
try {
|
|
206
|
-
await this.k8sClient.waitForPodsWithFailureDetection(
|
|
207
|
-
this._log(`RHDH deployment is ready in namespace ${this.deploymentConfig.namespace}`);
|
|
273
|
+
await this.k8sClient.waitForPodsWithFailureDetection(namespace, labelSelector, timeout);
|
|
208
274
|
}
|
|
209
275
|
catch (error) {
|
|
210
|
-
throw new Error(`RHDH deployment failed in
|
|
276
|
+
throw new Error(`RHDH deployment failed in ${namespace}: ${error instanceof Error ? error.message : error}`);
|
|
211
277
|
}
|
|
278
|
+
// Use remaining timeout for route readiness check
|
|
279
|
+
const remaining = timeout * 1000 - (Date.now() - startTime);
|
|
280
|
+
await expect(async () => {
|
|
281
|
+
const context = await request.newContext({ ignoreHTTPSErrors: true });
|
|
282
|
+
const response = await context.get(this.rhdhUrl);
|
|
283
|
+
await context.dispose();
|
|
284
|
+
expect(response.ok()).toBeTruthy();
|
|
285
|
+
}).toPass({ timeout: Math.max(remaining, 30_000), intervals: [5_000] });
|
|
286
|
+
this._log(`RHDH is ready in ${namespace}`);
|
|
212
287
|
}
|
|
213
288
|
async teardown() {
|
|
214
289
|
await this.k8sClient.deleteNamespace(this.deploymentConfig.namespace);
|
|
215
290
|
}
|
|
291
|
+
async _deploymentExists() {
|
|
292
|
+
try {
|
|
293
|
+
await $ `oc get deployment redhat-developer-hub -n ${this.deploymentConfig.namespace} --no-headers 2>/dev/null`;
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
216
300
|
async _resolveChartVersion(version) {
|
|
217
301
|
let resolvedVersion = version;
|
|
218
302
|
// Handle "next" tag by looking up the corresponding version from downstream image
|
|
@@ -278,23 +362,23 @@ export class RHDHDeployment {
|
|
|
278
362
|
version,
|
|
279
363
|
namespace: input.namespace ?? this.deploymentConfig.namespace,
|
|
280
364
|
auth: input.auth ?? "keycloak",
|
|
281
|
-
appConfig: input.appConfig ??
|
|
282
|
-
secrets: input.secrets ??
|
|
283
|
-
dynamicPlugins: input.dynamicPlugins ??
|
|
365
|
+
appConfig: input.appConfig ?? WorkspacePaths.appConfig,
|
|
366
|
+
secrets: input.secrets ?? WorkspacePaths.secrets,
|
|
367
|
+
dynamicPlugins: input.dynamicPlugins ?? WorkspacePaths.dynamicPlugins,
|
|
284
368
|
disableWrappers: input.disableWrappers ?? [],
|
|
285
369
|
};
|
|
286
370
|
if (method === "helm") {
|
|
287
371
|
return {
|
|
288
372
|
...base,
|
|
289
373
|
method,
|
|
290
|
-
valueFile: input.valueFile ??
|
|
374
|
+
valueFile: input.valueFile ?? WorkspacePaths.valueFile,
|
|
291
375
|
};
|
|
292
376
|
}
|
|
293
377
|
else if (method === "operator") {
|
|
294
378
|
return {
|
|
295
379
|
...base,
|
|
296
380
|
method,
|
|
297
|
-
subscription: input.subscription ??
|
|
381
|
+
subscription: input.subscription ?? WorkspacePaths.subscription,
|
|
298
382
|
};
|
|
299
383
|
}
|
|
300
384
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/playwright/fixtures/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/playwright/fixtures/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAIzC,KAAK,0BAA0B,GAAG;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AAkDF,eAAO,MAAM,IAAI;;CAEf,CAAC;AAEH,cAAc,kBAAkB,CAAC"}
|
|
@@ -2,10 +2,18 @@ import { RHDHDeployment } from "../../deployment/rhdh/index.js";
|
|
|
2
2
|
import { test as base } from "@playwright/test";
|
|
3
3
|
import { LoginHelper, UIhelper } from "../helpers/index.js";
|
|
4
4
|
import { runOnce } from "../run-once.js";
|
|
5
|
+
import { $ } from "../../utils/bash.js";
|
|
6
|
+
import path from "path";
|
|
5
7
|
const baseTest = base.extend({
|
|
6
8
|
rhdhDeploymentWorker: [
|
|
7
9
|
// eslint-disable-next-line no-empty-pattern
|
|
8
10
|
async ({}, use, workerInfo) => {
|
|
11
|
+
// Set CWD to the workspace's e2e-tests directory so that relative
|
|
12
|
+
// config paths resolve correctly even when Playwright runs from the repo root.
|
|
13
|
+
// Each worker is a separate process, so this doesn't affect other workers.
|
|
14
|
+
const e2eRoot = path.resolve(workerInfo.project.testDir, "..");
|
|
15
|
+
process.chdir(e2eRoot);
|
|
16
|
+
$.cwd = e2eRoot;
|
|
9
17
|
const rhdhDeployment = new RHDHDeployment(workerInfo.project.name);
|
|
10
18
|
await rhdhDeployment.configure();
|
|
11
19
|
await use(rhdhDeployment);
|
|
@@ -7,7 +7,7 @@ export declare class LoginHelper {
|
|
|
7
7
|
loginAsGuest(): Promise<void>;
|
|
8
8
|
signOut(): Promise<void>;
|
|
9
9
|
private logintoGithub;
|
|
10
|
-
logintoKeycloak(userid: string, password: string): Promise<void>;
|
|
10
|
+
logintoKeycloak(popup: Page, userid: string, password: string): Promise<void>;
|
|
11
11
|
loginAsKeycloakUser(userid?: string, password?: string): Promise<void>;
|
|
12
12
|
loginAsGithubUser(userid?: string): Promise<void>;
|
|
13
13
|
checkAndReauthorizeGithubApp(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAOhE,qBAAa,WAAW;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;gBAEP,IAAI,EAAE,IAAI;IAKhB,YAAY;IAcZ,OAAO;YAMC,aAAa;IAyCrB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAOhE,qBAAa,WAAW;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;gBAEP,IAAI,EAAE,IAAI;IAKhB,YAAY;IAcZ,OAAO;YAMC,aAAa;IAyCrB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAO7D,mBAAmB,CACvB,MAAM,GAAE,MAAkC,EAC1C,QAAQ,GAAE,MAAkC;IAWxC,iBAAiB,CACrB,MAAM,GAAE,MAA+C;IA6BnD,4BAA4B;IAqB5B,YAAY,CAAC,KAAK,EAAE,MAAM;IA2B1B,2BAA2B,CAAC,KAAK,UAAQ;IAU/C,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIxC,mBAAmB,IAAI,MAAM;IAIvB,mBAAmB;IAgBzB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAevC,eAAe,IAAI,MAAM;IAKnB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;YAqCxC,sBAAsB;IAoD9B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAYjE,2BAA2B,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;IAYb,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CA2C7D;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ;;;GAWtE"}
|
|
@@ -54,22 +54,19 @@ export class LoginHelper {
|
|
|
54
54
|
}
|
|
55
55
|
await this.page.waitForTimeout(3_000);
|
|
56
56
|
}
|
|
57
|
-
async logintoKeycloak(userid, password) {
|
|
58
|
-
await
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await popup.locator("#password").fill(password);
|
|
63
|
-
await popup.locator("#kc-login").click();
|
|
64
|
-
resolve();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
57
|
+
async logintoKeycloak(popup, userid, password) {
|
|
58
|
+
await popup.waitForLoadState();
|
|
59
|
+
await popup.locator("#username").fill(userid);
|
|
60
|
+
await popup.locator("#password").fill(password);
|
|
61
|
+
await popup.locator("#kc-login").click();
|
|
67
62
|
}
|
|
68
63
|
async loginAsKeycloakUser(userid = DEFAULT_USERS[0].username, password = DEFAULT_USERS[0].password) {
|
|
69
64
|
await this.page.goto("/");
|
|
70
65
|
await this.uiHelper.waitForLoad(240000);
|
|
66
|
+
const popupPromise = this.page.waitForEvent("popup");
|
|
71
67
|
await this.uiHelper.clickButton("Sign In");
|
|
72
|
-
await
|
|
68
|
+
const popup = await popupPromise;
|
|
69
|
+
await this.logintoKeycloak(popup, userid, password);
|
|
73
70
|
await this.page.waitForSelector("nav a", { timeout: 10_000 });
|
|
74
71
|
}
|
|
75
72
|
async loginAsGithubUser(userid = process.env.VAULT_GH_USER_ID) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-helper.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/ui-helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAMtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,qBAAa,QAAQ;IACnB,OAAO,CAAC,IAAI,CAAO;gBAEP,IAAI,EAAE,IAAI;IAIhB,WAAW,CAAC,OAAO,SAAS;IAS5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE;IAMnE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIvC,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAItD;;;;OAIG;IACG,sBAAsB,CAAC,UAAU,EAAE,MAAM;IAOzC,oBAAoB,CAAC,UAAU,EAAE,MAAM;IAIvC,QAAQ;IAIR,aAAa,CAAC,IAAI,EAAE,MAAM;IAO1B,eAAe,CAAC,IAAI,EAAE,MAAM;IAO5B,WAAW,CACf,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAG1C;IAgBG,2BAA2B,CAAC,KAAK,EAAE,MAAM;IASzC,iBAAiB,CAAC,UAAU,EAAE,MAAM;IAM1C;;;;;OAKG;IACG,iBAAiB,CACrB,UAAU,EAAE,MAAM,GAAG,MAAM,EAC3B,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;KAKjB;IAkBG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIzC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;IAiBpE,mBAAmB;IAQnB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"ui-helper.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/ui-helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import type { Reporter, Suite } from "@playwright/test/reporter";
|
|
1
|
+
import type { Reporter, Suite, TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
2
|
/**
|
|
3
|
-
* Playwright reporter that deletes namespaces
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Playwright reporter that deletes namespaces per-project as soon as all tests
|
|
4
|
+
* in that project finish. This frees cluster resources early instead of waiting
|
|
5
|
+
* for the entire suite to complete.
|
|
6
|
+
*
|
|
7
|
+
* Handles retries: a test is only counted as done when it passes/is skipped,
|
|
8
|
+
* or exhausts all retry attempts.
|
|
9
|
+
*
|
|
10
|
+
* Falls back in onEnd() to clean up any projects that didn't complete naturally
|
|
11
|
+
* (e.g., interrupted runs, maxFailures).
|
|
12
|
+
*
|
|
13
|
+
* Only active when process.env.CI === "true".
|
|
6
14
|
*
|
|
7
15
|
* By default, deletes the namespace matching the project name.
|
|
8
16
|
* For custom namespaces, consumers can register them via registerTeardownNamespace().
|
|
9
17
|
*/
|
|
10
18
|
export default class TeardownReporter implements Reporter {
|
|
11
|
-
private
|
|
19
|
+
private _projectTestCounts;
|
|
20
|
+
private _projectCompleted;
|
|
21
|
+
private _pendingDeletions;
|
|
12
22
|
onBegin(_config: unknown, suite: Suite): void;
|
|
23
|
+
onTestEnd(test: TestCase, result: TestResult): void;
|
|
13
24
|
onEnd(): Promise<void>;
|
|
25
|
+
private _deleteProjectNamespaces;
|
|
14
26
|
}
|
|
15
27
|
//# sourceMappingURL=teardown-reporter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teardown-reporter.d.ts","sourceRoot":"","sources":["../../src/playwright/teardown-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"teardown-reporter.d.ts","sourceRoot":"","sources":["../../src/playwright/teardown-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAInC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,YAAW,QAAQ;IACvD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,iBAAiB,CAAoC;IAE7D,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAa7C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IA0B7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAed,wBAAwB;CAuBvC"}
|
|
@@ -1,25 +1,68 @@
|
|
|
1
1
|
import { KubernetesClientHelper } from "../utils/kubernetes-client.js";
|
|
2
2
|
import { getTeardownNamespaces } from "./teardown-namespaces.js";
|
|
3
3
|
/**
|
|
4
|
-
* Playwright reporter that deletes namespaces
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Playwright reporter that deletes namespaces per-project as soon as all tests
|
|
5
|
+
* in that project finish. This frees cluster resources early instead of waiting
|
|
6
|
+
* for the entire suite to complete.
|
|
7
|
+
*
|
|
8
|
+
* Handles retries: a test is only counted as done when it passes/is skipped,
|
|
9
|
+
* or exhausts all retry attempts.
|
|
10
|
+
*
|
|
11
|
+
* Falls back in onEnd() to clean up any projects that didn't complete naturally
|
|
12
|
+
* (e.g., interrupted runs, maxFailures).
|
|
13
|
+
*
|
|
14
|
+
* Only active when process.env.CI === "true".
|
|
7
15
|
*
|
|
8
16
|
* By default, deletes the namespace matching the project name.
|
|
9
17
|
* For custom namespaces, consumers can register them via registerTeardownNamespace().
|
|
10
18
|
*/
|
|
11
19
|
export default class TeardownReporter {
|
|
12
|
-
|
|
20
|
+
_projectTestCounts = new Map();
|
|
21
|
+
_projectCompleted = new Map();
|
|
22
|
+
_pendingDeletions = new Map();
|
|
13
23
|
onBegin(_config, suite) {
|
|
14
24
|
for (const test of suite.allTests()) {
|
|
15
25
|
const name = test.parent.project()?.name;
|
|
16
|
-
if (name)
|
|
17
|
-
this.
|
|
26
|
+
if (name) {
|
|
27
|
+
this._projectTestCounts.set(name, (this._projectTestCounts.get(name) ?? 0) + 1);
|
|
28
|
+
this._projectCompleted.set(name, 0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
onTestEnd(test, result) {
|
|
33
|
+
if (process.env.CI !== "true")
|
|
34
|
+
return;
|
|
35
|
+
const project = test.parent.project();
|
|
36
|
+
if (!project)
|
|
37
|
+
return;
|
|
38
|
+
const isDone = result.status === "passed" ||
|
|
39
|
+
result.status === "skipped" ||
|
|
40
|
+
result.retry >= project.retries;
|
|
41
|
+
if (!isDone)
|
|
42
|
+
return;
|
|
43
|
+
const name = project.name;
|
|
44
|
+
const completed = (this._projectCompleted.get(name) ?? 0) + 1;
|
|
45
|
+
this._projectCompleted.set(name, completed);
|
|
46
|
+
// Start deletion immediately (fire-and-forget here, awaited in onEnd)
|
|
47
|
+
if (completed === this._projectTestCounts.get(name) &&
|
|
48
|
+
!this._pendingDeletions.has(name)) {
|
|
49
|
+
this._pendingDeletions.set(name, this._deleteProjectNamespaces(name));
|
|
18
50
|
}
|
|
19
51
|
}
|
|
20
52
|
async onEnd() {
|
|
21
|
-
if (
|
|
53
|
+
if (process.env.CI !== "true")
|
|
22
54
|
return;
|
|
55
|
+
// Await all in-flight deletions started from onTestEnd
|
|
56
|
+
await Promise.all(this._pendingDeletions.values());
|
|
57
|
+
// Fallback: clean up projects that didn't complete naturally
|
|
58
|
+
// (e.g., interrupted run, maxFailures hit)
|
|
59
|
+
for (const [project] of this._projectTestCounts) {
|
|
60
|
+
if (!this._pendingDeletions.has(project)) {
|
|
61
|
+
await this._deleteProjectNamespaces(project);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async _deleteProjectNamespaces(projectName) {
|
|
23
66
|
let k8sClient;
|
|
24
67
|
try {
|
|
25
68
|
k8sClient = new KubernetesClientHelper();
|
|
@@ -28,13 +71,11 @@ export default class TeardownReporter {
|
|
|
28
71
|
console.error(`[TeardownReporter] Cannot connect to cluster, skipping teardown:`, error);
|
|
29
72
|
return;
|
|
30
73
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
await k8sClient.deleteNamespace(ns);
|
|
37
|
-
}
|
|
74
|
+
const customNamespaces = getTeardownNamespaces(projectName);
|
|
75
|
+
const namespaces = customNamespaces.length > 0 ? customNamespaces : [projectName];
|
|
76
|
+
for (const ns of namespaces) {
|
|
77
|
+
console.log(`[TeardownReporter] Deleting namespace "${ns}" (project: ${projectName})`);
|
|
78
|
+
await k8sClient.deleteNamespace(ns);
|
|
38
79
|
}
|
|
39
80
|
}
|
|
40
81
|
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { envsubst, requireEnv } from "./common.js";
|
|
|
2
2
|
export { $, runQuietUnlessFailure } from "./bash.js";
|
|
3
3
|
export { mergeYamlFiles, mergeYamlFilesIfExists, mergeYamlFilesToFile, } from "./merge-yamls.js";
|
|
4
4
|
export { KubernetesClientHelper } from "./kubernetes-client.js";
|
|
5
|
+
export { WorkspacePaths } from "./workspace-paths.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { envsubst, requireEnv } from "./common.js";
|
|
|
2
2
|
export { $, runQuietUnlessFailure } from "./bash.js";
|
|
3
3
|
export { mergeYamlFiles, mergeYamlFilesIfExists, mergeYamlFilesToFile, } from "./merge-yamls.js";
|
|
4
4
|
export { KubernetesClientHelper } from "./kubernetes-client.js";
|
|
5
|
+
export { WorkspacePaths } from "./workspace-paths.js";
|
|
@@ -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,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;
|
|
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;IA2FhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA8BzB;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -514,8 +514,6 @@ class KubernetesClientHelper {
|
|
|
514
514
|
try {
|
|
515
515
|
console.log(`\n[K8sHelper] ─── Pod Status ───`);
|
|
516
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
517
|
console.log(`\n[K8sHelper] ─── Namespace Events ───`);
|
|
520
518
|
await $ `oc get events -n ${namespace} --sort-by='.lastTimestamp'`;
|
|
521
519
|
console.log(`\n[K8sHelper] ─── Pod Logs ───`);
|