Skip to content

Commit b80c535

Browse files
refactor env parameters
Issue: ZENKO-5259
1 parent 602bdc4 commit b80c535

6 files changed

Lines changed: 133 additions & 75 deletions

File tree

.github/scripts/end2end/setup-e2e-env.sh

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -146,26 +146,6 @@ else
146146
export SEED_KEYCLOAK_DEFAULT_ROLES=true
147147
export ZENKO_PORT="80"
148148

149-
# PRA admin credentials (may not exist for non-PRA runs; ignore errors)
150-
export ADMIN_PRA_ACCESS_KEY_ID=$(kubectl get secret ${ZENKO_NAME}-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.accessKey}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
151-
export ADMIN_PRA_SECRET_ACCESS_KEY=$(kubectl get secret ${ZENKO_NAME}-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.secretKey}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
152-
153-
# --- 11. Service user credentials ---
154-
BACKBEAT_LCBP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcbp-user-creds,app.kubernetes.io/instance=${ZENKO_NAME} -o jsonpath='{.items[0].data.backbeat-lifecycle-bp-1\.json}' | base64 -d)
155-
BACKBEAT_LCC_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcc-user-creds,app.kubernetes.io/instance=${ZENKO_NAME} -o jsonpath='{.items[0].data.backbeat-lifecycle-conductor-1\.json}' | base64 -d)
156-
BACKBEAT_LCOP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcop-user-creds,app.kubernetes.io/instance=${ZENKO_NAME} -o jsonpath='{.items[0].data.backbeat-lifecycle-op-1\.json}' | base64 -d)
157-
BACKBEAT_QP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-qp-user-creds,app.kubernetes.io/instance=${ZENKO_NAME} -o jsonpath='{.items[0].data.backbeat-qp-1\.json}' | base64 -d)
158-
SORBET_FWD_2_ACCESSKEY=$(kubectl get secret -l app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=${ZENKO_NAME} -o jsonpath='{.items[0].data.accessKey}' | base64 -d)
159-
SORBET_FWD_2_SECRETKEY=$(kubectl get secret -l app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=${ZENKO_NAME} -o jsonpath='{.items[0].data.secretKey}' | base64 -d)
160-
export SERVICE_USERS_CREDENTIALS=$(echo '{"backbeat-lifecycle-bp-1":'"${BACKBEAT_LCBP_1_CREDS}"',"backbeat-lifecycle-conductor-1":'"${BACKBEAT_LCC_1_CREDS}"',"backbeat-lifecycle-op-1":'"${BACKBEAT_LCOP_1_CREDS}"',"backbeat-qp-1":'"${BACKBEAT_QP_1_CREDS}"',"sorbet-fwd-2":{"accessKey":"'"${SORBET_FWD_2_ACCESSKEY}"'","secretKey":"'"${SORBET_FWD_2_SECRETKEY}"'"}}' | jq -R)
161-
162-
# --- 12. Kafka topics for sorbet ---
163-
SORBET_CONFIG=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=${ZENKO_NAME} \
164-
-o jsonpath='{.items[0].data.config\.json}' | base64 -di)
165-
export KAFKA_DEAD_LETTER_TOPIC=$(echo "${SORBET_CONFIG}" | jq -r '."kafka-dead-letter-topic"')
166-
export KAFKA_OBJECT_TASK_TOPIC=$(echo "${SORBET_CONFIG}" | jq -r '."kafka-object-task-topic"')
167-
export KAFKA_GC_REQUEST_TOPIC=$(echo "${SORBET_CONFIG}" | jq -r '."kafka-gc-request-topic"')
168-
169149
# --- 13. Kafka host from backbeat config + port-forward ---
170150
KAFKA_HOST_PORT_ORIG=$(kubectl get secret -l app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=${ZENKO_NAME} \
171151
-o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq -r .kafka.hosts)
@@ -317,7 +297,6 @@ else
317297
"StorageAccountOwnerUsername":"${STORAGE_ACCOUNT_OWNER_USER_NAME}",
318298
"DataConsumerUsername":"${DATA_CONSUMER_USER_NAME}",
319299
"DataAccessorUsername":"${DATA_ACCESSOR_USER_NAME}",
320-
"ServiceUsersCredentials":${SERVICE_USERS_CREDENTIALS},
321300
"AzureAccountName":"${AZURE_ACCOUNT_NAME}",
322301
"AzureAccountKey":"${AZURE_SECRET_KEY}",
323302
"AzureArchiveContainer":"${AZURE_ARCHIVE_BUCKET_NAME}",
@@ -326,16 +305,11 @@ else
326305
"AzureArchiveManifestTier":"${AZURE_ARCHIVE_MANIFEST_ACCESS_TIER}",
327306
"AzureArchiveQueue":"${AZURE_ARCHIVE_QUEUE_NAME:-}",
328307
"TimeProgressionFactor":"${TIME_PROGRESSION_FACTOR}",
329-
"KafkaObjectTaskTopic":"${KAFKA_OBJECT_TASK_TOPIC}",
330-
"KafkaGCRequestTopic":"${KAFKA_GC_REQUEST_TOPIC}",
331-
"KafkaDeadLetterQueueTopic":"${KAFKA_DEAD_LETTER_TOPIC}",
332308
"InstanceID":"${INSTANCE_ID}",
333309
"BackbeatApiHost":"${BACKBEAT_API_HOST}",
334310
"BackbeatApiPort":"${BACKBEAT_API_PORT}",
335311
"KafkaCleanerInterval":"${KAFKA_CLEANER_INTERVAL}",
336312
"SorbetdRestoreTimeout":"${SORBETD_RESTORE_TIMEOUT}",
337-
"DRAdminAccessKey":"${ADMIN_PRA_ACCESS_KEY_ID}",
338-
"DRAdminSecretKey":"${ADMIN_PRA_SECRET_ACCESS_KEY}",
339313
"UtilizationServiceHost":"${UTILIZATION_SERVICE_HOST}",
340314
"UtilizationServicePort":"${UTILIZATION_SERVICE_PORT}",
341315
"KubeconfigPath":"${KUBECONFIG:-${HOME}/.kube/config}"

