rhdh-e2e-test-utils 1.0.1 → 1.1.0

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.
Files changed (95) hide show
  1. package/README.md +577 -56
  2. package/dist/deployment/keycloak/config/keycloak-values.yaml +94 -0
  3. package/dist/deployment/keycloak/constants.d.ts +29 -0
  4. package/dist/deployment/keycloak/constants.d.ts.map +1 -0
  5. package/dist/deployment/keycloak/constants.js +75 -0
  6. package/dist/deployment/keycloak/deployment.d.ts +89 -0
  7. package/dist/deployment/keycloak/deployment.d.ts.map +1 -0
  8. package/dist/deployment/keycloak/deployment.js +437 -0
  9. package/dist/deployment/keycloak/index.d.ts +2 -0
  10. package/dist/deployment/keycloak/index.d.ts.map +1 -0
  11. package/dist/deployment/keycloak/index.js +1 -0
  12. package/dist/deployment/keycloak/types.d.ts +59 -0
  13. package/dist/deployment/keycloak/types.d.ts.map +1 -0
  14. package/dist/deployment/keycloak/types.js +1 -0
  15. package/dist/deployment/rhdh/config/auth/guest/app-config.yaml +5 -0
  16. package/dist/deployment/rhdh/config/auth/keycloak/app-config.yaml +19 -0
  17. package/dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml +3 -0
  18. package/dist/deployment/rhdh/config/auth/keycloak/secrets.yaml +12 -0
  19. package/dist/deployment/rhdh/config/{dynamic-plugins.yaml → common/dynamic-plugins.yaml} +1 -0
  20. package/dist/deployment/rhdh/constants.d.ts +6 -0
  21. package/dist/deployment/rhdh/constants.d.ts.map +1 -1
  22. package/dist/deployment/rhdh/constants.js +17 -5
  23. package/dist/deployment/rhdh/deployment.d.ts +8 -1
  24. package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
  25. package/dist/deployment/rhdh/deployment.js +47 -39
  26. package/dist/deployment/rhdh/index.d.ts +0 -1
  27. package/dist/deployment/rhdh/index.d.ts.map +1 -1
  28. package/dist/deployment/rhdh/types.d.ts +4 -1
  29. package/dist/deployment/rhdh/types.d.ts.map +1 -1
  30. package/dist/eslint/base.config.d.ts.map +1 -1
  31. package/dist/eslint/base.config.js +9 -2
  32. package/dist/playwright/base-config.d.ts +3 -3
  33. package/dist/playwright/base-config.d.ts.map +1 -1
  34. package/dist/playwright/base-config.js +5 -4
  35. package/dist/playwright/fixtures/test.d.ts +4 -1
  36. package/dist/playwright/fixtures/test.d.ts.map +1 -1
  37. package/dist/playwright/fixtures/test.js +16 -4
  38. package/dist/playwright/global-setup.d.ts.map +1 -1
  39. package/dist/playwright/global-setup.js +36 -1
  40. package/dist/playwright/helpers/accessibility.d.ts +13 -0
  41. package/dist/playwright/helpers/accessibility.d.ts.map +1 -0
  42. package/dist/playwright/helpers/accessibility.js +24 -0
  43. package/dist/playwright/helpers/api-endpoints.d.ts +11 -0
  44. package/dist/playwright/helpers/api-endpoints.d.ts.map +1 -0
  45. package/dist/playwright/helpers/api-endpoints.js +15 -0
  46. package/dist/playwright/helpers/api-helper.d.ts +77 -0
  47. package/dist/playwright/helpers/api-helper.d.ts.map +1 -0
  48. package/dist/playwright/helpers/api-helper.js +285 -0
  49. package/dist/playwright/helpers/common.d.ts +31 -0
  50. package/dist/playwright/helpers/common.d.ts.map +1 -0
  51. package/dist/playwright/helpers/common.js +342 -0
  52. package/dist/playwright/helpers/index.d.ts +5 -0
  53. package/dist/playwright/helpers/index.d.ts.map +1 -0
  54. package/dist/playwright/helpers/index.js +4 -0
  55. package/dist/playwright/helpers/navbar.d.ts +2 -0
  56. package/dist/playwright/helpers/navbar.d.ts.map +1 -0
  57. package/dist/playwright/helpers/navbar.js +1 -0
  58. package/dist/playwright/helpers/ui-helper.d.ts +106 -0
  59. package/dist/playwright/helpers/ui-helper.d.ts.map +1 -0
  60. package/dist/playwright/helpers/ui-helper.js +439 -0
  61. package/dist/playwright/page-objects/global-obj.d.ts +25 -0
  62. package/dist/playwright/page-objects/global-obj.d.ts.map +1 -0
  63. package/dist/playwright/page-objects/global-obj.js +24 -0
  64. package/dist/playwright/page-objects/page-obj.d.ts +41 -0
  65. package/dist/playwright/page-objects/page-obj.d.ts.map +1 -0
  66. package/dist/playwright/page-objects/page-obj.js +40 -0
  67. package/dist/playwright/pages/catalog-import.d.ts +31 -0
  68. package/dist/playwright/pages/catalog-import.d.ts.map +1 -0
  69. package/dist/playwright/pages/catalog-import.js +65 -0
  70. package/dist/playwright/pages/catalog.d.ts +14 -0
  71. package/dist/playwright/pages/catalog.d.ts.map +1 -0
  72. package/dist/playwright/pages/catalog.js +37 -0
  73. package/dist/playwright/pages/extensions.d.ts +38 -0
  74. package/dist/playwright/pages/extensions.d.ts.map +1 -0
  75. package/dist/playwright/pages/extensions.js +110 -0
  76. package/dist/playwright/pages/home-page.d.ts +10 -0
  77. package/dist/playwright/pages/home-page.d.ts.map +1 -0
  78. package/dist/playwright/pages/home-page.js +46 -0
  79. package/dist/playwright/pages/index.d.ts +6 -0
  80. package/dist/playwright/pages/index.d.ts.map +1 -0
  81. package/dist/playwright/pages/index.js +5 -0
  82. package/dist/playwright/pages/notifications.d.ts +24 -0
  83. package/dist/playwright/pages/notifications.d.ts.map +1 -0
  84. package/dist/playwright/pages/notifications.js +112 -0
  85. package/dist/utils/kubernetes-client.d.ts +9 -0
  86. package/dist/utils/kubernetes-client.d.ts.map +1 -1
  87. package/dist/utils/kubernetes-client.js +57 -2
  88. package/dist/utils/merge-yamls.d.ts +25 -4
  89. package/dist/utils/merge-yamls.d.ts.map +1 -1
  90. package/dist/utils/merge-yamls.js +52 -12
  91. package/package.json +18 -2
  92. /package/dist/deployment/rhdh/config/{app-config-rhdh.yaml → common/app-config-rhdh.yaml} +0 -0
  93. /package/dist/deployment/rhdh/config/{rhdh-secrets.yaml → common/rhdh-secrets.yaml} +0 -0
  94. /package/dist/deployment/rhdh/{helm → config/helm}/value_file.yaml +0 -0
  95. /package/dist/deployment/rhdh/{operator → config/operator}/subscription.yaml +0 -0
@@ -0,0 +1,437 @@
1
+ import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
2
+ import { KubernetesClientHelper } from "../../utils/kubernetes-client.js";
3
+ import { $ } from "../../utils/bash.js";
4
+ import { DEFAULT_KEYCLOAK_CONFIG, BITNAMI_CHART_REPO, BITNAMI_CHART_NAME, DEFAULT_CONFIG_PATHS, DEFAULT_RHDH_CLIENT, SERVICE_ACCOUNT_ROLES, DEFAULT_USERS, DEFAULT_GROUPS, } from "./constants.js";
5
+ export class KeycloakHelper {
6
+ k8sClient = new KubernetesClientHelper();
7
+ deploymentConfig;
8
+ keycloakUrl = "";
9
+ realm = "";
10
+ clientId = "";
11
+ clientSecret = "";
12
+ _adminClient = null;
13
+ constructor(options = {}) {
14
+ this.deploymentConfig = this._buildDeploymentConfig(options);
15
+ }
16
+ /**
17
+ * Deploy Keycloak using Helm and configure it for RHDH.
18
+ */
19
+ async deploy() {
20
+ this._log("Starting Keycloak deployment...");
21
+ await this.k8sClient.createNamespaceIfNotExists(this.deploymentConfig.namespace);
22
+ await this._deployWithHelm();
23
+ await this._createRoute();
24
+ await this._waitForKeycloak();
25
+ await this._initializeAdminClient();
26
+ }
27
+ /**
28
+ * Check if Keycloak is already running
29
+ */
30
+ async isRunning() {
31
+ try {
32
+ this.keycloakUrl = await this.getRouteLocation();
33
+ const response = await fetch(`${this.keycloakUrl}/realms/master`);
34
+ return response.ok;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ /**
41
+ * Configure Keycloak with realm, client, groups, and users for RHDH
42
+ */
43
+ async configureForRHDH(options) {
44
+ this._log("Configuring Keycloak for RHDH...");
45
+ await this._ensureAdminClient();
46
+ const realmName = options?.realm ?? DEFAULT_KEYCLOAK_CONFIG.realm;
47
+ // Create realm
48
+ await this.createRealm({ realm: realmName, enabled: true });
49
+ // Create client
50
+ const clientConfig = {
51
+ ...DEFAULT_RHDH_CLIENT,
52
+ ...options?.client,
53
+ };
54
+ await this.createClient(realmName, clientConfig);
55
+ // Store realm and client info for external access
56
+ this.realm = realmName;
57
+ this.clientId = clientConfig.clientId;
58
+ this.clientSecret = clientConfig.clientSecret;
59
+ // Assign service account roles
60
+ await this._assignServiceAccountRoles(realmName, clientConfig.clientId);
61
+ // Create groups
62
+ const groups = options?.groups ?? DEFAULT_GROUPS;
63
+ for (const group of groups) {
64
+ await this.createGroup(realmName, group);
65
+ }
66
+ // Create users
67
+ const users = options?.users ?? DEFAULT_USERS;
68
+ for (const user of users) {
69
+ await this.createUser(realmName, user);
70
+ }
71
+ }
72
+ /**
73
+ * Connect to an existing Keycloak instance
74
+ */
75
+ async connect(config) {
76
+ this.keycloakUrl = config.baseUrl;
77
+ this._adminClient = new KeycloakAdminClient({
78
+ baseUrl: config.baseUrl,
79
+ realmName: config.realm ?? "master",
80
+ });
81
+ if (config.username && config.password) {
82
+ await this._adminClient.auth({
83
+ username: config.username,
84
+ password: config.password,
85
+ grantType: "password",
86
+ clientId: config.clientId ?? "admin-cli",
87
+ });
88
+ }
89
+ else if (config.clientId && config.clientSecret) {
90
+ await this._adminClient.auth({
91
+ grantType: "client_credentials",
92
+ clientId: config.clientId,
93
+ clientSecret: config.clientSecret,
94
+ });
95
+ }
96
+ }
97
+ /**
98
+ * Create a new realm
99
+ */
100
+ async createRealm(config) {
101
+ await this._ensureAdminClient();
102
+ try {
103
+ await this._adminClient.realms.create({
104
+ realm: config.realm,
105
+ displayName: config.displayName ?? config.realm,
106
+ enabled: config.enabled ?? true,
107
+ });
108
+ this._log(`Created realm: ${config.realm}`);
109
+ }
110
+ catch (error) {
111
+ if (this._isConflictError(error)) {
112
+ this._log(`Realm ${config.realm} already exists`);
113
+ }
114
+ else {
115
+ throw error;
116
+ }
117
+ }
118
+ }
119
+ /**
120
+ * Create a new client in a realm
121
+ */
122
+ async createClient(realm, config) {
123
+ await this._ensureAdminClient();
124
+ try {
125
+ this._adminClient.setConfig({ realmName: realm });
126
+ await this._adminClient.clients.create({
127
+ clientId: config.clientId,
128
+ secret: config.clientSecret,
129
+ name: config.name ?? config.clientId,
130
+ description: config.description ?? "",
131
+ redirectUris: config.redirectUris ?? ["*"],
132
+ webOrigins: config.webOrigins ?? ["*"],
133
+ standardFlowEnabled: config.standardFlowEnabled ?? true,
134
+ implicitFlowEnabled: config.implicitFlowEnabled ?? true,
135
+ directAccessGrantsEnabled: config.directAccessGrantsEnabled ?? true,
136
+ serviceAccountsEnabled: config.serviceAccountsEnabled ?? true,
137
+ authorizationServicesEnabled: config.authorizationServicesEnabled ?? true,
138
+ publicClient: config.publicClient ?? false,
139
+ enabled: true,
140
+ protocol: "openid-connect",
141
+ fullScopeAllowed: true,
142
+ attributes: config.attributes,
143
+ defaultClientScopes: config.defaultClientScopes,
144
+ optionalClientScopes: config.optionalClientScopes,
145
+ });
146
+ this._log(`Created client: ${config.clientId}`);
147
+ }
148
+ catch (error) {
149
+ if (this._isConflictError(error)) {
150
+ this._log(`Client ${config.clientId} already exists`);
151
+ }
152
+ else {
153
+ throw error;
154
+ }
155
+ }
156
+ }
157
+ /**
158
+ * Create a group in a realm
159
+ */
160
+ async createGroup(realm, config) {
161
+ await this._ensureAdminClient();
162
+ try {
163
+ this._adminClient.setConfig({ realmName: realm });
164
+ await this._adminClient.groups.create({
165
+ name: config.name,
166
+ });
167
+ this._log(`Created group: ${config.name}`);
168
+ }
169
+ catch (error) {
170
+ if (this._isConflictError(error)) {
171
+ this._log(`Group ${config.name} already exists`);
172
+ }
173
+ else {
174
+ throw error;
175
+ }
176
+ }
177
+ }
178
+ /**
179
+ * Create a user in a realm with optional group membership
180
+ */
181
+ async createUser(realm, config) {
182
+ await this._ensureAdminClient();
183
+ try {
184
+ this._adminClient.setConfig({ realmName: realm });
185
+ // Create user
186
+ const createResponse = await this._adminClient.users.create({
187
+ username: config.username,
188
+ email: config.email,
189
+ firstName: config.firstName,
190
+ lastName: config.lastName,
191
+ enabled: config.enabled ?? true,
192
+ emailVerified: config.emailVerified ?? true,
193
+ });
194
+ this._log(`Created user: ${config.username}`);
195
+ const userId = createResponse.id;
196
+ // Set password if provided
197
+ if (config.password) {
198
+ await this._adminClient.users.resetPassword({
199
+ id: userId,
200
+ credential: {
201
+ type: "password",
202
+ value: config.password,
203
+ temporary: config.temporary ?? false,
204
+ },
205
+ });
206
+ }
207
+ // Add to groups if specified
208
+ if (config.groups?.length) {
209
+ for (const groupName of config.groups) {
210
+ await this._addUserToGroup(realm, userId, groupName);
211
+ }
212
+ }
213
+ }
214
+ catch (error) {
215
+ if (this._isConflictError(error)) {
216
+ this._log(`User ${config.username} already exists`);
217
+ }
218
+ else {
219
+ throw error;
220
+ }
221
+ }
222
+ }
223
+ /**
224
+ * Get all users in a realm
225
+ */
226
+ async getUsers(realm) {
227
+ await this._ensureAdminClient();
228
+ this._adminClient.setConfig({ realmName: realm });
229
+ const users = await this._adminClient.users.find();
230
+ return users.map((u) => ({
231
+ username: u.username,
232
+ email: u.email,
233
+ firstName: u.firstName,
234
+ lastName: u.lastName,
235
+ enabled: u.enabled,
236
+ emailVerified: u.emailVerified,
237
+ }));
238
+ }
239
+ /**
240
+ * Get all groups in a realm
241
+ */
242
+ async getGroups(realm) {
243
+ await this._ensureAdminClient();
244
+ this._adminClient.setConfig({ realmName: realm });
245
+ const groups = await this._adminClient.groups.find();
246
+ return groups.map((g) => ({ name: g.name }));
247
+ }
248
+ /**
249
+ * Delete a user from a realm
250
+ */
251
+ async deleteUser(realm, username) {
252
+ await this._ensureAdminClient();
253
+ this._adminClient.setConfig({ realmName: realm });
254
+ const users = await this._adminClient.users.find({ username });
255
+ if (users.length > 0) {
256
+ await this._adminClient.users.del({ id: users[0].id });
257
+ this._log(`Deleted user: ${username}`);
258
+ }
259
+ }
260
+ /**
261
+ * Delete a group from a realm
262
+ */
263
+ async deleteGroup(realm, groupName) {
264
+ await this._ensureAdminClient();
265
+ this._adminClient.setConfig({ realmName: realm });
266
+ const groups = await this._adminClient.groups.find({ search: groupName });
267
+ const group = groups.find((g) => g.name === groupName);
268
+ if (group) {
269
+ await this._adminClient.groups.del({ id: group.id });
270
+ this._log(`Deleted group: ${groupName}`);
271
+ }
272
+ }
273
+ /**
274
+ * Delete a realm
275
+ */
276
+ async deleteRealm(realm) {
277
+ await this._ensureAdminClient();
278
+ try {
279
+ await this._adminClient.realms.del({ realm });
280
+ this._log(`Deleted realm: ${realm}`);
281
+ }
282
+ catch (error) {
283
+ this._log(`Failed to delete realm ${realm}: ${error}`);
284
+ }
285
+ }
286
+ /**
287
+ * Teardown Keycloak deployment
288
+ */
289
+ async teardown() {
290
+ await this.k8sClient.deleteNamespace(this.deploymentConfig.namespace);
291
+ this._log(`Keycloak deployment torn down`);
292
+ }
293
+ /**
294
+ * Wait for Keycloak to be ready
295
+ */
296
+ async waitUntilReady(timeout = 300) {
297
+ this._log(`Waiting for Keycloak to be ready...`);
298
+ await this.k8sClient.waitForStatefulSetReady(this.deploymentConfig.namespace, this.deploymentConfig.releaseName, timeout);
299
+ }
300
+ // Private methods
301
+ _buildDeploymentConfig(options) {
302
+ return {
303
+ namespace: options.namespace ?? DEFAULT_KEYCLOAK_CONFIG.namespace,
304
+ releaseName: options.releaseName ?? DEFAULT_KEYCLOAK_CONFIG.releaseName,
305
+ valuesFile: options.valuesFile ?? DEFAULT_CONFIG_PATHS.valuesFile,
306
+ adminUser: options.adminUser ?? DEFAULT_KEYCLOAK_CONFIG.adminUser,
307
+ adminPassword: options.adminPassword ?? DEFAULT_KEYCLOAK_CONFIG.adminPassword,
308
+ };
309
+ }
310
+ async _deployWithHelm() {
311
+ await $ `helm repo add bitnami ${BITNAMI_CHART_REPO} || true`;
312
+ await $ `helm repo update > /dev/null 2>&1`;
313
+ await $ `helm upgrade --install ${this.deploymentConfig.releaseName} ${BITNAMI_CHART_NAME} \
314
+ --namespace ${this.deploymentConfig.namespace} \
315
+ --values ${this.deploymentConfig.valuesFile} > /dev/null 2>&1`;
316
+ await this.waitUntilReady();
317
+ }
318
+ async _createRoute() {
319
+ // Use TLS edge termination with Allow policy to support both HTTP and HTTPS
320
+ const routeManifest = `
321
+ apiVersion: route.openshift.io/v1
322
+ kind: Route
323
+ metadata:
324
+ name: ${this.deploymentConfig.releaseName}
325
+ namespace: ${this.deploymentConfig.namespace}
326
+ labels:
327
+ app.kubernetes.io/name: keycloak
328
+ app.kubernetes.io/instance: ${this.deploymentConfig.releaseName}
329
+ spec:
330
+ to:
331
+ kind: Service
332
+ name: ${this.deploymentConfig.releaseName}
333
+ weight: 100
334
+ port:
335
+ targetPort: http
336
+ tls:
337
+ termination: edge
338
+ insecureEdgeTerminationPolicy: Allow
339
+ wildcardPolicy: None
340
+ `;
341
+ await $ `echo ${routeManifest} | kubectl apply -f -`;
342
+ }
343
+ async getRouteLocation() {
344
+ return await this.k8sClient.getRouteLocation(this.deploymentConfig.namespace, this.deploymentConfig.releaseName);
345
+ }
346
+ async _waitForKeycloak() {
347
+ this._log("Waiting for Keycloak API to be ready...");
348
+ const timeout = 300;
349
+ const startTime = Date.now();
350
+ while (true) {
351
+ if (await this.isRunning()) {
352
+ break;
353
+ }
354
+ if ((Date.now() - startTime) / 1000 >= timeout) {
355
+ throw new Error("Keycloak API not ready after 5 minutes");
356
+ }
357
+ await new Promise((resolve) => setTimeout(resolve, 5000));
358
+ this._log(" Waiting for Keycloak API to be ready...");
359
+ }
360
+ }
361
+ async _initializeAdminClient() {
362
+ this._adminClient = new KeycloakAdminClient({
363
+ baseUrl: this.keycloakUrl,
364
+ realmName: "master",
365
+ });
366
+ await this._adminClient.auth({
367
+ username: this.deploymentConfig.adminUser,
368
+ password: this.deploymentConfig.adminPassword,
369
+ grantType: "password",
370
+ clientId: "admin-cli",
371
+ });
372
+ }
373
+ async _ensureAdminClient() {
374
+ if (!this._adminClient) {
375
+ throw new Error("Admin client not initialized. Call deploy() or connect() first.");
376
+ }
377
+ }
378
+ async _assignServiceAccountRoles(realm, clientId) {
379
+ await this._ensureAdminClient();
380
+ this._adminClient.setConfig({ realmName: realm });
381
+ // Get service account user
382
+ const clients = await this._adminClient.clients.find({ clientId });
383
+ if (clients.length === 0) {
384
+ throw new Error(`Client ${clientId} not found`);
385
+ }
386
+ const client = clients[0];
387
+ const serviceAccountUser = await this._adminClient.clients.getServiceAccountUser({
388
+ id: client.id,
389
+ });
390
+ // Get realm-management client
391
+ const realmMgmtClients = await this._adminClient.clients.find({
392
+ clientId: "realm-management",
393
+ });
394
+ if (realmMgmtClients.length === 0) {
395
+ throw new Error("realm-management client not found");
396
+ }
397
+ const realmMgmtClient = realmMgmtClients[0];
398
+ // Get roles
399
+ const allRoles = await this._adminClient.clients.listRoles({
400
+ id: realmMgmtClient.id,
401
+ });
402
+ const rolesToAssign = allRoles.filter((r) => SERVICE_ACCOUNT_ROLES.includes(r.name));
403
+ if (rolesToAssign.length > 0) {
404
+ await this._adminClient.users.addClientRoleMappings({
405
+ id: serviceAccountUser.id,
406
+ clientUniqueId: realmMgmtClient.id,
407
+ roles: rolesToAssign.map((r) => ({
408
+ id: r.id,
409
+ name: r.name,
410
+ })),
411
+ });
412
+ this._log(`Assigned service account roles: ${rolesToAssign.map((r) => r.name).join(", ")}`);
413
+ }
414
+ }
415
+ async _addUserToGroup(realm, userId, groupName) {
416
+ this._adminClient.setConfig({ realmName: realm });
417
+ const groups = await this._adminClient.groups.find({ search: groupName });
418
+ const group = groups.find((g) => g.name === groupName);
419
+ if (group) {
420
+ await this._adminClient.users.addToGroup({
421
+ id: userId,
422
+ groupId: group.id,
423
+ });
424
+ this._log(` Added user to group: ${groupName}`);
425
+ }
426
+ else {
427
+ this._log(` Warning: Group ${groupName} not found`);
428
+ }
429
+ }
430
+ _isConflictError(error) {
431
+ const err = error;
432
+ return err.response?.status === 409 || err.status === 409;
433
+ }
434
+ _log(...args) {
435
+ console.log("[Keycloak]", ...args);
436
+ }
437
+ }
@@ -0,0 +1,2 @@
1
+ export { KeycloakHelper } from "./deployment.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/deployment/keycloak/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1 @@
1
+ export { KeycloakHelper } from "./deployment.js";
@@ -0,0 +1,59 @@
1
+ export type KeycloakDeploymentOptions = {
2
+ namespace?: string;
3
+ releaseName?: string;
4
+ valuesFile?: string;
5
+ adminUser?: string;
6
+ adminPassword?: string;
7
+ };
8
+ export type KeycloakDeploymentConfig = {
9
+ namespace: string;
10
+ releaseName: string;
11
+ valuesFile: string;
12
+ adminUser: string;
13
+ adminPassword: string;
14
+ };
15
+ export type KeycloakClientConfig = {
16
+ clientId: string;
17
+ clientSecret: string;
18
+ name?: string;
19
+ description?: string;
20
+ redirectUris?: string[];
21
+ webOrigins?: string[];
22
+ standardFlowEnabled?: boolean;
23
+ implicitFlowEnabled?: boolean;
24
+ directAccessGrantsEnabled?: boolean;
25
+ serviceAccountsEnabled?: boolean;
26
+ authorizationServicesEnabled?: boolean;
27
+ publicClient?: boolean;
28
+ attributes?: Record<string, string>;
29
+ defaultClientScopes?: string[];
30
+ optionalClientScopes?: string[];
31
+ };
32
+ export type KeycloakUserConfig = {
33
+ username: string;
34
+ email?: string;
35
+ firstName?: string;
36
+ lastName?: string;
37
+ enabled?: boolean;
38
+ emailVerified?: boolean;
39
+ password?: string;
40
+ temporary?: boolean;
41
+ groups?: string[];
42
+ };
43
+ export type KeycloakGroupConfig = {
44
+ name: string;
45
+ };
46
+ export type KeycloakRealmConfig = {
47
+ realm: string;
48
+ displayName?: string;
49
+ enabled?: boolean;
50
+ };
51
+ export type KeycloakConnectionConfig = {
52
+ baseUrl: string;
53
+ realm?: string;
54
+ clientId?: string;
55
+ clientSecret?: string;
56
+ username?: string;
57
+ password?: string;
58
+ };
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/deployment/keycloak/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ auth:
2
+ environment: development
3
+ providers:
4
+ guest:
5
+ dangerouslyAllowOutsideDevelopment: true
@@ -0,0 +1,19 @@
1
+ auth:
2
+ environment: production
3
+ session:
4
+ secret: superSecretSecret
5
+ providers:
6
+ oidc:
7
+ production:
8
+ metadataUrl: "${KEYCLOAK_METADATA_URL}"
9
+ clientId: "${KEYCLOAK_CLIENT_ID}"
10
+ clientSecret: "${KEYCLOAK_CLIENT_SECRET}"
11
+ prompt: auto
12
+ callbackUrl: "${RHDH_BASE_URL}/api/auth/oidc/handler/frame"
13
+ signIn:
14
+ resolvers:
15
+ - resolver: emailLocalPartMatchingUserEntityName
16
+ signInPage: oidc
17
+ catalog:
18
+ rules:
19
+ - allow: [User, Group]
@@ -0,0 +1,3 @@
1
+ plugins:
2
+ - package: ./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic
3
+ disabled: false
@@ -0,0 +1,12 @@
1
+ apiVersion: v1
2
+ kind: Secret
3
+ metadata:
4
+ name: rhdh-secrets
5
+ type: Opaque
6
+ stringData:
7
+ KEYCLOAK_BASE_URL: $KEYCLOAK_BASE_URL
8
+ KEYCLOAK_METADATA_URL: $KEYCLOAK_METADATA_URL
9
+ KEYCLOAK_CLIENT_ID: $KEYCLOAK_CLIENT_ID
10
+ KEYCLOAK_CLIENT_SECRET: $KEYCLOAK_CLIENT_SECRET
11
+ KEYCLOAK_REALM: $KEYCLOAK_REALM
12
+ KEYCLOAK_LOGIN_REALM: $KEYCLOAK_LOGIN_REALM
@@ -1,2 +1,3 @@
1
1
  includes:
2
2
  - dynamic-plugins.default.yaml
3
+ plugins: []
@@ -1,3 +1,4 @@
1
+ import type { AuthProvider } from "./types.js";
1
2
  export declare const DEFAULT_CONFIG_PATHS: {
2
3
  appConfig: string;
3
4
  secrets: string;
@@ -9,5 +10,10 @@ export declare const DEFAULT_CONFIG_PATHS: {
9
10
  subscription: string;
10
11
  };
11
12
  };
13
+ export declare const AUTH_CONFIG_PATHS: Record<AuthProvider, {
14
+ appConfig: string;
15
+ secrets: string;
16
+ dynamicPlugins: string;
17
+ }>;
12
18
  export declare const CHART_URL = "oci://quay.io/rhdh/chart";
13
19
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB;;;;;;;;;;CAyBhC,CAAC;AAEF,eAAO,MAAM,SAAS,6BAA6B,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/constants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,eAAO,MAAM,oBAAoB;;;;;;;;;;CAyBhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CACpC,YAAY,EACZ;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAwB/D,CAAC;AAEF,eAAO,MAAM,SAAS,6BAA6B,CAAC"}
@@ -2,14 +2,26 @@ import path from "path";
2
2
  // Navigate from dist/deployment/rhdh/ to package root
3
3
  const PACKAGE_ROOT = path.resolve(import.meta.dirname, "../../..");
4
4
  export const DEFAULT_CONFIG_PATHS = {
5
- appConfig: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/app-config-rhdh.yaml"),
6
- secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/rhdh-secrets.yaml"),
7
- dynamicPlugins: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/dynamic-plugins.yaml"),
5
+ appConfig: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/common/app-config-rhdh.yaml"),
6
+ secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/common/rhdh-secrets.yaml"),
7
+ dynamicPlugins: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/common/dynamic-plugins.yaml"),
8
8
  helm: {
9
- valueFile: path.join(PACKAGE_ROOT, "src/deployment/rhdh/helm/value_file.yaml"),
9
+ valueFile: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/helm/value_file.yaml"),
10
10
  },
11
11
  operator: {
12
- subscription: path.join(PACKAGE_ROOT, "src/deployment/rhdh/operator/subscription.yaml"),
12
+ subscription: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/operator/subscription.yaml"),
13
+ },
14
+ };
15
+ export const AUTH_CONFIG_PATHS = {
16
+ guest: {
17
+ appConfig: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/guest/app-config.yaml"),
18
+ secrets: "",
19
+ dynamicPlugins: "",
20
+ },
21
+ keycloak: {
22
+ appConfig: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/keycloak/app-config.yaml"),
23
+ secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/keycloak/secrets.yaml"),
24
+ dynamicPlugins: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml"),
13
25
  },
14
26
  };
15
27
  export const CHART_URL = "oci://quay.io/rhdh/chart";
@@ -4,7 +4,7 @@ export declare class RHDHDeployment {
4
4
  k8sClient: KubernetesClientHelper;
5
5
  rhdhUrl: string;
6
6
  deploymentConfig: DeploymentConfig;
7
- constructor(deploymentOptions: DeploymentOptions);
7
+ constructor(namespace: string);
8
8
  deploy(): Promise<void>;
9
9
  private _applyAppConfig;
10
10
  private _applySecrets;
@@ -12,6 +12,12 @@ export declare class RHDHDeployment {
12
12
  private _deployWithHelm;
13
13
  private _deployWithOperator;
14
14
  rolloutRestart(): Promise<void>;
15
+ /**
16
+ * Performs a clean restart by scaling down to 0 first, waiting for pods to terminate,
17
+ * then scaling back up. This prevents MigrationLocked errors by ensuring no pods
18
+ * hold database locks when new pods start.
19
+ */
20
+ scaleDownAndRestart(): Promise<void>;
15
21
  waitUntilReady(timeout?: number): Promise<void>;
16
22
  teardown(): Promise<void>;
17
23
  private _resolveChartVersion;
@@ -19,5 +25,6 @@ export declare class RHDHDeployment {
19
25
  configure(deploymentOptions?: DeploymentOptions): Promise<void>;
20
26
  private _buildBaseUrl;
21
27
  private _log;
28
+ private _logBoxen;
22
29
  }
23
30
  //# sourceMappingURL=deployment.d.ts.map
@@ -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;AAS1E,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,iBAAiB,EAAE,iBAAiB;IAS1C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAoBf,eAAe;YAoBf,aAAa;YAab,oBAAoB;YAmBpB,eAAe;YAkDf,mBAAmB;IAkC3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAW/B,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,oBAAoB;IA6BlC,OAAO,CAAC,sBAAsB;IAkCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;CAGb"}
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAa1E,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;IAUvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBf,eAAe;YAgBf,aAAa;YAeb,oBAAoB;YAkBpB,eAAe;YA2Cf,mBAAmB;IA4B3B,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;IAwBpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,oBAAoB;IA6BlC,OAAO,CAAC,sBAAsB;IAoCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAGlB"}