tests/ctst/steps/azureArchive.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,9 @@ When('i run sorbetctl to retry failed restore for {string} location',
360360
{ timeout: 10 * 60 * 1000 }, async function (this: Zenko, location: string) {
361361
const command = `./sorbetctl forward list failed --trigger-retry --skip-invalid \
362362
--limit 300 \
363-
--kafka-dead-letter-topic=${this.parameters.KafkaDeadLetterQueueTopic} \
364-
--kafka-object-task-topic=${this.parameters.KafkaObjectTaskTopic} \
365-
--kafka-gc-request-topic=${this.parameters.KafkaGCRequestTopic} \
363+
--kafka-dead-letter-topic=${Zenko.kafkaTopics.deadLetterQueue} \
364+
--kafka-object-task-topic=${Zenko.kafkaTopics.objectTask} \
365+
--kafka-gc-request-topic=${Zenko.kafkaTopics.gcRequest} \
366366
--kafka-brokers ${this.parameters.KafkaHosts}`;
367367
try {
368368
this.logger.debug('Running command', { command, location });

tests/ctst/steps/dr/drctl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export default class ZenkoDrctl {
186186
}
187187

188188
private getKubeconfigPath(): string | undefined {
189-
const kp = this.world.parameters.KubeconfigPath as string | undefined;
189+
const kp = this.world.parameters.KubeconfigPath;
190190
return kp || process.env.KUBECONFIG;
191191
}
192192

tests/ctst/steps/reporting/storageUsageReporting.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ interface ReportingUsageResponse {
1515
}
1616

1717
Given('an identity with the {string} keycloak persona', function (this: Zenko, persona: string) {
18-
const username = (this.parameters as Record<string, string>)[persona] || persona;
18+
const params = this.parameters as unknown as Record<string, string>;
19+
const username = params[persona] || persona;
1920
this.addToSaved('keycloakPersona', username);
2021
});
2122

tests/ctst/steps/utils/kubernetes.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'fs';
22
import * as path from 'path';
3-
import { KubernetesHelper, Utils } from 'cli-testing';
3+
import { CacheHelper, KubernetesHelper, Utils } from 'cli-testing';
44
import Zenko from 'world/Zenko';
55
import {
66
V1Job,
@@ -534,6 +534,49 @@ export async function createSecret(
534534
}
535535
}
536536

537+
export async function getSecretByLabel(
538+
labelSelector: string,
539+
dataField: string,
540+
namespace = 'default',
541+
): Promise<string> {
542+
if (!KubernetesHelper.clientCore) {
543+
KubernetesHelper.init(CacheHelper.parameters);
544+
}
545+
const coreClient = KubernetesHelper.clientCore as CoreV1Api;
546+
const secretList = await coreClient.listNamespacedSecret({
547+
namespace,
548+
labelSelector,
549+
});
550+
const secret = secretList.items[0];
551+
if (!secret?.data?.[dataField]) {
552+
throw new Error(
553+
`Secret field "${dataField}" not found for label "${labelSelector}"`,
554+
);
555+
}
556+
return Buffer.from(secret.data[dataField], 'base64').toString('utf-8');
557+
}
558+
559+
export async function getSecretField(
560+
secretName: string,
561+
dataField: string,
562+
namespace = 'default',
563+
): Promise<string> {
564+
if (!KubernetesHelper.clientCore) {
565+
KubernetesHelper.init(CacheHelper.parameters);
566+
}
567+
const coreClient = KubernetesHelper.clientCore as CoreV1Api;
568+
const secret = await coreClient.readNamespacedSecret({
569+
name: secretName, namespace,
570+
});
571+
const value = secret.data?.[dataField];
572+
if (!value) {
573+
throw new Error(
574+
`Secret field "${dataField}" not found in secret "${secretName}"`,
575+
);
576+
}
577+
return Buffer.from(value, 'base64').toString('utf-8');
578+
}
579+
537580
export async function getMongoDBConfig(
538581
world: Zenko,
539582
namespace = 'default',

tests/ctst/world/Zenko.ts

Lines changed: 83 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,10 @@ import {
2424
} from 'cli-testing';
2525

2626
import { extractPropertyFromResults } from '../common/utils';
27+
import { getSecretByLabel, getSecretField } from 'steps/utils/kubernetes';
2728
import ZenkoDrctl from 'steps/dr/drctl';
2829
import assert from 'assert';
2930

30-
interface ServiceUsersCredentials {
31-
accessKey: string;
32-
secretKey: string;
33-
}
34-
3531
// Zenko entities
3632
export interface SavedIdentity {
3733
identityName: string;
@@ -54,8 +50,8 @@ export interface ZenkoWorldParameters extends ClientOptions {
5450
AccountName: string;
5551
AccountAccessKey: string;
5652
AccountSecretKey: string;
57-
DRAdminAccessKey?: string;
58-
DRAdminSecretKey?: string;
53+
AdminAccessKey: string;
54+
AdminSecretKey: string;
5955
DRSubdomain?: string;
6056
VaultAuthHost: string;
6157
NotificationDestination: string;
@@ -83,7 +79,6 @@ export interface ZenkoWorldParameters extends ClientOptions {
8379
StorageAccountOwnerUsername: string;
8480
DataConsumerUsername: string;
8581
DataAccessorUsername: string;
86-
ServiceUsersCredentials: string;
8782
KeycloakTestPassword: string;
8883
AzureAccountName: string;
8984
AzureAccountKey: string;
@@ -93,17 +88,14 @@ export interface ZenkoWorldParameters extends ClientOptions {
9388
AzureArchiveManifestTier: string;
9489
AzureArchiveQueue: string;
9590
TimeProgressionFactor: number;
96-
KafkaDeadLetterQueueTopic: string;
97-
KafkaObjectTaskTopic: string;
98-
KafkaGCRequestTopic: string;
9991
InstanceID: string;
10092
BackbeatApiHost: string;
10193
BackbeatApiPort: string;
10294
KafkaCleanerInterval: string;
10395
SorbetdRestoreTimeout: string;
10496
UtilizationServiceHost: string;
10597
UtilizationServicePort: string;
106-
[key: string]: unknown;
98+
KubeconfigPath?: string;
10799
}
108100

109101
/**
@@ -137,26 +129,19 @@ export default class Zenko extends World<ZenkoWorldParameters> {
137129
static readonly SECONDARY_SITE_NAME = 'dradmin';
138130
static readonly PRA_INSTALL_COUNT_KEY = 'praInstallCount';
139131

132+
static kafkaTopics: {
133+
deadLetterQueue: string;
134+
objectTask: string;
135+
gcRequest: string;
136+
};
137+
140138
/**
141139
* @constructor
142140
* @param {Object} options - parameters provided as a CLI parameter when running the tests
143141
*/
144142
constructor(options: IWorldOptions<ZenkoWorldParameters>) {
145143
super(options);
146144
Logger.createLogger(this);
147-
// store service users credentials from world parameters
148-
if (this.parameters.ServiceUsersCredentials) {
149-
const serviceUserCredentials =
150-
JSON.parse(this.parameters.ServiceUsersCredentials) as Record<string, ServiceUsersCredentials>;
151-
for (const serviceUserName in serviceUserCredentials) {
152-
if (!Identity.hasIdentity(IdentityEnum.SERVICE_USER, serviceUserName, this.parameters.AccountName)) {
153-
Identity.addIdentity(IdentityEnum.SERVICE_USER, serviceUserName, {
154-
accessKeyId: serviceUserCredentials[serviceUserName].accessKey,
155-
secretAccessKey: serviceUserCredentials[serviceUserName].secretKey,
156-
}, this.parameters.AccountName);
157-
}
158-
}
159-
}
160145

161146
// Workaround to be able to access global parameters in BeforeAll/AfterAll hooks
162147
CacheHelper.cacheParameters({
@@ -191,29 +176,11 @@ export default class Zenko extends World<ZenkoWorldParameters> {
191176
};
192177
}
193178

194-
if (this.needsSecondarySite()) {
195-
if (!Identity.hasIdentity(IdentityEnum.ADMIN, Zenko.SECONDARY_SITE_NAME)) {
196-
Identity.addIdentity(IdentityEnum.ADMIN, Zenko.SECONDARY_SITE_NAME, {
197-
accessKeyId: this.parameters.DRAdminAccessKey!,
198-
secretAccessKey: this.parameters.DRAdminSecretKey!,
199-
}, undefined, undefined, undefined, this.parameters.DRSubdomain);
200-
}
201-
202-
Zenko.sites['sink'] = {
203-
accountName: `dr${this.parameters.AccountName}`,
204-
adminIdentityName: Zenko.SECONDARY_SITE_NAME,
205-
};
206-
}
207-
208179
this.logger.debug('Zenko sites', {
209180
sites: Zenko.sites,
210181
});
211182
}
212183

213-
private needsSecondarySite() {
214-
return this.parameters.DRAdminAccessKey && this.parameters.DRAdminSecretKey && this.parameters.DRSubdomain;
215-
}
216-
217184
/**
218185
* This function will dynamically determine if the result from the AWS command
219186
* is a success or a failure. Based on the fact that AWS either return an empty string
@@ -630,6 +597,74 @@ export default class Zenko extends World<ZenkoWorldParameters> {
630597
this.saveIdentityInformation(roleName, IdentityEnum.ASSUMED_ROLE, Identity.getCurrentAccountName());
631598
}
632599

600+
private static async loadServiceUsers(accountName: string) {
601+
const instanceSelector = 'app.kubernetes.io/instance=end2end';
602+
const serviceUsers: Record<string, string> = {
603+
'backbeat-lcbp-user-creds': 'backbeat-lifecycle-bp-1',
604+
'backbeat-lcc-user-creds': 'backbeat-lifecycle-conductor-1',
605+
'backbeat-lcop-user-creds': 'backbeat-lifecycle-op-1',
606+
'backbeat-qp-user-creds': 'backbeat-qp-1',
607+
};
608+
609+
for (const [secretName, userName] of Object.entries(serviceUsers)) {
610+
const labelSelector =`app.kubernetes.io/name=${secretName},${instanceSelector}`;
611+
const raw = await getSecretByLabel(labelSelector, `${userName}.json`);
612+
const parsed = JSON.parse(raw) as { accessKey: string; secretKey: string };
613+
Identity.addIdentity(IdentityEnum.SERVICE_USER, userName, {
614+
accessKeyId: parsed.accessKey,
615+
secretAccessKey: parsed.secretKey,
616+
}, accountName);
617+
}
618+
619+
const sorbetSelector =
620+
`app.kubernetes.io/name=sorbet-fwd-creds,${instanceSelector}`;
621+
const accessKey = await getSecretByLabel(sorbetSelector, 'accessKey');
622+
const secretKey = await getSecretByLabel(sorbetSelector, 'secretKey');
623+
Identity.addIdentity(IdentityEnum.SERVICE_USER, 'sorbet-fwd-2', {
624+
accessKeyId: accessKey,
625+
secretAccessKey: secretKey,
626+
}, accountName);
627+
}
628+
629+
private static async loadSorbetConfig() {
630+
const labelSelector =
631+
'app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive' +
632+
',app.kubernetes.io/instance=end2end';
633+
const raw = await getSecretByLabel(labelSelector, 'config.json');
634+
const config = JSON.parse(raw) as Record<string, string>;
635+
Zenko.kafkaTopics = {
636+
deadLetterQueue: config['kafka-dead-letter-topic'],
637+
objectTask: config['kafka-object-task-topic'],
638+
gcRequest: config['kafka-gc-request-topic'],
639+
};
640+
}
641+
642+
private static async loadPRACredentials(parameters: ZenkoWorldParameters) {
643+
if (!parameters.DRSubdomain) {
644+
return;
645+
}
646+
const secretName = 'end2end-pra-management-vault-admin-creds.v1';
647+
let accessKey: string;
648+
let secretKey: string;
649+
try {
650+
accessKey = await getSecretField(secretName, 'accessKey');
651+
secretKey = await getSecretField(secretName, 'secretKey');
652+
} catch {
653+
// PRA secret does not exist — fine for non-PRA tests
654+
return;
655+
}
656+
if (!Identity.hasIdentity(IdentityEnum.ADMIN, Zenko.SECONDARY_SITE_NAME)) {
657+
Identity.addIdentity(IdentityEnum.ADMIN, Zenko.SECONDARY_SITE_NAME, {
658+
accessKeyId: accessKey,
659+
secretAccessKey: secretKey,
660+
}, undefined, undefined, undefined, parameters.DRSubdomain);
661+
}
662+
Zenko.sites['sink'] = {
663+
accountName: `dr${parameters.AccountName}`,
664+
adminIdentityName: Zenko.SECONDARY_SITE_NAME,
665+
};
666+
}
667+
633668
/**
634669
* Hook Zenko is a utility function to prepare a Zenko
635670
* @param {Object.<string,*>} parameters - the client-provided parameters
@@ -639,6 +674,11 @@ export default class Zenko extends World<ZenkoWorldParameters> {
639674
CacheHelper.logger.debug('Initializing Zenko', {
640675
parameters,
641676
});
677+
678+
await Zenko.loadServiceUsers(parameters.AccountName);
679+
await Zenko.loadSorbetConfig();
680+
await Zenko.loadPRACredentials(parameters);
681+
642682
// Create the default account for each site configured
643683
// and generate access keys for it
644684
for (const siteKey in Zenko.sites) {

0 commit comments

Comments
 (0)