From 995dcc0a655ee910b5bbeb83c6cb6d681ce8ccfa Mon Sep 17 00:00:00 2001 From: williamlardier Date: Tue, 9 Sep 2025 11:34:02 +0200 Subject: [PATCH 01/14] 1: run DMF in parallel --- solution/deps.yaml | 2 +- tests/ctst/HOW_TO_WRITE_TESTS.md | 17 ++++ tests/ctst/common/hooks.ts | 1 - tests/ctst/steps/dmf.ts | 124 ++++++++++++++++++++++--- tests/ctst/steps/utils/kubernetes.ts | 132 +++++++++++++++++++++++++-- 5 files changed, 255 insertions(+), 21 deletions(-) diff --git a/solution/deps.yaml b/solution/deps.yaml index d0a83f3192..64d4788588 100644 --- a/solution/deps.yaml +++ b/solution/deps.yaml @@ -113,7 +113,7 @@ sorbet: policy: sorbet/sorbet-policies dashboard: sorbet/sorbet-dashboards image: sorbet - tag: v1.2.0-preview.1 + tag: 931169543e23666c99a20e1c679e7661ec1c50eb envsubst: SORBET_TAG stern: # tail any pod logs with pattern matchin tag: 1.30.0 diff --git a/tests/ctst/HOW_TO_WRITE_TESTS.md b/tests/ctst/HOW_TO_WRITE_TESTS.md index 4f5bbe74d6..13f9ae830f 100644 --- a/tests/ctst/HOW_TO_WRITE_TESTS.md +++ b/tests/ctst/HOW_TO_WRITE_TESTS.md @@ -59,6 +59,23 @@ possible. Solutions exist: relative checks. - As a last resort, we might have a dedicated test suite. +### Cold Storage Tests and Parallel Execution + +Previously, `@ColdStorage` tests were forced to run sequentially due to shared +DMF volume access. This has been resolved with the following improvements: + +- **Bucket-specific file isolation**: The sorbet mock backend now uses S3 alias + naming (`/cold-data/data/s3-aliases/{bucket}-{key}-{versionId}/`) which provides + perfect isolation between parallel test runs. +- **Intelligent file counting**: DMF volume checks now scan only for files + belonging to the specific test's bucket name. +- **Per-test cleanup**: Each test cleans up only its own files, preventing + interference with parallel tests. + +This means `@ColdStorage` tests can now run with full parallelization, +significantly reducing test execution time. Please follow the rules for parallel +execution, if you are using `@ColdStorage` tests. + ## 5. Focus on validating features. We only want to assert against externally visible state, as given in the diff --git a/tests/ctst/common/hooks.ts b/tests/ctst/common/hooks.ts index acf621a8a1..158715e56d 100644 --- a/tests/ctst/common/hooks.ts +++ b/tests/ctst/common/hooks.ts @@ -27,7 +27,6 @@ export const replicationLockTags = [ const noParallelRun = atMostOnePicklePerTag([ '@AfterAll', '@PRA', - '@ColdStorage', ...replicationLockTags ]); diff --git a/tests/ctst/steps/dmf.ts b/tests/ctst/steps/dmf.ts index 1f4acdd269..f09b4527ed 100644 --- a/tests/ctst/steps/dmf.ts +++ b/tests/ctst/steps/dmf.ts @@ -1,23 +1,110 @@ import { Then, Given, After } from '@cucumber/cucumber'; import assert from 'assert'; -import { execShellCommand } from 'common/utils'; import Zenko from 'world/Zenko'; +import { execInCluster } from './utils/kubernetes'; +import { Utils } from 'cli-testing'; -async function cleanDmfVolume() { - await execShellCommand('rm -rf /cold-data/*'); +/** + * Clean up S3 alias files for this specific bucket + * @param world - The Zenko world object + * @param bucketName - The name of the bucket to clean up + * @returns void + */ +async function cleanDmfVolumeForBucket(world: Zenko, bucketName: string) { + if (!bucketName) { + return; + } + + const commands = [ + `find /cold-data/data/s3-aliases -name "${bucketName}-*" -type f -delete 2>/dev/null || true`, + `find /cold-data/data/s3-aliases -name "${bucketName}-*" -type d -empty -delete 2>/dev/null || true` + ]; + + for (const command of commands) { + await execInCluster(world, command); + } } +/** + * Check if the DMF volume contains the expected number of objects. + * This requires sorbet mock backend with UseS3Naming=true. + * Files are stored as: /cold-data/data/s3-aliases/{bucket}-{key}-{versionId}/content + * This enables parallel test execution by providing bucket-level isolation + * @param this - The Zenko world object + * @param objectCount - The expected number of objects + * @returns void + */ Then('dmf volume should contain {int} objects', - { timeout: 2 * 60 * 1000 }, async (objectCount: number) => { + { timeout: 2 * 60 * 1000 }, async function (this: Zenko, objectCount: number) { + const bucketName = this.getSaved('bucketName'); + if (!bucketName) { + throw new Error('bucketName not found in test context. Ensure bucket is created before this step.'); + } + let conditionOk = false; - while (!conditionOk) { - // Getting the number of objects inside the volume used - // by the mock dmf to store transitioned objects - const outStr = await execShellCommand('find /cold-data -type f | wc -l'); - // we store two files per object (content and manifest.json) - conditionOk = Number(outStr) === objectCount * 2; + let attempts = 0; + const maxAttempts = 60; + + while (!conditionOk && attempts < maxAttempts) { + try { + const outStr = await execInCluster( + this, + `find /cold-data/data/s3-aliases -name "${bucketName}-*" -type f | wc -l` + ); + const fileCount = Number(outStr.trim()); + + // We expect 2 files per object (content + manifest.json) + const expectedFileCount = objectCount * 2; + conditionOk = fileCount === expectedFileCount; + + if (!conditionOk) { + this.logger.debug(`DMF volume check for bucket ${bucketName}`, { + expected: expectedFileCount, + found: fileCount, + attempt: attempts + 1, + maxAttempts + }); + + if (attempts % 10 === 0) { + const filesFound = await execInCluster( + this, + `find /cold-data/data/s3-aliases -name "${bucketName}-*" -type f 2>/dev/null` + ); + this.logger.debug(`Files found for bucket ${bucketName}:`, { files: filesFound }); + } + + await Utils.sleep(2000); + attempts++; + } + } catch (error) { + this.logger.error('Error checking DMF volume', { error, bucket: bucketName }); + throw error; + } + } + + if (!conditionOk) { + const finalCount = await execInCluster( + this, + `find /cold-data/data/s3-aliases -name "${bucketName}-*" -type f | wc -l` + ); + const actualFiles = await execInCluster( + this, + `find /cold-data/data/s3-aliases -name "${bucketName}-*" -type f 2>/dev/null` + ); + + assert.fail( + `DMF volume should contain ${objectCount * 2} files for bucket ${bucketName}, ` + + `but found ${finalCount.trim()} after ${attempts} attempts. ` + + `Files found: ${actualFiles}` + ); } - assert(conditionOk); + + this.logger.debug(`DMF volume check passed for bucket ${bucketName}`, { + expectedObjects: objectCount, + foundFiles: objectCount * 2, + attempts, + maxAttempts, + }); }); Given('a flaky backend that will require {int} retries for {string}', @@ -29,6 +116,17 @@ Given('a flaky backend that will require {int} retries for {string}', this.addToSaved('backendFlakiness', op); }); -After({ tags: '@Dmf' }, async () => { - await cleanDmfVolume(); +After({ tags: '@Dmf' }, async function (this: Zenko, results) { + const bucketName = this.getSaved('bucketName'); + + if (results.result?.status === 'FAILED') { + this.logger.warn('DMF volume was not cleaned for failed test', { + bucket: bucketName, + reason: 'test failed - keeping files for debugging' + }); + return; + } + + await cleanDmfVolumeForBucket(this, bucketName); + this.logger.debug(`Cleaned DMF volume for bucket: ${bucketName}`); }); diff --git a/tests/ctst/steps/utils/kubernetes.ts b/tests/ctst/steps/utils/kubernetes.ts index 4650fa14b5..fa0ff8a983 100644 --- a/tests/ctst/steps/utils/kubernetes.ts +++ b/tests/ctst/steps/utils/kubernetes.ts @@ -174,7 +174,7 @@ export async function createJobAndWaitForCompletion( ); }); } catch (err: unknown) { - world.logger.error('Error creating or waiting for job completion', { + world.logger.debug('Error creating or waiting for job completion', { jobName, err, }); @@ -219,7 +219,7 @@ export async function createAndRunPod( resolve(); } else if (phase === 'Failed') { clearTimeout(timeoutId); - world.logger.error('Pod failed', { + world.logger.debug('Pod failed', { podName, status: watchObj.object?.status }); @@ -248,7 +248,7 @@ export async function createAndRunPod( return response.body; } catch (err: unknown) { - world.logger.error('Failed to create and run pod:', { err }); + world.logger.debug('Failed to create and run pod:', { err }); throw new Error(`Failed to create and run pod: ${err}`); } } @@ -295,7 +295,7 @@ export async function waitForZenkoToStabilize( 'zenkos', 'end2end', ).catch(err => { - world.logger.error('Error getting Zenko CR', { + world.logger.debug('Error getting Zenko CR', { err: err as unknown, }); return null; @@ -433,7 +433,7 @@ export async function displayCRStatus(world: Zenko, namespace = 'default') { 'zenkos', 'end2end', ).catch(err => { - world.logger.error('Error getting Zenko CR', { + world.logger.debug('Error getting Zenko CR', { err: err as unknown, }); return null; @@ -524,7 +524,7 @@ export async function createSecret( const response = await coreClient.createNamespacedSecret(namespace, secret); return response; } catch (err) { - world.logger.error('Error creating secret', { + world.logger.debug('Error creating secret', { namespace, secret, err, @@ -615,3 +615,123 @@ export async function getZenkoVersion( } } +/** + * Execute a shell command in a pod with host volume access + * Simplified to only support host path mounting for system volumes + * @param world - The Zenko world object + * @param command - The command to execute + * @param options - The options for the command execution + * @returns The output of the command + */ +export async function execCommandWithVolumeAccess( + world: Zenko, + command: string, + options: { + volumeMountPath?: string; + hostPath?: string; + image?: string; + namespace?: string; + timeout?: number; + cleanup?: boolean; + } = {} +): Promise { + const { + volumeMountPath = '/cold-data', + hostPath = '/cold-data', + image = 'alpine:3.22', + namespace = 'default', + timeout = 30000, + cleanup = true, + } = options; + + // Generate unique pod name to prevent conflicts between concurrent tests + const timestamp = Date.now(); + const randomId = Math.random().toString(36).substring(2, 8); + const testContext = world.getSaved?.('bucketName') || 'test'; + const podName = `ctst-exec-${testContext}-${timestamp}-${randomId}`.toLowerCase(); + + const podManifest: V1Pod = { + apiVersion: 'v1', + kind: 'Pod', + metadata: { + name: podName, + namespace, + labels: { + 'app.kubernetes.io/name': 'ctst-command-executor', + 'app.kubernetes.io/component': 'test-utility', + 'ctst.test/execution-id': `${timestamp}-${randomId}` + } + }, + spec: { + restartPolicy: 'Never', + securityContext: { + runAsNonRoot: false, + fsGroup: 0 + }, + containers: [{ + name: 'executor', + image, + command: ['/bin/sh', '-c', command], + securityContext: { + runAsUser: 0, + allowPrivilegeEscalation: false, + readOnlyRootFilesystem: false, + capabilities: { + drop: ['ALL'] + } + }, + volumeMounts: [{ + name: 'host-volume', + mountPath: volumeMountPath + }] + }], + volumes: [{ + name: 'host-volume', + hostPath: { + path: hostPath, + type: 'DirectoryOrCreate' + } + }] + } + }; + + try { + await createAndRunPod(world, podManifest, true, cleanup, timeout); + + const coreClient = createKubeCoreClient(world); + const logs = await coreClient.readNamespacedPodLog(podName, namespace); + + return logs.body.trim(); + } catch (error) { + world.logger.debug('Command execution failed', { + command, + podName, + error: error instanceof Error ? error.message : String(error) + }); + throw error; + } +} + +/** + * Execute command in Kubernetes cluster with host volume access + * Designed for concurrent test execution without conflicts + * Uses unique pod names and labels for isolation + */ +export async function execInCluster( + world: Zenko, + command: string, + volumeOptions?: Parameters[2] +): Promise { + world.logger.debug('Executing command in cluster', { command }); + + try { + return await execCommandWithVolumeAccess(world, command, volumeOptions); + } catch (error) { + world.logger.debug('Kubernetes command execution failed', { + command, + error, + }); + throw error; + } +} + From b996f4f57892db6fca731ac3ea84aa293ac7807b Mon Sep 17 00:00:00 2001 From: williamlardier Date: Tue, 9 Sep 2025 14:01:59 +0200 Subject: [PATCH 02/14] Use docker layers cache for ctst --- tests/ctst/Dockerfile | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/ctst/Dockerfile b/tests/ctst/Dockerfile index 93bda2472e..880c3d1b1e 100644 --- a/tests/ctst/Dockerfile +++ b/tests/ctst/Dockerfile @@ -7,21 +7,32 @@ FROM ghcr.io/scality/zenko-drctl:$DRCTL_TAG AS drctl FROM ghcr.io/scality/cli-testing:$CTST_TAG COPY package.json /tmp/package.json + +USER root +RUN npm install typescript@5.8.3 -g + +# CTST does it if needed, but better to merge dependencies +# here so we benefit from Docker layer caching +WORKDIR /ctst +RUN merge-packages /tmp/package.json package.json --output package.json && \ + jq 'del(.devDependencies)' package.json > package.json.tmp && \ + mv package.json.tmp package.json && \ + echo "Merged package.json:" && cat package.json && \ + yarn install --prod --network-concurrency=1 && \ + yarn cache clean && \ + echo "Dependencies pre-installed at build time - runtime will skip installation" && \ + touch .init + COPY ./features /ctst/features COPY ./common /ctst/common COPY ./steps /ctst/steps COPY ./world /ctst/world -USER root -RUN npm install typescript@5.8.3 -g - RUN chmod 0777 -R /tmp/ RUN chmod 0777 -R /ctst/ ENV SDK=true -WORKDIR /ctst - COPY --from=sorbet /sorbetctl . COPY --from=drctl /zenko-drctl . From 68b05cdc54dd2f11f80c096d0ee129db81e937a6 Mon Sep 17 00:00:00 2001 From: williamlardier Date: Tue, 9 Sep 2025 16:45:34 +0200 Subject: [PATCH 03/14] Setup CTST in BeforeAll --- .github/scripts/end2end/configure-e2e-ctst.sh | 49 -- .github/scripts/end2end/run-e2e-ctst.sh | 142 +-- .github/scripts/end2end/setup-ctst-local.sh | 50 ++ tests/ctst/CTST_MANUAL_SETUP_ANALYSIS.md | 218 +++++ tests/ctst/MIGRATION_TO_CTST_ONLY.md | 142 +++ tests/ctst/common/common.ts | 6 +- tests/ctst/package.json | 3 +- tests/ctst/steps/cloudserverAuth.ts | 3 +- tests/ctst/steps/pra.ts | 2 - tests/ctst/steps/setup/setup.ts | 815 ++++++++++++++++++ tests/ctst/steps/utils/kubernetes.ts | 98 ++- tests/ctst/steps/utils/utils.ts | 3 +- tests/ctst/world/Zenko.ts | 6 + tests/ctst/world/ZenkoCR.ts | 494 +++++++++++ tests/ctst/yarn.lock | 7 + 15 files changed, 1824 insertions(+), 214 deletions(-) create mode 100755 .github/scripts/end2end/setup-ctst-local.sh create mode 100644 tests/ctst/CTST_MANUAL_SETUP_ANALYSIS.md create mode 100644 tests/ctst/MIGRATION_TO_CTST_ONLY.md create mode 100644 tests/ctst/steps/setup/setup.ts create mode 100644 tests/ctst/world/ZenkoCR.ts diff --git a/.github/scripts/end2end/configure-e2e-ctst.sh b/.github/scripts/end2end/configure-e2e-ctst.sh index e1439867ee..703afbcab7 100755 --- a/.github/scripts/end2end/configure-e2e-ctst.sh +++ b/.github/scripts/end2end/configure-e2e-ctst.sh @@ -1,54 +1,5 @@ #!/bin/bash set -exu -# Setup test environment variables -export ZENKO_NAME=${1:-"end2end"} -# Getting kafka host from backbeat's config -KAFKA_HOST_PORT=$(kubectl get secret -l app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end \ - -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .kafka.hosts) -KAFKA_HOST_PORT=${KAFKA_HOST_PORT:1:-1} -# Removing the port -export NOTIF_KAFKA_HOST=${KAFKA_HOST_PORT%:*} -export NOTIF_KAFKA_PORT=${KAFKA_HOST_PORT#*:} - -UUID=$(kubectl get secret -l app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end \ - -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .extensions.replication.topic) -UUID=${UUID%.*} -UUID=${UUID:1} - echo "127.0.0.1 iam.zenko.local ui.zenko.local s3-local-file.zenko.local keycloak.zenko.local \ sts.zenko.local management.zenko.local s3.zenko.local website.mywebsite.com utilization.zenko.local" | sudo tee -a /etc/hosts - -# Add bucket notification target -envsubst < ./configs/notification_destinations.yaml | kubectl apply -f - -# Wait for service stabilization -kubectl wait --for condition=DeploymentInProgress=true --timeout 10m zenko/${ZENKO_NAME} -kubectl wait --for condition=DeploymentFailure=false --timeout 10m zenko/${ZENKO_NAME} -kubectl wait --for condition=DeploymentInProgress=false --timeout 10m zenko/${ZENKO_NAME} - -# Get kafka image name and tag -KAFKA_REGISTRY_NAME=$(yq eval ".kafka.sourceRegistry" ../../../solution/deps.yaml) -KAFKA_IMAGE_NAME=$(yq eval ".kafka.image" ../../../solution/deps.yaml) -KAFKA_IMAGE_TAG=$(yq eval ".kafka.tag" ../../../solution/deps.yaml) -KAFKA_IMAGE=$KAFKA_REGISTRY_NAME/$KAFKA_IMAGE_NAME:$KAFKA_IMAGE_TAG - -# Cold location topic -AZURE_ARCHIVE_STATUS_TOPIC="${UUID}.cold-status-e2e-azure-archive" -AZURE_ARCHIVE_STATUS_TOPIC_2_NV="${UUID}.cold-status-e2e-azure-archive-2-non-versioned" -AZURE_ARCHIVE_STATUS_TOPIC_2_V="${UUID}.cold-status-e2e-azure-archive-2-versioned" -AZURE_ARCHIVE_STATUS_TOPIC_2_S="${UUID}.cold-status-e2e-azure-archive-2-suspended" - -# Creating bucket notification topic in kafka -kubectl run kafka-topics \ - --image=$KAFKA_IMAGE \ - --pod-running-timeout=5m \ - --rm \ - --restart=Never \ - --attach=True \ - --command -- bash -c \ - "kafka-topics.sh --create --topic $NOTIF_DEST_TOPIC --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ - kafka-topics.sh --create --topic $NOTIF_ALT_DEST_TOPIC --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ - kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ - kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC_2_NV --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ - kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC_2_V --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ - kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC_2_S --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists" diff --git a/.github/scripts/end2end/run-e2e-ctst.sh b/.github/scripts/end2end/run-e2e-ctst.sh index 195f3241c0..063cc1049a 100755 --- a/.github/scripts/end2end/run-e2e-ctst.sh +++ b/.github/scripts/end2end/run-e2e-ctst.sh @@ -13,145 +13,37 @@ JUNIT_REPORT_PATH=${JUNIT_REPORT_PATH:-"ctst-junit.xml"} # Zenko Version VERSION=$(cat ../../../VERSION | grep -Po 'VERSION="\K[^"]*') -# Zenko Environment -ZENKO_ACCOUNT_NAME="zenko-ctst" -ADMIN_ACCESS_KEY_ID=$(kubectl get secret end2end-management-vault-admin-creds.v1 -o jsonpath='{.data.accessKey}' | base64 -d) -ADMIN_SECRET_ACCESS_KEY=$(kubectl get secret end2end-management-vault-admin-creds.v1 -o jsonpath='{.data.secretKey}' | base64 -d) -ADMIN_PRA_ACCESS_KEY_ID=$(kubectl get secret end2end-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.accessKey}' | base64 -d) -ADMIN_PRA_SECRET_ACCESS_KEY=$(kubectl get secret end2end-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.secretKey}' | base64 -d) -STORAGE_MANAGER_USER_NAME="ctst_storage_manager" -STORAGE_ACCOUNT_OWNER_USER_NAME="ctst_storage_account_owner" -DATA_CONSUMER_USER_NAME="ctst_data_consumer" -DATA_ACCESSOR_USER_NAME="ctst_data_accessor" -VAULT_AUTH_HOST="${ZENKO_NAME}-connector-vault-auth-api.default.svc.cluster.local" -ZENKO_PORT="80" -KEYCLOAK_TEST_USER=${OIDC_USERNAME} -KEYCLOAK_TEST_PASSWORD=${OIDC_PASSWORD} -KEYCLOAK_TEST_HOST=${OIDC_HOST} -KEYCLOAK_TEST_PORT="80" -KEYCLOAK_TEST_REALM_NAME=${OIDC_REALM} -KEYCLOAK_TEST_CLIENT_ID=${OIDC_CLIENT_ID} -KEYCLOAK_TEST_GRANT_TYPE="password" +# Minimal environment setup - CTST will handle all Kubernetes discovery -# get Zenko service users credentials -BACKBEAT_LCBP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcbp-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-lifecycle-bp-1\.json}' | base64 -d) -BACKBEAT_LCC_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcc-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-lifecycle-conductor-1\.json}' | base64 -d) -BACKBEAT_LCOP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcop-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-lifecycle-op-1\.json}' | base64 -d) -BACKBEAT_QP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-qp-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-qp-1\.json}' | base64 -d) -SORBET_FWD_2_ACCESSKEY=$(kubectl get secret -l app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.accessKey}' | base64 -d) -SORBET_FWD_2_SECRETKEY=$(kubectl get secret -l app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.secretKey}' | base64 -d) -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) - -# Get KAFKA topics for sorbet -KAFKA_DEAD_LETTER_TOPIC=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end \ - -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq '."kafka-dead-letter-topic"' | cut -d "\"" -f 2) - -KAFKA_OBJECT_TASK_TOPIC=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end \ - -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq '."kafka-object-task-topic"' | cut -d "\"" -f 2) - -KAFKA_GC_REQUEST_TOPIC=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end \ - -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq '."kafka-gc-request-topic"' | cut -d "\"" -f 2) - -DR_ADMIN_ACCESS_KEY_ID=$(kubectl get secret end2end-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.accessKey}' | base64 -d) -DR_ADMIN_SECRET_ACCESS_KEY=$(kubectl get secret end2end-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.secretKey}' | base64 -d) - -# Extracting kafka host from bacbeat's config -KAFKA_HOST_PORT=$(kubectl get secret -l app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end \ - -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .kafka.hosts) -KAFKA_HOST_PORT=${KAFKA_HOST_PORT:1:-1} - -TIME_PROGRESSION_FACTOR=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath="{.metadata.annotations.zenko\.io/time-progression-factor}") -INSTANCE_ID=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.status.instanceID}') - -# Azure archive tests -AZURE_ARCHIVE_ACCESS_TIER="Hot" -AZURE_ARCHIVE_MANIFEST_ACCESS_TIER="Hot" - -BACKBEAT_API_HOST=$(kubectl get secret -l app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .backbeat.host) -BACKBEAT_API_HOST=${BACKBEAT_API_HOST:1:-1} -BACKBEAT_API_PORT=$(kubectl get secret -l app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .backbeat.port) - -KAFKA_CLEANER_INTERVAL=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.spec.kafkaCleaner.interval}') -SORBETD_RESTORE_TIMEOUT=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.spec.sorbet.server.azure.restoreTimeout}') - -# Utilization service -UTILIZATION_SERVICE_HOST=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.spec.scuba.api.ingress.hostname}') -UTILIZATION_SERVICE_PORT="80" - -# Setting CTST world params +# Minimal CTST world params - CTST handles all Kubernetes discovery WORLD_PARAMETERS="$(jq -c </dev/null; then + echo "Patching CoreDNS for mock service resolution..." + bash patch-coredns.sh +else + echo "CoreDNS already configured" +fi + +# 3. Setup /etc/hosts for local development (requires sudo) +echo "Checking /etc/hosts configuration..." +if ! grep -q "zenko.local" /etc/hosts 2>/dev/null; then + echo "Setting up /etc/hosts (requires sudo)..." + echo "127.0.0.1 iam.zenko.local ui.zenko.local s3-local-file.zenko.local keycloak.zenko.local \ + sts.zenko.local management.zenko.local s3.zenko.local website.mywebsite.com utilization.zenko.local" | sudo tee -a /etc/hosts +else + echo "/etc/hosts already configured" +fi + +# 4. Wait for Zenko to be ready +echo "Waiting for Zenko deployment to be ready..." +kubectl wait --for condition=DeploymentFailure=false --timeout 10m zenko/end2end -n $NAMESPACE 2>/dev/null || echo "Zenko wait failed or not found" +kubectl wait --for condition=DeploymentInProgress=false --timeout 10m zenko/end2end -n $NAMESPACE 2>/dev/null || echo "Zenko wait failed or not found" + +echo "CTST local environment ready!" +echo "" +echo "Usage:" +echo " cd tests/ctst" +echo " npm test # Run all CTST tests" +echo " npm run test -- --tags @PRA # Run specific test tags" +echo "" +echo "Note: CTST will handle all Kubernetes setup (mocks, topics, deployments, etc.) automatically" \ No newline at end of file diff --git a/tests/ctst/CTST_MANUAL_SETUP_ANALYSIS.md b/tests/ctst/CTST_MANUAL_SETUP_ANALYSIS.md new file mode 100644 index 0000000000..312a4921af --- /dev/null +++ b/tests/ctst/CTST_MANUAL_SETUP_ANALYSIS.md @@ -0,0 +1,218 @@ +# CTST Manual Setup Requirements Analysis + +This document contains a comprehensive analysis of all manual setup requirements for CTST tests, excluding Zenko deployment itself. + +## **COMPLETE LIST OF MANUAL CTST SETUP REQUIREMENTS** + +### **1. ENVIRONMENT VARIABLES SETUP** +These are currently set via shell scripts and need to be extracted from Kubernetes secrets: + +#### **Admin Credentials** +```bash +ADMIN_ACCESS_KEY_ID=$(kubectl get secret end2end-management-vault-admin-creds.v1 -o jsonpath='{.data.accessKey}' | base64 -d) +ADMIN_SECRET_ACCESS_KEY=$(kubectl get secret end2end-management-vault-admin-creds.v1 -o jsonpath='{.data.secretKey}' | base64 -d) +ADMIN_PRA_ACCESS_KEY_ID=$(kubectl get secret end2end-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.accessKey}' | base64 -d) +ADMIN_PRA_SECRET_ACCESS_KEY=$(kubectl get secret end2end-pra-management-vault-admin-creds.v1 -o jsonpath='{.data.secretKey}' | base64 -d) +``` + +#### **Service User Credentials** +```bash +BACKBEAT_LCBP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcbp-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-lifecycle-bp-1\.json}' | base64 -d) +BACKBEAT_LCC_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcc-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-lifecycle-conductor-1\.json}' | base64 -d) +BACKBEAT_LCOP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-lcop-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-lifecycle-op-1\.json}' | base64 -d) +BACKBEAT_QP_1_CREDS=$(kubectl get secret -l app.kubernetes.io/name=backbeat-qp-user-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.backbeat-qp-1\.json}' | base64 -d) +SORBET_FWD_2_ACCESSKEY=$(kubectl get secret -l app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.accessKey}' | base64 -d) +SORBET_FWD_2_SECRETKEY=$(kubectl get secret -l app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.secretKey}' | base64 -d) +``` + +#### **Kafka Topics and Configuration** +```bash +KAFKA_DEAD_LETTER_TOPIC=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq '."kafka-dead-letter-topic"' | cut -d "\"" -f 2) +KAFKA_OBJECT_TASK_TOPIC=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq '."kafka-object-task-topic"' | cut -d "\"" -f 2) +KAFKA_GC_REQUEST_TOPIC=$(kubectl get secret -l app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq '."kafka-gc-request-topic"' | cut -d "\"" -f 2) +KAFKA_HOST_PORT=$(kubectl get secret -l app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .kafka.hosts) +``` + +#### **Backbeat API Configuration** +```bash +BACKBEAT_API_HOST=$(kubectl get secret -l app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .backbeat.host) +BACKBEAT_API_PORT=$(kubectl get secret -l app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq .backbeat.port) +``` + +#### **Zenko Instance Configuration** +```bash +TIME_PROGRESSION_FACTOR=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath="{.metadata.annotations.zenko\.io/time-progression-factor}") +INSTANCE_ID=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.status.instanceID}') +KAFKA_CLEANER_INTERVAL=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.spec.kafkaCleaner.interval}') +SORBETD_RESTORE_TIMEOUT=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.spec.sorbet.server.azure.restoreTimeout}') +UTILIZATION_SERVICE_HOST=$(kubectl get zenko ${ZENKO_NAME} -o jsonpath='{.spec.scuba.api.ingress.hostname}') +``` + +### **2. KUBERNETES RBAC SETUP** +**Critical Security Risk - Currently Grants Cluster Admin to ALL Service Accounts:** +```bash +kubectl create clusterrolebinding serviceaccounts-cluster-admin \ + --clusterrole=cluster-admin \ + --group=system:serviceaccounts +``` + +### **3. DEPLOYMENT ENVIRONMENT MODIFICATIONS** +```bash +kubectl set env deployment end2end-connector-cloudserver SCUBA_HEALTHCHECK_FREQUENCY=100 +kubectl rollout status deployment end2end-connector-cloudserver +``` + +### **4. HOST NETWORK CONFIGURATION** +**Requires sudo access to modify /etc/hosts:** +```bash +echo "127.0.0.1 iam.zenko.local ui.zenko.local s3-local-file.zenko.local keycloak.zenko.local \ + sts.zenko.local management.zenko.local s3.zenko.local website.mywebsite.com utilization.zenko.local" | sudo tee -a /etc/hosts +``` + +### **5. KEYCLOAK SETUP AND CONFIGURATION** +#### **Keycloak Realm and User Creation via Docker Container:** +```bash +docker run \ + --rm \ + --network=host \ + "${E2E_IMAGE}" /bin/bash \ + -c "SUBDOMAIN=${SUBDOMAIN} CONTROL_PLANE_INGRESS_ENDPOINT=${OIDC_ENDPOINT} ACCOUNT=${ZENKO_ACCOUNT_NAME} KEYCLOAK_REALM=${KEYCLOAK_TEST_REALM_NAME} STORAGE_MANAGER=${STORAGE_MANAGER_USER_NAME} STORAGE_ACCOUNT_OWNER=${STORAGE_ACCOUNT_OWNER_USER_NAME} DATA_CONSUMER=${DATA_CONSUMER_USER_NAME} DATA_ACCESSOR=${DATA_ACCESSOR_USER_NAME} /ctst/bin/seedKeycloak.sh" +``` + +#### **Keycloak Configuration Requirements:** +- **Realm creation** with specific roles: `StorageManager`, `AccountTest::DataAccessor`, `AccountTest::DataConsumer`, `AccountTest::StorageAccountOwner` +- **User creation** with instance IDs and roles +- **Client configuration** for OIDC integration + +### **6. KAFKA TOPIC CREATION** +**Multiple Kafka topics need to be created:** +```bash +kubectl run kafka-topics \ + --image=$KAFKA_IMAGE \ + --pod-running-timeout=5m \ + --rm \ + --restart=Never \ + --attach=True \ + --command -- bash -c \ + "kafka-topics.sh --create --topic $NOTIF_DEST_TOPIC --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ + kafka-topics.sh --create --topic $NOTIF_ALT_DEST_TOPIC --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ + kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ + kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC_2_NV --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ + kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC_2_V --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists ; \ + kafka-topics.sh --create --topic $AZURE_ARCHIVE_STATUS_TOPIC_2_S --partitions 10 --bootstrap-server $KAFKA_HOST_PORT --if-not-exists" +``` + +### **7. KUBERNETES CUSTOM RESOURCES** +#### **ZenkoNotificationTarget Resources:** +```yaml +apiVersion: zenko.io/v1alpha2 +kind: ZenkoNotificationTarget +metadata: + name: ${NOTIF_DEST_NAME} + labels: + app.kubernetes.io/instance: ${ZENKO_NAME} +spec: + type: kafka + host: ${NOTIF_KAFKA_HOST} + port: ${NOTIF_KAFKA_PORT} + destinationTopic: ${NOTIF_DEST_TOPIC} +``` + +### **8. MOCK SERVICES DEPLOYMENT** +#### **Azure Mock Service:** +- **Azurite container** for Azure Blob/Queue simulation +- **Ingress configuration** for multiple Azure endpoints +- **TLS certificates** for secure communication + +#### **AWS Mock Service:** +- **CloudServer container** with pre-configured metadata +- **ConfigMap** with mock metadata tar.gz +- **Ingress configuration** for AWS endpoints + +### **9. EXTERNAL DEPENDENCIES** +#### **Required Environment Variables from External Systems:** +- `SUBDOMAIN`, `DR_SUBDOMAIN` +- `NOTIF_DEST_NAME`, `NOTIF_DEST_TOPIC`, `NOTIF_ALT_DEST_NAME`, `NOTIF_ALT_DEST_TOPIC` +- `KAFKA_EXTERNAL_IP` +- `PROMETHEUS_NAME` +- `OIDC_USERNAME`, `OIDC_PASSWORD`, `OIDC_HOST`, `OIDC_REALM`, `OIDC_CLIENT_ID`, `OIDC_ENDPOINT` +- `AZURE_ACCOUNT_NAME`, `AZURE_SECRET_KEY`, `AZURE_BACKEND_ENDPOINT`, `AZURE_BACKEND_QUEUE_ENDPOINT` +- `AZURE_ARCHIVE_BUCKET_NAME`, `AZURE_ARCHIVE_BUCKET_NAME_2`, `AZURE_ARCHIVE_QUEUE_NAME` + +### **10. VOLUME AND STORAGE REQUIREMENTS** +#### **Pod Volume Mounts:** +```yaml +volumeMounts: + - name: "cold-data" + mountPath: "/cold-data" + - name: "reports" + mountPath: "/reports" +volumes: + - name: "cold-data" + persistentVolumeClaim: + claimName: "sorbet-data" + - name: "reports" + hostPath: + path: "/data/reports" + type: "DirectoryOrCreate" +``` + +### **11. SERVICE ACCOUNT AND RBAC FOR CONFIGURATION** +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ${SERVICE_ACCOUNT} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: ${SERVICE_ACCOUNT} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["*"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ${SERVICE_ACCOUNT} +subjects: +- kind: ServiceAccount + name: ${SERVICE_ACCOUNT} +roleRef: + kind: Role + name: ${SERVICE_ACCOUNT} + apiGroup: rbac.authorization.k8s.io +``` + +### **12. ZENKO STATUS WAITING** +**Wait for Zenko deployment stabilization:** +```bash +kubectl wait --for condition=DeploymentInProgress=true --timeout 10m zenko/${ZENKO_NAME} +kubectl wait --for condition=DeploymentFailure=false --timeout 10m zenko/${ZENKO_NAME} +kubectl wait --for condition=DeploymentInProgress=false --timeout 10m zenko/${ZENKO_NAME} +``` + +--- + +## **SUMMARY** + +This comprehensive analysis reveals **12 major categories** of manual setup requirements that need to be automated in BeforeAll hooks: + +1. **Environment Variables Extraction** (from ~30+ kubectl secret/configmap queries) +2. **Kubernetes RBAC Setup** (security-critical cluster-admin binding) +3. **Deployment Modifications** (environment variable injection) +4. **Host Network Configuration** (requires sudo access) +5. **Keycloak Setup** (Docker container execution + realm/user creation) +6. **Kafka Topic Creation** (6+ topics with specific configurations) +7. **Custom Resource Creation** (ZenkoNotificationTarget resources) +8. **Mock Services Deployment** (Azure + AWS mock services) +9. **External Dependencies** (20+ environment variables from CI/external systems) +10. **Volume/Storage Setup** (PVC and hostPath configurations) +11. **Service Account/RBAC** (for configuration pods) +12. **Status Synchronization** (waiting for Zenko stabilization) + +**Key Challenge:** The current setup has significant **security implications** (cluster-admin to all service accounts) and **external dependencies** (Docker execution, sudo access, external environment variables) that will need careful handling in the BeforeAll automation. + +The goal of moving everything to BeforeAll hooks using the Kubernetes client is achievable, but will require handling these external dependencies and security considerations properly. \ No newline at end of file diff --git a/tests/ctst/MIGRATION_TO_CTST_ONLY.md b/tests/ctst/MIGRATION_TO_CTST_ONLY.md new file mode 100644 index 0000000000..7481365619 --- /dev/null +++ b/tests/ctst/MIGRATION_TO_CTST_ONLY.md @@ -0,0 +1,142 @@ +# Migration to CTST-Only Test Suite + +This document outlines the plan to migrate all E2E tests to CTST and remove legacy test suites. + +## **Current State Analysis** + +### **Existing Test Suites:** +1. **Python E2E Tests** (`tests/zenko_tests/`) + - Configuration-based tests using `configuration.py` + - Account, location, workflow setup via Management API + - Bucket creation and management + +2. **Node.js Tests** (`tests/zenko_tests/node_tests/`) + - Backbeat tests + - CloudServer tests + - IAM policy tests + - Smoke tests + - UI tests + +3. **CTST Tests** (`tests/ctst/`) + - Feature-based Gherkin tests + - TypeScript step definitions + - Modern Cucumber.js framework + +### **Current Workflow Steps:** +```yaml +- Configure E2E test environment (Python) +- Run Python E2E tests (end2end, iam-policies, object-api, smoke, backbeat) +- Configure E2E CTST test environment +- Run CTST tests +``` + +## **Migration Strategy** + +### **Phase 1: CTST Infrastructure Simplification** ✅ (COMPLETED) +- [x] Simplified CTST setup to minimal parameter extraction +- [x] Moved static values to Zenko class +- [x] Removed complex Kubernetes setup from CTST (now handled internally) +- [x] Created comprehensive ZenkoCR TypeScript types + +### **Phase 2: Feature Migration** (IN PROGRESS) +Migrate existing test functionality to CTST: + +#### **Already in CTST:** +- [x] Azure Archive tests (`azureArchive.feature`) +- [x] PRA tests (`pra.feature`) +- [x] CloudServer Auth (`cloudserverAuth.feature`) +- [x] Bucket Website (`bucketWebsite.feature`) +- [x] IAM Policies (`iam-policies/`) +- [x] Quotas (`quotas/`) +- [x] Resource Policies (`resource-policies/`) +- [x] Utilization (`utilization/`) +- [x] Bucket Notifications (`bucket-notifications/`) + +#### **Need Migration to CTST:** +- [ ] **Backbeat tests** → Convert to CTST features +- [ ] **Object API tests** → Convert to CTST features +- [ ] **Smoke tests** → Convert to CTST features +- [ ] **UI tests** → Convert to CTST features +- [ ] **Configuration management** → Convert to CTST Background steps + +### **Phase 3: Infrastructure Consolidation** (FUTURE) +Replace shared infrastructure with CTST-native approach: + +#### **Current Shared Infrastructure:** +- `install-mocks.sh` → CTST built-in mock management +- `patch-coredns.sh` → CTST built-in DNS configuration +- `keycloak-helper.sh` → CTST built-in Keycloak setup +- `configuration.py` → CTST Background steps + +#### **CTST-Native Replacements:** +```typescript +// Replace install-mocks.sh +@Before +async function setupMockServices() { + // CTST handles mock services internally +} + +// Replace configuration.py +@Background +async function setupTestConfiguration() { + // CTST handles accounts, locations, workflows +} +``` + +### **Phase 4: Workflow Simplification** (FUTURE) +Final simplified workflow: + +```yaml +steps: + - name: Deploy Zenko + uses: ./.github/actions/deploy + - name: Setup CTST Prerequisites + run: bash setup-ctst-local.sh + - name: Run All E2E Tests via CTST + run: bash run-e2e-ctst.sh +``` + +## **Local Development Support** + +### **Current Approach:** +```bash +# Setup prerequisites (DNS, /etc/hosts) +.github/scripts/end2end/setup-ctst-local.sh + +# Run CTST tests +cd tests/ctst +npm test +``` + +### **Future CTST-Only Approach:** +```bash +# CTST handles everything +cd tests/ctst +npm test # CTST auto-detects environment and sets up everything needed +``` + +## **Benefits of CTST-Only Architecture** + +1. **Unified Framework**: Single test framework for all E2E testing +2. **Better Maintainability**: No duplicate test logic across frameworks +3. **Modern Tooling**: TypeScript, Gherkin, modern Cucumber.js +4. **Self-Contained**: CTST handles all infrastructure setup internally +5. **Developer Experience**: Simple `npm test` for all E2E testing + +## **Migration Checklist** + +- [x] Simplify CTST parameter extraction +- [x] Remove complex Kubernetes setup from CTST +- [x] Create CTST-native local development script +- [ ] Migrate remaining Python tests to CTST features +- [ ] Migrate Node.js tests to CTST steps +- [ ] Remove legacy test suites +- [ ] Simplify GitHub Actions workflow +- [ ] Update documentation + +## **Timeline** + +1. **Immediate**: CTST infrastructure simplified (DONE) +2. **Short-term**: Migrate critical test features to CTST +3. **Medium-term**: Deprecate Python/Node.js test suites +4. **Long-term**: CTST becomes the only E2E test suite \ No newline at end of file diff --git a/tests/ctst/common/common.ts b/tests/ctst/common/common.ts index 07a4c3eb0f..1da90a1ce2 100644 --- a/tests/ctst/common/common.ts +++ b/tests/ctst/common/common.ts @@ -124,15 +124,13 @@ async function createBucket(world: Zenko, versioning: string, bucketName: string } Given('a {string} bucket with dot', async function (this: Zenko, versioning: string) { - const preName = this.getSaved('accountName') || - this.parameters.AccountName || Constants.ACCOUNT_NAME; + const preName = this.getSaved('accountName') || Zenko.ACCOUNT_NAME; await createBucket(this, versioning, `${preName}.${Constants.BUCKET_NAME_TEST}${Utils.randomString()}`.toLocaleLowerCase()); }); Given('a {string} bucket', async function (this: Zenko, versioning: string) { - const preName = this.getSaved('accountName') || - this.parameters.AccountName || Constants.ACCOUNT_NAME; + const preName = this.getSaved('accountName') || Zenko.ACCOUNT_NAME; await createBucket(this, versioning, `${preName}${Constants.BUCKET_NAME_TEST}${Utils.randomString()}`.toLocaleLowerCase()); }); diff --git a/tests/ctst/package.json b/tests/ctst/package.json index 631c9a7601..80f862d4a2 100644 --- a/tests/ctst/package.json +++ b/tests/ctst/package.json @@ -18,7 +18,8 @@ "prometheus-query": "^3.4.0", "proper-lockfile": "^4.1.2", "qs": "^6.13.0", - "scubaclient": "git+https://github.com/scality/scubaclient#^1.1.2" + "scubaclient": "git+https://github.com/scality/scubaclient#^1.1.2", + "werelogs": "scality/werelogs#8.2.2" }, "devDependencies": { "@aws-sdk/client-iam": "^3.582.0", diff --git a/tests/ctst/steps/cloudserverAuth.ts b/tests/ctst/steps/cloudserverAuth.ts index a75a104be4..db724170ef 100644 --- a/tests/ctst/steps/cloudserverAuth.ts +++ b/tests/ctst/steps/cloudserverAuth.ts @@ -35,8 +35,7 @@ When('the user tries to perform DeleteObjects', async function (this: Zenko) { When('the user tries to perform CreateBucket', async function (this: Zenko) { this.resetCommand(); this.useSavedIdentity(); - const preName = this.getSaved('accountName') || - this.parameters.AccountName || Constants.ACCOUNT_NAME; + const preName = this.getSaved('accountName') || Zenko.ACCOUNT_NAME; const usedBucketName = `${preName}${Constants.BUCKET_NAME_TEST}${Utils.randomString()}`.toLocaleLowerCase(); this.addToSaved('bucketName', usedBucketName); this.addCommandParameter({ bucket: usedBucketName }); diff --git a/tests/ctst/steps/pra.ts b/tests/ctst/steps/pra.ts index 4542658ce0..1f0c343993 100644 --- a/tests/ctst/steps/pra.ts +++ b/tests/ctst/steps/pra.ts @@ -3,7 +3,6 @@ import Zenko from 'world/Zenko'; import ZenkoDrctl from './dr/drctl'; import { createSecret, - displayCRStatus, getDRSink, getDRSource, getPVCFromLabel, @@ -85,7 +84,6 @@ export function preparePRA(world: Zenko) { } export async function displayDebuggingInformation(world: Zenko) { - await displayCRStatus(world); const drSource = await getDRSource(world); const drSink = await getDRSink(world); diff --git a/tests/ctst/steps/setup/setup.ts b/tests/ctst/steps/setup/setup.ts new file mode 100644 index 0000000000..3d4e057702 --- /dev/null +++ b/tests/ctst/steps/setup/setup.ts @@ -0,0 +1,815 @@ +import Werelogs from 'werelogs'; +import { BeforeAll } from '@cucumber/cucumber'; +import { CacheHelper } from 'cli-testing'; +import lockFile from 'proper-lockfile'; +import * as fs from 'fs'; +import { + KubeConfig, + CoreV1Api, + CustomObjectsApi, + AppsV1Api, + RbacAuthorizationV1Api, + V1Pod, + V1Service, + V1ClusterRoleBinding, +} from '@kubernetes/client-node'; +import Zenko, { ZenkoWorldParameters } from 'world/Zenko'; +import { getZenkoCR, waitForDeploymentRollout, waitForZenkoToStabilize } from 'steps/utils/kubernetes'; + +type AdminCredentials = { + AdminAccessKey: string; + AdminSecretKey: string; + AccountAccessKey: string; + AccountSecretKey: string; +}; + +type PRACredentials = { + DRAdminAccessKey?: string; + DRAdminSecretKey?: string; + DRSubdomain?: string; +}; + +type ServiceUserCredentials = { + accessKey: string; + secretKey: string; +}; + +type ZenkoInstanceInfo = { + TimeProgressionFactor: number; + InstanceID?: string; + KafkaCleanerInterval?: string; + SorbetdRestoreTimeout?: string; + UtilizationServiceHost?: string; +}; + +const logger = new Werelogs.Logger('CTST').newRequestLogger(); + +const SETUP_COMPLETED_FILE = '/tmp/ctst-setup-completed'; +const SETUP_TIMEOUT = 60_000; + +function initializeKubernetesClients() { + const kc = new KubeConfig(); + kc.loadFromDefault(); + + return { + coreClient: kc.makeApiClient(CoreV1Api), + customObjectClient: kc.makeApiClient(CustomObjectsApi), + appsClient: kc.makeApiClient(AppsV1Api), + rbacClient: kc.makeApiClient(RbacAuthorizationV1Api), + }; +} + +/** + * Thread-safe setup coordinator using file locks + * Ensures only one worker performs setup while others wait + */ +async function coordinateSetup(parameters: ZenkoWorldParameters): Promise { + const workerId = `worker-${process.pid}`; + + if (!fs.existsSync(SETUP_COMPLETED_FILE)) { + fs.writeFileSync(SETUP_COMPLETED_FILE, JSON.stringify({ + ready: false, + })); + } + + let releaseLock: (() => Promise) | null = null; + try { + releaseLock = await lockFile.lock(SETUP_COMPLETED_FILE, { + stale: SETUP_TIMEOUT, + retries: { + retries: 5, + factor: 3, + minTimeout: 1000, + maxTimeout: 5000, + } + }); + + const setupData = JSON.parse(fs.readFileSync(SETUP_COMPLETED_FILE, 'utf8')); + if (setupData.ready) { + logger.info(`${workerId} found setup already completed by ${setupData.completedBy || 'unknown'}`); + await extractAndCacheParameters(parameters); + } else { + logger.info(`${workerId} performing CTST cluster setup...`); + await setupClusterConfiguration(parameters); + logger.info(`${workerId} setup configuration completed, writing completion file...`); + fs.writeFileSync(SETUP_COMPLETED_FILE, JSON.stringify({ + ready: true, + completedBy: workerId, + completedAt: new Date().toISOString(), + pid: process.pid + })); + logger.info(`${workerId} extracting and caching parameters...`); + await extractAndCacheParameters(parameters); + logger.info(`CTST cluster setup completed by ${workerId}`); + } + } catch (error) { + // Only handle lock-related errors with fallback behavior + if ((error as { code?: string }).code === 'ELOCKED') { + logger.warn(`${workerId} could not acquire lock, waiting + for setup completion`, { error: (error as { message?: string }).message }); + + // Poll for completion since another worker is doing the setup + let attempts = 0; + const maxAttempts = 450; // Wait up to 15 minutes (10min stabilization + 5min buffer) + const pollInterval = 2000; // 2 seconds + + while (attempts < maxAttempts) { + try { + const setupData = JSON.parse(fs.readFileSync(SETUP_COMPLETED_FILE, 'utf8')); + if (setupData.ready) { + logger.info(`${workerId} detected setup completion by ${setupData.completedBy}, + proceeding with parameter extraction`); + await extractAndCacheParameters(parameters); + return; + } + } catch (readError) { + logger.info(`${workerId} could not read setup file, retrying...`, { readError }); + } + + attempts++; + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + + // If we've waited long enough, try to proceed anyway + logger.error(`${workerId} timed out waiting for setup completion, + attempting parameter extraction anyway`); + try { + await extractAndCacheParameters(parameters); + return; + } catch (fallbackError) { + logger.error(`${workerId} fallback parameter extraction also failed`, { fallbackError }); + throw new Error(`Setup coordination failed: could not acquire lock + and fallback failed. Original lock error: ${(error as { message?: string }).message}`); + } + } else { + // For non-lock errors (actual setup failures), re-throw immediately + logger.error(`${workerId} setup failed with non-lock error`, { error }); + throw error; + } + } finally { + if (releaseLock) { + await releaseLock(); + } + } +} + +/** + * Extract admin credentials from environment or Kubernetes + */ +async function extractAdminCredentials(coreClient: CoreV1Api, parameters: ZenkoWorldParameters): + Promise { + let adminAccessKey = parameters.AdminAccessKey; + let adminSecretKey = parameters.AdminSecretKey; + + if (!adminAccessKey || !adminSecretKey) { + const adminSecret = await coreClient + .readNamespacedSecret('end2end-management-vault-admin-creds.v1', parameters.Namespace); + adminAccessKey = Buffer.from(adminSecret.body.data?.accessKey || '', 'base64').toString(); + adminSecretKey = Buffer.from(adminSecret.body.data?.secretKey || '', 'base64').toString(); + } + + const finalAdminAccessKey = adminAccessKey || parameters.AdminAccessKey || 'admin'; + const finalAdminSecretKey = adminSecretKey || parameters.AdminSecretKey || 'password'; + + logger.info('Extracted admin credentials', { + finalAdminAccessKey, + finalAdminSecretKey, + }); + return { + AdminAccessKey: finalAdminAccessKey, + AdminSecretKey: finalAdminSecretKey, + AccountAccessKey: parameters.AccountAccessKey || finalAdminAccessKey, + AccountSecretKey: parameters.AccountSecretKey || finalAdminSecretKey, + }; +} + +/** + * Extract PRA admin credentials from environment or Kubernetes + * Only attempts to fetch PRA credentials if DR subdomain is configured and PRA secret exists + */ +async function extractPRACredentials(coreClient: CoreV1Api, parameters: ZenkoWorldParameters): Promise { + let praAdminAccessKey = parameters.DRAdminAccessKey; + let praAdminSecretKey = parameters.DRAdminSecretKey; + + if (parameters.DRSubdomain) { + // Check if PRA secret exists and extract credentials in one call + try { + const praAdminSecret = await coreClient + .readNamespacedSecret('end2end-pra-management-vault-admin-creds.v1', parameters.Namespace); + + praAdminAccessKey = Buffer.from(praAdminSecret.body.data?.accessKey || '', 'base64').toString(); + praAdminSecretKey = Buffer.from(praAdminSecret.body.data?.secretKey || '', 'base64').toString(); + logger.info('PRA credentials extracted from Kubernetes secret'); + } catch (error) { + if ((error as { response?: { statusCode?: number } }).response?.statusCode === 404) { + logger.info('PRA secret not found, no PRA setup detected - skipping PRA credential extraction'); + // PRA is not set up, skip entirely and use parameters/defaults + } else { + logger.warn('Error reading PRA secret, skipping PRA credential extraction', { + error: (error as { message?: string }).message, + }); + } + } + } else { + logger.info('DRSubdomain not configured, skipping PRA credential extraction'); + } + + logger.info('Extracted PRA credentials', { + praAdminAccessKey, + praAdminSecretKey, + }); + + return { + DRAdminAccessKey: praAdminAccessKey || parameters.DRAdminAccessKey, + DRAdminSecretKey: praAdminSecretKey || parameters.DRAdminSecretKey, + DRSubdomain: parameters.DRSubdomain, + }; +} + +/** + * Extract service user credentials from Kubernetes + */ +async function extractServiceCredentials(coreClient: CoreV1Api, parameters: ZenkoWorldParameters) { + if (parameters.ServiceUsersCredentials) { + return { accessKey: parameters.ServiceUsersCredentials, secretKey: parameters.ServiceUsersCredentials }; + } + + const serviceUserSecrets = await Promise.allSettled([ + coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=backbeat-lcbp-user-creds,app.kubernetes.io/instance=end2end'), + coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=backbeat-lcc-user-creds,app.kubernetes.io/instance=end2end'), + coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=backbeat-lcop-user-creds,app.kubernetes.io/instance=end2end'), + coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=backbeat-qp-user-creds,app.kubernetes.io/instance=end2end'), + coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end'), + ]); + + const credentials: Record = {}; + + const serviceCredentialHandlers = [ + { index: 0, key: 'backbeat-lifecycle-bp-1.json', name: 'backbeat-lifecycle-bp-1' }, + { index: 1, key: 'backbeat-lifecycle-conductor-1.json', name: 'backbeat-lifecycle-conductor-1' }, + { index: 2, key: 'backbeat-lifecycle-op-1.json', name: 'backbeat-lifecycle-op-1' }, + { index: 3, key: 'backbeat-qp-1.json', name: 'backbeat-qp-1' }, + ]; + + serviceCredentialHandlers.forEach(({ index, key, name }) => { + if (serviceUserSecrets[index].status === 'fulfilled' && serviceUserSecrets[index].value.body.items.length > 0) { + const data = + Buffer.from(serviceUserSecrets[index].value.body.items[0].data?.[key] || '', 'base64').toString(); + credentials[name] = JSON.parse(data) as ServiceUserCredentials; + } + }); + + if (serviceUserSecrets[4].status === 'fulfilled' && serviceUserSecrets[4].value.body.items.length > 0) { + const sorbetAccessKey = + Buffer.from(serviceUserSecrets[4].value.body.items[0].data?.accessKey || '', 'base64').toString(); + const sorbetSecretKey = + Buffer.from(serviceUserSecrets[4].value.body.items[0].data?.secretKey || '', 'base64').toString(); + credentials['sorbet-fwd-2'] = { + accessKey: sorbetAccessKey, + secretKey: sorbetSecretKey, + }; + } + + logger.info('Extracted service user credentials', { + credentials, + }); + + return credentials; +} + +/** + * Extract Kafka configuration from Kubernetes secrets + */ +async function extractKafkaConfiguration(coreClient: CoreV1Api, parameters: ZenkoWorldParameters) { + let kafkaHosts = parameters.KafkaHosts; + let kafkaDeadLetterTopic = parameters.KafkaDeadLetterQueueTopic; + let kafkaObjectTaskTopic = parameters.KafkaObjectTaskTopic; + let kafkaGCRequestTopic = parameters.KafkaGCRequestTopic; + let backbeatApiHost = parameters.BackbeatApiHost; + let backbeatApiPort = parameters.BackbeatApiPort; + + const configExtractionTasks = []; + + if (!kafkaHosts || !backbeatApiHost) { + configExtractionTasks.push( + coreClient.listNamespacedSecret( + parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end' + ).then(backbeatConfigSecrets => { + if (backbeatConfigSecrets.body.items.length > 0) { + const configData = Buffer.from( + backbeatConfigSecrets.body.items[0].data?.['config.json'] || '', + 'base64', + ).toString(); + const config = JSON.parse(configData); + kafkaHosts = kafkaHosts || config.kafka?.hosts || ''; + } + }) + ); + + configExtractionTasks.push( + coreClient.listNamespacedSecret( + parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end' + ).then(cloudserverConfigSecrets => { + if (cloudserverConfigSecrets.body.items.length > 0) { + const cloudserverConfigData = Buffer.from( + cloudserverConfigSecrets.body.items[0].data?.['config.json'] || '', + 'base64', + ).toString(); + const cloudserverConfig = JSON.parse(cloudserverConfigData); + backbeatApiHost = backbeatApiHost || cloudserverConfig.backbeat?.host?.replace(/"/g, '') || ''; + backbeatApiPort = backbeatApiPort || cloudserverConfig.backbeat?.port || ''; + } + }) + ); + } + + if (!kafkaDeadLetterTopic || !kafkaObjectTaskTopic || !kafkaGCRequestTopic) { + configExtractionTasks.push( + coreClient.listNamespacedSecret( + parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end' + ).then(sorbetConfigSecrets => { + if (sorbetConfigSecrets.body.items.length > 0) { + const sorbetConfigData = + Buffer.from(sorbetConfigSecrets.body.items[0].data?.['config.json'] || '', 'base64').toString(); + const sorbetConfig = JSON.parse(sorbetConfigData); + kafkaDeadLetterTopic = kafkaDeadLetterTopic || sorbetConfig['kafka-dead-letter-topic'] || ''; + kafkaObjectTaskTopic = kafkaObjectTaskTopic || sorbetConfig['kafka-object-task-topic'] || ''; + kafkaGCRequestTopic = kafkaGCRequestTopic || sorbetConfig['kafka-gc-request-topic'] || ''; + } + }) + ); + } + + await Promise.allSettled(configExtractionTasks); + + logger.info('Extracted Kafka configuration', { + kafkaHosts, + kafkaDeadLetterTopic, + kafkaObjectTaskTopic, + kafkaGCRequestTopic, + backbeatApiHost, + backbeatApiPort, + }); + + return { + KafkaHosts: kafkaHosts || 'kafka.default.svc.cluster.local:9092', + KafkaDeadLetterQueueTopic: kafkaDeadLetterTopic, + KafkaObjectTaskTopic: kafkaObjectTaskTopic, + KafkaGCRequestTopic: kafkaGCRequestTopic, + BackbeatApiHost: backbeatApiHost, + BackbeatApiPort: backbeatApiPort, + }; +} + +/** + * Extract Zenko Custom Resource information + */ +async function extractZenkoCRInfo(parameters: ZenkoWorldParameters): Promise { + let timeProgressionFactor = parameters.TimeProgressionFactor; + let instanceId = parameters.InstanceID; + let kafkaCleanerInterval = parameters.KafkaCleanerInterval; + let sorbetdRestoreTimeout = parameters.SorbetdRestoreTimeout; + let utilizationServiceHost = parameters.UtilizationServiceHost; + + if (!instanceId || !timeProgressionFactor) { + // Zenko world is not yet created in global hooks. + const zenkoBody = await getZenkoCR({ + parameters, + logger, + } as Zenko, parameters.Namespace, 'end2end'); + + if (zenkoBody) { + timeProgressionFactor = timeProgressionFactor || + parseInt(zenkoBody.metadata?.annotations?.['zenko.io/time-progression-factor'] || '1', 10); + instanceId = instanceId || zenkoBody.status?.instanceID || ''; + kafkaCleanerInterval = kafkaCleanerInterval || zenkoBody.spec?.kafkaCleaner?.interval || ''; + sorbetdRestoreTimeout = sorbetdRestoreTimeout || + zenkoBody.spec?.sorbet?.server?.azure?.restoreTimeout || ''; + utilizationServiceHost = utilizationServiceHost || zenkoBody.spec?.scuba?.api?.ingress?.hostname || ''; + } + } + + logger.info('Extracted Zenko Custom Resource information', { + timeProgressionFactor, + instanceId, + kafkaCleanerInterval, + sorbetdRestoreTimeout, + utilizationServiceHost, + }); + + return { + TimeProgressionFactor: timeProgressionFactor || 1, + InstanceID: instanceId, + KafkaCleanerInterval: kafkaCleanerInterval, + SorbetdRestoreTimeout: sorbetdRestoreTimeout, + UtilizationServiceHost: utilizationServiceHost, + }; +} + +/** + * Setup cluster configuration (done only by first worker who gets the lock) + */ +async function setupClusterConfiguration(parameters: ZenkoWorldParameters): Promise { + logger.info('Configuring Kubernetes cluster for CTST...'); + + const { coreClient, customObjectClient, appsClient, rbacClient } = initializeKubernetesClients(); + + const setupTasks = [ + setupClusterRBAC(rbacClient), + setupKafkaTopics(coreClient, parameters), + setupMockServices(coreClient, parameters), + setupNotificationTargets(customObjectClient, parameters), + applyDeploymentModifications(appsClient, parameters), + ]; + + await Promise.allSettled(setupTasks); + + // Setup storage locations after infrastructure is ready + logger.info('Infrastructure setup completed, creating storage locations...'); + await setupStorageLocations(); + + logger.info('Setup tasks completed, waiting for Zenko to stabilize (up to 10 minutes)...'); + + // Wait for Zenko deployment to stabilize with proper timeout + const stabilizationTimeout = 10 * 60 * 1000; // 10 minutes + const stabilizationPromise = waitForZenkoToStabilize({ parameters, logger } as Zenko, true); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Zenko stabilization timed out after 10 minutes')), stabilizationTimeout); + }); + + try { + await Promise.race([stabilizationPromise, timeoutPromise]); + logger.info('Zenko stabilization completed successfully'); + } catch (error) { + logger.error('Zenko stabilization failed or timed out', { error }); + throw error; // Let the setup fail if stabilization fails + } +} + +/** + * Extract and cache parameters from the configured cluster (done by all workers) + */ +async function extractAndCacheParameters(parameters: ZenkoWorldParameters): Promise { + const { coreClient } = initializeKubernetesClients(); + + const [adminCreds, praCreds, serviceCredentials, kafkaConfig, zenkoInfo] = await Promise.all([ + extractAdminCredentials(coreClient, parameters), + extractPRACredentials(coreClient, parameters), + extractServiceCredentials(coreClient, parameters), + extractKafkaConfiguration(coreClient, parameters), + extractZenkoCRInfo(parameters), + ]); + + const setupParameters = { + ...parameters, + ...adminCreds, + ...praCreds, + ...serviceCredentials, + ...kafkaConfig, + ...zenkoInfo, + AccountName: Zenko.ACCOUNT_NAME, + ssl: Zenko.SSL_ENABLED, + port: Zenko.PORT, + VaultAuthHost: parameters.VaultAuthHost || 'end2end-connector-vault-auth-api.default.svc.cluster.local', + UtilizationServicePort: parameters.UtilizationServicePort || '80', + NotificationDestination: parameters.NotificationDestination || 'ctst-notif-dest', + NotificationDestinationTopic: parameters.NotificationDestinationTopic || 'ctst-notif-topic', + NotificationDestinationAlt: parameters.NotificationDestinationAlt || 'ctst-notif-dest-alt', + NotificationDestinationTopicAlt: parameters.NotificationDestinationTopicAlt || 'ctst-notif-topic-alt', + KafkaExternalIps: parameters.KafkaExternalIps || '', + PrometheusService: parameters.PrometheusService || 'prometheus-operated.default.svc.cluster.local', + StorageManagerUsername: parameters.StorageManagerUsername || 'ctst_storage_manager', + StorageAccountOwnerUsername: parameters.StorageAccountOwnerUsername || 'ctst_storage_account_owner', + DataConsumerUsername: parameters.DataConsumerUsername || 'ctst_data_consumer', + DataAccessorUsername: parameters.DataAccessorUsername || 'ctst_data_accessor', + AzureArchiveAccessTier: parameters.AzureArchiveAccessTier || 'Hot', + AzureArchiveManifestTier: parameters.AzureArchiveManifestTier || 'Hot', + + KeycloakUsername: parameters.KeycloakUsername || 'testuser', + KeycloakPassword: parameters.KeycloakPassword || 'testpass', + KeycloakPort: parameters.KeycloakPort || '80', + KeycloakGrantType: parameters.KeycloakGrantType || 'password', + KeycloakHost: parameters.KeycloakHost || 'keycloak.zenko.local', + KeycloakRealm: parameters.KeycloakRealm || 'zenko', + KeycloakClientId: parameters.KeycloakClientId || 'zenko-ui', + + AzureAccountName: parameters.AzureAccountName || 'devstoreaccount1', + AzureAccountKey: parameters.AzureAccountKey || + 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==', + AzureArchiveContainer: parameters.AzureArchiveContainer || 'archive-container', + AzureArchiveContainer2: parameters.AzureArchiveContainer2 || 'archive-container-2', + AzureArchiveQueue: parameters.AzureArchiveQueue || 'archive-queue', + + subdomain: parameters.subdomain || 'zenko.local', + }; + + CacheHelper.cacheParameters(setupParameters); + + logger.info('CTST parameters extracted and cached'); +} + +/** + * Setup Kafka topics + */ +async function setupKafkaTopics(coreClient: CoreV1Api, parameters: ZenkoWorldParameters): Promise { + const kafkaConfig = await extractKafkaConfiguration(coreClient, parameters); + if (!kafkaConfig.KafkaHosts) { + logger.info('Kafka hosts not found, skipping Kafka topics setup'); + return; + } + + // Extract UUID from backbeat config like the old script + let uuid = parameters.Namespace; + try { + const backbeatConfigSecrets = await coreClient.listNamespacedSecret( + parameters.Namespace, undefined, undefined, undefined, undefined, + 'app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end' + ); + if (backbeatConfigSecrets.body.items.length > 0) { + const configData = Buffer.from( + backbeatConfigSecrets.body.items[0].data?.['config.json'] || '', + 'base64', + ).toString(); + const config = JSON.parse(configData); + const replicationTopic = config.extensions?.replication?.topic; + if (replicationTopic) { + // Extract UUID from topic like "uuid.backbeat-replication" + uuid = replicationTopic.split('.')[0]; + } + } + } catch (error) { + logger.info('Failed to extract UUID from backbeat config, using namespace', { error }); + } + + const topics = [ + parameters.NotificationDestinationTopic || 'ctst-notif-topic', + parameters.NotificationDestinationTopicAlt || 'ctst-notif-topic-alt', + `${uuid}.cold-status-e2e-azure-archive`, + `${uuid}.cold-status-e2e-azure-archive-2-non-versioned`, + `${uuid}.cold-status-e2e-azure-archive-2-versioned`, + `${uuid}.cold-status-e2e-azure-archive-2-suspended`, + ]; + + const topicCommands = topics.map(topic => + // eslint-disable-next-line max-len + `kafka-topics.sh --create --topic ${topic} --partitions 10 --bootstrap-server ${kafkaConfig.KafkaHosts} --if-not-exists` + ).join(' ; '); + + const kafkaTopicsPod: V1Pod = { + apiVersion: 'v1', + kind: 'Pod', + metadata: { + name: `kafka-topics-setup-${Date.now()}`, + namespace: parameters.Namespace, + }, + spec: { + restartPolicy: 'Never', + containers: [ + { + name: 'kafka-topics', + image: 'confluentinc/cp-kafka:latest', + command: ['bash'], + args: ['-c', topicCommands], + }, + ], + }, + }; + + await coreClient.createNamespacedPod(parameters.Namespace, kafkaTopicsPod); + logger.info('Kafka topics setup initiated', { + kafkaTopicsPod, + }); +} + +/** + * Setup mock services + */ +async function setupMockServices(coreClient: CoreV1Api, parameters: ZenkoWorldParameters): Promise { + const existingPods = await coreClient.listNamespacedPod( + parameters.Namespace, undefined, undefined, undefined, undefined, 'component=mock' + ); + + if (existingPods.body.items.length > 0) { + logger.info('Mock services already deployed', { + existingPods, + }); + return; + } + + const azureMockService: V1Service = { + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: 'azure-mock', + namespace: parameters.Namespace, + }, + spec: { + selector: { name: 'azure-mock' }, + type: 'ClusterIP', + ports: [ + { name: 'blob', port: 80, targetPort: 'blob' }, + { name: 'queue', port: 81, targetPort: 'queue' }, + ], + }, + }; + + const azureMockPod: V1Pod = { + apiVersion: 'v1', + kind: 'Pod', + metadata: { + name: 'azure-mock-pod', + namespace: parameters.Namespace, + labels: { name: 'azure-mock', component: 'mock' }, + }, + spec: { + hostname: 'devstoreaccount1', + subdomain: 'azure-mock', + containers: [ + { + name: 'azurite', + image: 'mcr.microsoft.com/azure-storage/azurite:3.35.0', + command: [ + 'azurite', '-l', '/data', + '--blobHost', '0.0.0.0', '--blobPort', '80', + '--queueHost', '0.0.0.0', '--queuePort', '81', + ], + ports: [ + { name: 'blob', containerPort: 80 }, + { name: 'queue', containerPort: 81 }, + ], + }, + ], + }, + }; + + await Promise.allSettled([ + coreClient.createNamespacedService(parameters.Namespace, azureMockService), + coreClient.createNamespacedPod(parameters.Namespace, azureMockPod), + ]); + + logger.info('Mock services deployment initiated', { + azureMockService, + azureMockPod, + }); +} + +/** + * Setup notification targets + */ +async function setupNotificationTargets(customObjectClient: CustomObjectsApi, parameters: ZenkoWorldParameters) { + const { coreClient } = initializeKubernetesClients(); + const kafkaConfig = await extractKafkaConfiguration(coreClient, parameters); + if (!kafkaConfig.KafkaHosts) { + return; + } + + const [kafkaHost, kafkaPort] = kafkaConfig.KafkaHosts.split(':'); + + const targets = [ + { + name: parameters.NotificationDestination || 'ctst-notif-dest', + topic: parameters.NotificationDestinationTopic || 'ctst-notif-topic' + }, + { + name: parameters.NotificationDestinationAlt || 'ctst-notif-dest-alt', + topic: parameters.NotificationDestinationTopicAlt || 'ctst-notif-topic-alt' + }, + ]; + + for (const target of targets) { + const notificationTarget = { + apiVersion: 'zenko.io/v1alpha2', + kind: 'ZenkoNotificationTarget', + metadata: { + name: target.name, + namespace: parameters.Namespace, + labels: { 'app.kubernetes.io/instance': 'end2end' }, + }, + spec: { + type: 'kafka', + host: kafkaHost, + port: parseInt(kafkaPort || '9092', 10), + destinationTopic: target.topic, + }, + }; + + await customObjectClient.createNamespacedCustomObject( + 'zenko.io', 'v1alpha2', parameters.Namespace, 'zenkonotificationtargets', notificationTarget + ).catch(err => { + if (err.response?.statusCode !== 409) { + throw err; + } + }); + } + + logger.info('Notification targets configured', { + targets, + }); +} + +/** + * Apply deployment modifications + */ +async function applyDeploymentModifications(appsClient: AppsV1Api, parameters: ZenkoWorldParameters): Promise { + const deploymentName = 'end2end-connector-cloudserver'; + + const deployment = await appsClient.readNamespacedDeployment(deploymentName, parameters.Namespace); + + const containers = deployment.body.spec?.template?.spec?.containers || []; + const cloudserverContainer = containers.find(c => c.name === 'cloudserver'); + + if (!cloudserverContainer) { + throw new Error(`CloudServer container not found in deployment ${deploymentName}`); + } + + if (!cloudserverContainer.env) { + cloudserverContainer.env = []; + } + + // We need to set the healthcheck frequency to 100 to speed up the testing of quota-related features. + const existingEnvIndex = cloudserverContainer.env.findIndex(e => e.name === 'SCUBA_HEALTHCHECK_FREQUENCY'); + if (existingEnvIndex >= 0) { + cloudserverContainer.env[existingEnvIndex].value = '100'; + } else { + cloudserverContainer.env.push({ + name: 'SCUBA_HEALTHCHECK_FREQUENCY', + value: '100', + }); + } + + await appsClient.patchNamespacedDeployment( + deploymentName, + parameters.Namespace, + deployment.body, + undefined, undefined, undefined, undefined, undefined, + { headers: { 'Content-Type': 'application/merge-patch+json' } } + ); + + await waitForDeploymentRollout(appsClient, deploymentName, parameters.Namespace); + + logger.info('Deployment modifications applied', { + deploymentName, + parameters, + }); +} + +/** + * Setup storage locations - placeholder for future implementation + * TODO: Implement proper location creation via Management API with Keycloak auth + */ +async function setupStorageLocations(): Promise { + logger.info('Storage location setup placeholder - locations will be created by individual tests'); + // For now, let individual tests create their own locations as they do currently + // This can be enhanced later to create common locations during setup +} + +/** + * Setup cluster RBAC (cluster-admin binding for service accounts) + */ +async function setupClusterRBAC(rbacClient: RbacAuthorizationV1Api): Promise { + const clusterRoleBinding: V1ClusterRoleBinding = { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'ClusterRoleBinding', + metadata: { + name: 'serviceaccounts-cluster-admin', + }, + roleRef: { + apiGroup: 'rbac.authorization.k8s.io', + kind: 'ClusterRole', + name: 'cluster-admin', + }, + subjects: [ + { + apiGroup: 'rbac.authorization.k8s.io', + kind: 'Group', + name: 'system:serviceaccounts', + }, + ], + }; + + try { + await rbacClient.createClusterRoleBinding(clusterRoleBinding); + logger.info('Cluster RBAC binding created successfully'); + } catch (error) { + if ((error as { response?: { statusCode: number } }).response?.statusCode === 409) { + logger.info('Cluster RBAC binding already exists'); + } else { + logger.error('Failed to create cluster RBAC binding', { error }); + throw error; + } + } +} + +BeforeAll({ timeout: 15 * 60 * 1000 }, async function () { + await coordinateSetup(this.parameters as ZenkoWorldParameters); + // print the final parameters + logger.info('Final parameters:', { parameters: this.parameters, cachedParameters: CacheHelper.parameters }); +}); diff --git a/tests/ctst/steps/utils/kubernetes.ts b/tests/ctst/steps/utils/kubernetes.ts index fa0ff8a983..97a17b248d 100644 --- a/tests/ctst/steps/utils/kubernetes.ts +++ b/tests/ctst/steps/utils/kubernetes.ts @@ -15,6 +15,7 @@ import { BatchV1Api, V1Pod, } from '@kubernetes/client-node'; +import { ZenkoCR } from 'world/ZenkoCR'; type ZenkoStatusValue = { lastTransitionTime: string, @@ -284,7 +285,7 @@ export async function waitForZenkoToStabilize( // zenko status. let reconciliationDetected = !needsReconciliation; - world.logger.debug('Waiting for Zenko to stabilize'); + world.logger.info('Waiting for Zenko to stabilize'); const zenkoClient = createKubeCustomObjectClient(world); while (!status && Date.now() - startTime < timeout) { @@ -295,7 +296,7 @@ export async function waitForZenkoToStabilize( 'zenkos', 'end2end', ).catch(err => { - world.logger.debug('Error getting Zenko CR', { + world.logger.info('Error getting Zenko CR', { err: err as unknown, }); return null; @@ -322,7 +323,7 @@ export async function waitForZenkoToStabilize( } }); - world.logger.debug('Checking Zenko CR status', { + world.logger.info('Checking Zenko CR status', { conditions, deploymentFailure, deploymentInProgress, @@ -423,31 +424,6 @@ export async function waitForDataServicesToStabilize(world: Zenko, timeout = 15 return allRunning; } -export async function displayCRStatus(world: Zenko, namespace = 'default') { - const zenkoClient = createKubeCustomObjectClient(world); - - const zenkoCR = await zenkoClient.getNamespacedCustomObject( - 'zenko.io', - 'v1alpha2', - namespace, - 'zenkos', - 'end2end', - ).catch(err => { - world.logger.debug('Error getting Zenko CR', { - err: err as unknown, - }); - return null; - }); - - if (!zenkoCR) { - return; - } - - world.logger.debug('Checking Zenko CR status', { - zenkoCR, - }); -} - export async function getDRSource(world: Zenko, namespace = 'default') { const zenkoClient = createKubeCustomObjectClient(world); @@ -696,11 +672,20 @@ export async function execCommandWithVolumeAccess( }; try { - await createAndRunPod(world, podManifest, true, cleanup, timeout); + await createAndRunPod(world, podManifest, true, false, timeout); const coreClient = createKubeCoreClient(world); const logs = await coreClient.readNamespacedPodLog(podName, namespace); + if (cleanup) { + try { + await coreClient.deleteNamespacedPod(podName, namespace); + world.logger.debug('Pod cleaned up after log retrieval', { podName }); + } catch (cleanupErr) { + world.logger.warn('Failed to cleanup pod after log retrieval', { podName, err: cleanupErr }); + } + } + return logs.body.trim(); } catch (error) { world.logger.debug('Command execution failed', { @@ -708,6 +693,12 @@ export async function execCommandWithVolumeAccess( podName, error: error instanceof Error ? error.message : String(error) }); + + if (cleanup) { + const coreClient = createKubeCoreClient(world); + await coreClient.deleteNamespacedPod(podName, namespace); + } + throw error; } } @@ -735,3 +726,52 @@ export async function execInCluster( } } +/** + * Wait for deployment rollout to complete + */ +export async function waitForDeploymentRollout( + appsClient: AppsV1Api, + deploymentName: string, + namespace: string, + timeoutMs = 120000, +) { + const startTime = Date.now(); + + while (Date.now() - startTime < timeoutMs) { + const deployment = await appsClient.readNamespacedDeployment(deploymentName, namespace); + const status = deployment.body.status; + + if (status?.readyReplicas === status?.replicas && + status?.updatedReplicas === status?.replicas && + status?.replicas && status.replicas > 0) { + return; + } + + await new Promise(resolve => setTimeout(resolve, 2000)); + } + + throw new Error(`Deployment ${deploymentName} rollout timed out after ${timeoutMs}ms`); +} + +/** + * Get Zenko Custom Resource + */ +export async function getZenkoCR(world: Zenko, namespace = 'default', name = 'end2end'): Promise { + const zenkoClient = createKubeCustomObjectClient(world); + + const zenkoCR = await zenkoClient.getNamespacedCustomObject( + 'zenko.io', + 'v1alpha2', + namespace, + 'zenkos', + name, + ).catch(err => { + world.logger.debug('Error getting Zenko CR', { + err: err as unknown, + }); + return; + }); + + return zenkoCR?.body as ZenkoCR; +} + diff --git a/tests/ctst/steps/utils/utils.ts b/tests/ctst/steps/utils/utils.ts index ceef07ece0..2220696b46 100644 --- a/tests/ctst/steps/utils/utils.ts +++ b/tests/ctst/steps/utils/utils.ts @@ -169,8 +169,7 @@ async function createBucketWithConfiguration( withObjectLock?: string, retentionMode?: string) { world.resetCommand(); - const preName = world.getSaved('accountName') || - world.parameters.AccountName || Constants.ACCOUNT_NAME; + const preName = world.getSaved('accountName') || Zenko.ACCOUNT_NAME; const usedBucketName = bucketName || `${preName}${Constants.BUCKET_NAME_TEST}${Utils.randomString()}`.toLocaleLowerCase(); world.addToSaved('bucketName', usedBucketName); diff --git a/tests/ctst/world/Zenko.ts b/tests/ctst/world/Zenko.ts index ef94be2d6c..5418a6515d 100644 --- a/tests/ctst/world/Zenko.ts +++ b/tests/ctst/world/Zenko.ts @@ -89,6 +89,7 @@ export interface ZenkoWorldParameters extends ClientOptions { SorbetdRestoreTimeout: string; UtilizationServiceHost: string; UtilizationServicePort: string; + Namespace: string; [key: string]: unknown; } @@ -122,6 +123,11 @@ export default class Zenko extends World { static readonly PRIMARY_SITE_NAME = 'admin'; static readonly SECONDARY_SITE_NAME = 'dradmin'; static readonly PRA_INSTALL_COUNT_KEY = 'praInstallCount'; + + // Static CTST configuration values + static readonly ACCOUNT_NAME = 'zenko-ctst'; + static readonly SSL_ENABLED = false; + static readonly PORT = '80'; /** * @constructor diff --git a/tests/ctst/world/ZenkoCR.ts b/tests/ctst/world/ZenkoCR.ts new file mode 100644 index 0000000000..18956205f3 --- /dev/null +++ b/tests/ctst/world/ZenkoCR.ts @@ -0,0 +1,494 @@ +export type ZenkoConditionType = 'Available' | 'DeploymentInProgress' | 'DeploymentFailure'; + +export type ZenkoCondition = { + type: ZenkoConditionType; + status: 'True' | 'False' | 'Unknown'; + lastTransitionTime?: string; + reason?: string; + message?: string; +}; + +export type ZenkoComponentIngress = { + hostname?: string; + annotations?: Record; +}; + +export type ZenkoSpecKafkaCleaner = { + logLevel?: string; + interval?: string; + kafkaErrorsWarningThreshold?: number; +}; + +export type ZenkoSpecSorbetServerAzureConfig = { + archiveTier?: string; + rehydrationTier?: string; + rehydrationPrefix?: string; + restoreTimeout?: string; + blockSize?: string; + pollingDelay?: string; +}; + +export type ZenkoSpecSorbetServerConfig = { + jobTimeout?: string; + azure?: ZenkoSpecSorbetServerAzureConfig; +}; + +export type ZenkoSpecSorbet = { + enable?: boolean; + jobStoreCollection?: string; + expirationJobStoreCollection?: string; + server?: ZenkoSpecSorbetServerConfig; +}; + +export type ZenkoSpecScubaExternalAPI = { + replicas?: number; + ingress?: ZenkoComponentIngress; +}; + +export type ZenkoSpecScuba = { + replicas?: number; + api?: ZenkoSpecScubaExternalAPI; +}; + +export type ZenkoSpecManagementOIDC = { + provider?: string; + federatedProviders?: string[]; + uiClientId?: string; + uiControlPlaneClientId?: string; + uiWorkloadPlaneProviderUrl?: string; + uiControlPlaneProviderUrl?: string; + vaultClientId?: string; + ingress?: ZenkoComponentIngress; +}; + +export type ZenkoSpecManagement = { + provider?: 'InCluster' | 'Orbit'; + endpoint?: string; + pushEndpoint?: string; + oidc?: ZenkoSpecManagementOIDC; +}; + +export type ZenkoSpecPersistence = { + resources?: ZenkoSpecResourcesSpec; +}; + +// v1alpha1 types that are extended in v1alpha2 +export type ZenkoSpecResourcesSpec = { + requestCPU?: string; + requestMemory?: string; + limitCPU?: string; + limitMemory?: string; +}; + +export type ZenkoSpecAffinity = { + nodeAffinity?: Record; + podAffinity?: Record; + podAntiAffinity?: Record; +}; + +export type ZenkoSpecComponentSpec = { + replicas?: number; + resources?: ZenkoSpecResourcesSpec; +}; + +export type ZenkoSpecMongoDB = { + provider?: 'External'; + persistence?: ZenkoSpecPersistence; + endpoints?: string[]; + userSecretName?: string; + usernameKey?: string; + passwordKey?: string; + replicaSetName?: string; + writeConcern?: string; + readPreference?: 'primary' | 'primaryPreferred' | 'secondary' | 'secondaryPreferred' | 'nearest'; + databaseName?: string; + enableSharding?: boolean; + metricsJobName?: string; + metricsShardJobName?: string; +}; + +export type ZenkoSpecZookeeperProvider = 'Managed' | 'External'; + +export type ZenkoSpecZookeeper = { + provider?: ZenkoSpecZookeeperProvider; + persistence?: ZenkoSpecPersistence; + endpoints?: string[]; +}; + +export type ZenkoSpecKafkaProvider = 'Managed' | 'External'; + +// v1alpha2 enhanced Kafka types +export type ZenkoSpecKafkaManagedClusterUser = { + name?: string; + secretName?: string; +}; + +export type ZenkoSpecKafkaManagedClusterExternalListener = { + type?: 'haproxy'; + tls?: boolean; + port?: number; + brokerIpAddresses?: Record; +}; + +export type ZenkoSpecKafkaManagedCluster = { + persistence?: ZenkoSpecPersistence; + zkAddresses?: string[]; + jmxJarPath?: string; + kafkaConfigFilesCM?: string; + resources?: ZenkoSpecResourcesSpec; + externalListener?: ZenkoSpecKafkaManagedClusterExternalListener; + users?: ZenkoSpecKafkaManagedClusterUser[]; + defaultBrokerAffinity?: ZenkoSpecAffinity; +}; + +export type ZenkoSpecKafkaExternalClusterTLSCA = { + secretName?: string; +}; + +export type ZenkoSpecKafkaExternalClusterTLSAuth = { + secretName?: string; +}; + +export type ZenkoSpecKafkaExternalClusterTLS = { + enable?: boolean; + auth?: ZenkoSpecKafkaExternalClusterTLSAuth; + ca?: ZenkoSpecKafkaExternalClusterTLSCA; + hostAliases?: Record[]; +}; + +export type ZenkoSpecKafkaExternalCluster = { + brokers?: string[]; + tls?: ZenkoSpecKafkaExternalClusterTLS; +}; + +export type ZenkoSpecKafkaCluster = { + replicas?: number; + managed?: ZenkoSpecKafkaManagedCluster; + external?: ZenkoSpecKafkaExternalCluster; + connect?: ZenkoSpecKafkaConnect; + maxMessageBytes?: string; + versions?: Record; + remainingDiskSpaceWarningThreshold?: number; + maxConsumerLagMessagesWarningThreshold?: number; + maxConsumerLagSecondsWarningThreshold?: number; +}; + +// v1alpha1 Kafka (simpler version) +export type ZenkoSpecKafka = { + provider?: ZenkoSpecKafkaProvider; + persistence?: ZenkoSpecPersistence; + brokers?: string[]; + jmxJarPath?: string; + kafkaConfigFiles?: string; + + // v1alpha2 extensions + cluster?: ZenkoSpecKafkaCluster; +}; + +export type ZenkoSpecRedisProvider = 'Zenko'; + +export type ZenkoSpecRedis = { + provider?: ZenkoSpecRedisProvider; + persistence?: ZenkoSpecPersistence; +}; + +export type ZenkoSpecLocalData = { + persistence?: ZenkoSpecPersistence; +}; + +export type ZenkoSpecBlobserver = { + replicas?: number; + enable?: boolean; +}; + +export type ZenkoSpecJabba = { + replicas?: number; + ingress?: ZenkoComponentIngress; +}; + +// v1alpha2 enhanced component types +export type ZenkoSpecMetrics = { + scrapePath?: string; + scrapePort?: string; + scrapeInterval?: string; +}; + +export type ZenkoSpecLogging = { + logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error'; + dumpLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error'; +}; + +export type ZenkoSpecComponentSpecV2 = { + replicas?: number; + metrics?: ZenkoSpecMetrics; + logging?: ZenkoSpecLogging; + // resources.SchedulingSpec inline + nodeSelector?: Record; + tolerations?: Record[]; + affinity?: ZenkoSpecAffinity; +}; + +export type ZenkoSpecCloudserver = { + replicas?: number; + ingress?: ZenkoComponentIngress; + + // v1alpha2 enhancements + metrics?: ZenkoSpecMetrics; + logging?: ZenkoSpecLogging; + systemErrorsWarningThreshold?: number; + systemErrorsCriticalThreshold?: number; + listingLatencyWarningThreshold?: number; + listingLatencyCriticalThreshold?: number; + deleteLatencyWarningThreshold?: number; + deleteLatencyCriticalThreshold?: number; + quotaUnavailabilityThreshold?: number; +}; + +export type ZenkoSpecInternalCloudserver = { + enable?: boolean; + replicas?: number; + ingress?: ZenkoComponentIngress; + metrics?: ZenkoSpecMetrics; + logging?: ZenkoSpecLogging; +}; + +// v1alpha2 enhanced Backbeat types +export type ZenkoSpecBackbeatRetryBackoff = { + minMs?: number; + maxMs?: number; + jitterPercent?: number; + factorPercent?: number; +}; + +export type ZenkoSpecBackbeatRetry = { + maxRetries?: number; + timeoutSeconds?: number; + backoff?: ZenkoSpecBackbeatRetryBackoff; +}; + +export type ZenkoSpecBackbeatConcurrency = { + concurrency?: number; +}; + +export type ZenkoSpecCircuitBreakerProbe = { + noop?: boolean; + kafkaLag?: boolean; + prometheus?: boolean; +}; + +export type ZenkoSpecCircuitBreaker = { + probes?: ZenkoSpecCircuitBreakerProbe[]; + nominalEvaluateInterval?: string; + trippedEvaluateInterval?: string; + stabilizingEvaluateInterval?: string; + stabilizeAfterNSuccesses?: number; +}; + +export type ZenkoSpecBackbeatComponentSpec = { + replicas?: number; + concurrency?: number; + retry?: ZenkoSpecBackbeatRetry; + circuitBreaker?: ZenkoSpecCircuitBreaker; +}; + +export type ZenkoSpecBackbeatLifecycleConductor = { + concurrency?: number; + cronRule?: string; + circuitBreaker?: ZenkoSpecCircuitBreaker; + concurrentIndexBuildLimit?: number; +}; + +export type ZenkoSpecBackbeatLifecycleBucketProcessor = { + replicas?: number; + concurrency?: number; + triggerTransitionsOneDayEarlierForTesting?: boolean; +}; + +export type ZenkoSpecBackbeatReplicationRetry = { + aws?: ZenkoSpecBackbeatRetry; + azure?: ZenkoSpecBackbeatRetry; + gcp?: ZenkoSpecBackbeatRetry; + scality?: ZenkoSpecBackbeatRetry; +}; + +export type ZenkoSpecBackbeatReplicationDataProcessor = { + replicas?: number; + concurrency?: number; + mpuPartsConcurrency?: number; + retry?: ZenkoSpecBackbeatReplicationRetry; + circuitBreaker?: ZenkoSpecCircuitBreaker; +}; + +export type ZenkoSpecBackbeatBucketNotification = { + replicas?: number; + concurrency?: number; + backbeatTopic?: string; + useDedicatedTopicsByDefault?: boolean; + enable?: boolean; + failedNotificationWarningThreshold?: number; + failedNotificationCriticalThreshold?: number; +}; + +export type ZenkoSpecBackbeat = { + replicas?: number; + triggerExpirationsOneDayEarlierForTesting?: boolean; + replicationErrorsWarningThreshold?: number; + replicationErrorsCriticalThreshold?: number; + rpoWarningThreshold?: string; + rpoCriticalThreshold?: string; + latencyWarningThreshold?: string; + latencyCriticalThreshold?: string; + lifecycleLatencyWarningThreshold?: string; + lifecycleLatencyCriticalThreshold?: string; + + // v1alpha2 component specifications + lifecyclePopulator?: ZenkoSpecBackbeatComponentSpec; + lifecycleConductor?: ZenkoSpecBackbeatLifecycleConductor; + lifecycleBucketProcessor?: ZenkoSpecBackbeatLifecycleBucketProcessor; + lifecycleObjectProcessor?: ZenkoSpecBackbeatComponentSpec; + lifecycleTransitionProcessor?: ZenkoSpecBackbeatComponentSpec; + replicationProducer?: ZenkoSpecBackbeatComponentSpec; + replicationDataProcessor?: ZenkoSpecBackbeatReplicationDataProcessor; + replicationStatusProcessor?: ZenkoSpecBackbeatComponentSpec; + garbageCollector?: ZenkoSpecBackbeatComponentSpec; + ingestionProducer?: ZenkoSpecBackbeatComponentSpec; + ingestionProcessor?: ZenkoSpecBackbeatComponentSpec; + oplogPopulator?: ZenkoSpecBackbeatComponentSpec; + notificationProducer?: ZenkoSpecBackbeatComponentSpec; + bucketNotification?: ZenkoSpecBackbeatBucketNotification; +}; + +export type ZenkoSpecUtapi = { + replicas?: number; + enable?: boolean; + controlPlaneIngress?: ZenkoComponentIngress; + workloadPlaneIngress?: ZenkoComponentIngress; +}; + +export type ZenkoSpecVault = { + replicas?: number; + enable?: boolean; + iamIngress?: ZenkoComponentIngress; + stsIngress?: ZenkoComponentIngress; + noImplicitDeny?: boolean; + + // v1alpha2 enhancements + metrics?: ZenkoSpecMetrics; + logging?: ZenkoSpecLogging; + systemErrorsWarningThreshold?: number; + systemErrorsCriticalThreshold?: number; + authenticationLatencyWarningThreshold?: number; + authenticationLatencyCriticalThreshold?: number; + authorizationLatencyWarningThreshold?: number; + authorizationLatencyCriticalThreshold?: number; +}; + +// v1alpha2 additional component types +export type ZenkoSpecVeeamSOSApi = { + enable?: boolean; + cronRule?: string; +}; + +export type ZenkoSpecS3utils = { + metrics?: ZenkoSpecMetrics; + countItemsJobDurationThreshold?: string; + updateBucketCapacityInfoJobDurationThreshold?: string; + updateBucketCapacityInfoSuccessJobExistenceDurationThreshold?: string; + cronRule?: string; +}; + +export type ZenkoSpecKafkaConnect = { + replicas?: number; + clusterGroupId?: string; + configStorageTopic?: string; + offsetStorageTopic?: string; + statusStorageTopic?: string; +}; + +export type ZenkoSpecRegistry = { + imagePullSecretNames?: string[]; +}; + +export type ZenkoSpecIngress = { + controlPlaneClass?: string; + workloadPlaneClass?: string; + certificates?: Record[]; + annotations?: Record; +}; + +export type ZenkoSpecEgressProxyCA = { + 'ca.crt'?: string; + secretName?: string; + secretAttributeName?: string; +}; + +export type ZenkoSpecEgressProxy = { + https?: string; + http?: string; + ca?: ZenkoSpecEgressProxyCA; + exclude?: string[]; +}; + +export type ZenkoSpecEgress = { + proxy?: ZenkoSpecEgressProxy; + skipTLSVerify?: boolean; +}; + +export type ZenkoSpec = { + version?: string; + replicas?: number; + management?: ZenkoSpecManagement; + registry?: ZenkoSpecRegistry; + ingress?: ZenkoSpecIngress; + egress?: ZenkoSpecEgress; + initialConfiguration?: Record; + + // v1alpha1 components + mongodb?: ZenkoSpecMongoDB; + zookeeper?: ZenkoSpecZookeeper; + kafka?: ZenkoSpecKafka; + redis?: ZenkoSpecRedis; + localData?: ZenkoSpecLocalData; + blobserver?: ZenkoSpecBlobserver; + jabba?: ZenkoSpecJabba; + cloudserver?: ZenkoSpecCloudserver; + backbeat?: ZenkoSpecBackbeat; + utapi?: ZenkoSpecUtapi; + vault?: ZenkoSpecVault; + + // v1alpha2 additions and enhancements + kafkaCleaner?: ZenkoSpecKafkaCleaner; + kafkaConnect?: ZenkoSpecKafkaConnect; + sorbet?: ZenkoSpecSorbet; + scuba?: ZenkoSpecScuba; + internalCloudserver?: ZenkoSpecInternalCloudserver; + + // Additional v1alpha2 fields + dashboardFolder?: string; + veeamSosApi?: ZenkoSpecVeeamSOSApi; + s3utils?: ZenkoSpecS3utils; +}; + +export type ZenkoStatus = { + instanceID?: string; + readyReplicas?: number; + conditions?: ZenkoCondition[]; + observedGeneration?: number; +}; + +export type ZenkoCR = { + apiVersion?: string; + kind?: string; + metadata?: { + name?: string; + namespace?: string; + annotations?: { + 'zenko.io/time-progression-factor'?: string; + 'zenko.io/service-users-seq'?: string; + [key: string]: string | undefined; + }; + labels?: Record; + }; + spec?: ZenkoSpec; + status?: ZenkoStatus; +}; diff --git a/tests/ctst/yarn.lock b/tests/ctst/yarn.lock index b87653453a..f5c26afd00 100644 --- a/tests/ctst/yarn.lock +++ b/tests/ctst/yarn.lock @@ -4709,6 +4709,13 @@ werelogs@scality/werelogs#8.2.0: fast-safe-stringify "^2.1.1" safe-json-stringify "^1.2.0" +werelogs@scality/werelogs#8.2.2: + version "8.2.2" + resolved "https://codeload.github.com/scality/werelogs/tar.gz/e53bef5145697bf8af940dcbe59408988d64854f" + dependencies: + fast-safe-stringify "^2.1.1" + safe-json-stringify "^1.2.0" + which-typed-array@^1.1.16, which-typed-array@^1.1.2: version "1.1.19" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" From 2b337b6d242dbad5213ad197aab09f488b7c6bdf Mon Sep 17 00:00:00 2001 From: williamlardier Date: Thu, 11 Sep 2025 17:45:42 +0200 Subject: [PATCH 04/14] Auto seed Keycloak from CTST and introduce coordinate --- .github/scripts/end2end/run-e2e-ctst.sh | 5 +- .github/workflows/end2end.yaml | 8 +- tests/ctst/Dockerfile | 8 +- tests/ctst/common/utils.ts | 120 +- tests/ctst/package.json | 6 +- tests/ctst/steps/setup/setup.ts | 473 ++++-- tests/ctst/world/Zenko.ts | 123 +- tests/ctst/yarn.lock | 1831 ++++++++++++++++++----- 8 files changed, 1957 insertions(+), 617 deletions(-) diff --git a/.github/scripts/end2end/run-e2e-ctst.sh b/.github/scripts/end2end/run-e2e-ctst.sh index 063cc1049a..6f3031ae2f 100755 --- a/.github/scripts/end2end/run-e2e-ctst.sh +++ b/.github/scripts/end2end/run-e2e-ctst.sh @@ -54,8 +54,9 @@ kubectl run $POD_NAME \ --rm \ --attach=True \ --image-pull-policy=IfNotPresent \ - --env=TARGET_VERSION=$VERSION \ - --env=AZURE_BLOB_URL=$AZURE_BACKEND_ENDPOINT \ + --env=TARGET_VERSION=$VERSION \ + --env=SEED_KEYCLOAK_DEFAULT_ROLES=true \ + --env=AZURE_BLOB_URL=$AZURE_BACKEND_ENDPOINT \ --env=AZURE_QUEUE_URL=$AZURE_BACKEND_QUEUE_ENDPOINT \ --env=VERBOSE=1 \ --override-type strategic \ diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index d8c36a5b93..e716dd2dc1 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -393,12 +393,12 @@ jobs: GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: yarn cache-dependency-path: tests/ctst/yarn.lock - name: Install ctst test dependencies working-directory: tests/ctst - run: yarn install + run: yarn install --network-concurrency=1 - name: Lint ctst tests working-directory: tests/ctst run: yarn lint @@ -421,6 +421,9 @@ jobs: SORBET_TAG=$(yq eval '.sorbet.tag' deps.yaml) DRCTL_TAG=$(yq eval .drctl.tag deps.yaml) EOF + - name: Prepare build context + run: cp .github/scripts/mocks/aws/mock-metadata.tar.gz tests/ctst/mock-metadata.tar.gz + - name: Build and push CI image uses: docker/build-push-action@v5 with: @@ -430,6 +433,7 @@ jobs: CTST_TAG=${{ env.CTST_TAG }} SORBET_TAG=${{ env.SORBET_TAG }} DRCTL_TAG=${{ env.DRCTL_TAG}} + GIT_ACCESS_TOKEN=${{ steps.app-token.outputs.token }} tags: "${{ env.E2E_CTST_IMAGE_NAME }}:${{ env.E2E_IMAGE_TAG }}" cache-from: type=gha,scope=end2end-ctst cache-to: type=gha,mode=max,scope=end2end-ctst diff --git a/tests/ctst/Dockerfile b/tests/ctst/Dockerfile index 880c3d1b1e..19ebf06222 100644 --- a/tests/ctst/Dockerfile +++ b/tests/ctst/Dockerfile @@ -1,6 +1,7 @@ ARG SORBET_TAG=latest ARG CTST_TAG=latest ARG DRCTL_TAG=latest +ARG GIT_ACCESS_TOKEN= FROM ghcr.io/scality/sorbet:$SORBET_TAG AS sorbet FROM ghcr.io/scality/zenko-drctl:$DRCTL_TAG AS drctl @@ -9,7 +10,12 @@ FROM ghcr.io/scality/cli-testing:$CTST_TAG COPY package.json /tmp/package.json USER root -RUN npm install typescript@5.8.3 -g +RUN npm install typescript@5.9.2 -g + +# Configure git to use the access token for private repositories +RUN git config --global url.https://x-access-token:${GIT_ACCESS_TOKEN}@github.com/.insteadOf https://github.com/ && \ + git config --global url.https://x-access-token:${GIT_ACCESS_TOKEN}@github.com/.insteadOf github.com: && \ + git config --global url.https://x-access-token:${GIT_ACCESS_TOKEN}@github.com/.insteadOf ssh://git@github.com/ # CTST does it if needed, but better to merge dependencies # here so we benefit from Docker layer caching diff --git a/tests/ctst/common/utils.ts b/tests/ctst/common/utils.ts index dd1fbb917c..8659c8ebc6 100644 --- a/tests/ctst/common/utils.ts +++ b/tests/ctst/common/utils.ts @@ -1,7 +1,7 @@ import { exec } from 'child_process'; import http from 'http'; import { createHash } from 'crypto'; -import { Command, IAM, Identity, IdentityEnum } from 'cli-testing'; +import { Command, coordinate, IAM, Identity, IdentityEnum } from 'cli-testing'; import { AttachedPolicy, Group, @@ -12,7 +12,6 @@ import { import { AWSCliOptions } from 'cli-testing'; import Zenko from 'world/Zenko'; import fs from 'fs'; -import lockFile from 'proper-lockfile'; import { ITestCaseHookParameter } from '@cucumber/cucumber'; import { AWSCredentials, Constants, Utils } from 'cli-testing'; import { createBucketWithConfiguration, putObject } from '../steps/utils/utils'; @@ -323,9 +322,6 @@ export async function prepareMetricsScenarios( const { gherkinDocument, pickle } = scenarioConfiguration; const featureName = gherkinDocument.feature?.name?.replace(/ /g, '-').toLowerCase() || 'metrics'; const filePath = `/tmp/${featureName}`; - let initiated = false; - let releaseLock: (() => Promise) | false = false; - const output: Record = {}; const { versioning = '', @@ -333,73 +329,57 @@ export async function prepareMetricsScenarios( jobNamespace = `${featureName}-setup` } = options; - if (!fs.existsSync(filePath)) { - fs.writeFileSync(filePath, JSON.stringify({ - ready: false, - })); - } else { - initiated = true; - } - - if (!initiated) { - try { - releaseLock = await lockFile.lock(filePath, { stale: Constants.DEFAULT_TIMEOUT / 2 }); - } catch (err) { - world.logger.error('Unable to acquire lock', { err }); - releaseLock = false; - } - } - - if (releaseLock) { - const scenarioIds = new Set(); - - for (const scenario of gherkinDocument.feature?.children || []) { - for (const example of scenario.scenario?.examples || []) { - for (const values of example.tableBody || []) { - const scenarioWithExampleID = hashStringAndKeepFirst20Characters(`${values.id}`); - scenarioIds.add(scenarioWithExampleID); + await coordinate( + { + lockName: featureName, + timeout: Constants.DEFAULT_TIMEOUT, + logger: world.logger, + }, + // Work function: executed exactly once + async () => { + const output: Record = {}; + const scenarioIds = new Set(); + + for (const scenario of gherkinDocument.feature?.children || []) { + for (const example of scenario.scenario?.examples || []) { + for (const values of example.tableBody || []) { + const scenarioWithExampleID = hashStringAndKeepFirst20Characters(`${values.id}`); + scenarioIds.add(scenarioWithExampleID); + } } } - } - - for (const scenarioId of scenarioIds) { - await world.createAccount(scenarioId, true); - await createBucketWithConfiguration(world, scenarioId, versioning); - await putObject(world); - output[scenarioId] = Identity.getCurrentCredentials()!; - } - - await createJobAndWaitForCompletion(world, jobName, jobNamespace); - - await Utils.sleep(2000); - fs.writeFileSync(filePath, JSON.stringify({ - ready: true, - ...output, - })); - - await releaseLock(); - } else { - while (!fs.existsSync(filePath)) { - await Utils.sleep(100); - } + + for (const scenarioId of scenarioIds) { + await world.createAccount(scenarioId, true); + await createBucketWithConfiguration(world, scenarioId, versioning); + await putObject(world); + output[scenarioId] = Identity.getCurrentCredentials()!; + } - let configuration: { ready: boolean } = JSON.parse(fs.readFileSync(filePath, 'utf8')) as { ready: boolean }; - while (!configuration.ready) { - await Utils.sleep(100); - configuration = JSON.parse(fs.readFileSync(filePath, 'utf8')) as { ready: boolean }; + await createJobAndWaitForCompletion(world, jobName, jobNamespace); + + await Utils.sleep(2000); + + // Store output in a temporary file for other workers to read + fs.writeFileSync(filePath, JSON.stringify({ + ready: true, + ...output, + })); + }, + // Post-processing: all workers read the configuration + async () => { + const configuration: Record = JSON.parse(fs.readFileSync(filePath, 'utf8')); + const key = hashStringAndKeepFirst20Characters(`${pickle.astNodeIds[1]}`); + world.logger.debug('Scenario key', { key, from: `${pickle.astNodeIds[1]}`, configuration }); + + world.addToSaved('bucketName', key); + world.addToSaved('accountName', key); + world.addToSaved('accountNameForScenario', key); + world.addToSaved('metricsEnvironmentSetup', true); + + if (configuration[key]) { + Identity.addIdentity(IdentityEnum.ACCOUNT, key, configuration[key], undefined, true, true); + } } - } - - const configuration: typeof output = JSON.parse(fs.readFileSync(filePath, 'utf8')) as typeof output; - const key = hashStringAndKeepFirst20Characters(`${pickle.astNodeIds[1]}`); - world.logger.debug('Scenario key', { key, from: `${pickle.astNodeIds[1]}`, configuration }); - - world.addToSaved('bucketName', key); - world.addToSaved('accountName', key); - world.addToSaved('accountNameForScenario', key); - world.addToSaved('metricsEnvironmentSetup', true); - - if (configuration[key]) { - Identity.addIdentity(IdentityEnum.ACCOUNT, key, configuration[key], undefined, true, true); - } + ); } diff --git a/tests/ctst/package.json b/tests/ctst/package.json index 80f862d4a2..30c1e2b47c 100644 --- a/tests/ctst/package.json +++ b/tests/ctst/package.json @@ -9,6 +9,7 @@ "private": true, "dependencies": { "@kubernetes/client-node": "^0.21.0", + "@types/node": "^18.19.121", "@types/proper-lockfile": "^4.1.4", "@types/qs": "^6.9.15", "assert": "^2.1.0", @@ -16,9 +17,8 @@ "kafkajs": "^2.2.4", "node-gyp": "^10.2.0", "prometheus-query": "^3.4.0", - "proper-lockfile": "^4.1.2", "qs": "^6.13.0", - "scubaclient": "git+https://github.com/scality/scubaclient#^1.1.2", + "scubaclient": "git+https://github.com/scality/scubaclient#^1.1.3", "werelogs": "scality/werelogs#8.2.2" }, "devDependencies": { @@ -26,7 +26,7 @@ "@aws-sdk/client-s3": "^3.583.0", "@aws-sdk/client-sts": "^3.583.0", "@eslint/compat": "^1.1.1", - "cli-testing": "github:scality/cli-testing.git#1.2.4", + "cli-testing": "github:scality/cli-testing.git#83e8f3d0636de162390e78012ca8b05b94af8b77", "eslint": "^9.9.1", "eslint-config-scality": "scality/Guidelines#8.3.0", "typescript-eslint": "^8.4.0" diff --git a/tests/ctst/steps/setup/setup.ts b/tests/ctst/steps/setup/setup.ts index 3d4e057702..1b376e48de 100644 --- a/tests/ctst/steps/setup/setup.ts +++ b/tests/ctst/steps/setup/setup.ts @@ -1,8 +1,8 @@ import Werelogs from 'werelogs'; import { BeforeAll } from '@cucumber/cucumber'; -import { CacheHelper } from 'cli-testing'; -import lockFile from 'proper-lockfile'; -import * as fs from 'fs'; +import { CacheHelper, coordinate } from 'cli-testing'; +import fs from 'fs'; +import path from 'path'; import { KubeConfig, CoreV1Api, @@ -12,6 +12,7 @@ import { V1Pod, V1Service, V1ClusterRoleBinding, + V1ConfigMap, } from '@kubernetes/client-node'; import Zenko, { ZenkoWorldParameters } from 'world/Zenko'; import { getZenkoCR, waitForDeploymentRollout, waitForZenkoToStabilize } from 'steps/utils/kubernetes'; @@ -44,9 +45,6 @@ type ZenkoInstanceInfo = { const logger = new Werelogs.Logger('CTST').newRequestLogger(); -const SETUP_COMPLETED_FILE = '/tmp/ctst-setup-completed'; -const SETUP_TIMEOUT = 60_000; - function initializeKubernetesClients() { const kc = new KubeConfig(); kc.loadFromDefault(); @@ -59,100 +57,6 @@ function initializeKubernetesClients() { }; } -/** - * Thread-safe setup coordinator using file locks - * Ensures only one worker performs setup while others wait - */ -async function coordinateSetup(parameters: ZenkoWorldParameters): Promise { - const workerId = `worker-${process.pid}`; - - if (!fs.existsSync(SETUP_COMPLETED_FILE)) { - fs.writeFileSync(SETUP_COMPLETED_FILE, JSON.stringify({ - ready: false, - })); - } - - let releaseLock: (() => Promise) | null = null; - try { - releaseLock = await lockFile.lock(SETUP_COMPLETED_FILE, { - stale: SETUP_TIMEOUT, - retries: { - retries: 5, - factor: 3, - minTimeout: 1000, - maxTimeout: 5000, - } - }); - - const setupData = JSON.parse(fs.readFileSync(SETUP_COMPLETED_FILE, 'utf8')); - if (setupData.ready) { - logger.info(`${workerId} found setup already completed by ${setupData.completedBy || 'unknown'}`); - await extractAndCacheParameters(parameters); - } else { - logger.info(`${workerId} performing CTST cluster setup...`); - await setupClusterConfiguration(parameters); - logger.info(`${workerId} setup configuration completed, writing completion file...`); - fs.writeFileSync(SETUP_COMPLETED_FILE, JSON.stringify({ - ready: true, - completedBy: workerId, - completedAt: new Date().toISOString(), - pid: process.pid - })); - logger.info(`${workerId} extracting and caching parameters...`); - await extractAndCacheParameters(parameters); - logger.info(`CTST cluster setup completed by ${workerId}`); - } - } catch (error) { - // Only handle lock-related errors with fallback behavior - if ((error as { code?: string }).code === 'ELOCKED') { - logger.warn(`${workerId} could not acquire lock, waiting - for setup completion`, { error: (error as { message?: string }).message }); - - // Poll for completion since another worker is doing the setup - let attempts = 0; - const maxAttempts = 450; // Wait up to 15 minutes (10min stabilization + 5min buffer) - const pollInterval = 2000; // 2 seconds - - while (attempts < maxAttempts) { - try { - const setupData = JSON.parse(fs.readFileSync(SETUP_COMPLETED_FILE, 'utf8')); - if (setupData.ready) { - logger.info(`${workerId} detected setup completion by ${setupData.completedBy}, - proceeding with parameter extraction`); - await extractAndCacheParameters(parameters); - return; - } - } catch (readError) { - logger.info(`${workerId} could not read setup file, retrying...`, { readError }); - } - - attempts++; - await new Promise(resolve => setTimeout(resolve, pollInterval)); - } - - // If we've waited long enough, try to proceed anyway - logger.error(`${workerId} timed out waiting for setup completion, - attempting parameter extraction anyway`); - try { - await extractAndCacheParameters(parameters); - return; - } catch (fallbackError) { - logger.error(`${workerId} fallback parameter extraction also failed`, { fallbackError }); - throw new Error(`Setup coordination failed: could not acquire lock - and fallback failed. Original lock error: ${(error as { message?: string }).message}`); - } - } else { - // For non-lock errors (actual setup failures), re-throw immediately - logger.error(`${workerId} setup failed with non-lock error`, { error }); - throw error; - } - } finally { - if (releaseLock) { - await releaseLock(); - } - } -} - /** * Extract admin credentials from environment or Kubernetes */ @@ -192,7 +96,6 @@ async function extractPRACredentials(coreClient: CoreV1Api, parameters: ZenkoWor let praAdminSecretKey = parameters.DRAdminSecretKey; if (parameters.DRSubdomain) { - // Check if PRA secret exists and extract credentials in one call try { const praAdminSecret = await coreClient .readNamespacedSecret('end2end-pra-management-vault-admin-creds.v1', parameters.Namespace); @@ -203,7 +106,6 @@ async function extractPRACredentials(coreClient: CoreV1Api, parameters: ZenkoWor } catch (error) { if ((error as { response?: { statusCode?: number } }).response?.statusCode === 404) { logger.info('PRA secret not found, no PRA setup detected - skipping PRA credential extraction'); - // PRA is not set up, skip entirely and use parameters/defaults } else { logger.warn('Error reading PRA secret, skipping PRA credential extraction', { error: (error as { message?: string }).message, @@ -380,7 +282,6 @@ async function extractZenkoCRInfo(parameters: ZenkoWorldParameters): Promise { - setTimeout(() => reject(new Error('Zenko stabilization timed out after 10 minutes')), stabilizationTimeout); - }); - - try { - await Promise.race([stabilizationPromise, timeoutPromise]); - logger.info('Zenko stabilization completed successfully'); - } catch (error) { - logger.error('Zenko stabilization failed or timed out', { error }); - throw error; // Let the setup fail if stabilization fails - } + logger.info('Zenko stabilization completed successfully'); } /** @@ -589,6 +476,60 @@ async function setupKafkaTopics(coreClient: CoreV1Api, parameters: ZenkoWorldPar }); } +/** + * Create AWS mock configmap with metadata + */ +async function createAwsMockConfigMap(coreClient: CoreV1Api, parameters: ZenkoWorldParameters): Promise { + try { + // Try to find the tar.gz file in possible locations: + // Docker container path (from GitHub workflow copy) is prioritized + const possiblePaths = [ + '/ctst/mock-metadata.tar.gz', // Docker container location + path.join(__dirname, '../../../..', '.github/scripts/mocks/aws/mock-metadata.tar.gz'), + path.join(process.cwd(), '.github/scripts/mocks/aws/mock-metadata.tar.gz'), + ]; + + let tarGzPath: string | null = null; + for (const tarPath of possiblePaths) { + if (fs.existsSync(tarPath)) { + tarGzPath = tarPath; + break; + } + } + + if (!tarGzPath) { + throw new Error(`AWS mock metadata file not found. Searched paths: ${possiblePaths.join(', ')}`); + } + + // Read the tar.gz file and create configmap with it + const tarGzContent = fs.readFileSync(tarGzPath); + const configMapData = { + 'mock-metadata.tar.gz': tarGzContent.toString('base64'), + }; + logger.info('Using mock-metadata.tar.gz file', { tarGzPath }); + + const awsMockConfigMap: V1ConfigMap = { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { + name: 'aws-mock', + namespace: parameters.Namespace, + }, + binaryData: configMapData, + }; + + await coreClient.createNamespacedConfigMap(parameters.Namespace, awsMockConfigMap); + logger.info('AWS mock configmap created successfully'); + } catch (error) { + if ((error as { response?: { statusCode: number } }).response?.statusCode === 409) { + logger.info('AWS mock configmap already exists'); + } else { + logger.error('Failed to create AWS mock configmap', { error }); + throw error; + } + } +} + /** * Setup mock services */ @@ -604,6 +545,8 @@ async function setupMockServices(coreClient: CoreV1Api, parameters: ZenkoWorldPa return; } + await createAwsMockConfigMap(coreClient, parameters); + const azureMockService: V1Service = { apiVersion: 'v1', kind: 'Service', @@ -650,14 +593,101 @@ async function setupMockServices(coreClient: CoreV1Api, parameters: ZenkoWorldPa }, }; + const awsMockService: V1Service = { + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: 'aws-mock', + namespace: parameters.Namespace, + }, + spec: { + selector: { name: 'aws-mock' }, + type: 'ClusterIP', + ports: [ + { name: 'http', port: 80, targetPort: 'http' }, + ], + }, + }; + + const awsMockPod: V1Pod = { + apiVersion: 'v1', + kind: 'Pod', + metadata: { + name: 'aws-mock-pod', + namespace: parameters.Namespace, + labels: { name: 'aws-mock', component: 'mock' }, + }, + spec: { + initContainers: [ + { + name: 'setup', + image: 'zenko/cloudserver:latest', + imagePullPolicy: 'Always', + command: ['tar', '-xvf', '/static-config/mock-metadata.tar.gz', '-C', '/usr/src/app'], + volumeMounts: [ + { + name: 'configmap', + mountPath: '/static-config', + }, + { + name: 'metadata', + mountPath: '/usr/src/app/localMetadata', + }, + ], + }, + ], + containers: [ + { + name: 'aws-mock', + image: 'zenko/cloudserver:latest', + ports: [ + { name: 'http', containerPort: 8000 }, + ], + env: [ + { name: 'LOG_LEVEL', value: 'trace' }, + { name: 'REMOTE_MANAGEMENT_DISABLE', value: '1' }, + { name: 'ENDPOINT', value: 'aws-mock.zenko.local' }, + { name: 'S3BACKEND', value: 'file' }, + ], + volumeMounts: [ + { + name: 'metadata', + mountPath: '/usr/src/app/localMetadata', + }, + ], + resources: { + limits: { cpu: '1', memory: '2Gi' }, + requests: { cpu: '1', memory: '2Gi' }, + }, + }, + ], + volumes: [ + { + name: 'metadata', + emptyDir: {}, + }, + { + name: 'configmap', + configMap: { + name: 'aws-mock', + }, + }, + ], + }, + }; + await Promise.allSettled([ coreClient.createNamespacedService(parameters.Namespace, azureMockService), coreClient.createNamespacedPod(parameters.Namespace, azureMockPod), + coreClient.createNamespacedService(parameters.Namespace, awsMockService), + coreClient.createNamespacedPod(parameters.Namespace, awsMockPod), ]); logger.info('Mock services deployment initiated', { azureMockService, azureMockPod, + awsMockService, + awsMockPod, }); } @@ -762,13 +792,186 @@ async function applyDeploymentModifications(appsClient: AppsV1Api, parameters: Z } /** - * Setup storage locations - placeholder for future implementation - * TODO: Implement proper location creation via Management API with Keycloak auth + * Setup storage locations via Management API */ -async function setupStorageLocations(): Promise { - logger.info('Storage location setup placeholder - locations will be created by individual tests'); - // For now, let individual tests create their own locations as they do currently - // This can be enhanced later to create common locations during setup +async function setupStorageLocations(parameters: ZenkoWorldParameters): Promise { + if (!parameters.InstanceID) { + logger.info('InstanceID not available, skipping storage location setup'); + return; + } + + logger.info('Setting up storage locations...'); + + // Create a minimal Zenko instance for Management API calls + const zenkoInstance = { + parameters, + managementAPIRequest: async ( + method: string, + path: string, + headers: Record = {}, + body?: unknown, + ) => { + const axios = (await import('axios')).default; + const baseURL = `http://management.${parameters.subdomain}`; + + try { + const response = await axios({ + method, + url: `${baseURL}${path}`, + headers: { + 'Content-Type': 'application/json', + ...headers, + }, + data: body, + }); + return { statusCode: response.status, data: response.data }; + } catch (error: any) { // eslint-disable-line @typescript-eslint/no-explicit-any + if (error.response) { + return { statusCode: error.response.status, err: error.response.data }; + } + return { statusCode: 500, err: error.message }; + } + }, + logger, + }; + + const locations = [ + // AWS Backend Source Location + { + name: 'awsbackend', + locationType: 'location-aws-s3-v1', + details: { + bucketName: 'ci-zenko-aws-target-bucket', + endpoint: `aws-mock.${parameters.Namespace}.svc.cluster.local`, + accessKey: 'accessKey1', + secretKey: 'verySecretKey1', + bucketMatch: true, + repoId: [], + }, + }, + // AWS Backend Destination Location (for replication) + { + name: 'awsbackendmismatch', + locationType: 'location-aws-s3-v1', + legacyAwsBehavior: true, + details: { + bucketName: 'ci-zenko-aws-crr-target-bucket', + endpoint: `aws-mock.${parameters.Namespace}.svc.cluster.local`, + accessKey: 'accessKey1', + secretKey: 'verySecretKey1', + bucketMatch: false, + repoId: [], + }, + }, + // AWS Backend Fail Location (for failure testing) + { + name: 'awsbackendfail', + locationType: 'location-aws-s3-v1', + details: { + bucketName: 'ci-zenko-aws-fail-target-bucket', + endpoint: `aws-mock.${parameters.Namespace}.svc.cluster.local`, + accessKey: 'accessKey1', + secretKey: 'verySecretKey1', + bucketMatch: true, + repoId: [], + }, + }, + // AWS Backend Replication Fail CTST Location + { + name: 'awsbackendreplicationctstfail', + locationType: 'location-aws-s3-v1', + details: { + bucketName: 'ci-zenko-aws-replication-fail-ctst-bucket', + endpoint: `aws-mock.${parameters.Namespace}.svc.cluster.local`, + accessKey: 'accessKey1', + secretKey: 'verySecretKey1', + bucketMatch: false, + repoId: [], + }, + }, + // Cold Storage Location (used in dmf.feature, quotas.feature, pra.feature) + { + name: 'e2e-cold', + locationType: 'location-dmf-v1', + isCold: true, + details: { + endpoint: 'ws://mock-sorbet:5001/session', + username: 'user1', + password: 'pass1', + repoId: [ + '233aead6-1d7b-4647-a7cf-0d3280b5d1d7', + '81e78de8-df11-4acd-8ad1-577ff05a68db', + ], + nsId: '65f9fd61-42fe-4a68-9ac0-6ba25311cc85', + }, + }, + // Azure Archive Location (used extensively in azureArchive.feature with hardcoded name) + { + name: 'e2e-azure-archive', + locationType: 'location-azure-archive-v1', + isCold: true, + details: { + // eslint-disable-next-line max-len + endpoint: `https://${parameters.AzureAccountName}.blob.azure-mock.${parameters.Namespace}.svc.cluster.local`, + bucketName: parameters.AzureArchiveContainer, + queue: { + type: 'location-azure-storage-queue-v1', + queueName: parameters.AzureArchiveQueue, + // eslint-disable-next-line max-len + endpoint: `https://${parameters.AzureAccountName}.queue.azure-mock.${parameters.Namespace}.svc.cluster.local`, + }, + auth: { + type: 'location-azure-shared-key', + accountName: parameters.AzureAccountName, + accountKey: parameters.AzureAccountKey, + }, + repoId: ['233aead6-1d7b-4647-a7cf-0d3280b5d1d7'], + }, + }, + ]; + + const creationResults = await Promise.allSettled( + locations.map(async location => { + try { + const result = await zenkoInstance.managementAPIRequest( + 'POST', + `/config/${parameters.InstanceID}/location`, + {}, + location + ); + + if (result.statusCode === 201) { + logger.info(`Successfully created location: ${location.name}`); + return { location: location.name, success: true }; + } else if (result.statusCode === 409) { + logger.info(`Location already exists: ${location.name}`); + return { location: location.name, success: true, existed: true }; + } else { + logger.error(`Failed to create location ${location.name}`, { + statusCode: result.statusCode, + error: result.err || result.data, + }); + return { location: location.name, success: false, error: result.err || result.data }; + } + } catch (error) { + logger.error(`Exception creating location ${location.name}`, { error }); + return { location: location.name, success: false, error }; + } + }) + ); + + // Log results + const successful = creationResults.filter(r => r.status === 'fulfilled' && r.value.success); + const failed = creationResults.filter(r => r.status === 'rejected' || !r.value?.success); + + logger.info(`Storage location setup completed: ${successful.length} successful, ${failed.length} failed`); + + if (failed.length > 0) { + const failedNames = failed.map(r => + r.status === 'fulfilled' ? r.value.location : 'unknown' + ); + logger.warn(`Failed to create locations: ${failedNames.join(', ')}`); + } } /** @@ -809,7 +1012,19 @@ async function setupClusterRBAC(rbacClient: RbacAuthorizationV1Api): Promise { + logger.info('Performing CTST cluster setup...'); + await setupClusterConfiguration(this.parameters as ZenkoWorldParameters); + }, + async () => { + await extractAndCacheParameters(this.parameters as ZenkoWorldParameters); + } + ); logger.info('Final parameters:', { parameters: this.parameters, cachedParameters: CacheHelper.parameters }); }); diff --git a/tests/ctst/world/Zenko.ts b/tests/ctst/world/Zenko.ts index 5418a6515d..bb3fff2a32 100644 --- a/tests/ctst/world/Zenko.ts +++ b/tests/ctst/world/Zenko.ts @@ -4,8 +4,6 @@ import { AccessKey } from '@aws-sdk/client-iam'; import { Credentials } from '@aws-sdk/client-sts'; import { aws4Interceptor } from 'aws4-axios'; import qs from 'qs'; -import fs from 'fs'; -import lockFile from 'proper-lockfile'; import Werelogs from 'werelogs'; import { CacheHelper, @@ -20,6 +18,7 @@ import { Utils, AWSCredentials, Logger, + coordinate, } from 'cli-testing'; import { extractPropertyFromResults } from '../common/utils'; @@ -644,72 +643,68 @@ export default class Zenko extends World { if (!Identity.hasIdentity(IdentityEnum.ACCOUNT, accountName)) { Identity.useIdentity(IdentityEnum.ADMIN, site.adminIdentityName); - const filePath = `/tmp/account-init-${accountName}.json`; - if (!fs.existsSync(filePath)) { - fs.writeFileSync(filePath, JSON.stringify({ - ready: false, - })); - } - let account = null; - let releaseLock: (() => Promise) | null = null; - try { - releaseLock = await lockFile.lock(filePath, { - stale: Constants.DEFAULT_TIMEOUT / 2, - retries: { - retries: 5, - factor: 3, - minTimeout: 1000, - maxTimeout: 5000, + + await coordinate( + { + lockName: `account-init-${accountName}`, + timeout: Constants.DEFAULT_TIMEOUT / 2, + logger: CacheHelper.logger, + }, + // Work function: executed exactly once + async () => { + try { + await SuperAdmin.createAccount({ accountName }); + } catch (err) { + if (!(err as { EntityAlreadyExists: boolean }).EntityAlreadyExists && + (err as { code: string }).code !== 'EntityAlreadyExists') { + throw err; + } } - }); - - try { - await SuperAdmin.createAccount({ accountName }); - /* eslint-disable */ - } catch (err: any) { - if (!err.EntityAlreadyExists && err.code !== 'EntityAlreadyExists') { - throw err; + }, + // Post-processing function: executed by all workers + async () => { + // Waiting until the account exists, in case of parallel mode. + let remaining = Constants.MAX_ACCOUNT_CHECK_RETRIES; + let account = await SuperAdmin.getAccount({ accountName }); + while (!account && remaining > 0) { + await Utils.sleep(500); + account = await SuperAdmin.getAccount({ accountName }); + remaining--; + } + if (!account) { + throw new Error(`Account ${accountName} not found in site ${siteKey}.`); } - } - } finally { - if (releaseLock) { - await releaseLock(); - } - } - /* eslint-enable */ - // Waiting until the account exists, in case of parallel mode. - let remaining = Constants.MAX_ACCOUNT_CHECK_RETRIES; - account = await SuperAdmin.getAccount({ accountName }); - while (!account && remaining > 0) { - await Utils.sleep(500); - account = await SuperAdmin.getAccount({ accountName }); - remaining--; - } - if (!account) { - throw new Error(`Account ${accountName} not found in site ${siteKey}.`); - } - // Account was found, generate access keys if not provided - const accountAccessKeys = Identity.getCredentialsForIdentity( - IdentityEnum.ACCOUNT, accountName) || { - accessKeyId: '', - secretAccessKey: '', - }; - - if (!accountAccessKeys.accessKeyId || !accountAccessKeys.secretAccessKey) { - const accessKeys = await SuperAdmin.generateAccountAccessKey({ accountName }); - if (!Utils.isAccessKeys(accessKeys)) { - throw new Error('Failed to generate account access keys for site ${siteKey}'); - } - accountAccessKeys.accessKeyId = accessKeys.accessKeyId; - accountAccessKeys.secretAccessKey = accessKeys.secretAccessKey; - } + // Account was found, generate access keys if not provided + const accountAccessKeys = Identity.getCredentialsForIdentity( + IdentityEnum.ACCOUNT, accountName) || { + accessKeyId: '', + secretAccessKey: '', + }; + + if (!accountAccessKeys.accessKeyId || !accountAccessKeys.secretAccessKey) { + const accessKeys = await SuperAdmin.generateAccountAccessKey({ accountName }); + if (!Utils.isAccessKeys(accessKeys)) { + throw new Error(`Failed to generate account access keys for site ${siteKey}`); + } + accountAccessKeys.accessKeyId = accessKeys.accessKeyId; + accountAccessKeys.secretAccessKey = accessKeys.secretAccessKey; + } - CacheHelper.logger.debug('Adding account identity', { - accountName, - accountAccessKeys, - }); - Identity.addIdentity(IdentityEnum.ACCOUNT, accountName, accountAccessKeys, undefined, true, true); + CacheHelper.logger.debug('Adding account identity', { + accountName, + accountAccessKeys, + }); + Identity.addIdentity( + IdentityEnum.ACCOUNT, + accountName, + accountAccessKeys, + undefined, + true, + true, + ); + } + ); } } diff --git a/tests/ctst/yarn.lock b/tests/ctst/yarn.lock index f5c26afd00..f9ed05a4b9 100644 --- a/tests/ctst/yarn.lock +++ b/tests/ctst/yarn.lock @@ -80,7 +80,7 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-iam@^3.582.0", "@aws-sdk/client-iam@^3.637.0": +"@aws-sdk/client-iam@^3.582.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-iam/-/client-iam-3.864.0.tgz#8d250deb90f2f6e628c2bfc4d2da8520b2246fc8" integrity sha512-uTlSDLJntfHSC4LZVfuf8xhPx907cPlLYf00Wsz4gEPPLFkKNOPCbxzRqEsFG4haGkBENMc3U1cMt3iE/BGnSg== @@ -126,7 +126,53 @@ "@smithy/util-waiter" "^4.0.7" tslib "^2.6.2" -"@aws-sdk/client-s3@^3.583.0", "@aws-sdk/client-s3@^3.637.0": +"@aws-sdk/client-iam@^3.879.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-iam/-/client-iam-3.886.0.tgz#ab7c458ce6a304a482630e93295decbf6f885092" + integrity sha512-w/DJIoG2nOlSyuGKTl7d4AWs7hJ3+J612jLYJujqne0H6LlkPUZTW9IBNmzMXWuF3JYM8rVOMTfRpocKkcWS6A== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/credential-provider-node" "3.886.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.886.0" + "@aws-sdk/middleware-user-agent" "3.883.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.879.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.883.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.9.2" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.21" + "@smithy/middleware-retry" "^4.1.22" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.29" + "@smithy/util-defaults-mode-node" "^4.0.29" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + "@smithy/util-waiter" "^4.0.7" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.583.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.864.0.tgz#ffbcbf0ba861fad711261b4174da3be19b1c7d5f" integrity sha512-QGYi9bWliewxumsvbJLLyx9WC0a4DP4F+utygBcq0zwPxaM0xDfBspQvP1dsepi7mW5aAjZmJ2+Xb7X0EhzJ/g== @@ -190,6 +236,70 @@ tslib "^2.6.2" uuid "^9.0.1" +"@aws-sdk/client-s3@^3.879.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.886.0.tgz#5ee89042c0b7cc492549233af2c1802611369cf3" + integrity sha512-IuZ2EHiSMkEqjOJk/QMQG55zGpj/iDkKNS1V3oKV6ClstmErySBfW0Ri8GuW7WJ1k1pkFO+hvgL6yn1yO+Y6XQ== + dependencies: + "@aws-crypto/sha1-browser" "5.2.0" + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/credential-provider-node" "3.886.0" + "@aws-sdk/middleware-bucket-endpoint" "3.873.0" + "@aws-sdk/middleware-expect-continue" "3.873.0" + "@aws-sdk/middleware-flexible-checksums" "3.883.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-location-constraint" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.886.0" + "@aws-sdk/middleware-sdk-s3" "3.883.0" + "@aws-sdk/middleware-ssec" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.883.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/signature-v4-multi-region" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.879.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.883.0" + "@aws-sdk/xml-builder" "3.873.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.9.2" + "@smithy/eventstream-serde-browser" "^4.0.5" + "@smithy/eventstream-serde-config-resolver" "^4.1.3" + "@smithy/eventstream-serde-node" "^4.0.5" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-blob-browser" "^4.0.5" + "@smithy/hash-node" "^4.0.5" + "@smithy/hash-stream-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/md5-js" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.21" + "@smithy/middleware-retry" "^4.1.22" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.29" + "@smithy/util-defaults-mode-node" "^4.0.29" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-stream" "^4.2.4" + "@smithy/util-utf8" "^4.0.0" + "@smithy/util-waiter" "^4.0.7" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + "@aws-sdk/client-sso@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.864.0.tgz#4099313516d61ed61791551c6f0683259b9cbf5e" @@ -234,7 +344,51 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" -"@aws-sdk/client-sts@^3.4.1", "@aws-sdk/client-sts@^3.583.0", "@aws-sdk/client-sts@^3.637.0": +"@aws-sdk/client-sso@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.886.0.tgz#127dbe0651f90ac39e71b08001c673507dac80be" + integrity sha512-CwpPZBlONsUO6OvMzNNP9PETZ3dPCQum3nUisk5VuzLTvNd80w2aWeSN/TpcAAbNvcRYbM+FsC4gBm4Q4VWn0g== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.886.0" + "@aws-sdk/middleware-user-agent" "3.883.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.879.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.883.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.9.2" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.21" + "@smithy/middleware-retry" "^4.1.22" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.29" + "@smithy/util-defaults-mode-node" "^4.0.29" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sts@^3.4.1", "@aws-sdk/client-sts@^3.583.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.864.0.tgz#67eb3e35953a552331d999ed8f26ec458bdf6d15" integrity sha512-g3To8L5T9rRoF1Nsx7Bf7VxBd/6fYu/YdSnLmjAW7QJ4yGvP4l4gTY//jFksapniD/kLVJXyNuS5PJBwGzvw5Q== @@ -279,6 +433,51 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/client-sts@^3.879.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.886.0.tgz#4b1e5664f3ca26d8136a7b77933448483ab408b9" + integrity sha512-7ucGZylYyxtaTSJSOWLzsWdQ9bQoH8Yt+IPznTRCauX3oXPHKxBUCeikWwkcVzYCrjLHvGpej9fxWCxH67jqrQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/credential-provider-node" "3.886.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.886.0" + "@aws-sdk/middleware-user-agent" "3.883.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.879.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.883.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.9.2" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.21" + "@smithy/middleware-retry" "^4.1.22" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.29" + "@smithy/util-defaults-mode-node" "^4.0.29" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/core@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.864.0.tgz#5ea4e400bb479faf4e0aa71a32ec89e8a3f2ceaf" @@ -300,6 +499,27 @@ fast-xml-parser "5.2.5" tslib "^2.6.2" +"@aws-sdk/core@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.883.0.tgz#1674b371784188f26fd39ad6ea9edafd8c385e20" + integrity sha512-FmkqnqBLkXi4YsBPbF6vzPa0m4XKUuvgKDbamfw4DZX2CzfBZH6UU4IwmjNV3ZM38m0xraHarK8KIbGSadN3wg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@aws-sdk/xml-builder" "3.873.0" + "@smithy/core" "^3.9.2" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/signature-v4" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-utf8" "^4.0.0" + fast-xml-parser "5.2.5" + tslib "^2.6.2" + "@aws-sdk/credential-provider-env@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.864.0.tgz#32e048eafaad51e3c67ef34d1310cc19f2f67c38" @@ -311,6 +531,17 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/credential-provider-env@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.883.0.tgz#834f1e92eca815fe2005d2ae3ec021153e863788" + integrity sha512-Z6tPBXPCodfhIF1rvQKoeRGMkwL6TK0xdl1UoMIA1x4AfBpPICAF77JkFBExk/pdiFYq1d04Qzddd/IiujSlLg== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-http@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.864.0.tgz#e312b137c1fdce87adb5140b039516c077726f5c" @@ -327,6 +558,22 @@ "@smithy/util-stream" "^4.2.4" tslib "^2.6.2" +"@aws-sdk/credential-provider-http@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.883.0.tgz#91e9d15631cc83df068f7ce91a645938d142edcb" + integrity sha512-P589ug1lMOOEYLTaQJjSP+Gee34za8Kk2LfteNQfO9SpByHFgGj++Sg8VyIe30eZL8Q+i4qTt24WDCz1c+dgYg== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/util-stream" "^4.2.4" + tslib "^2.6.2" + "@aws-sdk/credential-provider-ini@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.864.0.tgz#3149745e91d030f191ad618e7ee15c92101ad24e" @@ -346,6 +593,25 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/credential-provider-ini@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.886.0.tgz#6f5bb13252b065477daebd7bb618b5eb3e6b3527" + integrity sha512-86ZuuUGLzzYqxkglFBUMCsvb7vSr+IeIPkXD/ERuX9wX0xPxBK961UG7pygO7yaAVzcHSWbWArAXOcEWVlk+7Q== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/credential-provider-env" "3.883.0" + "@aws-sdk/credential-provider-http" "3.883.0" + "@aws-sdk/credential-provider-process" "3.883.0" + "@aws-sdk/credential-provider-sso" "3.886.0" + "@aws-sdk/credential-provider-web-identity" "3.886.0" + "@aws-sdk/nested-clients" "3.886.0" + "@aws-sdk/types" "3.862.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-node@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.864.0.tgz#d01277b53ac179d2ea97ba16147ba0cb3f710aae" @@ -364,6 +630,24 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/credential-provider-node@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.886.0.tgz#89073d8a27689c56174fcf8215e248818b70916c" + integrity sha512-hyXQrUW6bXkSWOZlNWnNcbXsjM0CBIOfutDFd3tS7Ilhqkx8P3eptT0fVR8GFxNg/ruq5PvnybGK83brUmD7tw== + dependencies: + "@aws-sdk/credential-provider-env" "3.883.0" + "@aws-sdk/credential-provider-http" "3.883.0" + "@aws-sdk/credential-provider-ini" "3.886.0" + "@aws-sdk/credential-provider-process" "3.883.0" + "@aws-sdk/credential-provider-sso" "3.886.0" + "@aws-sdk/credential-provider-web-identity" "3.886.0" + "@aws-sdk/types" "3.862.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-process@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.864.0.tgz#5f39e34a084cfa07966874955fa3aa0f966bcf15" @@ -376,6 +660,18 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/credential-provider-process@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.883.0.tgz#00938bd9257a7affb041386d92b708dbdee8dc58" + integrity sha512-m1shbHY/Vppy4EdddG9r8x64TO/9FsCjokp5HbKcZvVoTOTgUJrdT8q2TAQJ89+zYIJDqsKbqfrmfwJ1zOdnGQ== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-sso@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.864.0.tgz#1556640016f9bd3dd1c2e140270098a75c922ca3" @@ -390,6 +686,20 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/credential-provider-sso@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.886.0.tgz#87899424255801ce85b0d711708b61d21e69b839" + integrity sha512-KxNgGcT/2ec7XBhiYGBYlk+UyiMqosi5LzLjq2qR4nYf8Deo/lCtbqXSQplwSQ0JIV2kNDcnMQiSafSS9TrL/A== + dependencies: + "@aws-sdk/client-sso" "3.886.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/token-providers" "3.886.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-web-identity@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.864.0.tgz#5cf54ec064957552e4c8c9070fd2b313f152a776" @@ -402,6 +712,18 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/credential-provider-web-identity@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.886.0.tgz#9f234d8624447e498a901acb1840cb0ebe19a985" + integrity sha512-pilcy1GUOr4lIWApTcgJLGL+t79SOoe66pzmranQhbn+HGAp2VgiZizeID9P3HLmZObStVal4yTaJur0hWb5ZQ== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/nested-clients" "3.886.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/hash-node@^3.110.0": version "3.374.0" resolved "https://registry.yarnpkg.com/@aws-sdk/hash-node/-/hash-node-3.374.0.tgz#fad2ddb51ae7091b91ed1308836fe3385d128f9e" @@ -423,6 +745,19 @@ "@smithy/util-config-provider" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-bucket-endpoint@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.873.0.tgz#cfc2d87328e3d9fecd165e4f5caa4cf1a22b220d" + integrity sha512-b4bvr0QdADeTUs+lPc9Z48kXzbKHXQKgTvxx/jXDgSW9tv4KmYPO1gIj6Z9dcrBkRWQuUtSW3Tu2S5n6pe+zeg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-arn-parser" "3.873.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + "@smithy/util-config-provider" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/middleware-expect-continue@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.862.0.tgz#f53c28c41f63859362797fd76e993365b598d0ba" @@ -433,6 +768,16 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-expect-continue@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.873.0.tgz#2d4fea9104070d06c26f6f978eb038e807f3ca34" + integrity sha512-GIqoc8WgRcf/opBOZXFLmplJQKwOMjiOMmDz9gQkaJ8FiVJoAp8EGVmK2TOWZMQUYsavvHYsHaor5R2xwPoGVg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/middleware-flexible-checksums@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.864.0.tgz#fcbb40ae1513f96185ec961693c0f55ec1f4da18" @@ -452,6 +797,25 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-flexible-checksums@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.883.0.tgz#b15629c1737e7bd52a7a7ee1a62149dd23b54681" + integrity sha512-EloU4ZjkH+CXCHJcYElXo5nZ1vK6Miam/S02YSHk5JTrJkm4RV478KXXO29TIIAwZXcLT/FEQOZ9ZH/JHFFCFQ== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-crypto/util" "5.2.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@smithy/is-array-buffer" "^4.0.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-stream" "^4.2.4" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/middleware-host-header@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.862.0.tgz#9b5fa0ad4c17a84816b4bfde7cda949116374042" @@ -462,6 +826,16 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-host-header@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz#81e9c2f61674b96337472bcaefd85ce3b7a24f7b" + integrity sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/middleware-location-constraint@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.862.0.tgz#d55babadc9f9b7150c56b028fc6953021a5a565a" @@ -471,6 +845,15 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-location-constraint@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.873.0.tgz#aab9e90d0545087102709b68ca1a1c816e3c58cf" + integrity sha512-r+hIaORsW/8rq6wieDordXnA/eAu7xAPLue2InhoEX6ML7irP52BgiibHLpt9R0psiCzIHhju8qqKa4pJOrmiw== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/middleware-logger@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.862.0.tgz#fba26924421135c824dec7e1cd0f75990a588fdb" @@ -480,6 +863,15 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-logger@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz#16ee45f7bcd887badc8f12d80eef9ba18a0ac97c" + integrity sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/middleware-recursion-detection@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.862.0.tgz#d83433251e550b7ed9cd731a447c92aaec378f01" @@ -490,6 +882,17 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-recursion-detection@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.886.0.tgz#2ba280503609b63fbe28bc92ca37ad51a048b06f" + integrity sha512-yMMlPqiX1SXFwQ0L1a/U19rdXx7eYseHsJEC9F9M5LUUPBI7k117nA0vXxvsvODVQ6JKtY7nTiPrc98GcVKgnw== + dependencies: + "@aws-sdk/types" "3.862.0" + "@aws/lambda-invoke-store" "^0.0.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/middleware-sdk-s3@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.864.0.tgz#5142210471ed702452277ad653af483147c42598" @@ -510,6 +913,26 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-sdk-s3@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.883.0.tgz#419019a4c301427d9941e7577cefbe4a6242692f" + integrity sha512-i4sGOj9xhSN6/LkYj3AJ2SRWENnpN9JySwNqIoRqO1Uon8gfyNLJd1yV+s43vXQsU5wbKWVXK8l9SRo+vNTQwg== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-arn-parser" "3.873.0" + "@smithy/core" "^3.9.2" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/protocol-http" "^5.1.3" + "@smithy/signature-v4" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-stream" "^4.2.4" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/middleware-ssec@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.862.0.tgz#d6c7d03c966cb6642acec8c7f046afd3a72c0f7c" @@ -519,6 +942,15 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-ssec@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.873.0.tgz#e7dd0d5184f536197c14a9e256e74e1354d74168" + integrity sha512-AF55J94BoiuzN7g3hahy0dXTVZahVi8XxRBLgzNp6yQf0KTng+hb/V9UQZVYY1GZaDczvvvnqC54RGe9OZZ9zQ== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/middleware-user-agent@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.864.0.tgz#7c8a5e7f09eb2855f9a045cdfeee56e099e15552" @@ -532,6 +964,19 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/middleware-user-agent@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.883.0.tgz#6b48003f6c047d8755ad85329bab192557f935b3" + integrity sha512-q58uLYnGLg7hsnWpdj7Cd1Ulsq1/PUJOHvAfgcBuiDE/+Fwh0DZxZZyjrU+Cr+dbeowIdUaOO8BEDDJ0CUenJw== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.879.0" + "@smithy/core" "^3.9.2" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/nested-clients@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.864.0.tgz#8d8b7e8e481649ae0f6ef37339b07cd8f6405e74" @@ -576,6 +1021,50 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/nested-clients@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.886.0.tgz#bc9a8227c63fc7917d7e9f7a69e33b4dda5c42ee" + integrity sha512-CqeRdkNyJ7LlKLQtMTzK11WIiryEK8JbSL5LCia0B1Lp22OByDUiUSFZZ3FZq9poD5qHQI63pHkzAr5WkLGS5A== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.883.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.886.0" + "@aws-sdk/middleware-user-agent" "3.883.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.879.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.883.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.9.2" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.21" + "@smithy/middleware-retry" "^4.1.22" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.5.2" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.29" + "@smithy/util-defaults-mode-node" "^4.0.29" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/region-config-resolver@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.862.0.tgz#99e7942be513abacb715d06781e6f4d62b3e9cf2" @@ -588,6 +1077,18 @@ "@smithy/util-middleware" "^4.0.5" tslib "^2.6.2" +"@aws-sdk/region-config-resolver@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz#9a5ddf8aa5a068d1c728dda3ef7e5b31561f7419" + integrity sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + tslib "^2.6.2" + "@aws-sdk/signature-v4-multi-region@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.864.0.tgz#75e24f5382aa77b7e629f8feb366bcf2a358ffb8" @@ -600,6 +1101,18 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/signature-v4-multi-region@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.883.0.tgz#127440b9a2a0242e3e0d25338c3d0f1f7244283c" + integrity sha512-86PO7+xhuQ48cD3xlZgEpRxVP1lBarWAJy23sB6zZLHgZSbnYXYjRFuyxX4PlFzqllM3PDKJvq3WnXeqSXeNsg== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/signature-v4" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/token-providers@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.864.0.tgz#c5f88c34bf268435a5b64b7814193c63ae330a68" @@ -613,6 +1126,19 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/token-providers@3.886.0": + version "3.886.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.886.0.tgz#9829095ea3b510cddf3e3c3c8cabab0584e10e1c" + integrity sha512-dYS3apmGcldFglpAiAajcdDKtKjBw/NkG6nRYIC2q7+OZsxeyzunT1EUSxV4xphLoqiuhuCg/fTnBI3WVtb3IQ== + dependencies: + "@aws-sdk/core" "3.883.0" + "@aws-sdk/nested-clients" "3.886.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/types@3.862.0", "@aws-sdk/types@^3.222.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.862.0.tgz#2f5622e1aa3a5281d4f419f5d2c90f87dd5ff0cf" @@ -628,6 +1154,13 @@ dependencies: tslib "^2.6.2" +"@aws-sdk/util-arn-parser@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.873.0.tgz#12c5ea852574dfb6fe78eaac1666433dff1acffa" + integrity sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg== + dependencies: + tslib "^2.6.2" + "@aws-sdk/util-endpoints@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.862.0.tgz#d66975bbedc1899721e3bf2a548fadfaee2ba2ee" @@ -639,6 +1172,17 @@ "@smithy/util-endpoints" "^3.0.7" tslib "^2.6.2" +"@aws-sdk/util-endpoints@3.879.0": + version "3.879.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz#e30c15beede883d327dbd290c47512d6d700a2e9" + integrity sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-endpoints" "^3.0.7" + tslib "^2.6.2" + "@aws-sdk/util-locate-window@^3.0.0": version "3.804.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz#a2ee8dc5d9c98276986e8e1ba03c0c84d9afb0f5" @@ -656,6 +1200,16 @@ bowser "^2.11.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-browser@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz#0fcc3c1877ae74aa692cc0b4ad874bc9a6ee1ad6" + integrity sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + bowser "^2.11.0" + tslib "^2.6.2" + "@aws-sdk/util-user-agent-node@3.864.0": version "3.864.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.864.0.tgz#2fd8276a6d7d0ee3d6fe75421c5565e63ae6a0d5" @@ -667,6 +1221,17 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/util-user-agent-node@3.883.0": + version "3.883.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.883.0.tgz#584fed4347a9a4434a98026bf3e201f236bd132a" + integrity sha512-28cQZqC+wsKUHGpTBr+afoIdjS6IoEJkMqcZsmo2Ag8LzmTa6BUWQenFYB0/9BmDy4PZFPUn+uX+rJgWKB+jzA== + dependencies: + "@aws-sdk/middleware-user-agent" "3.883.0" + "@aws-sdk/types" "3.862.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@aws-sdk/xml-builder@3.862.0": version "3.862.0" resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.862.0.tgz#d368c76f0f129d43b3ffbc2dc18f53ddd64ec328" @@ -675,6 +1240,19 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@aws-sdk/xml-builder@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz#b5a3acfdeecfc1b7fee8a7773cb2a45590eb5701" + integrity sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws/lambda-invoke-store@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz#92d792a7dda250dfcb902e13228f37a81be57c8f" + integrity sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw== + "@azure/abort-controller@^2.0.0", "@azure/abort-controller@^2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" @@ -775,7 +1353,7 @@ "@typespec/ts-http-runtime" "^0.3.0" tslib "^2.6.2" -"@azure/storage-blob@^12.24.0": +"@azure/storage-blob@^12.28.0": version "12.28.0" resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.28.0.tgz#a64ce49f0fe9fe08f1f7c1b36164033678d38cf6" integrity sha512-VhQHITXXO03SURhDiGuHhvc/k/sD2WvJUS7hqhiVNbErVCuQoLtWql7r97fleBlIRKHJaa9R7DpBjfE0pfLYcA== @@ -810,7 +1388,7 @@ events "^3.3.0" tslib "^2.8.1" -"@azure/storage-queue@^12.23.0": +"@azure/storage-queue@^12.27.0": version "12.27.0" resolved "https://registry.yarnpkg.com/@azure/storage-queue/-/storage-queue-12.27.0.tgz#cde0c3560b08320ed61e813132d27de716c45973" integrity sha512-GoviVZrJ1BkYCmsam0gOZFqAjH7bKbnbBIEVPkgzCz3RzsB/C05jumQep+3GavZoWw7Yw4iaCNPSyyS1lbN1Gg== @@ -828,7 +1406,7 @@ "@azure/storage-common" "^12.0.0" tslib "^2.8.1" -"@babel/code-frame@^7.0.0": +"@babel/code-frame@^7.26.2": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== @@ -859,36 +1437,38 @@ resolved "https://registry.yarnpkg.com/@cucumber/ci-environment/-/ci-environment-10.0.1.tgz#c8584f1d4a619e4318cf60c01b838db096d72ccd" integrity sha512-/+ooDMPtKSmvcPMDYnMZt4LuoipfFfHaYspStI4shqw8FyKcfQAmekz6G+QKWjQQrvM+7Hkljwx58MEwPCwwzg== -"@cucumber/cucumber-expressions@17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-17.1.0.tgz#1a428548a2c98ef3224bd484fc5666b4f7153a72" - integrity sha512-PCv/ppsPynniKPWJr5v566daCVe+pbxQpHGrIu/Ev57cCH9Rv+X0F6lio4Id3Z64TaG7btCRLUGewIgLwmrwOA== +"@cucumber/cucumber-expressions@18.0.1": + version "18.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-18.0.1.tgz#0899cbda2ed5546dbaa0e40f0c754b6e3bd1bb69" + integrity sha512-NSid6bI+7UlgMywl5octojY5NXnxR9uq+JisjOrO52VbFsQM6gTWuQFE8syI10KnIBEdPzuEUSVEeZ0VFzRnZA== dependencies: regexp-match-indices "1.0.2" -"@cucumber/cucumber@^10.9.0": - version "10.9.0" - resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-10.9.0.tgz#1ee505b3fc513367d2ddc651ad71059c3fd544c8" - integrity sha512-7XHJ6nmr9IkIag0nv6or82HfelbSInrEe3H4aT6dMHyTehwFLUifG6eQQ+uE4LZIOXAnzLPH37YmqygEO67vCA== +"@cucumber/cucumber@^12.2.0": + version "12.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-12.2.0.tgz#4243aeddf390a1514c28ed2afd7830c4f0966d3d" + integrity sha512-b7W4snvXYi1T2puUjxamASCCNhNzVSzb/fQUuGSkdjm/AFfJ24jo8kOHQyOcaoArCG71sVQci4vkZaITzl/V1w== dependencies: "@cucumber/ci-environment" "10.0.1" - "@cucumber/cucumber-expressions" "17.1.0" - "@cucumber/gherkin" "28.0.0" + "@cucumber/cucumber-expressions" "18.0.1" + "@cucumber/gherkin" "34.0.0" "@cucumber/gherkin-streams" "5.0.1" - "@cucumber/gherkin-utils" "9.0.0" - "@cucumber/html-formatter" "21.6.0" + "@cucumber/gherkin-utils" "9.2.0" + "@cucumber/html-formatter" "21.14.0" + "@cucumber/junit-xml-formatter" "0.8.1" "@cucumber/message-streams" "4.0.1" - "@cucumber/messages" "24.1.0" - "@cucumber/tag-expressions" "6.1.0" + "@cucumber/messages" "28.1.0" + "@cucumber/pretty-formatter" "1.0.1" + "@cucumber/tag-expressions" "6.2.0" assertion-error-formatter "^3.0.0" capital-case "^1.0.4" chalk "^4.1.2" - cli-table3 "0.6.3" - commander "^10.0.0" + cli-table3 "0.6.5" + commander "^14.0.0" debug "^4.3.4" error-stack-parser "^2.1.4" figures "^3.2.0" - glob "^10.3.10" + glob "^11.0.0" has-ansi "^4.0.1" indent-string "^4.0.0" is-installed-globally "^0.4.0" @@ -896,23 +1476,19 @@ knuth-shuffle-seeded "^1.0.6" lodash.merge "^4.6.2" lodash.mergewith "^4.6.2" - luxon "3.2.1" + luxon "3.7.1" mime "^3.0.0" - mkdirp "^2.1.5" + mkdirp "^3.0.0" mz "^2.7.0" progress "^2.0.3" - read-pkg-up "^7.0.1" - resolve-pkg "^2.0.0" - semver "7.5.3" + read-package-up "^11.0.0" + semver "7.7.2" string-argv "0.3.1" - strip-ansi "6.0.1" supports-color "^8.1.1" - tmp "0.2.3" - type-fest "^4.8.3" + type-fest "^4.41.0" util-arity "^1.1.0" - xmlbuilder "^15.1.1" yaml "^2.2.2" - yup "1.2.0" + yup "1.7.0" "@cucumber/gherkin-streams@5.0.1": version "5.0.1" @@ -922,45 +1498,82 @@ commander "9.1.0" source-map-support "0.5.21" -"@cucumber/gherkin-utils@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@cucumber/gherkin-utils/-/gherkin-utils-9.0.0.tgz#944c64c458742d8e73b750e5dde2cf56b161d674" - integrity sha512-clk4q39uj7pztZuZtyI54V8lRsCUz0Y/p8XRjIeHh7ExeEztpWkp4ca9q1FjUOPfQQ8E7OgqFbqoQQXZ1Bx7fw== +"@cucumber/gherkin-utils@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-utils/-/gherkin-utils-9.2.0.tgz#42c50a6232022f5a17ca677a734daa4a15d0b6e1" + integrity sha512-3nmRbG1bUAZP3fAaUBNmqWO0z0OSkykZZotfLjyhc8KWwDSOrOmMJlBTd474lpA8EWh4JFLAX3iXgynBqBvKzw== dependencies: - "@cucumber/gherkin" "^28.0.0" - "@cucumber/messages" "^24.0.0" + "@cucumber/gherkin" "^31.0.0" + "@cucumber/messages" "^27.0.0" "@teppeis/multimaps" "3.0.0" - commander "12.0.0" + commander "13.1.0" source-map-support "^0.5.21" -"@cucumber/gherkin@28.0.0", "@cucumber/gherkin@^28.0.0": - version "28.0.0" - resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-28.0.0.tgz#91246da622524807b21430c1692bedd319d3d4bb" - integrity sha512-Ee6zJQq0OmIUPdW0mSnsCsrWA2PZAELNDPICD2pLfs0Oz7RAPgj80UsD2UCtqyAhw2qAR62aqlktKUlai5zl/A== +"@cucumber/gherkin@34.0.0": + version "34.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-34.0.0.tgz#891ec27a7c09a9fc3695aaf3c3a3c8a1c594102f" + integrity sha512-659CCFsrsyvuBi/Eix1fnhSheMnojSfnBcqJ3IMPNawx7JlrNJDcXYSSdxcUw3n/nG05P+ptCjmiZY3i14p+tA== + dependencies: + "@cucumber/messages" ">=19.1.4 <29" + +"@cucumber/gherkin@^31.0.0": + version "31.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-31.0.0.tgz#12cff1a6e92b7d30cc5e374e91fbdd2135064aad" + integrity sha512-wlZfdPif7JpBWJdqvHk1Mkr21L5vl4EfxVUOS4JinWGf3FLRV6IKUekBv5bb5VX79fkDcfDvESzcQ8WQc07Wgw== dependencies: - "@cucumber/messages" ">=19.1.4 <=24" + "@cucumber/messages" ">=19.1.4 <=26" -"@cucumber/html-formatter@21.6.0": - version "21.6.0" - resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-21.6.0.tgz#bfd8c4db31c6c96a8520332efba2ea9838ca36f0" - integrity sha512-Qw1tdObBJrgXgXwVjKVjB3hFhFPI8WhIFb+ULy8g5lDl5AdnKDiyDXAMvAWRX+pphnRMMNdkPCt6ZXEfWvUuAA== +"@cucumber/html-formatter@21.14.0": + version "21.14.0" + resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-21.14.0.tgz#beb29b66892189f8e242243aa8b807f7b9dc1c6c" + integrity sha512-vQqbmQZc0QiN4c+cMCffCItpODJlOlYtPG7pH6We096dBOa7u0ttDMjT6KrMAnQlcln54rHL46r408IFpuznAw== + +"@cucumber/junit-xml-formatter@0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cucumber/junit-xml-formatter/-/junit-xml-formatter-0.8.1.tgz#f27e52054d6934326c3961c683fc7f5b4d827a89" + integrity sha512-FT1Y96pyd9/ifbE9I7dbkTCjkwEdW9C0MBobUZoKD13c8EnWAt0xl1Yy/v/WZLTk4XfCLte1DATtLx01jt+YiA== + dependencies: + "@cucumber/query" "^13.0.2" + "@teppeis/multimaps" "^3.0.0" + luxon "^3.5.0" + xmlbuilder "^15.1.1" "@cucumber/message-streams@4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@cucumber/message-streams/-/message-streams-4.0.1.tgz#a5339d3504594bb2edb5732aaae94dddb24d0970" integrity sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA== -"@cucumber/messages@24.1.0", "@cucumber/messages@>=19.1.4 <=24", "@cucumber/messages@^24.0.0": - version "24.1.0" - resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-24.1.0.tgz#a212c97b0548144c3ccfae021a96d6c56d3841d3" - integrity sha512-hxVHiBurORcobhVk80I9+JkaKaNXkW6YwGOEFIh/2aO+apAN+5XJgUUWjng9NwqaQrW1sCFuawLB1AuzmBaNdQ== +"@cucumber/messages@28.1.0", "@cucumber/messages@>=19.1.4 <29": + version "28.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-28.1.0.tgz#5fdcfc3f9b30103cb45c69044ebe9a892bec38ce" + integrity sha512-2LzZtOwYKNlCuNf31ajkrekoy2M4z0Z1QGiPH40n4gf5t8VOUFb7m1ojtR4LmGvZxBGvJZP8voOmRqDWzBzYKA== + dependencies: + "@types/uuid" "10.0.0" + class-transformer "0.5.1" + reflect-metadata "0.2.2" + uuid "11.1.0" + +"@cucumber/messages@>=19.1.4 <=26": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-26.0.1.tgz#18765481cf2580066977cbe26af111458e05c424" + integrity sha512-DIxSg+ZGariumO+Lq6bn4kOUIUET83A4umrnWmidjGFl8XxkBieUZtsmNbLYgH/gnsmP07EfxxdTr0hOchV1Sg== + dependencies: + "@types/uuid" "10.0.0" + class-transformer "0.5.1" + reflect-metadata "0.2.2" + uuid "10.0.0" + +"@cucumber/messages@^27.0.0": + version "27.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-27.2.0.tgz#ee0cc006a391568fb668d47a23ac2e5bf901ff3a" + integrity sha512-f2o/HqKHgsqzFLdq6fAhfG1FNOQPdBdyMGpKwhb7hZqg0yZtx9BVqkTyuoNk83Fcvk3wjMVfouFXXHNEk4nddA== dependencies: - "@types/uuid" "9.0.8" + "@types/uuid" "10.0.0" class-transformer "0.5.1" - reflect-metadata "0.2.1" - uuid "9.0.1" + reflect-metadata "0.2.2" + uuid "11.0.5" -"@cucumber/pretty-formatter@^1.0.1": +"@cucumber/pretty-formatter@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@cucumber/pretty-formatter/-/pretty-formatter-1.0.1.tgz#65d6c1df436920036a7bd02d08cb44d20e7af0ab" integrity sha512-A1lU4VVP0aUWdOTmpdzvXOyEYuPtBDI0xYwYJnmoMDplzxMdhcHk86lyyvYDoMoPzzq6OkOE3isuosvUU4X7IQ== @@ -970,10 +1583,25 @@ figures "^3.2.0" ts-dedent "^2.0.0" -"@cucumber/tag-expressions@6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@cucumber/tag-expressions/-/tag-expressions-6.1.0.tgz#cb7af908bdb43669b7574c606f71fa707196e962" - integrity sha512-+3DwRumrCJG27AtzCIL37A/X+A/gSfxOPLg8pZaruh5SLumsTmpvilwroVWBT2fPzmno/tGXypeK5a7NHU4RzA== +"@cucumber/pretty-formatter@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/pretty-formatter/-/pretty-formatter-2.1.0.tgz#78fc53d2d44b2ff3dcbbfb7d309b1e0a808b4a02" + integrity sha512-VufasjEZLPDzhGmikvjp8BeV/p3d7N2nArqplWn3LMqu+SddmicCS0tHk59GduQxi5SYr8ldfFe5mBDgJpUWbQ== + dependencies: + "@cucumber/query" "^13.5.0" + +"@cucumber/query@^13.0.2", "@cucumber/query@^13.5.0": + version "13.6.0" + resolved "https://registry.yarnpkg.com/@cucumber/query/-/query-13.6.0.tgz#56858d6db13c0f623f0dc18bc4114a3f03dce130" + integrity sha512-tiDneuD5MoWsJ9VKPBmQok31mSX9Ybl+U4wqDoXeZgsXHDURqzM3rnpWVV3bC34y9W6vuFxrlwF/m7HdOxwqRw== + dependencies: + "@teppeis/multimaps" "3.0.0" + lodash.sortby "^4.7.0" + +"@cucumber/tag-expressions@6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/tag-expressions/-/tag-expressions-6.2.0.tgz#9cd13c242b01b98a08142ed4987860b0b7e1d893" + integrity sha512-KIF0eLcafHbWOuSDWFw0lMmgJOLdDRWjEL1kfXEWrqHmx2119HxVAr35WuEd9z542d3Yyg+XNqSr+81rIKqEdg== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0": version "4.7.0" @@ -1074,6 +1702,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1111,6 +1751,25 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jsep-plugin/assignment@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@jsep-plugin/assignment/-/assignment-1.3.0.tgz#fcfc5417a04933f7ceee786e8ab498aa3ce2b242" + integrity sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ== + +"@jsep-plugin/regex@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@jsep-plugin/regex/-/regex-1.0.4.tgz#cb2fc423220fa71c609323b9ba7f7d344a755fcc" + integrity sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg== + +"@keycloak/keycloak-admin-client@^26.3.3": + version "26.3.3" + resolved "https://registry.yarnpkg.com/@keycloak/keycloak-admin-client/-/keycloak-admin-client-26.3.3.tgz#d84d0f13ca96d7370d47b4be5a26c8b9577bf49d" + integrity sha512-gDwiB0o9N09f/OZY2dTMIuoiu99Rs/aFscN3yEfGbVfV6/DtaLxtLqfcD5G5C7/Sq+yNpqh1tMa4Ezfislcgkg== + dependencies: + camelize-ts "^3.0.0" + url-join "^5.0.0" + url-template "^3.1.1" + "@kubernetes/client-node@^0.21.0": version "0.21.0" resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.21.0.tgz#c807af50e5d4ecbbaa571087636d79cd71a7d9cc" @@ -1133,6 +1792,28 @@ optionalDependencies: openid-client "^5.3.0" +"@kubernetes/client-node@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-1.3.0.tgz#6088bc1c4f6bb01f52e6e933f35008feb39fe909" + integrity sha512-IE0yrIpOT97YS5fg2QpzmPzm8Wmcdf4ueWMn+FiJSI3jgTTQT1u+LUhoYpdfhdHAVxdrNsaBg2C0UXSnOgMoCQ== + dependencies: + "@types/js-yaml" "^4.0.1" + "@types/node" "^22.0.0" + "@types/node-fetch" "^2.6.9" + "@types/stream-buffers" "^3.0.3" + form-data "^4.0.0" + hpagent "^1.2.0" + isomorphic-ws "^5.0.0" + js-yaml "^4.1.0" + jsonpath-plus "^10.3.0" + node-fetch "^2.6.9" + openid-client "^6.1.3" + rfc4648 "^1.3.0" + socks-proxy-agent "^8.0.4" + stream-buffers "^3.0.2" + tar-fs "^3.0.8" + ws "^8.18.2" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1185,6 +1866,14 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/abort-controller@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.1.1.tgz#9b3872ab6b2c061486175c281dadc0a853260533" + integrity sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/chunked-blob-reader-native@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz#33cbba6deb8a3c516f98444f65061784f7cd7f8c" @@ -1211,6 +1900,34 @@ "@smithy/util-middleware" "^4.0.5" tslib "^2.6.2" +"@smithy/config-resolver@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.2.1.tgz#12c24e550e2675e03a78bec64a652ed129bce718" + integrity sha512-FXil8q4QN7mgKwU2hCLm0ltab8NyY/1RiqEf25Jnf6WLS3wmb11zGAoLETqg1nur2Aoibun4w4MjeN9CMJ4G6A== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-config-provider" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + tslib "^2.6.2" + +"@smithy/core@^3.11.0", "@smithy/core@^3.9.2": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.11.0.tgz#18ee04696ca35889046169e422a894bea1bec59d" + integrity sha512-Abs5rdP1o8/OINtE49wwNeWuynCu0kme1r4RI3VXVrHr4odVDG7h7mTnw1WXXfN5Il+c25QOnrdL2y56USfxkA== + dependencies: + "@smithy/middleware-serde" "^4.1.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-stream" "^4.3.1" + "@smithy/util-utf8" "^4.1.0" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + "@smithy/core@^3.8.0": version "3.8.0" resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.8.0.tgz#321d03564b753025b92e4476579efcd5c505ab1f" @@ -1239,6 +1956,17 @@ "@smithy/url-parser" "^4.0.5" tslib "^2.6.2" +"@smithy/credential-provider-imds@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.1.tgz#e1535a0121a98a2d872eaffc2470eccc057cebd5" + integrity sha512-1WdBfM9DwA59pnpIizxnUvBf/de18p4GP+6zP2AqrlFzoW3ERpZaT4QueBR0nS9deDMaQRkBlngpVlnkuuTisQ== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/property-provider" "^4.1.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + tslib "^2.6.2" + "@smithy/eventstream-codec@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz#e742a4badaaf985ac9abcf4283ff4c39d7e48438" @@ -1295,6 +2023,17 @@ "@smithy/util-base64" "^4.0.0" tslib "^2.6.2" +"@smithy/fetch-http-handler@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz#fe284a00f1b3a35edf9fba454d287b7f74ef20af" + integrity sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng== + dependencies: + "@smithy/protocol-http" "^5.2.1" + "@smithy/querystring-builder" "^4.1.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + tslib "^2.6.2" + "@smithy/hash-blob-browser@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.5.tgz#f8f2857e59907c3359dc451a22c1623373115aea" @@ -1370,6 +2109,13 @@ dependencies: tslib "^2.6.2" +"@smithy/is-array-buffer@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz#d18a2f22280e7173633cb91a9bdb6f3d8a6560b8" + integrity sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ== + dependencies: + tslib "^2.6.2" + "@smithy/md5-js@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.0.5.tgz#77216159386050dbcf6b58f16f4ac14ac5183474" @@ -1402,6 +2148,20 @@ "@smithy/util-middleware" "^4.0.5" tslib "^2.6.2" +"@smithy/middleware-endpoint@^4.1.21", "@smithy/middleware-endpoint@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.1.tgz#54c61a113e6da7b615724d03517879d377d3888d" + integrity sha512-fUTMmQvQQZakXOuKizfu7fBLDpwvWZjfH6zUK2OLsoNZRZGbNUdNSdLJHpwk1vS208jtDjpUIskh+JoA8zMzZg== + dependencies: + "@smithy/core" "^3.11.0" + "@smithy/middleware-serde" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/shared-ini-file-loader" "^4.1.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-middleware" "^4.1.1" + tslib "^2.6.2" + "@smithy/middleware-retry@^4.1.19": version "4.1.19" resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.1.19.tgz#19c013c1a548e1185cc1bfabfab3f498667c9e89" @@ -1418,6 +2178,22 @@ tslib "^2.6.2" uuid "^9.0.1" +"@smithy/middleware-retry@^4.1.22": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.2.1.tgz#61be10c06e183c392a3769cb8b03c7846b37bee7" + integrity sha512-JzfvjwSJXWRl7LkLgIRTUTd2Wj639yr3sQGpViGNEOjtb0AkAuYqRAHs+jSOI/LPC0ZTjmFVVtfrCICMuebexw== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/service-error-classification" "^4.1.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-retry" "^4.1.1" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + "@smithy/middleware-serde@^4.0.9": version "4.0.9" resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz#71213158bb11c1d632829001ca3f233323fb2a7c" @@ -1427,6 +2203,15 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/middleware-serde@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz#cfb99f53c744d7730928235cbe66cc7ff8a8a9b2" + integrity sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg== + dependencies: + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/middleware-stack@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz#577050d4c0afe816f1ea85f335b2ef64f73e4328" @@ -1435,6 +2220,14 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/middleware-stack@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz#1d533fde4ccbb62d7fc0f0b8ac518b7e4791e311" + integrity sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/node-config-provider@^4.1.4": version "4.1.4" resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz#42f231b7027e5a7ce003fd80180e586fe814944a" @@ -1445,6 +2238,16 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/node-config-provider@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.2.1.tgz#31be8865dbea9a9f23aee278a6728317d0ed0250" + integrity sha512-AIA0BJZq2h295J5NeCTKhg1WwtdTA/GqBCaVjk30bDgMHwniUETyh5cP9IiE9VrId7Kt8hS7zvREVMTv1VfA6g== + dependencies: + "@smithy/property-provider" "^4.1.1" + "@smithy/shared-ini-file-loader" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/node-http-handler@^4.1.1": version "4.1.1" resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz#dd806d9e08b6e73125040dd0808ab56d16a178e9" @@ -1456,6 +2259,17 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/node-http-handler@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz#d7ab8e31659030d3d5a68f0982f15c00b1e67a0c" + integrity sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw== + dependencies: + "@smithy/abort-controller" "^4.1.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/querystring-builder" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/property-provider@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.0.5.tgz#d3b368b31d5b130f4c30cc0c91f9ebb28d9685fc" @@ -1464,6 +2278,14 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/property-provider@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.1.1.tgz#6e11ae6729840314afed05fd6ab48f62c654116b" + integrity sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/protocol-http@^4.1.8": version "4.1.8" resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.1.8.tgz#0461758671335f65e8ff3fc0885ab7ed253819c9" @@ -1480,6 +2302,14 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/protocol-http@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.2.1.tgz#33f2b8e4e1082c3ae0372d1322577e6fa71d7824" + integrity sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/querystring-builder@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz#158ae170f8ec2d8af6b84cdaf774205a7dfacf68" @@ -1489,6 +2319,15 @@ "@smithy/util-uri-escape" "^4.0.0" tslib "^2.6.2" +"@smithy/querystring-builder@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz#4d35c1735de8214055424045a117fa5d1d5cdec1" + integrity sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA== + dependencies: + "@smithy/types" "^4.5.0" + "@smithy/util-uri-escape" "^4.1.0" + tslib "^2.6.2" + "@smithy/querystring-parser@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz#95706e56aa769f09dc8922d1b19ffaa06946e252" @@ -1497,6 +2336,14 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/querystring-parser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz#21b861439b2db16abeb0a6789b126705fa25eea1" + integrity sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/service-error-classification@^4.0.7": version "4.0.7" resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz#24072198a8c110d29677762162a5096e29eb4862" @@ -1504,6 +2351,13 @@ dependencies: "@smithy/types" "^4.3.2" +"@smithy/service-error-classification@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.1.1.tgz#86a615298ae406c3b6c7dc63c1c1738c54cfdfc6" + integrity sha512-Iam75b/JNXyDE41UvrlM6n8DNOa/r1ylFyvgruTUx7h2Uk7vDNV9AAwP1vfL1fOL8ls0xArwEGVcGZVd7IO/Cw== + dependencies: + "@smithy/types" "^4.5.0" + "@smithy/shared-ini-file-loader@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz#8d8a493276cd82a7229c755bef8d375256c5ebb9" @@ -1512,17 +2366,12 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" -"@smithy/signature-v4@^2.1.1": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-2.3.0.tgz#c30dd4028ae50c607db99459981cce8cdab7a3fd" - integrity sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q== +"@smithy/shared-ini-file-loader@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.1.1.tgz#d4a748bb8027e1111635464c9b1e546d608fc089" + integrity sha512-YkpikhIqGc4sfXeIbzSj10t2bJI/sSoP5qxLue6zG+tEE3ngOBSm8sO3+djacYvS/R5DfpxN/L9CyZsvwjWOAQ== dependencies: - "@smithy/is-array-buffer" "^2.2.0" - "@smithy/types" "^2.12.0" - "@smithy/util-hex-encoding" "^2.2.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-uri-escape" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" + "@smithy/types" "^4.5.0" tslib "^2.6.2" "@smithy/signature-v4@^4.1.0": @@ -1553,6 +2402,20 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/signature-v4@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.2.1.tgz#0048489d2f1b3c888382595a085edd31967498f8" + integrity sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA== + dependencies: + "@smithy/is-array-buffer" "^4.1.0" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-hex-encoding" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-uri-escape" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + "@smithy/smithy-client@^4.4.10": version "4.4.10" resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.4.10.tgz#c4b49c1d1ff9eb813f88f1e425a5dfac25a03180" @@ -1566,6 +2429,19 @@ "@smithy/util-stream" "^4.2.4" tslib "^2.6.2" +"@smithy/smithy-client@^4.5.2", "@smithy/smithy-client@^4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.6.1.tgz#4bebcf313431bd274da0b28c7ddc4ba335f9994b" + integrity sha512-WolVLDb9UTPMEPPOncrCt6JmAMCSC/V2y5gst2STWJ5r7+8iNac+EFYQnmvDCYMfOLcilOSEpm5yXZXwbLak1Q== + dependencies: + "@smithy/core" "^3.11.0" + "@smithy/middleware-endpoint" "^4.2.1" + "@smithy/middleware-stack" "^4.1.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-stream" "^4.3.1" + tslib "^2.6.2" + "@smithy/types@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-1.2.0.tgz#9dc65767b0ee3d6681704fcc67665d6fc9b6a34e" @@ -1573,13 +2449,6 @@ dependencies: tslib "^2.5.0" -"@smithy/types@^2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.12.0.tgz#c44845f8ba07e5e8c88eda5aed7e6a0c462da041" - integrity sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw== - dependencies: - tslib "^2.6.2" - "@smithy/types@^3.7.2": version "3.7.2" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.7.2.tgz#05cb14840ada6f966de1bf9a9c7dd86027343e10" @@ -1594,6 +2463,13 @@ dependencies: tslib "^2.6.2" +"@smithy/types@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.5.0.tgz#850e334662a1ef1286c35814940c80880400a370" + integrity sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg== + dependencies: + tslib "^2.6.2" + "@smithy/url-parser@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.0.5.tgz#1824a9c108b85322c5a31f345f608d47d06f073a" @@ -1603,6 +2479,15 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/url-parser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.1.1.tgz#0e9a5e72b3cf9d7ab7305f9093af5528d9debaf6" + integrity sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg== + dependencies: + "@smithy/querystring-parser" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/util-base64@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.0.0.tgz#8345f1b837e5f636e5f8470c4d1706ae0c6d0358" @@ -1612,6 +2497,15 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/util-base64@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.1.0.tgz#5965026081d9aef4a8246f5702807570abe538b2" + integrity sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ== + dependencies: + "@smithy/util-buffer-from" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + "@smithy/util-body-length-browser@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz#965d19109a4b1e5fe7a43f813522cce718036ded" @@ -1619,6 +2513,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-body-length-browser@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz#636bdf4bc878c546627dab4b9b0e4db31b475be7" + integrity sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ== + dependencies: + tslib "^2.6.2" + "@smithy/util-body-length-node@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz#3db245f6844a9b1e218e30c93305bfe2ffa473b3" @@ -1658,6 +2559,14 @@ "@smithy/is-array-buffer" "^4.0.0" tslib "^2.6.2" +"@smithy/util-buffer-from@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz#21f9e644a0eb41226d92e4eff763f76a7db7e9cc" + integrity sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw== + dependencies: + "@smithy/is-array-buffer" "^4.1.0" + tslib "^2.6.2" + "@smithy/util-config-provider@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz#e0c7c8124c7fba0b696f78f0bd0ccb060997d45e" @@ -1665,6 +2574,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-config-provider@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz#6a07d73446c1e9a46d7a3c125f2a9301060bc957" + integrity sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ== + dependencies: + tslib "^2.6.2" + "@smithy/util-defaults-mode-browser@^4.0.26": version "4.0.26" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.26.tgz#fc04cd466bbb0d80e41930af8d6a8c33c48490f2" @@ -1676,6 +2592,17 @@ bowser "^2.11.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-browser@^4.0.29": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.1.tgz#40b9659d6fc15aa1101e440d1a92579cb66ebfa3" + integrity sha512-hA1AKIHFUMa9Tl6q6y8p0pJ9aWHCCG8s57flmIyLE0W7HcJeYrYtnqXDcGnftvXEhdQnSexyegXnzzTGk8bKLA== + dependencies: + "@smithy/property-provider" "^4.1.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + bowser "^2.11.0" + tslib "^2.6.2" + "@smithy/util-defaults-mode-node@^4.0.26": version "4.0.26" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.26.tgz#adfee8c54301ec4cbabed58cd604995a81b4a8dc" @@ -1689,6 +2616,19 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/util-defaults-mode-node@^4.0.29": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.1.tgz#bca834c5ee16949bf8d0db9ac7bf988ad0d3ce10" + integrity sha512-RGSpmoBrA+5D2WjwtK7tto6Pc2wO9KSXKLpLONhFZ8VyuCbqlLdiDAfuDTNY9AJe4JoE+Cx806cpTQQoQ71zPQ== + dependencies: + "@smithy/config-resolver" "^4.2.1" + "@smithy/credential-provider-imds" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/property-provider" "^4.1.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/util-endpoints@^3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz#9d52f2e7e7a1ea4814ae284270a5f1d3930b3773" @@ -1698,13 +2638,6 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" -"@smithy/util-hex-encoding@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-2.2.0.tgz#87edb7c88c2f422cfca4bb21f1394ae9602c5085" - integrity sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ== - dependencies: - tslib "^2.6.2" - "@smithy/util-hex-encoding@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz#32938b33d5bf2a15796cd3f178a55b4155c535e6" @@ -1719,12 +2652,11 @@ dependencies: tslib "^2.6.2" -"@smithy/util-middleware@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-2.2.0.tgz#80cfad40f6cca9ffe42a5899b5cb6abd53a50006" - integrity sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw== +"@smithy/util-hex-encoding@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz#9b27cf0c25d0de2c8ebfe75cc20df84e5014ccc9" + integrity sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w== dependencies: - "@smithy/types" "^2.12.0" tslib "^2.6.2" "@smithy/util-middleware@^3.0.11": @@ -1743,6 +2675,14 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/util-middleware@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.1.1.tgz#e19749a127499c9bdada713a8afd807d92d846e2" + integrity sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/util-retry@^4.0.7": version "4.0.7" resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.0.7.tgz#3169450193e917da170a87557fcbdfe0faa86779" @@ -1752,6 +2692,15 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" +"@smithy/util-retry@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.1.1.tgz#f4a99d9b0ffb9e4bb119ac5a24e54e54d891e22c" + integrity sha512-jGeybqEZ/LIordPLMh5bnmnoIgsqnp4IEimmUp5c5voZ8yx+5kAlN5+juyr7p+f7AtZTgvhmInQk4Q0UVbrZ0Q== + dependencies: + "@smithy/service-error-classification" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + "@smithy/util-stream@^4.2.4": version "4.2.4" resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.2.4.tgz#fa9f0e2fd5a8a5adbd013066b475ea8f9d4f900f" @@ -1766,11 +2715,18 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" -"@smithy/util-uri-escape@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-2.2.0.tgz#56f5764051a33b67bc93fdd2a869f971b0635406" - integrity sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA== - dependencies: +"@smithy/util-stream@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.3.1.tgz#63cce0f09d99d75142c6dc8fe03e55ac0171de47" + integrity sha512-khKkW/Jqkgh6caxMWbMuox9+YfGlsk9OnHOYCGVEdYQb/XVzcORXHLYUubHmmda0pubEDncofUrPNniS9d+uAA== + dependencies: + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-buffer-from" "^4.1.0" + "@smithy/util-hex-encoding" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" tslib "^2.6.2" "@smithy/util-uri-escape@^3.0.0": @@ -1787,6 +2743,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-uri-escape@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz#ed4a5c498f1da07122ca1e3df4ca3e2c67c6c18a" + integrity sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg== + dependencies: + tslib "^2.6.2" + "@smithy/util-utf8@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-1.1.0.tgz#b791ab1e3f694374edfe22811e39dd8424a1be69" @@ -1795,7 +2758,7 @@ "@smithy/util-buffer-from" "^1.1.0" tslib "^2.5.0" -"@smithy/util-utf8@^2.0.0", "@smithy/util-utf8@^2.3.0": +"@smithy/util-utf8@^2.0.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== @@ -1819,6 +2782,14 @@ "@smithy/util-buffer-from" "^4.0.0" tslib "^2.6.2" +"@smithy/util-utf8@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.1.0.tgz#912c33c1a06913f39daa53da79cb8f7ab740d97b" + integrity sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ== + dependencies: + "@smithy/util-buffer-from" "^4.1.0" + tslib "^2.6.2" + "@smithy/util-waiter@^4.0.7": version "4.0.7" resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.0.7.tgz#c013cf6a5918c21f8b430b4a825dbac132163f4a" @@ -1828,7 +2799,7 @@ "@smithy/types" "^4.3.2" tslib "^2.6.2" -"@teppeis/multimaps@3.0.0": +"@teppeis/multimaps@3.0.0", "@teppeis/multimaps@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@teppeis/multimaps/-/multimaps-3.0.0.tgz#bb9c3f8d569f589e548586fa0bbf423010ddfdc5" integrity sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q== @@ -1873,6 +2844,14 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/node-fetch@^2.6.9": + version "2.6.13" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.13.tgz#e0c9b7b5edbdb1b50ce32c127e85e880872d56ee" + integrity sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw== + dependencies: + "@types/node" "*" + form-data "^4.0.4" + "@types/node@*": version "24.3.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec" @@ -1880,6 +2859,13 @@ dependencies: undici-types "~7.10.0" +"@types/node@^18.19.121": + version "18.19.124" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.124.tgz#6f49e4fab8274910691a900e8a14316cbf3c7a31" + integrity sha512-hY4YWZFLs3ku6D2Gqo3RchTd9VRCcrjqp/I0mmohYeUVA5Y8eCXKJEasHxLAJVZRJuQogfd1GiJ9lgogBgKeuQ== + dependencies: + undici-types "~5.26.4" + "@types/node@^20.1.1": version "20.19.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4" @@ -1887,7 +2873,14 @@ dependencies: undici-types "~6.21.0" -"@types/normalize-package-data@^2.4.0": +"@types/node@^22.0.0": + version "22.18.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.1.tgz#cc85ee6999b2a2928739281d2f56ff410a140c52" + integrity sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw== + dependencies: + undici-types "~6.21.0" + +"@types/normalize-package-data@^2.4.3": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== @@ -1919,25 +2912,24 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.5.tgz#f090ff4bd8d2e5b940ff270ab39fd5ca1834a07e" integrity sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw== -"@types/semver@^7.5.8": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.0.tgz#64c441bdae033b378b6eef7d0c3d77c329b9378e" - integrity sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA== - -"@types/tar@^6.1.13": - version "6.1.13" - resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.13.tgz#9b5801c02175344101b4b91086ab2bbc8e93a9b6" - integrity sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw== +"@types/stream-buffers@^3.0.3": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.7.tgz#0b719fa1bd2ca2cc0908205a440e5e569e1aa21e" + integrity sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw== dependencies: "@types/node" "*" - minipass "^4.0.0" "@types/tough-cookie@*": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== -"@types/uuid@9.0.8", "@types/uuid@^9.0.1": +"@types/uuid@10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + +"@types/uuid@^9.0.1": version "9.0.8" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== @@ -2217,7 +3209,16 @@ aws4@^1.12.0, aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -axios@^1.7.4, axios@^1.8.4: +axios@^1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3" + integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.4" + proxy-from-env "^1.1.0" + +axios@^1.8.4: version "1.11.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.11.0.tgz#c2ec219e35e414c025b2095e8b8280278478fdb6" integrity sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA== @@ -2226,11 +3227,58 @@ axios@^1.7.4, axios@^1.8.4: form-data "^4.0.4" proxy-from-env "^1.1.0" +b4a@^1.6.4: + version "1.7.1" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.7.1.tgz#6fd4ec2fb33ba7a4ff341a2869bbfc88a6e57850" + integrity sha512-ZovbrBV0g6JxK5cGUF1Suby1vLfKjv4RWi8IxoaO/Mon8BDD9I21RxjHFtgQ+kskJqLAVyQZly3uMBui+vhc8Q== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-events@^2.2.0, bare-events@^2.5.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.6.1.tgz#f793b28bdc3dcf147d7cf01f882a6f0b12ccc4a2" + integrity sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g== + +bare-fs@^4.0.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-4.4.0.tgz#61cd6cbfdc7760731da5a14a87ba23f93b87be7f" + integrity sha512-DfdhZD+8fPM80vB28NMIlQIW4FBdkRMNLIa8o5HPSfGlxXek1C34qd98YItQzSl86Kt2IGom9+izFCrNuR7BXw== + dependencies: + bare-events "^2.5.4" + bare-path "^3.0.0" + bare-stream "^2.6.4" + bare-url "^2.2.2" + fast-fifo "^1.3.2" + +bare-os@^3.0.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.6.2.tgz#b3c4f5ad5e322c0fd0f3c29fc97d19009e2796e5" + integrity sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A== + +bare-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" + integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== + dependencies: + bare-os "^3.0.1" + +bare-stream@^2.6.4: + version "2.7.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.7.0.tgz#5b9e7dd0a354d06e82d6460c426728536c35d789" + integrity sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A== + dependencies: + streamx "^2.21.0" + +bare-url@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/bare-url/-/bare-url-2.2.2.tgz#1369d1972bbd7d9b358d0214d3f12abe2328d3e9" + integrity sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA== + dependencies: + bare-path "^3.0.0" + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -2331,6 +3379,11 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelize-ts@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelize-ts/-/camelize-ts-3.0.0.tgz#b9a7b4ff802464dc3d6475637a64a9742ad3db09" + integrity sha512-cgRwKKavoDKLTjO4FQTs3dRBePZp/2Y9Xpud0FhuCOTE86M2cniKN4CCXgRnsyXNMmQMifVHcv6SPaMtTx6ofQ== + capital-case@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" @@ -2373,16 +3426,7 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-table3@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" - integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== - dependencies: - string-width "^4.2.0" - optionalDependencies: - "@colors/colors" "1.5.0" - -cli-table3@^0.6.0: +cli-table3@0.6.5, cli-table3@^0.6.0: version "0.6.5" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== @@ -2391,31 +3435,31 @@ cli-table3@^0.6.0: optionalDependencies: "@colors/colors" "1.5.0" -"cli-testing@github:scality/cli-testing.git#1.2.4": - version "1.2.4" - resolved "git+ssh://git@github.com/scality/cli-testing.git#687c3bdb5d684c5de4082a5dd9f9c00e94ec104c" +"cli-testing@github:scality/cli-testing.git#827cd4683b55b8b7222ed3f5672ee5d826d3685f": + version "1.3.0" + resolved "git+ssh://git@github.com/scality/cli-testing.git#827cd4683b55b8b7222ed3f5672ee5d826d3685f" dependencies: "@aws-crypto/sha256-universal" "^5.2.0" - "@aws-sdk/client-iam" "^3.637.0" - "@aws-sdk/client-s3" "^3.637.0" - "@aws-sdk/client-sts" "^3.637.0" - "@azure/storage-blob" "^12.24.0" - "@azure/storage-queue" "^12.23.0" - "@cucumber/cucumber" "^10.9.0" - "@cucumber/pretty-formatter" "^1.0.1" - "@kubernetes/client-node" "^0.21.0" - "@smithy/signature-v4" "^4.1.0" - "@types/semver" "^7.5.8" - "@types/tar" "^6.1.13" + "@aws-sdk/client-iam" "^3.879.0" + "@aws-sdk/client-s3" "^3.879.0" + "@aws-sdk/client-sts" "^3.879.0" + "@azure/storage-blob" "^12.28.0" + "@azure/storage-queue" "^12.27.0" + "@cucumber/cucumber" "^12.2.0" + "@cucumber/pretty-formatter" "^2.1.0" + "@keycloak/keycloak-admin-client" "^26.3.3" + "@kubernetes/client-node" "^1.3.0" + "@smithy/signature-v4" "^5.1.3" junit-xml "^1.2.0" - node-rdkafka "^3.1.0" - semver "^7.6.3" + node-rdkafka "^3.5.0" + proper-lockfile "^4.1.2" + semver "^7.7.2" tar "^7.4.3" ts-node "^10.9.2" tsconfig-paths "^4.2.0" - typescript "^5.5.4" - vaultclient "git+https://github.com/scality/vaultclient#8.5.0" - werelogs scality/werelogs#8.2.0 + typescript "^5.9.2" + vaultclient "git+https://github.com/scality/vaultclient#8.5.3" + werelogs scality/werelogs#8.2.2 color-convert@^2.0.1: version "2.0.1" @@ -2441,20 +3485,20 @@ commander@11.1.0, commander@^11.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== -commander@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-12.0.0.tgz#b929db6df8546080adfd004ab215ed48cf6f2592" - integrity sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA== +commander@13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" + integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== commander@9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.1.0.tgz#a6b263b2327f2e188c6402c42623327909f2dbec" integrity sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w== -commander@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== +commander@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.0.tgz#f244fc74a92343514e56229f16ef5c5e22ced5e9" + integrity sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA== concat-map@0.0.1: version "0.0.1" @@ -2566,6 +3610,13 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" +end-of-stream@^1.1.0: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== + dependencies: + once "^1.4.0" + entities@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" @@ -2581,13 +3632,6 @@ err-code@^2.0.2: resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - error-stack-parser@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" @@ -2761,6 +3805,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" @@ -2827,13 +3876,10 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" +find-up-simple@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.1.tgz#18fb90ad49e45252c4d7fca56baade04fa3fca1e" + integrity sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ== find-up@^5.0.0: version "5.0.0" @@ -2868,7 +3914,7 @@ for-each@^0.3.5: dependencies: is-callable "^1.2.7" -foreground-child@^3.1.0: +foreground-child@^3.1.0, foreground-child@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== @@ -2893,7 +3939,7 @@ form-data@^2.5.5: mime-types "^2.1.35" safe-buffer "^5.2.1" -form-data@^4.0.4: +form-data@^4.0.0, form-data@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== @@ -2989,6 +4035,18 @@ glob@^10.2.2, glob@^10.3.10: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +glob@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" + integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== + dependencies: + foreground-child "^3.3.1" + jackspeak "^4.1.1" + minimatch "^10.0.3" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + global-dirs@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" @@ -3067,10 +4125,17 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + +hpagent@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== http-cache-semantics@^4.1.1: version "4.2.0" @@ -3150,6 +4215,11 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +index-to-position@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-1.1.0.tgz#2e50bd54c8040bdd6d9b3d95ec2a8fedf86b4d44" + integrity sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg== + inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" @@ -3173,23 +4243,11 @@ is-arguments@^1.0.4: call-bound "^1.0.2" has-tostringtag "^1.0.2" -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.16.0: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" - integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== - dependencies: - hasown "^2.0.2" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3304,11 +4362,23 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" + integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + jose@^4.15.9: version "4.15.9" resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== +jose@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.1.0.tgz#96285365689d16f2845a353964d2284bf19f464c" + integrity sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3326,16 +4396,16 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +jsep@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsep/-/jsep-1.4.0.tgz#19feccbfa51d8a79f72480b4b8e40ce2e17152f0" + integrity sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw== + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -3361,6 +4431,15 @@ json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonpath-plus@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz#59e22e4fa2298c68dfcd70659bb47f0cad525238" + integrity sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA== + dependencies: + "@jsep-plugin/assignment" "^1.3.0" + "@jsep-plugin/regex" "^1.0.4" + jsep "^1.4.0" + jsonpath-plus@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-8.1.0.tgz#68c92281215672d1d6c785b3c1bdc8acc097ba3f" @@ -3410,11 +4489,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - linkify-it@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" @@ -3422,13 +4496,6 @@ linkify-it@^4.0.1: dependencies: uc.micro "^1.0.1" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -3446,6 +4513,11 @@ lodash.mergewith@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -3458,6 +4530,11 @@ lru-cache@^10.0.1, lru-cache@^10.2.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.2.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.1.tgz#d426ac471521729c6c1acda5f7a633eadaa28db2" + integrity sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3465,10 +4542,15 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -luxon@3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.2.1.tgz#14f1af209188ad61212578ea7e3d518d18cee45f" - integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg== +luxon@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.1.tgz#9bd09aa84a56afb00c57ea78a8ec5bd16eb24ec0" + integrity sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg== + +luxon@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.2.tgz#d697e48f478553cca187a0f8436aff468e3ba0ba" + integrity sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew== make-error@^1.1.1: version "1.3.6" @@ -3557,6 +4639,13 @@ mime@^3.0.0: resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3622,11 +4711,6 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minipass@^4.0.0: - version "4.2.8" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" - integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== - minipass@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" @@ -3657,12 +4741,7 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^2.1.5: - version "2.1.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" - integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== - -mkdirp@^3.0.1: +mkdirp@^3.0.0, mkdirp@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== @@ -3704,6 +4783,13 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-gyp@^10.2.0: version "10.3.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.3.1.tgz#1dd1a1a1c6c5c59da1a76aea06a062786b2c8a1a" @@ -3720,7 +4806,7 @@ node-gyp@^10.2.0: tar "^6.2.1" which "^4.0.0" -node-rdkafka@^3.1.0: +node-rdkafka@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/node-rdkafka/-/node-rdkafka-3.5.0.tgz#ace9b750036ae8bdc2fc191b746edbb72a2f4232" integrity sha512-KaMJ4lEMJJWrVKKGW1WvWmXbiALJHvTLLBjjJsjWbF7vJyaNDuJW4aPmkbO1yJxS4uKwV40aWTyFEj8iC7vJ0Q== @@ -3735,21 +4821,25 @@ nopt@^7.0.0: dependencies: abbrev "^2.0.0" -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== +normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +oauth4webapi@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-3.8.1.tgz#a6e205570c09e33aa656982c85e78841a57a6fec" + integrity sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA== + object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -3795,6 +4885,13 @@ oidc-token-hash@^5.0.3: resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz#d35e31ca26d3a26678f5e9bda100b095ab58011f" integrity sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g== +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + openid-client@^5.3.0: version "5.7.1" resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.7.1.tgz#34cace862a3e6472ed7d0a8616ef73b7fb85a9c3" @@ -3805,6 +4902,14 @@ openid-client@^5.3.0: object-hash "^2.2.0" oidc-token-hash "^5.0.3" +openid-client@^6.1.3: + version "6.8.0" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-6.8.0.tgz#ae60540fc1aaf595ee9100004ebda722e1d80e60" + integrity sha512-oG1d1nAVhIIE+JSjLS+7E9wY1QOJpZltkzlJdbZ7kEn7Hp3hqur2TEeQ8gLOHoHkhbRAGZJKoOnEQcLOQJuIyg== + dependencies: + jose "^6.1.0" + oauth4webapi "^3.8.1" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -3817,13 +4922,6 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -3831,13 +4929,6 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -3852,11 +4943,6 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" @@ -3876,15 +4962,14 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== +parse-json@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.3.0.tgz#88a195a2157025139a2317a4f2f9252b61304ed5" + integrity sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ== dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" + "@babel/code-frame" "^7.26.2" + index-to-position "^1.1.0" + type-fest "^4.39.1" path-exists@^4.0.0: version "4.0.0" @@ -3896,11 +4981,6 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - path-scurry@^1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" @@ -3909,6 +4989,14 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -3985,6 +5073,14 @@ psl@^1.1.28: dependencies: punycode "^2.3.1" +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -4007,29 +5103,30 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== +read-package-up@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/read-package-up/-/read-package-up-11.0.0.tgz#71fb879fdaac0e16891e6e666df22de24a48d5ba" + integrity sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ== dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" + find-up-simple "^1.0.0" + read-pkg "^9.0.0" + type-fest "^4.6.0" -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== +read-pkg@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-9.0.1.tgz#b1b81fb15104f5dbb121b6bbdee9bbc9739f569b" + integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" + "@types/normalize-package-data" "^2.4.3" + normalize-package-data "^6.0.0" + parse-json "^8.0.0" + type-fest "^4.6.0" + unicorn-magic "^0.1.0" -reflect-metadata@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74" - integrity sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw== +reflect-metadata@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== regexp-match-indices@1.0.2: version "1.0.2" @@ -4079,27 +5176,6 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41" - integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ== - dependencies: - resolve-from "^5.0.0" - -resolve@^1.10.0: - version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" - integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== - dependencies: - is-core-module "^2.16.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -4151,32 +5227,20 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== -"scubaclient@git+https://github.com/scality/scubaclient#^1.1.2": - version "1.1.2" - resolved "git+https://github.com/scality/scubaclient#8a5738b474495aa00fc0fd9a5e9278054c5dc83c" +"scubaclient@git+https://github.com/scality/scubaclient#^1.1.3": + version "1.1.3" + resolved "git+https://github.com/scality/scubaclient#0263d4f38b66bf47ed76044b79785b87d9227b99" dependencies: "@aws-crypto/sha256-js" "^5.2.0" - "@smithy/signature-v4" "^2.1.1" - axios "^1.7.4" + "@smithy/signature-v4" "^5.2.1" + axios "^1.12.0" seed-random@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" integrity sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ== -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@7.5.3: - version "7.5.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" - integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.5, semver@^7.6.0, semver@^7.6.3: +semver@7.7.2, semver@^7.3.5, semver@^7.6.0, semver@^7.7.2: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== @@ -4260,7 +5324,7 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -socks-proxy-agent@^8.0.3: +socks-proxy-agent@^8.0.3, socks-proxy-agent@^8.0.4: version "8.0.5" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee" integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== @@ -4348,6 +5412,16 @@ stream-buffers@^3.0.2: resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.3.tgz#9fc6ae267d9c4df1190a781e011634cac58af3cd" integrity sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw== +streamx@^2.15.0, streamx@^2.21.0: + version "2.22.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.1.tgz#c97cbb0ce18da4f4db5a971dc9ab68ff5dc7f5a5" + integrity sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA== + dependencies: + fast-fifo "^1.3.2" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + string-argv@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -4387,7 +5461,7 @@ string-width@^5.0.1, string-width@^5.1.2: dependencies: ansi-regex "^5.0.1" -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -4430,10 +5504,25 @@ supports-color@^8.1.1: dependencies: has-flag "^4.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +tar-fs@^3.0.8: + version "3.1.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.1.0.tgz#4675e2254d81410e609d91581a762608de999d25" + integrity sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w== + dependencies: + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^4.0.1" + bare-path "^3.0.0" + +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" tar@^6.1.11, tar@^6.2.1: version "6.2.1" @@ -4459,6 +5548,13 @@ tar@^7.0.0, tar@^7.4.3: mkdirp "^3.0.1" yallist "^5.0.0" +text-decoder@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + dependencies: + b4a "^1.6.4" + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -4478,11 +5574,6 @@ tiny-case@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q== -tmp@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4503,6 +5594,11 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-api-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" @@ -4565,22 +5661,12 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-fest@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== -type-fest@^4.8.3: +type-fest@^4.39.1, type-fest@^4.41.0, type-fest@^4.6.0: version "4.41.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== @@ -4595,7 +5681,7 @@ typescript-eslint@^8.4.0: "@typescript-eslint/typescript-estree" "8.40.0" "@typescript-eslint/utils" "8.40.0" -typescript@^5.5.4: +typescript@^5.9.2: version "5.9.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== @@ -4605,6 +5691,11 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" @@ -4615,6 +5706,11 @@ undici-types@~7.10.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" @@ -4643,6 +5739,16 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-join@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-5.0.0.tgz#c2f1e5cbd95fa91082a93b58a1f42fecb4bdbcf1" + integrity sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA== + +url-template@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/url-template/-/url-template-3.1.1.tgz#c220d5f3f793d28b0de341002112879cc8a43905" + integrity sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA== + util-arity@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/util-arity/-/util-arity-1.1.0.tgz#59d01af1fdb3fede0ac4e632b0ab5f6ce97c9330" @@ -4659,22 +5765,37 @@ util@^0.12.5: is-typed-array "^1.1.3" which-typed-array "^1.1.2" -uuid@9.0.1, uuid@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +uuid@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + +uuid@11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.5.tgz#07b46bdfa6310c92c3fb3953a8720f170427fc62" + integrity sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA== + +uuid@11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -4682,9 +5803,9 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -"vaultclient@git+https://github.com/scality/vaultclient#8.5.0": - version "8.5.0" - resolved "git+https://github.com/scality/vaultclient#c20d56eaed2cef9579768389f7e2f2d4ec8b6f85" +"vaultclient@git+https://github.com/scality/vaultclient#8.5.3": + version "8.5.3" + resolved "git+https://github.com/scality/vaultclient#24e33cfe2a98cc4545ddc615371bfd8d2f2e98d1" dependencies: "@aws-crypto/sha256-universal" "^5.2.0" "@smithy/signature-v4" "^4.1.0" @@ -4702,6 +5823,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + werelogs@scality/werelogs#8.2.0: version "8.2.0" resolved "https://codeload.github.com/scality/werelogs/tar.gz/7bf334cea94002d118f27d7ec1c7a5af74b51b8d" @@ -4716,6 +5842,14 @@ werelogs@scality/werelogs#8.2.2: fast-safe-stringify "^2.1.1" safe-json-stringify "^1.2.0" +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which-typed-array@^1.1.16, which-typed-array@^1.1.2: version "1.1.19" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" @@ -4766,7 +5900,12 @@ wrap-ansi@^8.1.0: string-width "^5.0.1" strip-ansi "^7.0.1" -ws@^8.11.0: +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.11.0, ws@^8.18.2: version "8.18.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== @@ -4819,10 +5958,10 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -yup@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/yup/-/yup-1.2.0.tgz#9e51af0c63bdfc9be0fdc6c10aa0710899d8aff6" - integrity sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ== +yup@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/yup/-/yup-1.7.0.tgz#5d2feeccc1725c39bfed6ec677cc0622527dafaf" + integrity sha512-VJce62dBd+JQvoc+fCVq+KZfPHr+hXaxCcVgotfwWvlR0Ja3ffYKaJBT8rptPOSKOGJDCUnW2C2JWpud7aRP6Q== dependencies: property-expr "^2.0.5" tiny-case "^1.0.3" From c1813a2111c05204a7676b0494418629a89057db Mon Sep 17 00:00:00 2001 From: williamlardier Date: Fri, 12 Sep 2025 16:40:37 +0200 Subject: [PATCH 05/14] Prepare unified setup --- tests/@setup/.dockerignore | 14 + tests/@setup/.gitignore | 0 tests/@setup/Dockerfile | 63 + tests/@setup/README.md | 263 +++ tests/@setup/eslint.config.mjs | 28 + tests/@setup/package.json | 51 + tests/@setup/src/buckets.ts | 209 ++ tests/@setup/src/cli.ts | 216 +++ tests/@setup/src/dns.ts | 198 ++ tests/@setup/src/keycloak.ts | 320 +++ tests/@setup/src/locations.ts | 171 ++ tests/@setup/src/mocks.ts | 237 +++ tests/@setup/src/rbac.ts | 119 ++ tests/@setup/src/utils/k8s.ts | 193 ++ tests/@setup/src/utils/logger.ts | 9 + tests/@setup/tsconfig.json | 25 + tests/@setup/yarn.lock | 3103 ++++++++++++++++++++++++++++++ 17 files changed, 5219 insertions(+) create mode 100644 tests/@setup/.dockerignore create mode 100644 tests/@setup/.gitignore create mode 100644 tests/@setup/Dockerfile create mode 100644 tests/@setup/README.md create mode 100644 tests/@setup/eslint.config.mjs create mode 100644 tests/@setup/package.json create mode 100644 tests/@setup/src/buckets.ts create mode 100644 tests/@setup/src/cli.ts create mode 100644 tests/@setup/src/dns.ts create mode 100644 tests/@setup/src/keycloak.ts create mode 100644 tests/@setup/src/locations.ts create mode 100644 tests/@setup/src/mocks.ts create mode 100644 tests/@setup/src/rbac.ts create mode 100644 tests/@setup/src/utils/k8s.ts create mode 100644 tests/@setup/src/utils/logger.ts create mode 100644 tests/@setup/tsconfig.json create mode 100644 tests/@setup/yarn.lock diff --git a/tests/@setup/.dockerignore b/tests/@setup/.dockerignore new file mode 100644 index 0000000000..e5d1c8cb13 --- /dev/null +++ b/tests/@setup/.dockerignore @@ -0,0 +1,14 @@ +node_modules +npm-debug.log +.git +.gitignore +README.md +.env +.nyc_output +coverage +.nyc_output +.vscode +.idea +dist +*.log +.DS_Store \ No newline at end of file diff --git a/tests/@setup/.gitignore b/tests/@setup/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/@setup/Dockerfile b/tests/@setup/Dockerfile new file mode 100644 index 0000000000..cd18bdeb2a --- /dev/null +++ b/tests/@setup/Dockerfile @@ -0,0 +1,63 @@ +# Multi-stage build for smaller final image +FROM node:18-alpine AS builder + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package.json yarn.lock tsconfig.json ./ + +# Install dependencies +RUN yarn install --production --frozen-lockfile + +# Copy source code +COPY src/ ./src/ + +# Build the TypeScript code +RUN npm run build + +# Final runtime image +FROM node:18-alpine + +# Install kubectl for Kubernetes API access +RUN apk add --no-cache curl && \ + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \ + chmod +x kubectl && \ + mv kubectl /usr/local/bin/ + +# Create non-root user +RUN addgroup -g 1001 -S zenko && \ + adduser -S zenko -u 1001 -G zenko + +# Set working directory +WORKDIR /app + +# Copy built application from builder stage +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/package.json ./ + +# Create directory for kubeconfig mount +RUN mkdir -p /home/zenko/.kube && \ + chown -R zenko:zenko /home/zenko && \ + chown -R zenko:zenko /app + +# Switch to non-root user +USER zenko + +# Set environment variables +ENV NODE_ENV=production +ENV LOG_LEVEL=info + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD node -e "console.log('Health check passed')" || exit 1 + +# Default command +ENTRYPOINT ["node", "dist/cli.js"] +CMD ["--help"] + +# Labels for metadata +LABEL maintainer="Scality " +LABEL description="Unified CLI tool for Zenko test environment setup" +LABEL version="1.0.0" \ No newline at end of file diff --git a/tests/@setup/README.md b/tests/@setup/README.md new file mode 100644 index 0000000000..9dbce0f351 --- /dev/null +++ b/tests/@setup/README.md @@ -0,0 +1,263 @@ +# Zenko Test Setup CLI + +Unified CLI tool for Zenko test environment setup, consolidating all scattered setup scripts into a single TypeScript-based containerized solution. + +## Overview + +This tool replaces 22+ setup scripts scattered across Bash, Python, and TypeScript by providing a single, consistent interface for setting up Zenko test environments. + +## Features + +- **Mock Services**: AWS S3 (CloudServer) and Azure (Azurite) mock deployments +- **Test Buckets**: Automated creation across AWS, Azure, and Ring providers +- **Storage Locations**: Management API configuration for all storage backends +- **Keycloak Setup**: Realm, users, and role configuration +- **DNS Configuration**: CoreDNS rewrite rules for test domains +- **RBAC Permissions**: Service account cluster-admin permissions + +## Installation + +### Container Usage (Recommended) + +```bash +docker run --rm -v ~/.kube:/root/.kube \ + -e NAMESPACE=default \ + -e SUBDOMAIN=zenko.local \ + -e INSTANCE_ID=xyz123 \ + ghcr.io/scality/zenko-test-setup:latest \ + all --verbose +``` + +### Local Development + +```bash +# Clone and install +yarn install + +# Build +yarn build + +# Run locally +yarn dev -- all --namespace=default --subdomain=zenko.local +``` + +## Usage + +### Complete Setup + +Run all setup tasks: +```bash +zenko-setup all --namespace=my-namespace --subdomain=test.local +``` + +Skip specific components: +```bash +zenko-setup all --skip-mocks --skip-keycloak +``` + +### Individual Components + +Setup specific components: +```bash +# Mock services only +zenko-setup mocks --aws-only + +# Buckets for specific provider +zenko-setup buckets --provider=aws + +# Storage locations +zenko-setup locations + +# Keycloak realm and users +zenko-setup keycloak + +# DNS configuration +zenko-setup dns + +# RBAC permissions +zenko-setup rbac +``` + +### Options + +Global options available for all commands: + +- `--namespace `: Kubernetes namespace (default: default) +- `--subdomain `: DNS subdomain (default: zenko.local) +- `--instance-id `: Zenko instance ID for role assignments +- `--kubeconfig `: Path to kubeconfig file +- `--dry-run`: Show what would be done without executing +- `--verbose`: Enable verbose logging + +## Environment Variables + +Configure via environment variables: + +```bash +export NAMESPACE=my-namespace +export SUBDOMAIN=test.local +export INSTANCE_ID=abc123 +export KUBECONFIG=/path/to/kubeconfig +export LOG_LEVEL=debug +``` + +## Container Environment + +The container requires: + +1. **Kubernetes Access**: Mount kubeconfig or use in-cluster config +2. **Network Access**: Ability to reach Kubernetes API and services + +Example with kubeconfig mount: +```bash +docker run --rm \ + -v ~/.kube:/root/.kube:ro \ + -e NAMESPACE=zenko-test \ + -e SUBDOMAIN=test.local \ + ghcr.io/scality/zenko-test-setup:latest \ + all +``` + +Example with in-cluster config (when running in Kubernetes): +```yaml +apiVersion: v1 +kind: Pod +spec: + containers: + - name: setup + image: ghcr.io/scality/zenko-test-setup:latest + args: ["all", "--namespace=zenko-test"] + env: + - name: NAMESPACE + value: "zenko-test" + serviceAccountName: zenko-setup # with cluster-admin permissions +``` + +## Integration Examples + +### CTST Integration + +Replace existing setup logic: +```typescript +// Before: Complex setup in BeforeAll +beforeAll(async () => { + // Call container instead of individual scripts + await exec('docker run --rm -v ~/.kube:/root/.kube ghcr.io/scality/zenko-test-setup:latest all'); + + // Keep only info extraction + await extractInstanceInfo(); + await extractCredentials(); +}); +``` + +### GitHub Workflows + +Replace multiple script calls: +```yaml +# Before: Multiple script executions +- name: Setup Mocks + run: .github/scripts/end2end/install-mocks.sh +- name: Setup Keycloak + run: .github/scripts/end2end/keycloak-helper.sh + +# After: Single container call +- name: Setup Test Environment + run: | + docker run --rm \ + -v ${{ env.KUBECONFIG }}:/root/.kube/config \ + -e NAMESPACE=${{ env.NAMESPACE }} \ + ghcr.io/scality/zenko-test-setup:latest \ + all --verbose +``` + +### Node.js Test Integration + +```bash +# Replace Python scripts +# Before: python tests/zenko_tests/create_buckets.py +# After: +docker run --rm -v ~/.kube:/root/.kube ghcr.io/scality/zenko-test-setup:latest buckets +``` + +## Development + +### Project Structure + +``` +src/ +├── cli.ts # Main CLI interface +├── mocks.ts # AWS/Azure mock deployment +├── buckets.ts # Bucket creation (all providers) +├── locations.ts # Storage locations via Management API +├── keycloak.ts # Realm/users/roles +├── dns.ts # CoreDNS configuration +├── rbac.ts # Service account permissions +└── utils/ + ├── logger.ts # Logging utilities + └── k8s.ts # Kubernetes client wrapper +``` + +### Building + +```bash +# Install dependencies +yarn install + +# Build TypeScript +yarn build + +# Run tests +yarn test + +# Lint code +yarn lint +``` + +### Container Build + +```bash +# Build container image +docker build -t zenko-test-setup:latest . + +# Test container +docker run --rm zenko-test-setup:latest --help +``` + +## Troubleshooting + +### Common Issues + +1. **Kubeconfig not found**: Ensure kubeconfig is mounted at `/root/.kube/config` +2. **Permission denied**: Service account needs cluster-admin permissions +3. **DNS changes not applied**: CoreDNS pods may need manual restart +4. **Management API unavailable**: Check Zenko deployment status + +### Debug Mode + +Enable verbose logging: +```bash +zenko-setup all --verbose +``` + +Check container logs: +```bash +docker logs +``` + +### Dry Run + +Test what would be executed: +```bash +zenko-setup all --dry-run --verbose +``` + +## Contributing + +1. Follow TypeScript best practices +2. Add tests for new functionality +3. Update documentation +4. Ensure container builds successfully + +## License + +Apache License 2.0 \ No newline at end of file diff --git a/tests/@setup/eslint.config.mjs b/tests/@setup/eslint.config.mjs new file mode 100644 index 0000000000..517b7bbe41 --- /dev/null +++ b/tests/@setup/eslint.config.mjs @@ -0,0 +1,28 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import js from '@eslint/js'; +import { FlatCompat } from '@eslint/eslintrc'; +import tseslint from 'typescript-eslint'; +import { includeIgnoreFile } from '@eslint/compat'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const gitignorePath = path.resolve(__dirname, '.gitignore'); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, +}); + +export default tseslint.config( + ...compat.extends('scality'), + ...tseslint.configs.recommended, + includeIgnoreFile(gitignorePath), + { + rules: { + // CucumberJS steps start with an uppercase + 'new-cap': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, + }, +); diff --git a/tests/@setup/package.json b/tests/@setup/package.json new file mode 100644 index 0000000000..37486c9b0b --- /dev/null +++ b/tests/@setup/package.json @@ -0,0 +1,51 @@ +{ + "name": "zenko-test-setup", + "version": "1.0.0", + "description": "Unified CLI tool for Zenko test environment setup", + "main": "dist/cli.js", + "bin": { + "zenko-setup": "dist/cli.js" + }, + "scripts": { + "build": "tsc", + "dev": "ts-node src/cli.ts", + "start": "node dist/cli.js", + "lint": "eslint src/**/*.ts", + "clean": "rm -rf dist" + }, + "keywords": [ + "zenko", + "setup", + "testing", + "kubernetes", + "cli" + ], + "author": "Scality", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-iam": "^3.400.0", + "@aws-sdk/client-s3": "^3.400.0", + "@azure/storage-blob": "^12.15.0", + "@azure/storage-queue": "^12.15.0", + "@kubernetes/client-node": "^0.20.0", + "axios": "^1.5.0", + "commander": "^11.0.0", + "dotenv": "^16.3.1", + "werelogs": "scality/werelogs#8.2.2", + "yaml": "^2.3.2" + }, + "devDependencies": { + "@eslint/compat": "^1.3.2", + "@types/node": "^20.5.0", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "@typescript-eslint/parser": "^6.4.1", + "eslint": "^9.35.0", + "eslint-config-scality": "scality/Guidelines#8.3.0", + "ts-node": "^10.9.1", + "typescript": "^5.2.2", + "typescript-eslint": "^8.43.0" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/tests/@setup/src/buckets.ts b/tests/@setup/src/buckets.ts new file mode 100644 index 0000000000..176bf97fa1 --- /dev/null +++ b/tests/@setup/src/buckets.ts @@ -0,0 +1,209 @@ +import { S3Client, CreateBucketCommand, PutBucketVersioningCommand, PutObjectCommand } from '@aws-sdk/client-s3'; +import { BlobServiceClient, StorageSharedKeyCredential as BlobStorageSharedKeyCredential } from '@azure/storage-blob'; +import { QueueServiceClient, StorageSharedKeyCredential } from '@azure/storage-queue'; +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; + +export interface BucketsOptions { + namespace: string; + provider?: 'aws' | 'azure' | 'ring'; + dryRun?: boolean; +} + +export async function setupBuckets(options: BucketsOptions): Promise { + const k8s = new KubernetesClient(); + + if (!options.provider || options.provider === 'aws') { + await setupAWSBuckets(k8s, options); + } + + if (!options.provider || options.provider === 'azure') { + await setupAzureBuckets(k8s, options); + } + + if (!options.provider || options.provider === 'ring') { + await setupRingBuckets(k8s, options); + } +} + +async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions): Promise { + logger.info('Creating AWS test buckets'); + + // Get AWS credentials from mock service + const awsSecret = await k8s.coreApi.readNamespacedSecret('aws-mock-credentials', options.namespace); + const awsConfig = { + credentials: { + accessKeyId: Buffer.from(awsSecret.body.data!['aws-access-key-id'], 'base64').toString(), + secretAccessKey: Buffer.from(awsSecret.body.data!['aws-secret-access-key'], 'base64').toString() + }, + region: Buffer.from(awsSecret.body.data!['aws-region'], 'base64').toString(), + endpoint: Buffer.from(awsSecret.body.data!['aws-endpoint'], 'base64').toString(), + forcePathStyle: true + }; + + const s3Client = new S3Client(awsConfig); + + // Standard test buckets + const buckets = [ + 'ci-zenko-aws-source-bucket', + 'ci-zenko-aws-target-bucket', + 'ci-zenko-aws-versioned-bucket', + 'ci-zenko-aws-lifecycle-bucket', + 'ci-zenko-aws-replication-bucket', + 'ci-zenko-aws-notification-bucket' + ]; + + for (const bucketName of buckets) { + try { + await s3Client.send(new CreateBucketCommand({ Bucket: bucketName })); + logger.debug(`Created bucket: ${bucketName}`); + + // Enable versioning on versioned and replication buckets + if (bucketName.includes('versioned') || bucketName.includes('replication')) { + await s3Client.send(new PutBucketVersioningCommand({ + Bucket: bucketName, + VersioningConfiguration: { + Status: 'Enabled' + } + })); + logger.debug(`Enabled versioning on: ${bucketName}`); + } + + // Add test objects to source bucket + if (bucketName.includes('source')) { + const testObjects = [ + { Key: 'test-object-1.txt', Body: 'Test content 1' }, + { Key: 'test-object-2.txt', Body: 'Test content 2' }, + { Key: 'folder/nested-object.txt', Body: 'Nested content' } + ]; + + for (const obj of testObjects) { + await s3Client.send(new PutObjectCommand({ + Bucket: bucketName, + Key: obj.Key, + Body: obj.Body + })); + } + logger.debug(`Added test objects to: ${bucketName}`); + } + + } catch (error: any) { + if (error.name === 'BucketAlreadyOwnedByYou' || error.name === 'BucketAlreadyExists') { + logger.debug(`Bucket ${bucketName} already exists`); + } else { + logger.error(`Failed to create bucket ${bucketName}: ${error.message}`); + throw error; + } + } + } + + logger.info(`Created ${buckets.length} AWS test buckets`); +} + +async function setupAzureBuckets(k8s: KubernetesClient, options: BucketsOptions): Promise { + logger.info('Creating Azure test containers and queues'); + + // Get Azure credentials from mock service + const azureSecret = await k8s.coreApi.readNamespacedSecret('azure-mock-credentials', options.namespace); + const accountName = Buffer.from(azureSecret.body.data!['account-name'], 'base64').toString(); + const accountKey = Buffer.from(azureSecret.body.data!['account-key'], 'base64').toString(); + const blobEndpoint = Buffer.from(azureSecret.body.data!['blob-endpoint'], 'base64').toString(); + const queueEndpoint = Buffer.from(azureSecret.body.data!['queue-endpoint'], 'base64').toString(); + + const blobSharedKeyCredential = new BlobStorageSharedKeyCredential(accountName, accountKey); + const queueSharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey); + + // Setup blob containers + const blobServiceClient = new BlobServiceClient(blobEndpoint, blobSharedKeyCredential); + + const containers = [ + 'ci-zenko-azure-source-container', + 'ci-zenko-azure-target-container', + 'ci-zenko-azure-archive-container', + 'ci-zenko-azure-lifecycle-container' + ]; + + for (const containerName of containers) { + try { + const containerClient = blobServiceClient.getContainerClient(containerName); + await containerClient.create(); + logger.debug(`Created container: ${containerName}`); + + // Add test blobs to source container + if (containerName.includes('source')) { + const testBlobs = [ + { name: 'test-blob-1.txt', content: 'Azure test content 1' }, + { name: 'test-blob-2.txt', content: 'Azure test content 2' }, + { name: 'folder/nested-blob.txt', content: 'Azure nested content' } + ]; + + for (const blob of testBlobs) { + const blockBlobClient = containerClient.getBlockBlobClient(blob.name); + await blockBlobClient.upload(blob.content, blob.content.length); + } + logger.debug(`Added test blobs to: ${containerName}`); + } + + } catch (error: any) { + if (error.statusCode === 409) { + logger.debug(`Container ${containerName} already exists`); + } else { + logger.error(`Failed to create container ${containerName}: ${error.message}`); + throw error; + } + } + } + + // Setup queues for notification testing + const queueServiceClient = new QueueServiceClient(queueEndpoint, queueSharedKeyCredential); + + const queues = [ + 'ci-zenko-azure-notifications-queue', + 'ci-zenko-azure-status-queue' + ]; + + for (const queueName of queues) { + try { + const queueClient = queueServiceClient.getQueueClient(queueName); + await queueClient.create(); + logger.debug(`Created queue: ${queueName}`); + } catch (error: any) { + if (error.statusCode === 409) { + logger.debug(`Queue ${queueName} already exists`); + } else { + logger.error(`Failed to create queue ${queueName}: ${error.message}`); + throw error; + } + } + } + + logger.info(`Created ${containers.length} Azure containers and ${queues.length} queues`); +} + +async function setupRingBuckets(k8s: KubernetesClient, options: BucketsOptions): Promise { + logger.info('Creating Ring/S3C test buckets'); + + // Ring buckets are typically created through S3 API against Ring storage + // This would require Ring/S3C credentials and endpoint configuration + // For now, create a placeholder configuration + + const ringConfig = { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { + name: 'ring-test-buckets', + namespace: options.namespace + }, + data: { + 'buckets.json': JSON.stringify([ + 'ci-zenko-ring-source-bucket', + 'ci-zenko-ring-target-bucket', + 'ci-zenko-ring-archive-bucket' + ], null, 2) + } + }; + + await k8s.applyManifest(ringConfig, options.namespace); + + logger.info('Ring bucket configuration created (actual buckets require Ring/S3C setup)'); +} \ No newline at end of file diff --git a/tests/@setup/src/cli.ts b/tests/@setup/src/cli.ts new file mode 100644 index 0000000000..2668e111c4 --- /dev/null +++ b/tests/@setup/src/cli.ts @@ -0,0 +1,216 @@ +#!/usr/bin/env node + +import { Command } from 'commander'; +import { setupMocks } from './mocks'; +import { setupBuckets } from './buckets'; +import { setupLocations } from './locations'; +import { setupKeycloak } from './keycloak'; +import { setupDNS } from './dns'; +import { setupRBAC } from './rbac'; +import { logger } from './utils/logger'; + +const program = new Command(); + +program + .name('zenko-setup') + .description('Unified CLI tool for Zenko test environment setup') + .version('1.0.0'); + +program + .option('-n, --namespace ', 'Kubernetes namespace', 'default') + .option('-d, --subdomain ', 'DNS subdomain', 'zenko.local') + .option('-i, --instance-id ', 'Zenko instance ID') + .option('-k, --kubeconfig ', 'Path to kubeconfig file') + .option('--dry-run', 'Show what would be done without executing') + .option('-v, --verbose', 'Enable verbose logging'); + +program + .command('all') + .description('Run all setup tasks') + .option('--skip-mocks', 'Skip mock services setup') + .option('--skip-buckets', 'Skip bucket creation') + .option('--skip-locations', 'Skip storage locations setup') + .option('--skip-keycloak', 'Skip Keycloak realm/users setup') + .option('--skip-dns', 'Skip DNS configuration') + .option('--skip-rbac', 'Skip RBAC permissions setup') + .action(async (options) => { + const globalOptions = program.opts(); + await runSetup({ + ...globalOptions, + mocks: !options.skipMocks, + buckets: !options.skipBuckets, + locations: !options.skipLocations, + keycloak: !options.skipKeycloak, + dns: !options.skipDns, + rbac: !options.skipRbac, + }); + }); + +program + .command('mocks') + .description('Setup AWS and Azure mock services') + .option('--aws-only', 'Setup only AWS mocks') + .option('--azure-only', 'Setup only Azure mocks') + .action(async (options) => { + const globalOptions = program.opts(); + await setupMocks({ + namespace: globalOptions.namespace || 'default', + subdomain: globalOptions.subdomain || 'zenko.local', + instanceId: globalOptions.instanceId, + awsOnly: options.awsOnly, + azureOnly: options.azureOnly, + dryRun: globalOptions.dryRun, + }); + }); + +program + .command('buckets') + .description('Create test buckets across all providers') + .option('--provider ', 'Specific provider (aws|azure|ring)') + .action(async (options) => { + const globalOptions = program.opts(); + await setupBuckets({ + namespace: globalOptions.namespace || 'default', + provider: options.provider, + dryRun: globalOptions.dryRun, + }); + }); + +program + .command('locations') + .description('Setup storage locations via Management API') + .action(async () => { + const globalOptions = program.opts(); + await setupLocations({ + namespace: globalOptions.namespace || 'default', + instanceId: globalOptions.instanceId, + dryRun: globalOptions.dryRun, + }); + }); + +program + .command('keycloak') + .description('Setup Keycloak realm, users, and roles') + .action(async () => { + const globalOptions = program.opts(); + await setupKeycloak({ + namespace: globalOptions.namespace || 'default', + instanceId: globalOptions.instanceId, + dryRun: globalOptions.dryRun, + }); + }); + +program + .command('dns') + .description('Configure CoreDNS for test domains') + .action(async () => { + const globalOptions = program.opts(); + await setupDNS({ + namespace: globalOptions.namespace || 'default', + subdomain: globalOptions.subdomain || 'zenko.local', + dryRun: globalOptions.dryRun, + }); + }); + +program + .command('rbac') + .description('Setup RBAC permissions for service accounts') + .action(async () => { + const globalOptions = program.opts(); + await setupRBAC({ + namespace: globalOptions.namespace || 'default', + dryRun: globalOptions.dryRun, + }); + }); + +async function runSetup(options: any) { + try { + logger.info('🚀 Starting Zenko test environment setup'); + + const tasks = []; + + if (options.rbac) { + tasks.push({ + name: 'RBAC', fn: () => setupRBAC({ + namespace: options.namespace || 'default', + dryRun: options.dryRun, + }) + }); + } + + if (options.dns) { + tasks.push({ + name: 'DNS', fn: () => setupDNS({ + namespace: options.namespace || 'default', + subdomain: options.subdomain || 'zenko.local', + dryRun: options.dryRun, + }) + }); + } + + if (options.mocks) { + tasks.push({ + name: 'Mock Services', fn: () => setupMocks({ + namespace: options.namespace || 'default', + subdomain: options.subdomain || 'zenko.local', + instanceId: options.instanceId, + dryRun: options.dryRun, + }) + }); + } + + if (options.locations) { + tasks.push({ + name: 'Storage Locations', fn: () => setupLocations({ + namespace: options.namespace || 'default', + instanceId: options.instanceId, + dryRun: options.dryRun, + }) + }); + } + + if (options.buckets) { + tasks.push({ + name: 'Test Buckets', fn: () => setupBuckets({ + namespace: options.namespace || 'default', + dryRun: options.dryRun, + }) + }); + } + + if (options.keycloak) { + tasks.push({ + name: 'Keycloak', fn: () => setupKeycloak({ + namespace: options.namespace || 'default', + instanceId: options.instanceId, + dryRun: options.dryRun, + }) + }); + } + + for (const task of tasks) { + logger.info(`📝 Setting up ${task.name}...`); + + if (options.dryRun) { + logger.info(` [DRY RUN] Would execute ${task.name} setup`); + continue; + } + + try { + await task.fn(); + logger.info(` ✅ ${task.name} setup completed`); + } catch (error) { + logger.error(` ❌ ${task.name} setup failed`, { error: error instanceof Error ? error.message : String(error) }); + throw error; + } + } + + logger.info('🎉 Zenko test environment setup completed successfully!'); + + } catch (error) { + logger.error('💥 Setup failed', { error: error instanceof Error ? error.message : String(error) }); + process.exit(1); + } +} + +program.parse(); \ No newline at end of file diff --git a/tests/@setup/src/dns.ts b/tests/@setup/src/dns.ts new file mode 100644 index 0000000000..c89c44b558 --- /dev/null +++ b/tests/@setup/src/dns.ts @@ -0,0 +1,198 @@ +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; + +export interface DNSOptions { + namespace: string; + subdomain: string; + dryRun?: boolean; +} + +export async function setupDNS(options: DNSOptions): Promise { + const k8s = new KubernetesClient(); + + logger.info('Setting up CoreDNS configuration for test domains'); + + // Get the current CoreDNS ConfigMap + let coreDnsConfigMap; + try { + coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap('coredns', 'kube-system'); + } catch (error: any) { + if (error.response?.statusCode === 404) { + logger.warn('CoreDNS ConfigMap not found, attempting to find alternative'); + // Try different possible names/namespaces + const alternatives = [ + { name: 'coredns-custom', namespace: 'kube-system' }, + { name: 'coredns', namespace: 'kube-dns' } + ]; + + for (const alt of alternatives) { + try { + coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap(alt.name, alt.namespace); + break; + } catch (e) { + continue; + } + } + + if (!coreDnsConfigMap) { + logger.warn('Could not find CoreDNS ConfigMap, creating custom DNS setup'); + await createCustomDNSSetup(k8s, options); + return; + } + } else { + throw error; + } + } + + // Parse current Corefile + const currentCorefile = coreDnsConfigMap.body.data?.['Corefile'] || ''; + + // Generate rewrite rules for test domains + const rewriteRules = generateRewriteRules(options.subdomain, options.namespace); + + // Check if our rules already exist + if (currentCorefile.includes(`# Zenko test rewrite rules for ${options.subdomain}`)) { + logger.debug('DNS rewrite rules already configured'); + return; + } + + // Add our rewrite rules to the Corefile + const newCorefile = addRewriteRules(currentCorefile, rewriteRules, options.subdomain); + + // Update the ConfigMap + const updatedConfigMap = { + ...coreDnsConfigMap.body, + data: { + ...coreDnsConfigMap.body.data, + 'Corefile': newCorefile + } + }; + + await k8s.coreApi.replaceNamespacedConfigMap('coredns', 'kube-system', updatedConfigMap); + + // Restart CoreDNS deployment to pick up changes + await restartCoreDNS(k8s); + + logger.info('CoreDNS configuration updated successfully'); +} + +async function createCustomDNSSetup(k8s: KubernetesClient, options: DNSOptions): Promise { + logger.info('Creating custom DNS setup for test environment'); + + // Create a custom CoreDNS deployment for test domains + const customCorefile = ` +# Zenko test DNS configuration +${options.subdomain}:53 { + rewrite name regex (.+\\.)?aws-mock\\.${options.subdomain} cloudserver-mock.${options.namespace}.svc.cluster.local + rewrite name regex (.+\\.)?azure-mock\\.${options.subdomain} azurite-mock.${options.namespace}.svc.cluster.local + rewrite name regex iam\\.${options.subdomain} zenko-iam.${options.namespace}.svc.cluster.local + rewrite name regex ui\\.${options.subdomain} zenko-ui.${options.namespace}.svc.cluster.local + rewrite name regex s3\\.${options.subdomain} zenko-s3.${options.namespace}.svc.cluster.local + forward . /etc/resolv.conf + cache 30 + errors + log +} + +.:53 { + forward . /etc/resolv.conf + cache 30 + errors + log +} +`; + + const customDNSConfigMap = { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { + name: 'zenko-test-coredns', + namespace: options.namespace + }, + data: { + 'Corefile': customCorefile.trim() + } + }; + + await k8s.applyManifest(customDNSConfigMap, options.namespace); + logger.info('Custom DNS ConfigMap created'); +} + +function generateRewriteRules(subdomain: string, namespace: string): string { + return ` +# Zenko test rewrite rules for ${subdomain} +rewrite name regex (.+\\.)?aws-mock\\.${subdomain} cloudserver-mock.${namespace}.svc.cluster.local +rewrite name regex (.+\\.)?azure-mock\\.${subdomain} azurite-mock.${namespace}.svc.cluster.local +rewrite name regex iam\\.${subdomain} zenko-iam.${namespace}.svc.cluster.local +rewrite name regex ui\\.${subdomain} zenko-ui.${namespace}.svc.cluster.local +rewrite name regex s3\\.${subdomain} zenko-s3.${namespace}.svc.cluster.local +rewrite name regex management\\.${subdomain} zenko-management.${namespace}.svc.cluster.local`; +} + +function addRewriteRules(currentCorefile: string, rewriteRules: string, subdomain: string): string { + // Find the main server block (.:53 or similar) + const lines = currentCorefile.split('\\n'); + const newLines = []; + let insideMainBlock = false; + let foundMainBlock = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Detect main server block + if (line.trim().match(/^\\.:53\\s*{/) || line.trim().match(/^\\. {/)) { + insideMainBlock = true; + foundMainBlock = true; + newLines.push(line); + // Add our rewrite rules right after the opening brace + newLines.push(rewriteRules); + continue; + } + + // Detect end of server block + if (insideMainBlock && line.trim() === '}') { + insideMainBlock = false; + } + + newLines.push(line); + } + + // If no main block found, add our own + if (!foundMainBlock) { + newLines.push(''); + newLines.push(`# Zenko test server block`); + newLines.push(`.:53 {`); + newLines.push(rewriteRules); + newLines.push(' forward . /etc/resolv.conf'); + newLines.push(' cache 30'); + newLines.push(' errors'); + newLines.push(' log'); + newLines.push('}'); + } + + return newLines.join('\\n'); +} + +async function restartCoreDNS(k8s: KubernetesClient): Promise { + try { + // Get CoreDNS deployment + const deployment = await k8s.appsApi.readNamespacedDeployment('coredns', 'kube-system'); + + // Add/update restart annotation to trigger rolling restart + const annotations = deployment.body.spec?.template.metadata?.annotations || {}; + annotations['kubectl.kubernetes.io/restartedAt'] = new Date().toISOString(); + + deployment.body.spec!.template.metadata!.annotations = annotations; + + await k8s.appsApi.replaceNamespacedDeployment('coredns', 'kube-system', deployment.body); + + logger.debug('CoreDNS deployment restart triggered'); + + // Wait a bit for the restart to take effect + await new Promise(resolve => setTimeout(resolve, 10000)); + + } catch (error: any) { + logger.warn(`Could not restart CoreDNS deployment: ${error.message}`); + logger.info('DNS changes will take effect when CoreDNS pods are restarted'); + } +} \ No newline at end of file diff --git a/tests/@setup/src/keycloak.ts b/tests/@setup/src/keycloak.ts new file mode 100644 index 0000000000..b2ace3704a --- /dev/null +++ b/tests/@setup/src/keycloak.ts @@ -0,0 +1,320 @@ +import axios from 'axios'; +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; + +export interface KeycloakOptions { + namespace: string; + instanceId?: string; + dryRun?: boolean; +} + +interface KeycloakConfig { + endpoint: string; + adminUsername: string; + adminPassword: string; + realm: string; +} + +export async function setupKeycloak(options: KeycloakOptions): Promise { + logger.info('Setting up Keycloak realm, users, and roles'); + + const k8s = new KubernetesClient(); + const keycloakConfig = await getKeycloakConfig(k8s, options.namespace); + + if (!keycloakConfig) { + logger.warn('Keycloak not found or not configured, skipping Keycloak setup'); + return; + } + + const adminToken = await getAdminToken(keycloakConfig); + + await createRealm(keycloakConfig, adminToken); + await createRoles(keycloakConfig, adminToken, options.instanceId); + await createUsers(keycloakConfig, adminToken, options.instanceId); + + logger.info('Keycloak setup completed'); +} + +async function getKeycloakConfig(k8s: KubernetesClient, namespace: string): Promise { + try { + // Look for Keycloak service + const services = await k8s.coreApi.listNamespacedService(namespace); + const keycloakService = services.body.items.find(svc => + svc.metadata?.name?.toLowerCase().includes('keycloak') || + svc.metadata?.name?.toLowerCase().includes('auth') + ); + + if (!keycloakService) { + return null; + } + + // Look for Keycloak admin credentials + const secrets = await k8s.coreApi.listNamespacedSecret(namespace); + const keycloakSecret = secrets.body.items.find(secret => + secret.metadata?.name?.toLowerCase().includes('keycloak') && + (secret.metadata?.name?.toLowerCase().includes('admin') || + secret.metadata?.name?.toLowerCase().includes('credentials')) + ); + + let adminUsername = 'admin'; + let adminPassword = 'admin'; + + if (keycloakSecret?.data) { + adminUsername = keycloakSecret.data['username'] ? + Buffer.from(keycloakSecret.data['username'], 'base64').toString() : 'admin'; + adminPassword = keycloakSecret.data['password'] ? + Buffer.from(keycloakSecret.data['password'], 'base64').toString() : 'admin'; + } + + const serviceName = keycloakService.metadata!.name; + const port = keycloakService.spec?.ports?.[0]?.port || 8080; + const endpoint = `http://${serviceName}.${namespace}.svc.cluster.local:${port}`; + + return { + endpoint, + adminUsername, + adminPassword, + realm: 'zenko' + }; + + } catch (error) { + logger.debug(`Error getting Keycloak config: ${error}`); + return null; + } +} + +async function getAdminToken(config: KeycloakConfig): Promise { + try { + const response = await axios.post( + `${config.endpoint}/auth/realms/master/protocol/openid-connect/token`, + new URLSearchParams({ + grant_type: 'password', + client_id: 'admin-cli', + username: config.adminUsername, + password: config.adminPassword + }), + { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + timeout: 30000 + } + ); + + return response.data.access_token; + } catch (error: any) { + logger.error(`Failed to get Keycloak admin token: ${error.message}`); + throw error; + } +} + +async function createRealm(config: KeycloakConfig, token: string): Promise { + const realmData = { + realm: config.realm, + enabled: true, + displayName: 'Zenko Test Realm', + registrationAllowed: false, + resetPasswordAllowed: true, + editUsernameAllowed: false, + loginWithEmailAllowed: true, + duplicateEmailsAllowed: false, + verifyEmail: false, + loginTheme: 'keycloak', + accountTheme: 'keycloak', + adminTheme: 'keycloak', + emailTheme: 'keycloak' + }; + + try { + await axios.post( + `${config.endpoint}/auth/admin/realms`, + realmData, + { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + timeout: 30000 + } + ); + logger.debug(`Created realm: ${config.realm}`); + } catch (error: any) { + if (error.response?.status === 409) { + logger.debug(`Realm ${config.realm} already exists`); + } else { + throw error; + } + } +} + +async function createRoles(config: KeycloakConfig, token: string, instanceId?: string): Promise { + const baseRoles = [ + 'StorageManager', + 'StorageAccountOwner', + 'DataConsumer' + ]; + + // Add instance-specific account roles + const accountRoles = instanceId ? [ + `AccountTest::${instanceId}`, + `AccountTest::${instanceId}::StorageManager`, + `AccountTest::${instanceId}::DataConsumer` + ] : [ + 'AccountTest::xyz123', + 'AccountTest::xyz123::StorageManager', + 'AccountTest::xyz123::DataConsumer' + ]; + + const allRoles = [...baseRoles, ...accountRoles]; + + for (const roleName of allRoles) { + try { + await axios.post( + `${config.endpoint}/auth/admin/realms/${config.realm}/roles`, + { + name: roleName, + description: `Zenko test role: ${roleName}` + }, + { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + timeout: 30000 + } + ); + logger.debug(`Created role: ${roleName}`); + } catch (error: any) { + if (error.response?.status === 409) { + logger.debug(`Role ${roleName} already exists`); + } else { + logger.error(`Failed to create role ${roleName}: ${error.message}`); + } + } + } +} + +async function createUsers(config: KeycloakConfig, token: string, instanceId?: string): Promise { + const testUsers = [ + { + username: 'storage-manager', + email: 'storage-manager@test.local', + firstName: 'Storage', + lastName: 'Manager', + roles: ['StorageManager'], + password: 'password123' + }, + { + username: 'account-owner', + email: 'account-owner@test.local', + firstName: 'Account', + lastName: 'Owner', + roles: ['StorageAccountOwner'], + password: 'password123' + }, + { + username: 'data-consumer', + email: 'data-consumer@test.local', + firstName: 'Data', + lastName: 'Consumer', + roles: ['DataConsumer'], + password: 'password123' + } + ]; + + // Add instance-specific test user + if (instanceId) { + testUsers.push({ + username: `test-${instanceId}`, + email: `test-${instanceId}@test.local`, + firstName: 'Test', + lastName: 'User', + roles: [`AccountTest::${instanceId}`, `AccountTest::${instanceId}::StorageManager`], + password: 'password123' + }); + } + + for (const user of testUsers) { + try { + // Create user + const createUserResponse = await axios.post( + `${config.endpoint}/auth/admin/realms/${config.realm}/users`, + { + username: user.username, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + enabled: true, + emailVerified: true, + credentials: [{ + type: 'password', + value: user.password, + temporary: false + }] + }, + { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + timeout: 30000 + } + ); + + // Get user ID from Location header or by querying + let userId = ''; + if (createUserResponse.headers.location) { + userId = createUserResponse.headers.location.split('/').pop(); + } else { + // Query for user ID + const usersResponse = await axios.get( + `${config.endpoint}/auth/admin/realms/${config.realm}/users?username=${user.username}`, + { + headers: { 'Authorization': `Bearer ${token}` }, + timeout: 30000 + } + ); + userId = usersResponse.data[0]?.id; + } + + if (userId) { + // Assign roles to user + for (const roleName of user.roles) { + try { + // Get role details + const roleResponse = await axios.get( + `${config.endpoint}/auth/admin/realms/${config.realm}/roles/${roleName}`, + { + headers: { 'Authorization': `Bearer ${token}` }, + timeout: 30000 + } + ); + + // Assign role to user + await axios.post( + `${config.endpoint}/auth/admin/realms/${config.realm}/users/${userId}/role-mappings/realm`, + [roleResponse.data], + { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + timeout: 30000 + } + ); + logger.debug(`Assigned role ${roleName} to user ${user.username}`); + } catch (roleError) { + logger.warn(`Failed to assign role ${roleName} to user ${user.username}`); + } + } + } + + logger.debug(`Created user: ${user.username}`); + + } catch (error: any) { + if (error.response?.status === 409) { + logger.debug(`User ${user.username} already exists`); + } else { + logger.error(`Failed to create user ${user.username}: ${error.message}`); + } + } + } +} \ No newline at end of file diff --git a/tests/@setup/src/locations.ts b/tests/@setup/src/locations.ts new file mode 100644 index 0000000000..5fa391b1f9 --- /dev/null +++ b/tests/@setup/src/locations.ts @@ -0,0 +1,171 @@ +import axios from 'axios'; +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; + +export interface LocationsOptions { + namespace: string; + instanceId?: string; + dryRun?: boolean; +} + +interface StorageLocation { + name: string; + locationType: string; + details: any; +} + +export async function setupLocations(options: LocationsOptions): Promise { + const k8s = new KubernetesClient(); + + logger.info('Setting up storage locations via Management API'); + + // Get Management API endpoint and credentials + const managementEndpoint = await getManagementEndpoint(k8s, options.namespace); + const credentials = await getManagementCredentials(k8s, options.namespace); + + const locations: StorageLocation[] = [ + { + name: 'aws-s3-mock', + locationType: 'location-s3-v1', + details: { + endpoint: `http://cloudserver-mock.${options.namespace}.svc.cluster.local:8000`, + bucketName: 'ci-zenko-aws-target-bucket', + accessKey: 'accessKey1', + secretKey: 'verySecretKey1', + bucketMatch: false, + pathStyle: true + } + }, + { + name: 'azure-blob-mock', + locationType: 'location-azure-v1', + details: { + endpoint: `http://azurite-mock.${options.namespace}.svc.cluster.local:10000/devstoreaccount1`, + containerName: 'ci-zenko-azure-target-container', + accountName: 'devstoreaccount1', + accountKey: 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==' + } + }, + { + name: 'dmf-tape', + locationType: 'location-dmf-v1', + details: { + endpoint: 'http://dmf-service:7778', + repoId: ['repoId'], + nsId: 'nsId', + username: 'username', + password: 'password' + } + }, + { + name: 'ring-s3c', + locationType: 'location-s3-v1', + details: { + endpoint: 'http://ring-s3c:8080', + bucketName: 'ci-zenko-ring-target-bucket', + accessKey: 'ring-access-key', + secretKey: 'ring-secret-key', + bucketMatch: false, + pathStyle: true + } + } + ]; + + for (const location of locations) { + await createStorageLocation(managementEndpoint, credentials, location); + } + + logger.info(`Created ${locations.length} storage locations`); +} + +async function getManagementEndpoint(k8s: KubernetesClient, namespace: string): Promise { + try { + // Try to find Management API service + const services = await k8s.coreApi.listNamespacedService(namespace); + const mgmtService = services.body.items.find(svc => + svc.metadata?.name?.includes('management') || + svc.metadata?.name?.includes('api') || + svc.metadata?.name?.includes('zenko-management') + ); + + if (mgmtService) { + const serviceName = mgmtService.metadata!.name; + const port = mgmtService.spec?.ports?.[0]?.port || 8443; + return `http://${serviceName}.${namespace}.svc.cluster.local:${port}`; + } + + // Fallback to common endpoint + return `http://zenko-management.${namespace}.svc.cluster.local:8443`; + } catch (error) { + logger.warn('Could not determine Management API endpoint, using default'); + return `http://zenko-management.${namespace}.svc.cluster.local:8443`; + } +} + +async function getManagementCredentials(k8s: KubernetesClient, namespace: string): Promise<{ accessKey: string; secretKey: string }> { + try { + // Look for admin credentials in secrets + const secrets = await k8s.coreApi.listNamespacedSecret(namespace); + const adminSecret = secrets.body.items.find(secret => + secret.metadata?.name?.includes('admin') || + secret.metadata?.name?.includes('management') || + secret.metadata?.name?.includes('credentials') + ); + + if (adminSecret?.data) { + const accessKey = adminSecret.data['access-key'] || adminSecret.data['accessKey'] || adminSecret.data['AWS_ACCESS_KEY_ID']; + const secretKey = adminSecret.data['secret-key'] || adminSecret.data['secretKey'] || adminSecret.data['AWS_SECRET_ACCESS_KEY']; + + if (accessKey && secretKey) { + return { + accessKey: Buffer.from(accessKey, 'base64').toString(), + secretKey: Buffer.from(secretKey, 'base64').toString() + }; + } + } + } catch (error) { + logger.debug('Could not find admin credentials in secrets'); + } + + // Return default test credentials + logger.warn('Using default test credentials for Management API'); + return { + accessKey: 'accessKey1', + secretKey: 'verySecretKey1' + }; +} + +async function createStorageLocation(endpoint: string, credentials: { accessKey: string; secretKey: string }, location: StorageLocation): Promise { + try { + const response = await axios.post( + `${endpoint}/api/v1/config/${location.name}/location`, + { + locationType: location.locationType, + locationDetails: location.details + }, + { + headers: { + 'Authorization': `AWS ${credentials.accessKey}:${credentials.secretKey}`, + 'Content-Type': 'application/json' + }, + timeout: 30000 + } + ); + + if (response.status === 200 || response.status === 201) { + logger.debug(`Created storage location: ${location.name}`); + } else { + logger.warn(`Unexpected response creating location ${location.name}: ${response.status}`); + } + + } catch (error: any) { + if (error.response?.status === 409) { + logger.debug(`Storage location ${location.name} already exists`); + } else if (error.code === 'ECONNREFUSED') { + logger.warn(`Management API not available at ${endpoint}, skipping location ${location.name}`); + } else { + logger.error(`Failed to create storage location ${location.name}: ${error.message}`); + // Don't throw - continue with other locations + } + } +} \ No newline at end of file diff --git a/tests/@setup/src/mocks.ts b/tests/@setup/src/mocks.ts new file mode 100644 index 0000000000..1d3ec10e4c --- /dev/null +++ b/tests/@setup/src/mocks.ts @@ -0,0 +1,237 @@ +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; + +export interface MocksOptions { + namespace: string; + subdomain: string; + instanceId?: string; + awsOnly?: boolean; + azureOnly?: boolean; + dryRun?: boolean; +} + +export async function setupMocks(options: MocksOptions): Promise { + const k8s = new KubernetesClient(); + await k8s.ensureNamespace(options.namespace); + + if (!options.azureOnly) { + await setupAwsMocks(k8s, options); + } + + if (!options.awsOnly) { + await setupAzureMocks(k8s, options); + } +} + +async function setupAwsMocks(k8s: KubernetesClient, options: MocksOptions): Promise { + logger.info('Setting up AWS S3 mock (CloudServer)'); + + // CloudServer deployment for S3 API mocking + const cloudServerDeployment = { + apiVersion: 'apps/v1', + kind: 'Deployment', + metadata: { + name: 'cloudserver-mock', + namespace: options.namespace, + labels: { + app: 'cloudserver-mock', + component: 'aws-mock' + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: 'cloudserver-mock' + } + }, + template: { + metadata: { + labels: { + app: 'cloudserver-mock' + } + }, + spec: { + containers: [{ + name: 'cloudserver', + image: 'ghcr.io/scality/cloudserver:latest', + ports: [{ containerPort: 8000 }], + env: [ + { name: 'SCALITY_ACCESS_KEY_ID', value: 'accessKey1' }, + { name: 'SCALITY_SECRET_ACCESS_KEY', value: 'verySecretKey1' }, + { name: 'S3BACKEND', value: 'mem' }, + { name: 'LOG_LEVEL', value: 'info' }, + { name: 'REMOTE_MANAGEMENT_DISABLE', value: '1' } + ], + readinessProbe: { + httpGet: { + path: '/', + port: 8000 + }, + initialDelaySeconds: 10, + periodSeconds: 5 + } + }] + } + } + } + }; + + // CloudServer service + const cloudServerService = { + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: 'cloudserver-mock', + namespace: options.namespace, + labels: { + app: 'cloudserver-mock' + } + }, + spec: { + selector: { + app: 'cloudserver-mock' + }, + ports: [{ + port: 8000, + targetPort: 8000, + name: 's3' + }] + } + }; + + // AWS credentials secret for testing + const awsCredsSecret = { + apiVersion: 'v1', + kind: 'Secret', + metadata: { + name: 'aws-mock-credentials', + namespace: options.namespace + }, + type: 'Opaque', + stringData: { + 'aws-access-key-id': 'accessKey1', + 'aws-secret-access-key': 'verySecretKey1', + 'aws-region': 'us-east-1', + 'aws-endpoint': `http://cloudserver-mock.${options.namespace}.svc.cluster.local:8000` + } + }; + + await k8s.applyManifest(cloudServerDeployment, options.namespace); + await k8s.applyManifest(cloudServerService, options.namespace); + await k8s.applyManifest(awsCredsSecret, options.namespace); + + // Wait for deployment to be ready + await k8s.waitForDeployment('cloudserver-mock', options.namespace); + + logger.info('AWS S3 mock setup completed'); +} + +async function setupAzureMocks(k8s: KubernetesClient, options: MocksOptions): Promise { + logger.info('Setting up Azure Blob/Queue mock (Azurite)'); + + // Azurite deployment for Azure Storage API mocking + const azuriteDeployment = { + apiVersion: 'apps/v1', + kind: 'Deployment', + metadata: { + name: 'azurite-mock', + namespace: options.namespace, + labels: { + app: 'azurite-mock', + component: 'azure-mock' + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: 'azurite-mock' + } + }, + template: { + metadata: { + labels: { + app: 'azurite-mock' + } + }, + spec: { + containers: [{ + name: 'azurite', + image: 'mcr.microsoft.com/azure-storage/azurite:latest', + ports: [ + { containerPort: 10000, name: 'blob' }, + { containerPort: 10001, name: 'queue' }, + { containerPort: 10002, name: 'table' } + ], + command: [ + 'azurite', + '--blobHost', '0.0.0.0', + '--queueHost', '0.0.0.0', + '--tableHost', '0.0.0.0', + '--location', '/workspace', + '--debug', '/workspace/debug.log' + ], + readinessProbe: { + httpGet: { + path: '/', + port: 10000 + }, + initialDelaySeconds: 10, + periodSeconds: 5 + } + }] + } + } + } + }; + + // Azurite service + const azuriteService = { + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: 'azurite-mock', + namespace: options.namespace, + labels: { + app: 'azurite-mock' + } + }, + spec: { + selector: { + app: 'azurite-mock' + }, + ports: [ + { port: 10000, targetPort: 10000, name: 'blob' }, + { port: 10001, targetPort: 10001, name: 'queue' }, + { port: 10002, targetPort: 10002, name: 'table' } + ] + } + }; + + // Azure credentials secret for testing + const azureCredsSecret = { + apiVersion: 'v1', + kind: 'Secret', + metadata: { + name: 'azure-mock-credentials', + namespace: options.namespace + }, + type: 'Opaque', + stringData: { + 'account-name': 'devstoreaccount1', + 'account-key': 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==', + 'blob-endpoint': `http://azurite-mock.${options.namespace}.svc.cluster.local:10000/devstoreaccount1`, + 'queue-endpoint': `http://azurite-mock.${options.namespace}.svc.cluster.local:10001/devstoreaccount1` + } + }; + + await k8s.applyManifest(azuriteDeployment, options.namespace); + await k8s.applyManifest(azuriteService, options.namespace); + await k8s.applyManifest(azureCredsSecret, options.namespace); + + // Wait for deployment to be ready + await k8s.waitForDeployment('azurite-mock', options.namespace); + + logger.info('Azure Storage mock setup completed'); +} \ No newline at end of file diff --git a/tests/@setup/src/rbac.ts b/tests/@setup/src/rbac.ts new file mode 100644 index 0000000000..d8975387b3 --- /dev/null +++ b/tests/@setup/src/rbac.ts @@ -0,0 +1,119 @@ +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; + +export interface RBACOptions { + namespace: string; + dryRun?: boolean; +} + +export async function setupRBAC(options: RBACOptions): Promise { + const k8s = new KubernetesClient(); + await k8s.ensureNamespace(options.namespace); + + logger.info('Setting up RBAC permissions for service accounts'); + + // Create cluster role for test service accounts + const clusterRole = { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'ClusterRole', + metadata: { + name: 'zenko-test-admin' + }, + rules: [ + { + apiGroups: ['*'], + resources: ['*'], + verbs: ['*'] + } + ] + }; + + // Get all service accounts in the namespace + const serviceAccounts = await k8s.coreApi.listNamespacedServiceAccount(options.namespace); + const zenkoServiceAccounts = serviceAccounts.body.items.filter(sa => + sa.metadata?.name?.includes('zenko') || + sa.metadata?.name?.includes('cloudserver') || + sa.metadata?.name?.includes('backbeat') || + sa.metadata?.name?.includes('operator') + ); + + // Apply cluster role + try { + await k8s.rbacApi.createClusterRole(clusterRole); + } catch (error: any) { + if (error.response?.statusCode === 409) { + logger.debug('ClusterRole zenko-test-admin already exists'); + await k8s.rbacApi.replaceClusterRole('zenko-test-admin', clusterRole); + } else { + throw error; + } + } + + // Create cluster role bindings for each service account + for (const sa of zenkoServiceAccounts) { + const saName = sa.metadata?.name; + if (!saName) continue; + + const clusterRoleBinding = { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'ClusterRoleBinding', + metadata: { + name: `zenko-test-admin-${saName}` + }, + subjects: [{ + kind: 'ServiceAccount', + name: saName, + namespace: options.namespace + }], + roleRef: { + kind: 'ClusterRole', + name: 'zenko-test-admin', + apiGroup: 'rbac.authorization.k8s.io' + } + }; + + try { + await k8s.rbacApi.createClusterRoleBinding(clusterRoleBinding); + logger.debug(`Created ClusterRoleBinding for ${saName}`); + } catch (error: any) { + if (error.response?.statusCode === 409) { + logger.debug(`ClusterRoleBinding for ${saName} already exists`); + await k8s.rbacApi.replaceClusterRoleBinding(`zenko-test-admin-${saName}`, clusterRoleBinding); + } else { + throw error; + } + } + } + + // Create role binding for default service account in namespace + const defaultRoleBinding = { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'ClusterRoleBinding', + metadata: { + name: `zenko-test-admin-default-${options.namespace}` + }, + subjects: [{ + kind: 'ServiceAccount', + name: 'default', + namespace: options.namespace + }], + roleRef: { + kind: 'ClusterRole', + name: 'zenko-test-admin', + apiGroup: 'rbac.authorization.k8s.io' + } + }; + + try { + await k8s.rbacApi.createClusterRoleBinding(defaultRoleBinding); + } catch (error: any) { + if (error.response?.statusCode === 409) { + logger.debug(`Default ClusterRoleBinding already exists`); + await k8s.rbacApi.replaceClusterRoleBinding(`zenko-test-admin-default-${options.namespace}`, defaultRoleBinding); + } else { + throw error; + } + } + + logger.info(`RBAC setup completed for ${zenkoServiceAccounts.length + 1} service accounts`); +} \ No newline at end of file diff --git a/tests/@setup/src/utils/k8s.ts b/tests/@setup/src/utils/k8s.ts new file mode 100644 index 0000000000..fde55cd830 --- /dev/null +++ b/tests/@setup/src/utils/k8s.ts @@ -0,0 +1,193 @@ +import * as k8s from '@kubernetes/client-node'; +import { logger } from './logger'; + +export class KubernetesClient { + private kc: k8s.KubeConfig; + public coreApi: k8s.CoreV1Api; + public appsApi: k8s.AppsV1Api; + public customObjectsApi: k8s.CustomObjectsApi; + public rbacApi: k8s.RbacAuthorizationV1Api; + + constructor(kubeconfig?: string) { + this.kc = new k8s.KubeConfig(); + + if (kubeconfig) { + this.kc.loadFromFile(kubeconfig); + } else if (process.env.KUBECONFIG) { + this.kc.loadFromFile(process.env.KUBECONFIG); + } else { + try { + this.kc.loadFromDefault(); + } catch (error) { + logger.error( + 'Failed to load kubeconfig. Please provide --kubeconfig or set KUBECONFIG environment variable'); + throw error; + } + } + + this.coreApi = this.kc.makeApiClient(k8s.CoreV1Api); + this.appsApi = this.kc.makeApiClient(k8s.AppsV1Api); + this.customObjectsApi = this.kc.makeApiClient(k8s.CustomObjectsApi); + this.rbacApi = this.kc.makeApiClient(k8s.RbacAuthorizationV1Api); + } + + async ensureNamespace(namespace: string): Promise { + try { + await this.coreApi.readNamespace(namespace); + logger.debug(`Namespace ${namespace} exists`); + } catch (error: any) { + if (error.response?.statusCode === 404) { + logger.info(`Creating namespace ${namespace}`); + await this.coreApi.createNamespace({ + apiVersion: 'v1', + kind: 'Namespace', + metadata: { name: namespace } + }); + } else { + throw error; + } + } + } + + async applyManifest(manifest: any, namespace?: string): Promise { + const { kind, apiVersion, metadata } = manifest; + + if (namespace && !metadata.namespace) { + metadata.namespace = namespace; + } + + logger.debug(`Applying ${kind}/${metadata.name} in namespace ${metadata.namespace || 'default'}`); + + try { + switch (kind) { + case 'Deployment': + try { + await this.appsApi.readNamespacedDeployment( + metadata.name, + metadata.namespace || 'default', + ); + await this.appsApi.replaceNamespacedDeployment( + metadata.name, + metadata.namespace || 'default', + manifest, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.appsApi.createNamespacedDeployment( + metadata.namespace || 'default', + manifest, + ); + } else { + throw error; + } + } + break; + + case 'Service': + try { + await this.coreApi.readNamespacedService( + metadata.name, + metadata.namespace || 'default', + ); + await this.coreApi.replaceNamespacedService( + metadata.name, + metadata.namespace || 'default', + manifest, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedService(metadata.namespace || 'default', manifest); + } else { + throw error; + } + } + break; + + case 'ConfigMap': + try { + await this.coreApi.readNamespacedConfigMap( + metadata.name, + metadata.namespace || 'default', + ); + await this.coreApi.replaceNamespacedConfigMap( + metadata.name, + metadata.namespace || 'default', + manifest, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedConfigMap(metadata.namespace || 'default', manifest); + } else { + throw error; + } + } + break; + + case 'Secret': + try { + await this.coreApi.readNamespacedSecret( + metadata.name, + metadata.namespace || 'default', + ); + await this.coreApi.replaceNamespacedSecret( + metadata.name, + metadata.namespace || 'default', + manifest, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedSecret( + metadata.namespace || 'default', + manifest, + ); + } else { + throw error; + } + } + break; + + default: + // Handle custom resources + // eslint-disable-next-line no-case-declarations + const [group, version] = apiVersion.split('/'); + await this.customObjectsApi.createNamespacedCustomObject( + group, + version, + metadata.namespace || 'default', + `${kind.toLowerCase()}s`, + manifest + ); + } + } catch (error: any) { + if (error.response?.statusCode === 409) { + logger.debug(`Resource ${kind}/${metadata.name} already exists`); + } else { + throw error; + } + } + } + + async waitForDeployment(name: string, namespace: string, timeoutMs: number = 300000): Promise { + const startTime = Date.now(); + + while (Date.now() - startTime < timeoutMs) { + try { + const deployment = await this.appsApi.readNamespacedDeployment(name, namespace); + const status = deployment.body.status; + + if (status?.readyReplicas === status?.replicas && status?.replicas && status.replicas > 0) { + logger.debug(`Deployment ${name} is ready`); + return; + } + + logger.debug(`Waiting for deployment ${name} (${status?.readyReplicas || 0}/${status?.replicas || 0})`); + await new Promise(resolve => setTimeout(resolve, 5000)); + } catch (error) { + logger.debug(`Error checking deployment ${name}: ${error}`); + await new Promise(resolve => setTimeout(resolve, 5000)); + } + } + + throw new Error(`Deployment ${name} did not become ready within ${timeoutMs}ms`); + } +} diff --git a/tests/@setup/src/utils/logger.ts b/tests/@setup/src/utils/logger.ts new file mode 100644 index 0000000000..01e895a971 --- /dev/null +++ b/tests/@setup/src/utils/logger.ts @@ -0,0 +1,9 @@ +import Werelogs from 'werelogs'; + +// Configure werelogs +Werelogs.configure({ + level: (process.env.LOG_LEVEL as any) || 'info', + dump: (process.env.LOG_DUMP_LEVEL as any) || 'error', +}); + +export const logger = new Werelogs.Logger('ZenkoSetup').newRequestLogger(); diff --git a/tests/@setup/tsconfig.json b/tests/@setup/tsconfig.json new file mode 100644 index 0000000000..ebbbe2b307 --- /dev/null +++ b/tests/@setup/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts" + ] +} \ No newline at end of file diff --git a/tests/@setup/yarn.lock b/tests/@setup/yarn.lock new file mode 100644 index 0000000000..55a6d6492f --- /dev/null +++ b/tests/@setup/yarn.lock @@ -0,0 +1,3103 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aws-crypto/crc32@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" + integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/crc32c@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" + integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/sha1-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz#b0ee2d2821d3861f017e965ef3b4cb38e3b6a0f4" + integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== + dependencies: + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + dependencies: + "@aws-crypto/sha256-js" "^5.2.0" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/supports-web-crypto@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== + dependencies: + tslib "^2.6.2" + +"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-iam@^3.400.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-iam/-/client-iam-3.887.0.tgz#e7ec62be99fe7742c7aa9b3cda6466d6c903f885" + integrity sha512-4T9mFcaGOdm590+KKJtEMG4WRVKziUC1+VhwE0Zyx5DnwjwebkGzl7RBU2zEXhvsclHgkOmuM0fGOFxWbcaZRg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.887.0" + "@aws-sdk/credential-provider-node" "3.887.0" + "@aws-sdk/middleware-host-header" "3.887.0" + "@aws-sdk/middleware-logger" "3.887.0" + "@aws-sdk/middleware-recursion-detection" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/region-config-resolver" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-endpoints" "3.887.0" + "@aws-sdk/util-user-agent-browser" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.887.0" + "@smithy/config-resolver" "^4.2.1" + "@smithy/core" "^3.11.0" + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/hash-node" "^4.1.1" + "@smithy/invalid-dependency" "^4.1.1" + "@smithy/middleware-content-length" "^4.1.1" + "@smithy/middleware-endpoint" "^4.2.1" + "@smithy/middleware-retry" "^4.2.1" + "@smithy/middleware-serde" "^4.1.1" + "@smithy/middleware-stack" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-body-length-node" "^4.1.0" + "@smithy/util-defaults-mode-browser" "^4.1.1" + "@smithy/util-defaults-mode-node" "^4.1.1" + "@smithy/util-endpoints" "^3.1.1" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-retry" "^4.1.1" + "@smithy/util-utf8" "^4.1.0" + "@smithy/util-waiter" "^4.1.1" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.400.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.887.0.tgz#122bc244f740bf9819f056fd2a3e823f4d84c3ec" + integrity sha512-WEFiYbCgUBhd3OMj6Q3SCoJ5ekZduLPMnkLQ6czz3UGDuK2GCtdpscEGlbOyKSxm+BdLSV30+vU3gwjdtWUhCg== + dependencies: + "@aws-crypto/sha1-browser" "5.2.0" + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.887.0" + "@aws-sdk/credential-provider-node" "3.887.0" + "@aws-sdk/middleware-bucket-endpoint" "3.887.0" + "@aws-sdk/middleware-expect-continue" "3.887.0" + "@aws-sdk/middleware-flexible-checksums" "3.887.0" + "@aws-sdk/middleware-host-header" "3.887.0" + "@aws-sdk/middleware-location-constraint" "3.887.0" + "@aws-sdk/middleware-logger" "3.887.0" + "@aws-sdk/middleware-recursion-detection" "3.887.0" + "@aws-sdk/middleware-sdk-s3" "3.887.0" + "@aws-sdk/middleware-ssec" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/region-config-resolver" "3.887.0" + "@aws-sdk/signature-v4-multi-region" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-endpoints" "3.887.0" + "@aws-sdk/util-user-agent-browser" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.887.0" + "@aws-sdk/xml-builder" "3.887.0" + "@smithy/config-resolver" "^4.2.1" + "@smithy/core" "^3.11.0" + "@smithy/eventstream-serde-browser" "^4.1.1" + "@smithy/eventstream-serde-config-resolver" "^4.2.1" + "@smithy/eventstream-serde-node" "^4.1.1" + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/hash-blob-browser" "^4.1.1" + "@smithy/hash-node" "^4.1.1" + "@smithy/hash-stream-node" "^4.1.1" + "@smithy/invalid-dependency" "^4.1.1" + "@smithy/md5-js" "^4.1.1" + "@smithy/middleware-content-length" "^4.1.1" + "@smithy/middleware-endpoint" "^4.2.1" + "@smithy/middleware-retry" "^4.2.1" + "@smithy/middleware-serde" "^4.1.1" + "@smithy/middleware-stack" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-body-length-node" "^4.1.0" + "@smithy/util-defaults-mode-browser" "^4.1.1" + "@smithy/util-defaults-mode-node" "^4.1.1" + "@smithy/util-endpoints" "^3.1.1" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-retry" "^4.1.1" + "@smithy/util-stream" "^4.3.1" + "@smithy/util-utf8" "^4.1.0" + "@smithy/util-waiter" "^4.1.1" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + +"@aws-sdk/client-sso@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.887.0.tgz#7b9bce78df539b5d7139d0ca1654981b05c63c43" + integrity sha512-ZKN8BxkRdC6vK6wlnuLSYBhj7uufg14GP5bxqiRaDEooN1y2WcuY95GP13I3brLvM0uboFGbObIVpVrbeHifng== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.887.0" + "@aws-sdk/middleware-host-header" "3.887.0" + "@aws-sdk/middleware-logger" "3.887.0" + "@aws-sdk/middleware-recursion-detection" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/region-config-resolver" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-endpoints" "3.887.0" + "@aws-sdk/util-user-agent-browser" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.887.0" + "@smithy/config-resolver" "^4.2.1" + "@smithy/core" "^3.11.0" + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/hash-node" "^4.1.1" + "@smithy/invalid-dependency" "^4.1.1" + "@smithy/middleware-content-length" "^4.1.1" + "@smithy/middleware-endpoint" "^4.2.1" + "@smithy/middleware-retry" "^4.2.1" + "@smithy/middleware-serde" "^4.1.1" + "@smithy/middleware-stack" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-body-length-node" "^4.1.0" + "@smithy/util-defaults-mode-browser" "^4.1.1" + "@smithy/util-defaults-mode-node" "^4.1.1" + "@smithy/util-endpoints" "^3.1.1" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-retry" "^4.1.1" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@aws-sdk/core@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.887.0.tgz#2e03e3ee7c3c615f96a1f5ea2e4bc6d52b7f53e6" + integrity sha512-oiBsWhuuj1Lzh+FHY+gE0PyYuiDxqFf98F9Pd2WruY5Gu/+/xvDFEPEkIEOae8gWRaLZ5Eh8u+OY9LS4DXZhuQ== + dependencies: + "@aws-sdk/types" "3.887.0" + "@aws-sdk/xml-builder" "3.887.0" + "@smithy/core" "^3.11.0" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.2.1" + "@smithy/signature-v4" "^5.1.3" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-utf8" "^4.1.0" + fast-xml-parser "5.2.5" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.887.0.tgz#ac2ee94d850d5d8c0fdb19bca4f96b11f375304f" + integrity sha512-kv7L5E8mxlWTMhCK639wrQnFEmwUDfKvKzTMDo2OboXZ0iSbD+hBPoT0gkb49qHNetYnsl63BVOxc0VNiOA04w== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.887.0.tgz#aebae84484a08412c03d540ef5f43bb035f7988c" + integrity sha512-siLttHxSFgJ5caDgS+BHYs9GBDX7J3pgge4OmJvIQeGO+KaJC12TerBNPJOp+qRaRC3yuVw3T9RpSZa8mmaiyA== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.2.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/util-stream" "^4.3.1" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.887.0.tgz#94d7007f96b04ee76a521d9496f19ff9d237f4a1" + integrity sha512-Na9IjKdPuSNU/mBcCQ49HiIgomq/O7kZAuRyGwAXiRPbf86AacKv4dsUyPZY6lCgVIvVniRWgYlVaPgq22EIig== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/credential-provider-env" "3.887.0" + "@aws-sdk/credential-provider-http" "3.887.0" + "@aws-sdk/credential-provider-process" "3.887.0" + "@aws-sdk/credential-provider-sso" "3.887.0" + "@aws-sdk/credential-provider-web-identity" "3.887.0" + "@aws-sdk/nested-clients" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.887.0.tgz#7f6e30e5bad736d5ff0afb243dd7ee24f330bfa8" + integrity sha512-iJdCq/brBWYpJzJcXY2UhEoW7aA28ixIpvLKjxh5QUBfjCj19cImpj1gGwTIs6/fVcjVUw1tNveTBfn1ziTzVg== + dependencies: + "@aws-sdk/credential-provider-env" "3.887.0" + "@aws-sdk/credential-provider-http" "3.887.0" + "@aws-sdk/credential-provider-ini" "3.887.0" + "@aws-sdk/credential-provider-process" "3.887.0" + "@aws-sdk/credential-provider-sso" "3.887.0" + "@aws-sdk/credential-provider-web-identity" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.887.0.tgz#e562eda10ff42a144cf17a2d6a77fb6d94df3575" + integrity sha512-J5TIrQ/DUiyR65gXt1j3TEbLUwMcgYVB/G68/AVgBptPvb9kj+6zFG67bJJHwxtqJxRLVLTtTi9u/YDXTqGBpQ== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.887.0.tgz#c6d5269e9713201f66826e6c1f200e1a2db4fee4" + integrity sha512-Bv9wUActLu6Kn0MK2s72bgbbNxSLPVop/If4MVbCyJ3n+prJnm5RsTF3isoWQVyyXA5g4tIrS8mE5FpejSbyPQ== + dependencies: + "@aws-sdk/client-sso" "3.887.0" + "@aws-sdk/core" "3.887.0" + "@aws-sdk/token-providers" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.887.0.tgz#e2c865fd9e347bc64c5c822dfd38b2be68162244" + integrity sha512-PRh0KRukY2euN9xvvQ3cqhCAlEkMDJIWDLIfxQ1hTbv7JA3hrcLVrV+Jg5FRWsStDhweHIvD/VzruSkhJQS80g== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/nested-clients" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-bucket-endpoint@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.887.0.tgz#a89331e130effd1ee35dec309b8163fb4a9d4920" + integrity sha512-qRCte/3MtNiMhPh4ZEGk9cHfAXq6IDTflvi2t1tkOIVZFyshkSCvNQNJrrE2D/ljVbOK1f3XbBDaF43EoQzIRQ== + dependencies: + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-arn-parser" "3.873.0" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-config-provider" "^4.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-expect-continue@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.887.0.tgz#cc52bc31752875a8d3dfa84a5705e2b563ffc39f" + integrity sha512-AlrTZZScDTG9SYeT82BC5cK/6Q4N0miN5xqMW/pbBqP3fNXlsdJOWKB+EKD3V6DV41EV5GVKHKe/1065xKSQ4w== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-flexible-checksums@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.887.0.tgz#f065450a858cb3aa9a19221cf9c8bc78937ee302" + integrity sha512-QaRGWeeHNxRvY+OUuiQ+4A7H+4HPCWCtfTiQRPzILd3C968r7EFNg2ZWyjoqITW8cj3ZJZp3p8VcH08WBzAhcQ== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-crypto/util" "5.2.0" + "@aws-sdk/core" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/is-array-buffer" "^4.0.0" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-stream" "^4.3.1" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-host-header@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.887.0.tgz#765305b5a2c412e6bf53eb6d557f2ab831ff50a7" + integrity sha512-ulzqXv6NNqdu/kr0sgBYupWmahISHY+azpJidtK6ZwQIC+vBUk9NdZeqQpy7KVhIk2xd4+5Oq9rxapPwPI21CA== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-location-constraint@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.887.0.tgz#fdf76f587c04cc8d755f05e41d4df65a78b34127" + integrity sha512-eU/9Cq4gg2sS32bOomxdx2YF43kb+o70pMhnEBBnVVeqzE8co78SO5FQdWfRTfhNJgTyQ6Vgosx//CNMPIfZPg== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.887.0.tgz#fec1c731d158306425897b371cfabdf188d07f12" + integrity sha512-YbbgLI6jKp2qSoAcHnXrQ5jcuc5EYAmGLVFgMVdk8dfCfJLfGGSaOLxF4CXC7QYhO50s+mPPkhBYejCik02Kug== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-recursion-detection@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.887.0.tgz#4fdb1039042565a4ba0ff506584a99f3c7c3fd23" + integrity sha512-tjrUXFtQnFLo+qwMveq5faxP5MQakoLArXtqieHphSqZTXm21wDJM73hgT4/PQQGTwgYjDKqnqsE1hvk0hcfDw== + dependencies: + "@aws-sdk/types" "3.887.0" + "@aws/lambda-invoke-store" "^0.0.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-sdk-s3@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.887.0.tgz#5ff7bdf2c7a03c2ec17d2b3b9d6955f675bd83bb" + integrity sha512-vWMfd8esmMX5YSenzgendh9OSIw7IcKLH46ajaNvDBdF/9X0h6eobgNX/liLzrnNHd6t7Lru2KZXSjrwYgu7pA== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-arn-parser" "3.873.0" + "@smithy/core" "^3.11.0" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/signature-v4" "^5.1.3" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-stream" "^4.3.1" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-ssec@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.887.0.tgz#861a3bdb2e0565d492a0869651a348ff36ac5faf" + integrity sha512-1ixZks0IDkdac1hjPe4vdLSuD9HznkhblCEb4T0wNyw3Ee1fdXg+MlcPWywzG5zkPXLcIrULUzJg/OSYfaDXcQ== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.887.0.tgz#ecb60f439081bec36d2390832d6ee138f69d37f5" + integrity sha512-YjBz2J4l3uCeMv2g1natat5YSMRZYdEpEg60g3d7q6hoHUD10SmWy8M+Ca8djF0is70vPmF3Icm2cArK3mtoNA== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-endpoints" "3.887.0" + "@smithy/core" "^3.11.0" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/nested-clients@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.887.0.tgz#b95906d63b0040680e7f46edcf7e699d44edde57" + integrity sha512-h6/dHuAJhJnhSDihcQd0wfJBZoPmPajASVqGk8qDxYDBWxIU9/mYcKvM+kTrKw3f9Wf3S/eR5B/rYHHuxFheSw== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.887.0" + "@aws-sdk/middleware-host-header" "3.887.0" + "@aws-sdk/middleware-logger" "3.887.0" + "@aws-sdk/middleware-recursion-detection" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/region-config-resolver" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@aws-sdk/util-endpoints" "3.887.0" + "@aws-sdk/util-user-agent-browser" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.887.0" + "@smithy/config-resolver" "^4.2.1" + "@smithy/core" "^3.11.0" + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/hash-node" "^4.1.1" + "@smithy/invalid-dependency" "^4.1.1" + "@smithy/middleware-content-length" "^4.1.1" + "@smithy/middleware-endpoint" "^4.2.1" + "@smithy/middleware-retry" "^4.2.1" + "@smithy/middleware-serde" "^4.1.1" + "@smithy/middleware-stack" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-body-length-node" "^4.1.0" + "@smithy/util-defaults-mode-browser" "^4.1.1" + "@smithy/util-defaults-mode-node" "^4.1.1" + "@smithy/util-endpoints" "^3.1.1" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-retry" "^4.1.1" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@aws-sdk/region-config-resolver@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.887.0.tgz#5e89768b44cd5e5c624852ade958579144ac2eb5" + integrity sha512-VdSMrIqJ3yjJb/fY+YAxrH/lCVv0iL8uA+lbMNfQGtO5tB3Zx6SU9LEpUwBNX8fPK1tUpI65CNE4w42+MY/7Mg== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.1.1" + tslib "^2.6.2" + +"@aws-sdk/signature-v4-multi-region@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.887.0.tgz#aaaaec78c1f10d6b57041b40fcd2449d5b677478" + integrity sha512-xAmoHzSow3692IFeAblZKRIABp4Iv96XGQKMIlHE1LugSl4KuR/6M9+UfbNMfSdyfhRt0RkG6kMZ/7GwlxqoAQ== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/protocol-http" "^5.2.1" + "@smithy/signature-v4" "^5.1.3" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.887.0.tgz#0825955ec3e518fea88649cf381839b9b07ea859" + integrity sha512-3e5fTPMPeJ5DphZ+OSqzw4ymCgDf8SQVBgrlKVo4Bch9ZwmmAoOHbuQrXVa9xQHklEHJg1Gz2pkjxNaIgx7quA== + dependencies: + "@aws-sdk/core" "3.887.0" + "@aws-sdk/nested-clients" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/types@3.887.0", "@aws-sdk/types@^3.222.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.887.0.tgz#989f3b67d7ddb97443e4bdb80ad2313c604b240d" + integrity sha512-fmTEJpUhsPsovQ12vZSpVTEP/IaRoJAMBGQXlQNjtCpkBp6Iq3KQDa/HDaPINE+3xxo6XvTdtibsNOd5zJLV9A== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/util-arn-parser@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.873.0.tgz#12c5ea852574dfb6fe78eaac1666433dff1acffa" + integrity sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-endpoints@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.887.0.tgz#e4f2dfb608360b6d8b4e3793492d4625dba00275" + integrity sha512-kpegvT53KT33BMeIcGLPA65CQVxLUL/C3gTz9AzlU/SDmeusBHX4nRApAicNzI/ltQ5lxZXbQn18UczzBuwF1w== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-endpoints" "^3.1.1" + tslib "^2.6.2" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz#cc10edef3b7aecf365943ec657116d6eb470d9cb" + integrity sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-browser@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.887.0.tgz#1c5ccc82a0b31a4b159ad98cb12abda1e6c422c8" + integrity sha512-X71UmVsYc6ZTH4KU6hA5urOzYowSXc3qvroagJNLJYU1ilgZ529lP4J9XOYfEvTXkLR1hPFSRxa43SrwgelMjA== + dependencies: + "@aws-sdk/types" "3.887.0" + "@smithy/types" "^4.5.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.887.0.tgz#e0c2ae5667197b8b830feb1c2cd05cc4735a6640" + integrity sha512-eqnx2FWAf40Nw6EyhXWjVT5WYYMz0rLrKEhZR3GdRQyOFzgnnEfq74TtG2Xji9k/ODqkcKqkiI52RYDEcdh8Jg== + dependencies: + "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/types" "3.887.0" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws-sdk/xml-builder@3.887.0": + version "3.887.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.887.0.tgz#376754d19384bbe5b8139c0a0e6521a4a6500c67" + integrity sha512-lMwgWK1kNgUhHGfBvO/5uLe7TKhycwOn3eRCqsKPT9aPCx/HWuTlpcQp8oW2pCRGLS7qzcxqpQulcD+bbUL7XQ== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@aws/lambda-invoke-store@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz#92d792a7dda250dfcb902e13228f37a81be57c8f" + integrity sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw== + +"@azure/abort-controller@^2.0.0", "@azure/abort-controller@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" + integrity sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA== + dependencies: + tslib "^2.6.2" + +"@azure/core-auth@^1.10.0", "@azure/core-auth@^1.9.0": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.10.1.tgz#68a17fa861ebd14f6fd314055798355ef6bedf1b" + integrity sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-util" "^1.13.0" + tslib "^2.6.2" + +"@azure/core-client@^1.10.0", "@azure/core-client@^1.9.3": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.10.1.tgz#83d78f97d647ab22e6811a7a68bb4223e7a1d019" + integrity sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.10.0" + "@azure/core-rest-pipeline" "^1.22.0" + "@azure/core-tracing" "^1.3.0" + "@azure/core-util" "^1.13.0" + "@azure/logger" "^1.3.0" + tslib "^2.6.2" + +"@azure/core-http-compat@^2.0.0", "@azure/core-http-compat@^2.2.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz#2182e39a31c062800d4e3ad69bcf0109d87713dc" + integrity sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-client" "^1.10.0" + "@azure/core-rest-pipeline" "^1.22.0" + +"@azure/core-lro@^2.2.0": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.7.2.tgz#787105027a20e45c77651a98b01a4d3b01b75a08" + integrity sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.2.0" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-paging@^1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.6.2.tgz#40d3860dc2df7f291d66350b2cfd9171526433e7" + integrity sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA== + dependencies: + tslib "^2.6.2" + +"@azure/core-rest-pipeline@^1.19.1", "@azure/core-rest-pipeline@^1.22.0": + version "1.22.1" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.1.tgz#f47bc02ff9a79f62e6a32aa375420b1b86dcbccd" + integrity sha512-UVZlVLfLyz6g3Hy7GNDpooMQonUygH7ghdiSASOOHy97fKj/mPLqgDX7aidOijn+sCMU+WU8NjlPlNTgnvbcGA== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.10.0" + "@azure/core-tracing" "^1.3.0" + "@azure/core-util" "^1.13.0" + "@azure/logger" "^1.3.0" + "@typespec/ts-http-runtime" "^0.3.0" + tslib "^2.6.2" + +"@azure/core-tracing@^1.2.0", "@azure/core-tracing@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.3.1.tgz#e971045c901ea9c110616b0e1db272507781d5f6" + integrity sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ== + dependencies: + tslib "^2.6.2" + +"@azure/core-util@^1.11.0", "@azure/core-util@^1.13.0", "@azure/core-util@^1.2.0": + version "1.13.1" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.13.1.tgz#6dff2ff6d3c9c6430c6f4d3b3e65de531f10bafe" + integrity sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@typespec/ts-http-runtime" "^0.3.0" + tslib "^2.6.2" + +"@azure/core-xml@^1.4.3", "@azure/core-xml@^1.4.5": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-xml/-/core-xml-1.5.0.tgz#cd82d511d7bcc548d206f5627c39724c5d5a4434" + integrity sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw== + dependencies: + fast-xml-parser "^5.0.7" + tslib "^2.8.1" + +"@azure/logger@^1.0.0", "@azure/logger@^1.1.4", "@azure/logger@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.3.0.tgz#5501cf85d4f52630602a8cc75df76568c969a827" + integrity sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA== + dependencies: + "@typespec/ts-http-runtime" "^0.3.0" + tslib "^2.6.2" + +"@azure/storage-blob@^12.15.0": + version "12.28.0" + resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.28.0.tgz#a64ce49f0fe9fe08f1f7c1b36164033678d38cf6" + integrity sha512-VhQHITXXO03SURhDiGuHhvc/k/sD2WvJUS7hqhiVNbErVCuQoLtWql7r97fleBlIRKHJaa9R7DpBjfE0pfLYcA== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.9.0" + "@azure/core-client" "^1.9.3" + "@azure/core-http-compat" "^2.2.0" + "@azure/core-lro" "^2.2.0" + "@azure/core-paging" "^1.6.2" + "@azure/core-rest-pipeline" "^1.19.1" + "@azure/core-tracing" "^1.2.0" + "@azure/core-util" "^1.11.0" + "@azure/core-xml" "^1.4.5" + "@azure/logger" "^1.1.4" + "@azure/storage-common" "^12.0.0-beta.2" + events "^3.0.0" + tslib "^2.8.1" + +"@azure/storage-common@^12.0.0", "@azure/storage-common@^12.0.0-beta.2": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@azure/storage-common/-/storage-common-12.0.0.tgz#a652d7daeb252b7827362b4e818f52fee15a1264" + integrity sha512-QyEWXgi4kdRo0wc1rHum9/KnaWZKCdQGZK1BjU4fFL6Jtedp7KLbQihgTTVxldFy1z1ZPtuDPx8mQ5l3huPPbA== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.9.0" + "@azure/core-http-compat" "^2.2.0" + "@azure/core-rest-pipeline" "^1.19.1" + "@azure/core-tracing" "^1.2.0" + "@azure/core-util" "^1.11.0" + "@azure/logger" "^1.1.4" + events "^3.3.0" + tslib "^2.8.1" + +"@azure/storage-queue@^12.15.0": + version "12.27.0" + resolved "https://registry.yarnpkg.com/@azure/storage-queue/-/storage-queue-12.27.0.tgz#cde0c3560b08320ed61e813132d27de716c45973" + integrity sha512-GoviVZrJ1BkYCmsam0gOZFqAjH7bKbnbBIEVPkgzCz3RzsB/C05jumQep+3GavZoWw7Yw4iaCNPSyyS1lbN1Gg== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.9.0" + "@azure/core-client" "^1.9.3" + "@azure/core-http-compat" "^2.0.0" + "@azure/core-paging" "^1.6.2" + "@azure/core-rest-pipeline" "^1.19.1" + "@azure/core-tracing" "^1.2.0" + "@azure/core-util" "^1.11.0" + "@azure/core-xml" "^1.4.3" + "@azure/logger" "^1.1.4" + "@azure/storage-common" "^12.0.0" + tslib "^2.8.1" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.5.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/compat@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-1.3.2.tgz#1a8766e447fad3d091b1a88b9f78f867832285b7" + integrity sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA== + +"@eslint/config-array@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636" + integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz#d316e47905bd0a1a931fa50e669b9af4104d1617" + integrity sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA== + +"@eslint/core@^0.15.2": + version "0.15.2" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.2.tgz#59386327d7862cc3603ebc7c78159d2dcc4a868f" + integrity sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.35.0": + version "9.35.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.35.0.tgz#ffbc7e13cf1204db18552e9cd9d4a8e17c692d07" + integrity sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz#fd8764f0ee79c8ddab4da65460c641cefee017c5" + integrity sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w== + dependencies: + "@eslint/core" "^0.15.2" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.7" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.4.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@kubernetes/client-node@^0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.20.0.tgz#4447ae27fd6eef3d4830a5a039f3b84ffd5c5913" + integrity sha512-xxlv5GLX4FVR/dDKEsmi4SPeuB49aRc35stndyxcC73XnUEEwF39vXbROpHOirmDse8WE9vxOjABnSVS+jb7EA== + dependencies: + "@types/js-yaml" "^4.0.1" + "@types/node" "^20.1.1" + "@types/request" "^2.47.1" + "@types/ws" "^8.5.3" + byline "^5.0.0" + isomorphic-ws "^5.0.0" + js-yaml "^4.1.0" + jsonpath-plus "^7.2.0" + request "^2.88.0" + rfc4648 "^1.3.0" + stream-buffers "^3.0.2" + tar "^6.1.11" + tslib "^2.4.1" + ws "^8.11.0" + optionalDependencies: + openid-client "^5.3.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@smithy/abort-controller@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.1.1.tgz#9b3872ab6b2c061486175c281dadc0a853260533" + integrity sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader-native@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.1.0.tgz#4d814dd07ebb1f579daf51671945389f9772400f" + integrity sha512-Bnv0B3nSlfB2mPO0WgM49I/prl7+kamF042rrf3ezJ3Z4C7csPYvyYgZfXTGXwXfj1mAwDWjE/ybIf49PzFzvA== + dependencies: + "@smithy/util-base64" "^4.1.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.1.0.tgz#48fa62c85b146be2a06525f0457ce58a46d69ab0" + integrity sha512-a36AtR7Q7XOhRPt6F/7HENmTWcB8kN7mDJcOFM/+FuKO6x88w8MQJfYCufMWh4fGyVkPjUh3Rrz/dnqFQdo6OQ== + dependencies: + tslib "^2.6.2" + +"@smithy/config-resolver@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.2.1.tgz#12c24e550e2675e03a78bec64a652ed129bce718" + integrity sha512-FXil8q4QN7mgKwU2hCLm0ltab8NyY/1RiqEf25Jnf6WLS3wmb11zGAoLETqg1nur2Aoibun4w4MjeN9CMJ4G6A== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-config-provider" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + tslib "^2.6.2" + +"@smithy/core@^3.11.0": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.11.0.tgz#18ee04696ca35889046169e422a894bea1bec59d" + integrity sha512-Abs5rdP1o8/OINtE49wwNeWuynCu0kme1r4RI3VXVrHr4odVDG7h7mTnw1WXXfN5Il+c25QOnrdL2y56USfxkA== + dependencies: + "@smithy/middleware-serde" "^4.1.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-body-length-browser" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-stream" "^4.3.1" + "@smithy/util-utf8" "^4.1.0" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/credential-provider-imds@^4.0.7", "@smithy/credential-provider-imds@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.1.tgz#e1535a0121a98a2d872eaffc2470eccc057cebd5" + integrity sha512-1WdBfM9DwA59pnpIizxnUvBf/de18p4GP+6zP2AqrlFzoW3ERpZaT4QueBR0nS9deDMaQRkBlngpVlnkuuTisQ== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/property-provider" "^4.1.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + tslib "^2.6.2" + +"@smithy/eventstream-codec@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.1.1.tgz#637eb4bceecc3ef588b86c28506439a9cdd7a41f" + integrity sha512-PwkQw1hZwHTQB6X5hSUWz2OSeuj5Z6enWuAqke7DgWoP3t6vg3ktPpqPz3Erkn6w+tmsl8Oss6nrgyezoea2Iw== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@smithy/types" "^4.5.0" + "@smithy/util-hex-encoding" "^4.1.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-browser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.1.1.tgz#f7df13ebd5a6028b12b496f12eecdd08c4c9b792" + integrity sha512-Q9QWdAzRaIuVkefupRPRFAasaG/droBqn1feiMnmLa+LLEUG45pqX1+FurHFmlqiCfobB3nUlgoJfeXZsr7MPA== + dependencies: + "@smithy/eventstream-serde-universal" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-config-resolver@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.2.1.tgz#77333a110361bfe2749b685d31e01299ede87c40" + integrity sha512-oSUkF9zDN9zcOUBMtxp8RewJlh71E9NoHWU8jE3hU9JMYCsmW4assVTpgic/iS3/dM317j6hO5x18cc3XrfvEw== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-node@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.1.1.tgz#635819a756cb8a69a7e3eb91ca9076284ea00939" + integrity sha512-tn6vulwf/ScY0vjhzptSJuDJJqlhNtUjkxJ4wiv9E3SPoEqTEKbaq6bfqRO7nvhTG29ALICRcvfFheOUPl8KNA== + dependencies: + "@smithy/eventstream-serde-universal" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-universal@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.1.1.tgz#803cdde6a17ac501cc700ce38400caf70715ecb1" + integrity sha512-uLOAiM/Dmgh2CbEXQx+6/ssK7fbzFhd+LjdyFxXid5ZBCbLHTFHLdD/QbXw5aEDsLxQhgzDxLLsZhsftAYwHJA== + dependencies: + "@smithy/eventstream-codec" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/fetch-http-handler@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz#fe284a00f1b3a35edf9fba454d287b7f74ef20af" + integrity sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng== + dependencies: + "@smithy/protocol-http" "^5.2.1" + "@smithy/querystring-builder" "^4.1.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + tslib "^2.6.2" + +"@smithy/hash-blob-browser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.1.1.tgz#fbcab0008b973ccf370c698cd11ec8d9584607c8" + integrity sha512-avAtk++s1e/1VODf+rg7c9R2pB5G9y8yaJaGY4lPZI2+UIqVyuSDMikWjeWfBVmFZ3O7NpDxBbUCyGhThVUKWQ== + dependencies: + "@smithy/chunked-blob-reader" "^5.1.0" + "@smithy/chunked-blob-reader-native" "^4.1.0" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/hash-node@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.1.1.tgz#86ceca92487492267e944e4f4507106b731e7971" + integrity sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA== + dependencies: + "@smithy/types" "^4.5.0" + "@smithy/util-buffer-from" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@smithy/hash-stream-node@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-4.1.1.tgz#1d8e4485fa15c458d7a8248a50d0f5f91705cced" + integrity sha512-3ztT4pV0Moazs3JAYFdfKk11kYFDo4b/3R3+xVjIm6wY9YpJf+xfz+ocEnNKcWAdcmSMqi168i2EMaKmJHbJMA== + dependencies: + "@smithy/types" "^4.5.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@smithy/invalid-dependency@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz#2511335ff889944701c7d2a3b1e4a4d6fe9ddfab" + integrity sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + +"@smithy/is-array-buffer@^4.0.0", "@smithy/is-array-buffer@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz#d18a2f22280e7173633cb91a9bdb6f3d8a6560b8" + integrity sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ== + dependencies: + tslib "^2.6.2" + +"@smithy/md5-js@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.1.1.tgz#df81396bef83eb17bce531c871af935df986bdfc" + integrity sha512-MvWXKK743BuHjr/hnWuT6uStdKEaoqxHAQUvbKJPPZM5ZojTNFI5D+47BoQfBE5RgGlRRty05EbWA+NXDv+hIA== + dependencies: + "@smithy/types" "^4.5.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@smithy/middleware-content-length@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz#eaea7bd14c7a0b64aef87b8c372c2a04d7b9cb72" + integrity sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w== + dependencies: + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/middleware-endpoint@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.1.tgz#54c61a113e6da7b615724d03517879d377d3888d" + integrity sha512-fUTMmQvQQZakXOuKizfu7fBLDpwvWZjfH6zUK2OLsoNZRZGbNUdNSdLJHpwk1vS208jtDjpUIskh+JoA8zMzZg== + dependencies: + "@smithy/core" "^3.11.0" + "@smithy/middleware-serde" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/shared-ini-file-loader" "^4.1.1" + "@smithy/types" "^4.5.0" + "@smithy/url-parser" "^4.1.1" + "@smithy/util-middleware" "^4.1.1" + tslib "^2.6.2" + +"@smithy/middleware-retry@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.2.1.tgz#61be10c06e183c392a3769cb8b03c7846b37bee7" + integrity sha512-JzfvjwSJXWRl7LkLgIRTUTd2Wj639yr3sQGpViGNEOjtb0AkAuYqRAHs+jSOI/LPC0ZTjmFVVtfrCICMuebexw== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/service-error-classification" "^4.1.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-retry" "^4.1.1" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/middleware-serde@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz#cfb99f53c744d7730928235cbe66cc7ff8a8a9b2" + integrity sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg== + dependencies: + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/middleware-stack@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz#1d533fde4ccbb62d7fc0f0b8ac518b7e4791e311" + integrity sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/node-config-provider@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.2.1.tgz#31be8865dbea9a9f23aee278a6728317d0ed0250" + integrity sha512-AIA0BJZq2h295J5NeCTKhg1WwtdTA/GqBCaVjk30bDgMHwniUETyh5cP9IiE9VrId7Kt8hS7zvREVMTv1VfA6g== + dependencies: + "@smithy/property-provider" "^4.1.1" + "@smithy/shared-ini-file-loader" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/node-http-handler@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz#d7ab8e31659030d3d5a68f0982f15c00b1e67a0c" + integrity sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw== + dependencies: + "@smithy/abort-controller" "^4.1.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/querystring-builder" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/property-provider@^4.0.5", "@smithy/property-provider@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.1.1.tgz#6e11ae6729840314afed05fd6ab48f62c654116b" + integrity sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/protocol-http@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.2.1.tgz#33f2b8e4e1082c3ae0372d1322577e6fa71d7824" + integrity sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/querystring-builder@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz#4d35c1735de8214055424045a117fa5d1d5cdec1" + integrity sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA== + dependencies: + "@smithy/types" "^4.5.0" + "@smithy/util-uri-escape" "^4.1.0" + tslib "^2.6.2" + +"@smithy/querystring-parser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz#21b861439b2db16abeb0a6789b126705fa25eea1" + integrity sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/service-error-classification@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.1.1.tgz#86a615298ae406c3b6c7dc63c1c1738c54cfdfc6" + integrity sha512-Iam75b/JNXyDE41UvrlM6n8DNOa/r1ylFyvgruTUx7h2Uk7vDNV9AAwP1vfL1fOL8ls0xArwEGVcGZVd7IO/Cw== + dependencies: + "@smithy/types" "^4.5.0" + +"@smithy/shared-ini-file-loader@^4.0.5", "@smithy/shared-ini-file-loader@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.1.1.tgz#d4a748bb8027e1111635464c9b1e546d608fc089" + integrity sha512-YkpikhIqGc4sfXeIbzSj10t2bJI/sSoP5qxLue6zG+tEE3ngOBSm8sO3+djacYvS/R5DfpxN/L9CyZsvwjWOAQ== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/signature-v4@^5.1.3": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.2.1.tgz#0048489d2f1b3c888382595a085edd31967498f8" + integrity sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA== + dependencies: + "@smithy/is-array-buffer" "^4.1.0" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-hex-encoding" "^4.1.0" + "@smithy/util-middleware" "^4.1.1" + "@smithy/util-uri-escape" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@smithy/smithy-client@^4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.6.1.tgz#4bebcf313431bd274da0b28c7ddc4ba335f9994b" + integrity sha512-WolVLDb9UTPMEPPOncrCt6JmAMCSC/V2y5gst2STWJ5r7+8iNac+EFYQnmvDCYMfOLcilOSEpm5yXZXwbLak1Q== + dependencies: + "@smithy/core" "^3.11.0" + "@smithy/middleware-endpoint" "^4.2.1" + "@smithy/middleware-stack" "^4.1.1" + "@smithy/protocol-http" "^5.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-stream" "^4.3.1" + tslib "^2.6.2" + +"@smithy/types@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.5.0.tgz#850e334662a1ef1286c35814940c80880400a370" + integrity sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg== + dependencies: + tslib "^2.6.2" + +"@smithy/url-parser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.1.1.tgz#0e9a5e72b3cf9d7ab7305f9093af5528d9debaf6" + integrity sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg== + dependencies: + "@smithy/querystring-parser" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/util-base64@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.1.0.tgz#5965026081d9aef4a8246f5702807570abe538b2" + integrity sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ== + dependencies: + "@smithy/util-buffer-from" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@smithy/util-body-length-browser@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz#636bdf4bc878c546627dab4b9b0e4db31b475be7" + integrity sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-body-length-node@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz#646750e4af58f97254a5d5cfeaba7d992f0152ec" + integrity sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + dependencies: + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-buffer-from@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz#21f9e644a0eb41226d92e4eff763f76a7db7e9cc" + integrity sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw== + dependencies: + "@smithy/is-array-buffer" "^4.1.0" + tslib "^2.6.2" + +"@smithy/util-config-provider@^4.0.0", "@smithy/util-config-provider@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz#6a07d73446c1e9a46d7a3c125f2a9301060bc957" + integrity sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-defaults-mode-browser@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.1.tgz#40b9659d6fc15aa1101e440d1a92579cb66ebfa3" + integrity sha512-hA1AKIHFUMa9Tl6q6y8p0pJ9aWHCCG8s57flmIyLE0W7HcJeYrYtnqXDcGnftvXEhdQnSexyegXnzzTGk8bKLA== + dependencies: + "@smithy/property-provider" "^4.1.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@smithy/util-defaults-mode-node@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.1.tgz#bca834c5ee16949bf8d0db9ac7bf988ad0d3ce10" + integrity sha512-RGSpmoBrA+5D2WjwtK7tto6Pc2wO9KSXKLpLONhFZ8VyuCbqlLdiDAfuDTNY9AJe4JoE+Cx806cpTQQoQ71zPQ== + dependencies: + "@smithy/config-resolver" "^4.2.1" + "@smithy/credential-provider-imds" "^4.1.1" + "@smithy/node-config-provider" "^4.2.1" + "@smithy/property-provider" "^4.1.1" + "@smithy/smithy-client" "^4.6.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/util-endpoints@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.1.1.tgz#62c7e10e3a685c9cbb4080220d9e819ee79be8ff" + integrity sha512-qB4R9kO0SetA11Rzu6MVGFIaGYX3p6SGGGfWwsKnC6nXIf0n/0AKVwRTsYsz9ToN8CeNNtNgQRwKFBndGJZdyw== + dependencies: + "@smithy/node-config-provider" "^4.2.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/util-hex-encoding@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz#9b27cf0c25d0de2c8ebfe75cc20df84e5014ccc9" + integrity sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w== + dependencies: + tslib "^2.6.2" + +"@smithy/util-middleware@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.1.1.tgz#e19749a127499c9bdada713a8afd807d92d846e2" + integrity sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg== + dependencies: + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/util-retry@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.1.1.tgz#f4a99d9b0ffb9e4bb119ac5a24e54e54d891e22c" + integrity sha512-jGeybqEZ/LIordPLMh5bnmnoIgsqnp4IEimmUp5c5voZ8yx+5kAlN5+juyr7p+f7AtZTgvhmInQk4Q0UVbrZ0Q== + dependencies: + "@smithy/service-error-classification" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@smithy/util-stream@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.3.1.tgz#63cce0f09d99d75142c6dc8fe03e55ac0171de47" + integrity sha512-khKkW/Jqkgh6caxMWbMuox9+YfGlsk9OnHOYCGVEdYQb/XVzcORXHLYUubHmmda0pubEDncofUrPNniS9d+uAA== + dependencies: + "@smithy/fetch-http-handler" "^5.2.1" + "@smithy/node-http-handler" "^4.2.1" + "@smithy/types" "^4.5.0" + "@smithy/util-base64" "^4.1.0" + "@smithy/util-buffer-from" "^4.1.0" + "@smithy/util-hex-encoding" "^4.1.0" + "@smithy/util-utf8" "^4.1.0" + tslib "^2.6.2" + +"@smithy/util-uri-escape@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz#ed4a5c498f1da07122ca1e3df4ca3e2c67c6c18a" + integrity sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg== + dependencies: + tslib "^2.6.2" + +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-utf8@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.1.0.tgz#912c33c1a06913f39daa53da79cb8f7ab740d97b" + integrity sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ== + dependencies: + "@smithy/util-buffer-from" "^4.1.0" + tslib "^2.6.2" + +"@smithy/util-waiter@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.1.1.tgz#5b74429ca9e37f61838800b919d0063b1a865bef" + integrity sha512-PJBmyayrlfxM7nbqjomF4YcT1sApQwZio0NHSsT0EzhJqljRmvhzqZua43TyEs80nJk2Cn2FGPg/N8phH6KeCQ== + dependencies: + "@smithy/abort-controller" "^4.1.1" + "@smithy/types" "^4.5.0" + tslib "^2.6.2" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/caseless@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== + +"@types/estree@^1.0.6": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/js-yaml@^4.0.1": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" + integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== + +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*": + version "24.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.1.tgz#b0a3fb2afed0ef98e8d7f06d46ef6349047709f3" + integrity sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g== + dependencies: + undici-types "~7.10.0" + +"@types/node@^20.1.1", "@types/node@^20.5.0": + version "20.19.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.13.tgz#b79004a05068e28fb2de281b3a44c5c993650e59" + integrity sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g== + dependencies: + undici-types "~6.21.0" + +"@types/request@^2.47.1": + version "2.48.13" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.13.tgz#abdf4256524e801ea8fdda54320f083edb5a6b80" + integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.5" + +"@types/semver@^7.5.0": + version "7.7.1" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" + integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== + +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + +"@types/uuid@^9.0.1": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + +"@types/ws@^8.5.3": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz#4d730c2becd8e47ef76e59f68aee0fb560927cfc" + integrity sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.43.0" + "@typescript-eslint/type-utils" "8.43.0" + "@typescript-eslint/utils" "8.43.0" + "@typescript-eslint/visitor-keys" "8.43.0" + graphemer "^1.4.0" + ignore "^7.0.0" + natural-compare "^1.4.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/eslint-plugin@^6.4.1": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" + integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/type-utils" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.43.0.tgz#4024159925e7671f1782bdd3498bdcfbd48f9137" + integrity sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw== + dependencies: + "@typescript-eslint/scope-manager" "8.43.0" + "@typescript-eslint/types" "8.43.0" + "@typescript-eslint/typescript-estree" "8.43.0" + "@typescript-eslint/visitor-keys" "8.43.0" + debug "^4.3.4" + +"@typescript-eslint/parser@^6.4.1": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + +"@typescript-eslint/project-service@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.43.0.tgz#958dbaa16fbd1e81d46ab86e139f6276757140f8" + integrity sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.43.0" + "@typescript-eslint/types" "^8.43.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + +"@typescript-eslint/scope-manager@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz#009ebc09cc6e7e0dd67898a0e9a70d295361c6b9" + integrity sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg== + dependencies: + "@typescript-eslint/types" "8.43.0" + "@typescript-eslint/visitor-keys" "8.43.0" + +"@typescript-eslint/tsconfig-utils@8.43.0", "@typescript-eslint/tsconfig-utils@^8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz#e6721dba183d61769a90ffdad202aebc383b18c8" + integrity sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA== + +"@typescript-eslint/type-utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" + integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== + dependencies: + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/type-utils@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz#29ea2e34eeae5b8e9fe4f4730c5659fa330aa04e" + integrity sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg== + dependencies: + "@typescript-eslint/types" "8.43.0" + "@typescript-eslint/typescript-estree" "8.43.0" + "@typescript-eslint/utils" "8.43.0" + debug "^4.3.4" + ts-api-utils "^2.1.0" + +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== + +"@typescript-eslint/types@8.43.0", "@typescript-eslint/types@^8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.43.0.tgz#00d34a5099504eb1b263e022cc17c4243ff2302e" + integrity sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw== + +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/typescript-estree@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz#39e5d431239b4d90787072ae0c2290cbd3e0a562" + integrity sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw== + dependencies: + "@typescript-eslint/project-service" "8.43.0" + "@typescript-eslint/tsconfig-utils" "8.43.0" + "@typescript-eslint/types" "8.43.0" + "@typescript-eslint/visitor-keys" "8.43.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" + integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + semver "^7.5.4" + +"@typescript-eslint/utils@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.43.0.tgz#5c391133a52f8500dfdabd7026be72a537d7b59e" + integrity sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g== + dependencies: + "@eslint-community/eslint-utils" "^4.7.0" + "@typescript-eslint/scope-manager" "8.43.0" + "@typescript-eslint/types" "8.43.0" + "@typescript-eslint/typescript-estree" "8.43.0" + +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== + dependencies: + "@typescript-eslint/types" "6.21.0" + eslint-visitor-keys "^3.4.1" + +"@typescript-eslint/visitor-keys@8.43.0": + version "8.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz#633d3414afec3cf0a0e4583e1575f4101ef51d30" + integrity sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw== + dependencies: + "@typescript-eslint/types" "8.43.0" + eslint-visitor-keys "^4.2.1" + +"@typespec/ts-http-runtime@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.1.tgz#2fa94050f25b4d85d0bc8b9d97874b8d347a9173" + integrity sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww== + dependencies: + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + tslib "^2.6.2" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + +ajv@^6.12.3, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.13.2" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" + integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== + +axios@^1.5.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3" + integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.4" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +bowser@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.12.1.tgz#f9ad78d7aebc472feb63dd9635e3ce2337e0e2c1" + integrity sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@11.1.0, commander@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dotenv@^16.3.1: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +entities@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-scality@scality/Guidelines#8.3.0: + version "8.3.0" + resolved "https://codeload.github.com/scality/Guidelines/tar.gz/666b90495dc7e9a401a37ba4d58c7eba89db90ac" + dependencies: + commander "11.1.0" + markdownlint "0.31.1" + +eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^9.35.0: + version "9.35.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.35.0.tgz#7a89054b7b9ee1dfd1b62035d8ce75547773f47e" + integrity sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg== + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.21.0" + "@eslint/config-helpers" "^0.3.1" + "@eslint/core" "^0.15.2" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.35.0" + "@eslint/plugin-kit" "^0.3.5" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +events@^3.0.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9, fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-safe-stringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fast-xml-parser@5.2.5, fast-xml-parser@^5.0.7: + version "5.2.5" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz#4809fdfb1310494e341098c25cb1341a01a9144a" + integrity sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ== + dependencies: + strnum "^2.1.0" + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +follow-redirects@^1.15.6: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data@^2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" + +form-data@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +ignore@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +jose@^4.15.9: + version "4.15.9" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" + integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +jsonpath-plus@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz#7ad94e147b3ed42f7939c315d2b9ce490c5a3899" + integrity sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA== + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +linkify-it@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" + integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== + dependencies: + uc.micro "^1.0.1" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +markdown-it@13.0.1: + version "13.0.1" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430" + integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +markdownlint-micromark@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/markdownlint-micromark/-/markdownlint-micromark-0.1.7.tgz#c465091b30d61a56027ccbfb981c80c96448c165" + integrity sha512-BbRPTC72fl5vlSKv37v/xIENSRDYL/7X/XoFzZ740FGEbs9vZerLrIkFRY0rv7slQKxDczToYuMmqQFN61fi4Q== + +markdownlint@0.31.1: + version "0.31.1" + resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.31.1.tgz#f014ed2d3614c5dbc351b7f65641ccc0a5facdb7" + integrity sha512-CKMR2hgcIBrYlIUccDCOvi966PZ0kJExDrUi1R+oF9PvqQmCrTqjOsgIvf2403OmJ+CWomuzDoylr6KbuMyvHA== + dependencies: + markdown-it "13.0.1" + markdownlint-micromark "0.1.7" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-hash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + +oidc-token-hash@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz#d35e31ca26d3a26678f5e9bda100b095ab58011f" + integrity sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g== + +openid-client@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.7.1.tgz#34cace862a3e6472ed7d0a8616ef73b7fb85a9c3" + integrity sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew== + dependencies: + jose "^4.15.9" + lru-cache "^6.0.0" + object-hash "^2.2.0" + oidc-token-hash "^5.0.3" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +psl@^1.1.28: + version "1.15.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== + dependencies: + punycode "^2.3.1" + +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +rfc4648@^1.3.0: + version "1.5.4" + resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.4.tgz#1174c0afba72423a0b70c386ecfeb80aa61b05ca" + integrity sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-json-stringify@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.5.4, semver@^7.6.0: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +sshpk@^1.7.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stream-buffers@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.3.tgz#9fc6ae267d9c4df1190a781e011634cac58af3cd" + integrity sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strnum@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.1.tgz#cf2a6e0cf903728b8b2c4b971b7e36b4e82d46ab" + integrity sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +tar@^6.1.11: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +ts-api-utils@^1.0.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" + integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== + +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + +ts-node@^10.9.1: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@^2.4.1, tslib@^2.6.2, tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +typescript-eslint@^8.43.0: + version "8.43.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.43.0.tgz#335ac16a859f385dfb23022e0d8298962364d099" + integrity sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w== + dependencies: + "@typescript-eslint/eslint-plugin" "8.43.0" + "@typescript-eslint/parser" "8.43.0" + "@typescript-eslint/typescript-estree" "8.43.0" + "@typescript-eslint/utils" "8.43.0" + +typescript@^5.2.2: + version "5.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" + integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +undici-types@~7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" + integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +werelogs@scality/werelogs#8.2.2: + version "8.2.2" + resolved "https://codeload.github.com/scality/werelogs/tar.gz/e53bef5145697bf8af940dcbe59408988d64854f" + dependencies: + fast-safe-stringify "^2.1.1" + safe-json-stringify "^1.2.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +ws@^8.11.0: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.3.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" + integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From f6eec856e9f7f5a990b69185a30ee4195bac2a4d Mon Sep 17 00:00:00 2001 From: williamlardier Date: Fri, 12 Sep 2025 16:40:47 +0200 Subject: [PATCH 06/14] Use latest ctst --- tests/ctst/package.json | 2 +- tests/ctst/yarn.lock | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/ctst/package.json b/tests/ctst/package.json index 30c1e2b47c..f75344abc6 100644 --- a/tests/ctst/package.json +++ b/tests/ctst/package.json @@ -26,7 +26,7 @@ "@aws-sdk/client-s3": "^3.583.0", "@aws-sdk/client-sts": "^3.583.0", "@eslint/compat": "^1.1.1", - "cli-testing": "github:scality/cli-testing.git#83e8f3d0636de162390e78012ca8b05b94af8b77", + "cli-testing": "github:scality/cli-testing.git#5931b87f9394bec1c1370caf9ba5162cceffac0c", "eslint": "^9.9.1", "eslint-config-scality": "scality/Guidelines#8.3.0", "typescript-eslint": "^8.4.0" diff --git a/tests/ctst/yarn.lock b/tests/ctst/yarn.lock index f9ed05a4b9..2d8a679734 100644 --- a/tests/ctst/yarn.lock +++ b/tests/ctst/yarn.lock @@ -2912,6 +2912,11 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.5.tgz#f090ff4bd8d2e5b940ff270ab39fd5ca1834a07e" integrity sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw== +"@types/semver@^7.7.1": + version "7.7.1" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" + integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== + "@types/stream-buffers@^3.0.3": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.7.tgz#0b719fa1bd2ca2cc0908205a440e5e569e1aa21e" @@ -3435,9 +3440,9 @@ cli-table3@0.6.5, cli-table3@^0.6.0: optionalDependencies: "@colors/colors" "1.5.0" -"cli-testing@github:scality/cli-testing.git#827cd4683b55b8b7222ed3f5672ee5d826d3685f": +"cli-testing@github:scality/cli-testing.git#5931b87f9394bec1c1370caf9ba5162cceffac0c": version "1.3.0" - resolved "git+ssh://git@github.com/scality/cli-testing.git#827cd4683b55b8b7222ed3f5672ee5d826d3685f" + resolved "git+ssh://git@github.com/scality/cli-testing.git#5931b87f9394bec1c1370caf9ba5162cceffac0c" dependencies: "@aws-crypto/sha256-universal" "^5.2.0" "@aws-sdk/client-iam" "^3.879.0" @@ -3450,6 +3455,8 @@ cli-table3@0.6.5, cli-table3@^0.6.0: "@keycloak/keycloak-admin-client" "^26.3.3" "@kubernetes/client-node" "^1.3.0" "@smithy/signature-v4" "^5.1.3" + "@types/proper-lockfile" "^4.1.4" + "@types/semver" "^7.7.1" junit-xml "^1.2.0" node-rdkafka "^3.5.0" proper-lockfile "^4.1.2" From 9217ed914be0c70c014087596494a812eb134882 Mon Sep 17 00:00:00 2001 From: williamlardier Date: Fri, 12 Sep 2025 17:36:02 +0200 Subject: [PATCH 07/14] bump kube client --- tests/@setup/src/buckets.ts | 18 +- tests/@setup/src/dns.ts | 8 +- tests/@setup/src/keycloak.ts | 4 +- tests/@setup/src/locations.ts | 4 +- tests/@setup/src/rbac.ts | 2 +- tests/@setup/src/utils/k8s.ts | 2 +- tests/ctst/package.json | 2 +- tests/ctst/steps/setup/setup.ts | 157 ++++++----- tests/ctst/steps/utils/kubernetes.ts | 149 ++++++----- tests/ctst/yarn.lock | 372 +-------------------------- 10 files changed, 191 insertions(+), 527 deletions(-) diff --git a/tests/@setup/src/buckets.ts b/tests/@setup/src/buckets.ts index 176bf97fa1..f8a5564e66 100644 --- a/tests/@setup/src/buckets.ts +++ b/tests/@setup/src/buckets.ts @@ -33,11 +33,11 @@ async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions): const awsSecret = await k8s.coreApi.readNamespacedSecret('aws-mock-credentials', options.namespace); const awsConfig = { credentials: { - accessKeyId: Buffer.from(awsSecret.body.data!['aws-access-key-id'], 'base64').toString(), - secretAccessKey: Buffer.from(awsSecret.body.data!['aws-secret-access-key'], 'base64').toString() - }, - region: Buffer.from(awsSecret.body.data!['aws-region'], 'base64').toString(), - endpoint: Buffer.from(awsSecret.body.data!['aws-endpoint'], 'base64').toString(), + accessKeyId: Buffer.from(awsSecret.data!['aws-access-key-id'], 'base64').toString(), + secretAccessKey: Buffer.from(awsSecret.data!['aws-secret-access-key'], 'base64').toString() + }, + region: Buffer.from(awsSecret.data!['aws-region'], 'base64').toString(), + endpoint: Buffer.from(awsSecret.data!['aws-endpoint'], 'base64').toString(), forcePathStyle: true }; @@ -105,10 +105,10 @@ async function setupAzureBuckets(k8s: KubernetesClient, options: BucketsOptions) // Get Azure credentials from mock service const azureSecret = await k8s.coreApi.readNamespacedSecret('azure-mock-credentials', options.namespace); - const accountName = Buffer.from(azureSecret.body.data!['account-name'], 'base64').toString(); - const accountKey = Buffer.from(azureSecret.body.data!['account-key'], 'base64').toString(); - const blobEndpoint = Buffer.from(azureSecret.body.data!['blob-endpoint'], 'base64').toString(); - const queueEndpoint = Buffer.from(azureSecret.body.data!['queue-endpoint'], 'base64').toString(); + const accountName = Buffer.from(azureSecret.data!['account-name'], 'base64').toString(); + const accountKey = Buffer.from(azureSecret.data!['account-key'], 'base64').toString(); + const blobEndpoint = Buffer.from(azureSecret.data!['blob-endpoint'], 'base64').toString(); + const queueEndpoint = Buffer.from(azureSecret.data!['queue-endpoint'], 'base64').toString(); const blobSharedKeyCredential = new BlobStorageSharedKeyCredential(accountName, accountKey); const queueSharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey); diff --git a/tests/@setup/src/dns.ts b/tests/@setup/src/dns.ts index c89c44b558..582193da39 100644 --- a/tests/@setup/src/dns.ts +++ b/tests/@setup/src/dns.ts @@ -45,7 +45,7 @@ export async function setupDNS(options: DNSOptions): Promise { } // Parse current Corefile - const currentCorefile = coreDnsConfigMap.body.data?.['Corefile'] || ''; + const currentCorefile = coreDnsConfigMap.data?.['Corefile'] || ''; // Generate rewrite rules for test domains const rewriteRules = generateRewriteRules(options.subdomain, options.namespace); @@ -63,7 +63,7 @@ export async function setupDNS(options: DNSOptions): Promise { const updatedConfigMap = { ...coreDnsConfigMap.body, data: { - ...coreDnsConfigMap.body.data, + ...coreDnsConfigMap.data, 'Corefile': newCorefile } }; @@ -179,10 +179,10 @@ async function restartCoreDNS(k8s: KubernetesClient): Promise { const deployment = await k8s.appsApi.readNamespacedDeployment('coredns', 'kube-system'); // Add/update restart annotation to trigger rolling restart - const annotations = deployment.body.spec?.template.metadata?.annotations || {}; + const annotations = deployment.spec?.template.metadata?.annotations || {}; annotations['kubectl.kubernetes.io/restartedAt'] = new Date().toISOString(); - deployment.body.spec!.template.metadata!.annotations = annotations; + deployment.spec!.template.metadata!.annotations = annotations; await k8s.appsApi.replaceNamespacedDeployment('coredns', 'kube-system', deployment.body); diff --git a/tests/@setup/src/keycloak.ts b/tests/@setup/src/keycloak.ts index b2ace3704a..5d3eb5b569 100644 --- a/tests/@setup/src/keycloak.ts +++ b/tests/@setup/src/keycloak.ts @@ -39,7 +39,7 @@ async function getKeycloakConfig(k8s: KubernetesClient, namespace: string): Prom try { // Look for Keycloak service const services = await k8s.coreApi.listNamespacedService(namespace); - const keycloakService = services.body.items.find(svc => + const keycloakService = services.items.find(svc => svc.metadata?.name?.toLowerCase().includes('keycloak') || svc.metadata?.name?.toLowerCase().includes('auth') ); @@ -50,7 +50,7 @@ async function getKeycloakConfig(k8s: KubernetesClient, namespace: string): Prom // Look for Keycloak admin credentials const secrets = await k8s.coreApi.listNamespacedSecret(namespace); - const keycloakSecret = secrets.body.items.find(secret => + const keycloakSecret = secrets.items.find(secret => secret.metadata?.name?.toLowerCase().includes('keycloak') && (secret.metadata?.name?.toLowerCase().includes('admin') || secret.metadata?.name?.toLowerCase().includes('credentials')) diff --git a/tests/@setup/src/locations.ts b/tests/@setup/src/locations.ts index 5fa391b1f9..6a77874436 100644 --- a/tests/@setup/src/locations.ts +++ b/tests/@setup/src/locations.ts @@ -82,7 +82,7 @@ async function getManagementEndpoint(k8s: KubernetesClient, namespace: string): try { // Try to find Management API service const services = await k8s.coreApi.listNamespacedService(namespace); - const mgmtService = services.body.items.find(svc => + const mgmtService = services.items.find(svc => svc.metadata?.name?.includes('management') || svc.metadata?.name?.includes('api') || svc.metadata?.name?.includes('zenko-management') @@ -106,7 +106,7 @@ async function getManagementCredentials(k8s: KubernetesClient, namespace: string try { // Look for admin credentials in secrets const secrets = await k8s.coreApi.listNamespacedSecret(namespace); - const adminSecret = secrets.body.items.find(secret => + const adminSecret = secrets.items.find(secret => secret.metadata?.name?.includes('admin') || secret.metadata?.name?.includes('management') || secret.metadata?.name?.includes('credentials') diff --git a/tests/@setup/src/rbac.ts b/tests/@setup/src/rbac.ts index d8975387b3..65aaf7d5c4 100644 --- a/tests/@setup/src/rbac.ts +++ b/tests/@setup/src/rbac.ts @@ -30,7 +30,7 @@ export async function setupRBAC(options: RBACOptions): Promise { // Get all service accounts in the namespace const serviceAccounts = await k8s.coreApi.listNamespacedServiceAccount(options.namespace); - const zenkoServiceAccounts = serviceAccounts.body.items.filter(sa => + const zenkoServiceAccounts = serviceAccounts.items.filter(sa => sa.metadata?.name?.includes('zenko') || sa.metadata?.name?.includes('cloudserver') || sa.metadata?.name?.includes('backbeat') || diff --git a/tests/@setup/src/utils/k8s.ts b/tests/@setup/src/utils/k8s.ts index fde55cd830..e0fb4da326 100644 --- a/tests/@setup/src/utils/k8s.ts +++ b/tests/@setup/src/utils/k8s.ts @@ -173,7 +173,7 @@ export class KubernetesClient { while (Date.now() - startTime < timeoutMs) { try { const deployment = await this.appsApi.readNamespacedDeployment(name, namespace); - const status = deployment.body.status; + const status = deployment.status; if (status?.readyReplicas === status?.replicas && status?.replicas && status.replicas > 0) { logger.debug(`Deployment ${name} is ready`); diff --git a/tests/ctst/package.json b/tests/ctst/package.json index f75344abc6..5c99bde6a7 100644 --- a/tests/ctst/package.json +++ b/tests/ctst/package.json @@ -8,7 +8,7 @@ "license": "ISC", "private": true, "dependencies": { - "@kubernetes/client-node": "^0.21.0", + "@kubernetes/client-node": "^1.3.0", "@types/node": "^18.19.121", "@types/proper-lockfile": "^4.1.4", "@types/qs": "^6.9.15", diff --git a/tests/ctst/steps/setup/setup.ts b/tests/ctst/steps/setup/setup.ts index 1b376e48de..e7f099cf01 100644 --- a/tests/ctst/steps/setup/setup.ts +++ b/tests/ctst/steps/setup/setup.ts @@ -67,9 +67,12 @@ async function extractAdminCredentials(coreClient: CoreV1Api, parameters: ZenkoW if (!adminAccessKey || !adminSecretKey) { const adminSecret = await coreClient - .readNamespacedSecret('end2end-management-vault-admin-creds.v1', parameters.Namespace); - adminAccessKey = Buffer.from(adminSecret.body.data?.accessKey || '', 'base64').toString(); - adminSecretKey = Buffer.from(adminSecret.body.data?.secretKey || '', 'base64').toString(); + .readNamespacedSecret({ + name: 'end2end-management-vault-admin-creds.v1', + namespace: parameters.Namespace + }); + adminAccessKey = Buffer.from(adminSecret.data?.accessKey || '', 'base64').toString(); + adminSecretKey = Buffer.from(adminSecret.data?.secretKey || '', 'base64').toString(); } const finalAdminAccessKey = adminAccessKey || parameters.AdminAccessKey || 'admin'; @@ -98,10 +101,13 @@ async function extractPRACredentials(coreClient: CoreV1Api, parameters: ZenkoWor if (parameters.DRSubdomain) { try { const praAdminSecret = await coreClient - .readNamespacedSecret('end2end-pra-management-vault-admin-creds.v1', parameters.Namespace); + .readNamespacedSecret({ + name: 'end2end-pra-management-vault-admin-creds.v1', + namespace: parameters.Namespace + }); - praAdminAccessKey = Buffer.from(praAdminSecret.body.data?.accessKey || '', 'base64').toString(); - praAdminSecretKey = Buffer.from(praAdminSecret.body.data?.secretKey || '', 'base64').toString(); + praAdminAccessKey = Buffer.from(praAdminSecret.data?.accessKey || '', 'base64').toString(); + praAdminSecretKey = Buffer.from(praAdminSecret.data?.secretKey || '', 'base64').toString(); logger.info('PRA credentials extracted from Kubernetes secret'); } catch (error) { if ((error as { response?: { statusCode?: number } }).response?.statusCode === 404) { @@ -137,16 +143,26 @@ async function extractServiceCredentials(coreClient: CoreV1Api, parameters: Zenk } const serviceUserSecrets = await Promise.allSettled([ - coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=backbeat-lcbp-user-creds,app.kubernetes.io/instance=end2end'), - coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=backbeat-lcc-user-creds,app.kubernetes.io/instance=end2end'), - coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=backbeat-lcop-user-creds,app.kubernetes.io/instance=end2end'), - coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=backbeat-qp-user-creds,app.kubernetes.io/instance=end2end'), - coreClient.listNamespacedSecret(parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end'), + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=backbeat-lcbp-user-creds,app.kubernetes.io/instance=end2end' + }), + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=backbeat-lcc-user-creds,app.kubernetes.io/instance=end2end' + }), + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=backbeat-lcop-user-creds,app.kubernetes.io/instance=end2end' + }), + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=backbeat-qp-user-creds,app.kubernetes.io/instance=end2end' + }), + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=sorbet-fwd-creds,app.kubernetes.io/instance=end2end' + }), ]); const credentials: Record = {}; @@ -159,18 +175,18 @@ async function extractServiceCredentials(coreClient: CoreV1Api, parameters: Zenk ]; serviceCredentialHandlers.forEach(({ index, key, name }) => { - if (serviceUserSecrets[index].status === 'fulfilled' && serviceUserSecrets[index].value.body.items.length > 0) { + if (serviceUserSecrets[index].status === 'fulfilled' && serviceUserSecrets[index].value.items.length > 0) { const data = - Buffer.from(serviceUserSecrets[index].value.body.items[0].data?.[key] || '', 'base64').toString(); + Buffer.from(serviceUserSecrets[index].value.items[0].data?.[key] || '', 'base64').toString(); credentials[name] = JSON.parse(data) as ServiceUserCredentials; } }); - if (serviceUserSecrets[4].status === 'fulfilled' && serviceUserSecrets[4].value.body.items.length > 0) { + if (serviceUserSecrets[4].status === 'fulfilled' && serviceUserSecrets[4].value.items.length > 0) { const sorbetAccessKey = - Buffer.from(serviceUserSecrets[4].value.body.items[0].data?.accessKey || '', 'base64').toString(); + Buffer.from(serviceUserSecrets[4].value.items[0].data?.accessKey || '', 'base64').toString(); const sorbetSecretKey = - Buffer.from(serviceUserSecrets[4].value.body.items[0].data?.secretKey || '', 'base64').toString(); + Buffer.from(serviceUserSecrets[4].value.items[0].data?.secretKey || '', 'base64').toString(); credentials['sorbet-fwd-2'] = { accessKey: sorbetAccessKey, secretKey: sorbetSecretKey, @@ -199,13 +215,13 @@ async function extractKafkaConfiguration(coreClient: CoreV1Api, parameters: Zenk if (!kafkaHosts || !backbeatApiHost) { configExtractionTasks.push( - coreClient.listNamespacedSecret( - parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end' - ).then(backbeatConfigSecrets => { - if (backbeatConfigSecrets.body.items.length > 0) { + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end' + }).then(backbeatConfigSecrets => { + if (backbeatConfigSecrets.items.length > 0) { const configData = Buffer.from( - backbeatConfigSecrets.body.items[0].data?.['config.json'] || '', + backbeatConfigSecrets.items[0].data?.['config.json'] || '', 'base64', ).toString(); const config = JSON.parse(configData); @@ -215,13 +231,13 @@ async function extractKafkaConfiguration(coreClient: CoreV1Api, parameters: Zenk ); configExtractionTasks.push( - coreClient.listNamespacedSecret( - parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end' - ).then(cloudserverConfigSecrets => { - if (cloudserverConfigSecrets.body.items.length > 0) { + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=connector-cloudserver-config,app.kubernetes.io/instance=end2end' + }).then(cloudserverConfigSecrets => { + if (cloudserverConfigSecrets.items.length > 0) { const cloudserverConfigData = Buffer.from( - cloudserverConfigSecrets.body.items[0].data?.['config.json'] || '', + cloudserverConfigSecrets.items[0].data?.['config.json'] || '', 'base64', ).toString(); const cloudserverConfig = JSON.parse(cloudserverConfigData); @@ -234,13 +250,14 @@ async function extractKafkaConfiguration(coreClient: CoreV1Api, parameters: Zenk if (!kafkaDeadLetterTopic || !kafkaObjectTaskTopic || !kafkaGCRequestTopic) { configExtractionTasks.push( - coreClient.listNamespacedSecret( - parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end' - ).then(sorbetConfigSecrets => { - if (sorbetConfigSecrets.body.items.length > 0) { + coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + // eslint-disable-next-line max-len + labelSelector: 'app.kubernetes.io/name=cold-sorbet-config-e2e-azure-archive,app.kubernetes.io/instance=end2end' + }).then(sorbetConfigSecrets => { + if (sorbetConfigSecrets.items.length > 0) { const sorbetConfigData = - Buffer.from(sorbetConfigSecrets.body.items[0].data?.['config.json'] || '', 'base64').toString(); + Buffer.from(sorbetConfigSecrets.items[0].data?.['config.json'] || '', 'base64').toString(); const sorbetConfig = JSON.parse(sorbetConfigData); kafkaDeadLetterTopic = kafkaDeadLetterTopic || sorbetConfig['kafka-dead-letter-topic'] || ''; kafkaObjectTaskTopic = kafkaObjectTaskTopic || sorbetConfig['kafka-object-task-topic'] || ''; @@ -416,13 +433,13 @@ async function setupKafkaTopics(coreClient: CoreV1Api, parameters: ZenkoWorldPar // Extract UUID from backbeat config like the old script let uuid = parameters.Namespace; try { - const backbeatConfigSecrets = await coreClient.listNamespacedSecret( - parameters.Namespace, undefined, undefined, undefined, undefined, - 'app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end' - ); - if (backbeatConfigSecrets.body.items.length > 0) { + const backbeatConfigSecrets = await coreClient.listNamespacedSecret({ + namespace: parameters.Namespace, + labelSelector: 'app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=end2end' + }); + if (backbeatConfigSecrets.items.length > 0) { const configData = Buffer.from( - backbeatConfigSecrets.body.items[0].data?.['config.json'] || '', + backbeatConfigSecrets.items[0].data?.['config.json'] || '', 'base64', ).toString(); const config = JSON.parse(configData); @@ -470,7 +487,7 @@ async function setupKafkaTopics(coreClient: CoreV1Api, parameters: ZenkoWorldPar }, }; - await coreClient.createNamespacedPod(parameters.Namespace, kafkaTopicsPod); + await coreClient.createNamespacedPod({ namespace: parameters.Namespace, body: kafkaTopicsPod }); logger.info('Kafka topics setup initiated', { kafkaTopicsPod, }); @@ -518,7 +535,7 @@ async function createAwsMockConfigMap(coreClient: CoreV1Api, parameters: ZenkoWo binaryData: configMapData, }; - await coreClient.createNamespacedConfigMap(parameters.Namespace, awsMockConfigMap); + await coreClient.createNamespacedConfigMap({ namespace: parameters.Namespace, body: awsMockConfigMap }); logger.info('AWS mock configmap created successfully'); } catch (error) { if ((error as { response?: { statusCode: number } }).response?.statusCode === 409) { @@ -534,11 +551,12 @@ async function createAwsMockConfigMap(coreClient: CoreV1Api, parameters: ZenkoWo * Setup mock services */ async function setupMockServices(coreClient: CoreV1Api, parameters: ZenkoWorldParameters): Promise { - const existingPods = await coreClient.listNamespacedPod( - parameters.Namespace, undefined, undefined, undefined, undefined, 'component=mock' - ); + const existingPods = await coreClient.listNamespacedPod({ + namespace: parameters.Namespace, + labelSelector: 'component=mock' + }); - if (existingPods.body.items.length > 0) { + if (existingPods.items.length > 0) { logger.info('Mock services already deployed', { existingPods, }); @@ -677,10 +695,10 @@ async function setupMockServices(coreClient: CoreV1Api, parameters: ZenkoWorldPa }; await Promise.allSettled([ - coreClient.createNamespacedService(parameters.Namespace, azureMockService), - coreClient.createNamespacedPod(parameters.Namespace, azureMockPod), - coreClient.createNamespacedService(parameters.Namespace, awsMockService), - coreClient.createNamespacedPod(parameters.Namespace, awsMockPod), + coreClient.createNamespacedService({ namespace: parameters.Namespace, body: azureMockService }), + coreClient.createNamespacedPod({ namespace: parameters.Namespace, body: azureMockPod }), + coreClient.createNamespacedService({ namespace: parameters.Namespace, body: awsMockService }), + coreClient.createNamespacedPod({ namespace: parameters.Namespace, body: awsMockPod }), ]); logger.info('Mock services deployment initiated', { @@ -731,9 +749,13 @@ async function setupNotificationTargets(customObjectClient: CustomObjectsApi, pa }, }; - await customObjectClient.createNamespacedCustomObject( - 'zenko.io', 'v1alpha2', parameters.Namespace, 'zenkonotificationtargets', notificationTarget - ).catch(err => { + await customObjectClient.createNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha2', + namespace: parameters.Namespace, + plural: 'zenkonotificationtargets', + body: notificationTarget + }).catch(err => { if (err.response?.statusCode !== 409) { throw err; } @@ -751,9 +773,10 @@ async function setupNotificationTargets(customObjectClient: CustomObjectsApi, pa async function applyDeploymentModifications(appsClient: AppsV1Api, parameters: ZenkoWorldParameters): Promise { const deploymentName = 'end2end-connector-cloudserver'; - const deployment = await appsClient.readNamespacedDeployment(deploymentName, parameters.Namespace); + // eslint-disable-next-line max-len + const deployment = await appsClient.readNamespacedDeployment({ name: deploymentName, namespace: parameters.Namespace }); - const containers = deployment.body.spec?.template?.spec?.containers || []; + const containers = deployment.spec?.template?.spec?.containers || []; const cloudserverContainer = containers.find(c => c.name === 'cloudserver'); if (!cloudserverContainer) { @@ -775,13 +798,11 @@ async function applyDeploymentModifications(appsClient: AppsV1Api, parameters: Z }); } - await appsClient.patchNamespacedDeployment( - deploymentName, - parameters.Namespace, - deployment.body, - undefined, undefined, undefined, undefined, undefined, - { headers: { 'Content-Type': 'application/merge-patch+json' } } - ); + await appsClient.patchNamespacedDeployment({ + name: deploymentName, + namespace: parameters.Namespace, + body: deployment + }); await waitForDeploymentRollout(appsClient, deploymentName, parameters.Namespace); @@ -999,7 +1020,7 @@ async function setupClusterRBAC(rbacClient: RbacAuthorizationV1Api): Promise((resolve, reject) => { @@ -196,8 +196,8 @@ export async function createAndRunPod( const watchClient = createKubeWatchClient(world); try { - const response = await clientCore.createNamespacedPod('default', podManifest); - const podName = response.body.metadata?.name; + const response = await clientCore.createNamespacedPod({ namespace: 'default', body: podManifest }); + const podName = response.metadata?.name; if (waitForCompletion && podName) { world.logger.debug('Waiting for pod completion', { podName }); @@ -241,13 +241,13 @@ export async function createAndRunPod( if (cleanup && podName) { world.logger.debug('Cleaning up pod', { podName }); try { - await clientCore.deleteNamespacedPod(podName, 'default'); + await clientCore.deleteNamespacedPod({ name: podName, namespace: 'default' }); } catch (cleanupErr) { world.logger.warn('Failed to cleanup pod', { podName, err: cleanupErr }); } } - return response.body; + return response; } catch (err: unknown) { world.logger.debug('Failed to create and run pod:', { err }); throw new Error(`Failed to create and run pod: ${err}`); @@ -289,13 +289,13 @@ export async function waitForZenkoToStabilize( const zenkoClient = createKubeCustomObjectClient(world); while (!status && Date.now() - startTime < timeout) { - const zenkoCR = await zenkoClient.getNamespacedCustomObject( - 'zenko.io', - 'v1alpha2', + const zenkoCR = await zenkoClient.getNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha2', namespace, - 'zenkos', - 'end2end', - ).catch(err => { + plural: 'zenkos', + name: 'end2end' + }).catch(err => { world.logger.info('Error getting Zenko CR', { err: err as unknown, }); @@ -307,7 +307,7 @@ export async function waitForZenkoToStabilize( continue; } - const conditions: ZenkoStatus = (zenkoCR.body as { + const conditions: ZenkoStatus = (zenkoCR as { status: { conditions: ZenkoStatus, }, @@ -368,8 +368,8 @@ export async function waitForDataServicesToStabilize(world: Zenko, timeout = 15 // First list all deployments, and then filter the ones with an annotation that matches the data services const deployments: V1Deployment[] = []; - const serviceDeployments = await appsClient.listNamespacedDeployment(namespace); - for (const deployment of serviceDeployments.body.items) { + const serviceDeployments = await appsClient.listNamespacedDeployment({ namespace }); + for (const deployment of serviceDeployments.items) { const annotations = deployment.metadata?.annotations; if (annotations && dataServices.some(service => annotations[annotationKey]?.includes(service))) { deployments.push(deployment); @@ -390,11 +390,12 @@ export async function waitForDataServicesToStabilize(world: Zenko, timeout = 15 throw new Error('Deployment name not found'); } - const deploymentStatus = await appsClient.readNamespacedDeploymentStatus(deploymentName, namespace); - const replicas = deploymentStatus.body.status?.replicas; - const readyReplicas = deploymentStatus.body.status?.readyReplicas; - const updatedReplicas = deploymentStatus.body.status?.updatedReplicas; - const availableReplicas = deploymentStatus.body.status?.availableReplicas; + // eslint-disable-next-line max-len + const deploymentStatus = await appsClient.readNamespacedDeploymentStatus({ name: deploymentName, namespace }); + const replicas = deploymentStatus.status?.replicas; + const readyReplicas = deploymentStatus.status?.readyReplicas; + const updatedReplicas = deploymentStatus.status?.updatedReplicas; + const availableReplicas = deploymentStatus.status?.availableReplicas; world.logger.debug('Checking deployment status', { deployment: deploymentName, @@ -427,44 +428,44 @@ export async function waitForDataServicesToStabilize(world: Zenko, timeout = 15 export async function getDRSource(world: Zenko, namespace = 'default') { const zenkoClient = createKubeCustomObjectClient(world); - const zenkoCR = await zenkoClient.getNamespacedCustomObject( - 'zenko.io', - 'v1alpha1', + const zenkoCR = await zenkoClient.getNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha1', namespace, - 'zenkodrsources', - 'end2end-source', - ).catch(err => { + plural: 'zenkodrsources', + name: 'end2end-source' + }).catch(err => { world.logger.debug('Error getting Zenko CR', { err: err as unknown, }); }); - return zenkoCR?.body; + return zenkoCR; } export async function getDRSink(world: Zenko, namespace = 'default') { const zenkoClient = createKubeCustomObjectClient(world); - const zenkoCR = await zenkoClient.getNamespacedCustomObject( - 'zenko.io', - 'v1alpha1', + const zenkoCR = await zenkoClient.getNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha1', namespace, - 'zenkodrsinks', - 'end2end-pra-sink', - ).catch(err => { + plural: 'zenkodrsinks', + name: 'end2end-pra-sink' + }).catch(err => { world.logger.debug('Error getting Zenko CR', { err: err as unknown, }); }); - return zenkoCR?.body; + return zenkoCR; } export async function getPVCFromLabel(world: Zenko, label: string, value: string, namespace = 'default') { const coreClient = createKubeCoreClient(world); - const pvcList = await coreClient.listNamespacedPersistentVolumeClaim(namespace); - const pvc = pvcList.body.items.find((pvc: V1PersistentVolumeClaim) => pvc.metadata?.labels?.[label] === value); + const pvcList = await coreClient.listNamespacedPersistentVolumeClaim({ namespace }); + const pvc = pvcList.items.find((pvc: V1PersistentVolumeClaim) => pvc.metadata?.labels?.[label] === value); return pvc; } @@ -487,7 +488,7 @@ export async function createSecret( }; try { - await coreClient.deleteNamespacedSecret(secretName, namespace); + await coreClient.deleteNamespacedSecret({ name: secretName, namespace }); } catch (err) { world.logger.debug('Secret does not exist, creating new', { secretName, @@ -497,7 +498,7 @@ export async function createSecret( } try { - const response = await coreClient.createNamespacedSecret(namespace, secret); + const response = await coreClient.createNamespacedSecret({ namespace, body: secret }); return response; } catch (err) { world.logger.debug('Error creating secret', { @@ -516,15 +517,15 @@ export async function getMongoDBConfig( const customObjectClient = createKubeCustomObjectClient(world); try { // Get replicaSetHosts from Zenko CR - const zenkoCR = await customObjectClient.getNamespacedCustomObject( - 'zenko.io', - 'v1alpha2', + const zenkoCR = await customObjectClient.getNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha2', namespace, - 'zenkos', - 'end2end' - ); + plural: 'zenkos', + name: 'end2end' + }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const mongodbSpec = (zenkoCR.body as any)?.spec?.mongodb; + const mongodbSpec = (zenkoCR as any)?.spec?.mongodb; const mongodbConfig = { replicaSetHosts: mongodbSpec?.endpoints || [], }; @@ -544,16 +545,12 @@ export async function getLocationConfigs( const coreClient = createKubeCoreClient(world); try { // Get location configurations from connector-cloudserver-config secret - const secretList = await coreClient.listNamespacedSecret( + const secretList = await coreClient.listNamespacedSecret({ namespace, - undefined, - undefined, - undefined, - undefined, - 'app.kubernetes.io/name=connector-cloudserver-config' - ); - - const secret = secretList.body.items[0]; + labelSelector: 'app.kubernetes.io/name=connector-cloudserver-config' + }); + + const secret = secretList.items[0]; const locationConfigData = secret.data?.['locationConfig.json']; if (!locationConfigData) { throw new Error('locationConfig.json not found in secret'); @@ -573,13 +570,13 @@ export async function getZenkoVersion( ): Promise { const customObjectClient = createKubeCustomObjectClient(world); try { - const zenkoVersionList = await customObjectClient.listNamespacedCustomObject( - 'zenko.io', - 'v1alpha1', + const zenkoVersionList = await customObjectClient.listNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha1', namespace, - 'zenkoversions' - ); - const zenkoVersionItems = (zenkoVersionList.body as { items: ZenkoVersion[] })?.items; + plural: 'zenkoversions' + }); + const zenkoVersionItems = (zenkoVersionList as { items: ZenkoVersion[] })?.items; if (!zenkoVersionItems || zenkoVersionItems.length === 0) { throw new Error('No ZenkoVersion resources found'); } @@ -675,18 +672,18 @@ export async function execCommandWithVolumeAccess( await createAndRunPod(world, podManifest, true, false, timeout); const coreClient = createKubeCoreClient(world); - const logs = await coreClient.readNamespacedPodLog(podName, namespace); + const logs = await coreClient.readNamespacedPodLog({ name: podName, namespace }); if (cleanup) { try { - await coreClient.deleteNamespacedPod(podName, namespace); + await coreClient.deleteNamespacedPod({ name: podName, namespace }); world.logger.debug('Pod cleaned up after log retrieval', { podName }); } catch (cleanupErr) { world.logger.warn('Failed to cleanup pod after log retrieval', { podName, err: cleanupErr }); } } - return logs.body.trim(); + return logs.trim(); } catch (error) { world.logger.debug('Command execution failed', { command, @@ -696,7 +693,7 @@ export async function execCommandWithVolumeAccess( if (cleanup) { const coreClient = createKubeCoreClient(world); - await coreClient.deleteNamespacedPod(podName, namespace); + await coreClient.deleteNamespacedPod({ name: podName, namespace }); } throw error; @@ -738,8 +735,8 @@ export async function waitForDeploymentRollout( const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { - const deployment = await appsClient.readNamespacedDeployment(deploymentName, namespace); - const status = deployment.body.status; + const deployment = await appsClient.readNamespacedDeployment({ name: deploymentName, namespace }); + const status = deployment.status; if (status?.readyReplicas === status?.replicas && status?.updatedReplicas === status?.replicas && @@ -759,19 +756,19 @@ export async function waitForDeploymentRollout( export async function getZenkoCR(world: Zenko, namespace = 'default', name = 'end2end'): Promise { const zenkoClient = createKubeCustomObjectClient(world); - const zenkoCR = await zenkoClient.getNamespacedCustomObject( - 'zenko.io', - 'v1alpha2', + const zenkoCR = await zenkoClient.getNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha2', namespace, - 'zenkos', - name, - ).catch(err => { + plural: 'zenkos', + name + }).catch(err => { world.logger.debug('Error getting Zenko CR', { err: err as unknown, }); return; }); - return zenkoCR?.body as ZenkoCR; + return zenkoCR as ZenkoCR; } diff --git a/tests/ctst/yarn.lock b/tests/ctst/yarn.lock index 2d8a679734..0854ec1217 100644 --- a/tests/ctst/yarn.lock +++ b/tests/ctst/yarn.lock @@ -1770,28 +1770,6 @@ url-join "^5.0.0" url-template "^3.1.1" -"@kubernetes/client-node@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.21.0.tgz#c807af50e5d4ecbbaa571087636d79cd71a7d9cc" - integrity sha512-yYRbgMeyQbvZDHt/ZqsW3m4lRefzhbbJEuj8sVXM+bufKrgmzriA2oq7lWPH/k/LQIicAME9ixPUadTrxIF6dQ== - dependencies: - "@types/js-yaml" "^4.0.1" - "@types/node" "^20.1.1" - "@types/request" "^2.47.1" - "@types/ws" "^8.5.3" - byline "^5.0.0" - isomorphic-ws "^5.0.0" - js-yaml "^4.1.0" - jsonpath-plus "^8.0.0" - request "^2.88.0" - rfc4648 "^1.3.0" - stream-buffers "^3.0.2" - tar "^7.0.0" - tslib "^2.4.1" - ws "^8.11.0" - optionalDependencies: - openid-client "^5.3.0" - "@kubernetes/client-node@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-1.3.0.tgz#6088bc1c4f6bb01f52e6e933f35008feb39fe909" @@ -2824,11 +2802,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/caseless@*": - version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" - integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== - "@types/estree@^1.0.6": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" @@ -2866,13 +2839,6 @@ dependencies: undici-types "~5.26.4" -"@types/node@^20.1.1": - version "20.19.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4" - integrity sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow== - dependencies: - undici-types "~6.21.0" - "@types/node@^22.0.0": version "22.18.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.1.tgz#cc85ee6999b2a2928739281d2f56ff410a140c52" @@ -2897,16 +2863,6 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== -"@types/request@^2.47.1": - version "2.48.13" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.13.tgz#abdf4256524e801ea8fdda54320f083edb5a6b80" - integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== - dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.5" - "@types/retry@*": version "0.12.5" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.5.tgz#f090ff4bd8d2e5b940ff270ab39fd5ca1834a07e" @@ -2924,11 +2880,6 @@ dependencies: "@types/node" "*" -"@types/tough-cookie@*": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" - integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== - "@types/uuid@10.0.0": version "10.0.0" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" @@ -2939,13 +2890,6 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== -"@types/ws@^8.5.3": - version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" - integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== - dependencies: - "@types/node" "*" - "@typescript-eslint/eslint-plugin@8.40.0": version "8.40.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz#19f959f273b32f5082c891903645e6a85328db4e" @@ -3095,7 +3039,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3152,18 +3096,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - assert@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" @@ -3196,11 +3128,6 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - aws4-axios@^3.3.8: version "3.4.0" resolved "https://registry.yarnpkg.com/aws4-axios/-/aws4-axios-3.4.0.tgz#a0b4c029bcec57579fdda0649aa09075767466b0" @@ -3209,7 +3136,7 @@ aws4-axios@^3.3.8: "@aws-sdk/client-sts" "^3.4.1" aws4 "^1.12.0" -aws4@^1.12.0, aws4@^1.8.0: +aws4@^1.12.0: version "1.13.2" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== @@ -3284,13 +3211,6 @@ bare-url@^2.2.2: dependencies: bare-path "^3.0.0" -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - bindings@^1.3.1: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -3330,11 +3250,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== - cacache@^18.0.0: version "18.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.4.tgz#4601d7578dadb59c66044e157d02a3314682d6a5" @@ -3398,11 +3313,6 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - chalk@^4.0.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -3480,7 +3390,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3512,11 +3422,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -3531,13 +3436,6 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" @@ -3592,14 +3490,6 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3792,21 +3682,6 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.2.tgz#a8f26adb96bf78e8cd8ad1037928d5e5c0679d91" integrity sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA== -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3929,23 +3804,6 @@ foreground-child@^3.1.0, foreground-child@^3.3.1: cross-spawn "^7.0.6" signal-exit "^4.0.1" -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@^2.5.5: - version "2.5.5" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" - integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.35" - safe-buffer "^5.2.1" - form-data@^4.0.0, form-data@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" @@ -3957,15 +3815,6 @@ form-data@^4.0.0, form-data@^4.0.4: hasown "^2.0.2" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -4009,13 +3858,6 @@ get-proto@^1.0.0, get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -4081,19 +3923,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - has-ansi@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-4.0.1.tgz#f216a8c8d7b129e490dc15f4a62cc1cdb9603ce8" @@ -4157,15 +3986,6 @@ http-proxy-agent@^7.0.0: agent-base "^7.1.0" debug "^4.3.4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - "httpagent@git+https://github.com/scality/httpagent#1.1.0": version "1.1.0" resolved "git+https://github.com/scality/httpagent#8f9958eb9cde086db7819a86582fba640a5f8876" @@ -4335,11 +4155,6 @@ is-typed-array@^1.1.3: dependencies: which-typed-array "^1.1.16" -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4355,11 +4170,6 @@ isomorphic-ws@^5.0.0: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -4376,11 +4186,6 @@ jackspeak@^4.1.1: dependencies: "@isaacs/cliui" "^8.0.2" -jose@^4.15.9: - version "4.15.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" - integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== - jose@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/jose/-/jose-6.1.0.tgz#96285365689d16f2845a353964d2284bf19f464c" @@ -4398,11 +4203,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - jsep@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/jsep/-/jsep-1.4.0.tgz#19feccbfa51d8a79f72480b4b8e40ce2e17152f0" @@ -4418,21 +4218,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - json5@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -4447,21 +4237,6 @@ jsonpath-plus@^10.3.0: "@jsep-plugin/regex" "^1.0.4" jsep "^1.4.0" -jsonpath-plus@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-8.1.0.tgz#68c92281215672d1d6c785b3c1bdc8acc097ba3f" - integrity sha512-qVTiuKztFGw0dGhYi3WNqvddx3/SHtyDT0xJaeyz4uP0d1tkpG+0y5uYQ4OcIo1TLAz3PE/qDOW9F0uDt3+CTw== - -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - junit-xml@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/junit-xml/-/junit-xml-1.2.0.tgz#47988b80d4173180b3da6b3727c43b206e1306ef" @@ -4542,13 +4317,6 @@ lru-cache@^11.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.1.tgz#d426ac471521729c6c1acda5f7a633eadaa28db2" integrity sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ== -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - luxon@3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.1.tgz#9bd09aa84a56afb00c57ea78a8ec5bd16eb24ec0" @@ -4634,7 +4402,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19: +mime-types@^2.1.12: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -4837,11 +4605,6 @@ normalize-package-data@^6.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - oauth4webapi@^3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-3.8.1.tgz#a6e205570c09e33aa656982c85e78841a57a6fec" @@ -4852,11 +4615,6 @@ object-assign@^4.0.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-hash@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== - object-inspect@^1.13.3: version "1.13.4" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" @@ -4887,11 +4645,6 @@ object.assign@^4.1.4: has-symbols "^1.1.0" object-keys "^1.1.1" -oidc-token-hash@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz#d35e31ca26d3a26678f5e9bda100b095ab58011f" - integrity sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g== - once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4899,16 +4652,6 @@ once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -openid-client@^5.3.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.7.1.tgz#34cace862a3e6472ed7d0a8616ef73b7fb85a9c3" - integrity sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew== - dependencies: - jose "^4.15.9" - lru-cache "^6.0.0" - object-hash "^2.2.0" - oidc-token-hash "^5.0.3" - openid-client@^6.1.3: version "6.8.0" resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-6.8.0.tgz#ae60540fc1aaf595ee9100004ebda722e1d80e60" @@ -5004,11 +4747,6 @@ path-scurry@^2.0.0: lru-cache "^11.0.0" minipass "^7.1.2" -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -5073,13 +4811,6 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -psl@^1.1.28: - version "1.15.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" - integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== - dependencies: - punycode "^2.3.1" - pump@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" @@ -5088,7 +4819,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: +punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -5100,11 +4831,6 @@ qs@^6.13.0: dependencies: side-channel "^1.1.0" -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -5152,32 +4878,6 @@ repeat-string@^1.5.2, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request@^2.88.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -5205,11 +4905,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-json-stringify@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" @@ -5224,7 +4919,7 @@ safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" -"safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -5387,21 +5082,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== -sshpk@^1.7.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" - integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - ssri@^10.0.0: version "10.0.6" resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" @@ -5543,7 +5223,7 @@ tar@^6.1.11, tar@^6.2.1: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^7.0.0, tar@^7.4.3: +tar@^7.4.3: version "7.4.3" resolved "https://registry.yarnpkg.com/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571" integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== @@ -5593,14 +5273,6 @@ toposort@^2.0.2: resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -5644,23 +5316,11 @@ tsconfig-paths@^4.2.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.3, tslib@^2.4.1, tslib@^2.5.0, tslib@^2.6.2, tslib@^2.8.1: +tslib@^2.0.3, tslib@^2.5.0, tslib@^2.6.2, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -5787,11 +5447,6 @@ uuid@11.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -5821,15 +5476,6 @@ validate-npm-package-license@^3.0.4: werelogs scality/werelogs#8.2.0 xml2js "^0.6.2" -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -5912,7 +5558,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^8.11.0, ws@^8.18.2: +ws@^8.18.2: version "8.18.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== From 81a7e2b4b2aa42069d46977ea4208929f45e32a4 Mon Sep 17 00:00:00 2001 From: williamlardier Date: Mon, 15 Sep 2025 08:47:49 +0200 Subject: [PATCH 08/14] Use keycloak setup in ctst --- tests/ctst/package.json | 2 +- tests/ctst/yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ctst/package.json b/tests/ctst/package.json index 5c99bde6a7..d79732d0c0 100644 --- a/tests/ctst/package.json +++ b/tests/ctst/package.json @@ -26,7 +26,7 @@ "@aws-sdk/client-s3": "^3.583.0", "@aws-sdk/client-sts": "^3.583.0", "@eslint/compat": "^1.1.1", - "cli-testing": "github:scality/cli-testing.git#5931b87f9394bec1c1370caf9ba5162cceffac0c", + "cli-testing": "github:scality/cli-testing.git#a30c83ed93e14d38cd4397d9393ed64bfed5c369", "eslint": "^9.9.1", "eslint-config-scality": "scality/Guidelines#8.3.0", "typescript-eslint": "^8.4.0" diff --git a/tests/ctst/yarn.lock b/tests/ctst/yarn.lock index 0854ec1217..71b31a3513 100644 --- a/tests/ctst/yarn.lock +++ b/tests/ctst/yarn.lock @@ -3350,9 +3350,9 @@ cli-table3@0.6.5, cli-table3@^0.6.0: optionalDependencies: "@colors/colors" "1.5.0" -"cli-testing@github:scality/cli-testing.git#5931b87f9394bec1c1370caf9ba5162cceffac0c": +"cli-testing@github:scality/cli-testing.git#a30c83ed93e14d38cd4397d9393ed64bfed5c369": version "1.3.0" - resolved "git+ssh://git@github.com/scality/cli-testing.git#5931b87f9394bec1c1370caf9ba5162cceffac0c" + resolved "git+ssh://git@github.com/scality/cli-testing.git#a30c83ed93e14d38cd4397d9393ed64bfed5c369" dependencies: "@aws-crypto/sha256-universal" "^5.2.0" "@aws-sdk/client-iam" "^3.879.0" From 7631b4f79b1eeb3d0268743889bf55bbda61ae6d Mon Sep 17 00:00:00 2001 From: williamlardier Date: Mon, 15 Sep 2025 12:28:24 +0200 Subject: [PATCH 09/14] improve unified setup file --- tests/@setup/README.md | 6 +- tests/@setup/package.json | 18 +- tests/@setup/src/buckets.ts | 20 +- tests/@setup/src/cli.ts | 65 +-- tests/@setup/src/dns.ts | 29 +- tests/@setup/src/keycloak.ts | 320 ------------ tests/@setup/src/locations.ts | 4 +- tests/@setup/src/rbac.ts | 31 +- tests/@setup/src/utils/k8s.ts | 194 ++++---- tests/@setup/yarn.lock | 893 ++++++++++++++-------------------- 10 files changed, 567 insertions(+), 1013 deletions(-) delete mode 100644 tests/@setup/src/keycloak.ts diff --git a/tests/@setup/README.md b/tests/@setup/README.md index 9dbce0f351..b5481a1f4e 100644 --- a/tests/@setup/README.md +++ b/tests/@setup/README.md @@ -11,7 +11,6 @@ This tool replaces 22+ setup scripts scattered across Bash, Python, and TypeScri - **Mock Services**: AWS S3 (CloudServer) and Azure (Azurite) mock deployments - **Test Buckets**: Automated creation across AWS, Azure, and Ring providers - **Storage Locations**: Management API configuration for all storage backends -- **Keycloak Setup**: Realm, users, and role configuration - **DNS Configuration**: CoreDNS rewrite rules for test domains - **RBAC Permissions**: Service account cluster-admin permissions @@ -52,7 +51,7 @@ zenko-setup all --namespace=my-namespace --subdomain=test.local Skip specific components: ```bash -zenko-setup all --skip-mocks --skip-keycloak +zenko-setup all --skip-mocks ``` ### Individual Components @@ -68,9 +67,6 @@ zenko-setup buckets --provider=aws # Storage locations zenko-setup locations -# Keycloak realm and users -zenko-setup keycloak - # DNS configuration zenko-setup dns diff --git a/tests/@setup/package.json b/tests/@setup/package.json index 37486c9b0b..4d9227827c 100644 --- a/tests/@setup/package.json +++ b/tests/@setup/package.json @@ -23,16 +23,16 @@ "author": "Scality", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-iam": "^3.400.0", - "@aws-sdk/client-s3": "^3.400.0", - "@azure/storage-blob": "^12.15.0", - "@azure/storage-queue": "^12.15.0", - "@kubernetes/client-node": "^0.20.0", - "axios": "^1.5.0", - "commander": "^11.0.0", - "dotenv": "^16.3.1", + "@aws-sdk/client-iam": "^3.888.0", + "@aws-sdk/client-s3": "^3.888.0", + "@azure/storage-blob": "^12.28.0", + "@azure/storage-queue": "^12.27.0", + "@kubernetes/client-node": "^1.3.0", + "axios": "^1.12.2", + "commander": "^14.0.1", + "dotenv": "^17.2.2", "werelogs": "scality/werelogs#8.2.2", - "yaml": "^2.3.2" + "yaml": "^2.8.1" }, "devDependencies": { "@eslint/compat": "^1.3.2", diff --git a/tests/@setup/src/buckets.ts b/tests/@setup/src/buckets.ts index f8a5564e66..5015198f13 100644 --- a/tests/@setup/src/buckets.ts +++ b/tests/@setup/src/buckets.ts @@ -30,14 +30,17 @@ async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions): logger.info('Creating AWS test buckets'); // Get AWS credentials from mock service - const awsSecret = await k8s.coreApi.readNamespacedSecret('aws-mock-credentials', options.namespace); + const awsSecret = await k8s.coreApi.readNamespacedSecret({ + name: 'aws-mock-credentials', + namespace: options.namespace, + }); const awsConfig = { credentials: { - accessKeyId: Buffer.from(awsSecret.data!['aws-access-key-id'], 'base64').toString(), - secretAccessKey: Buffer.from(awsSecret.data!['aws-secret-access-key'], 'base64').toString() - }, - region: Buffer.from(awsSecret.data!['aws-region'], 'base64').toString(), - endpoint: Buffer.from(awsSecret.data!['aws-endpoint'], 'base64').toString(), + accessKeyId: Buffer.from(awsSecret.data!['aws-access-key-id'], 'base64').toString(), + secretAccessKey: Buffer.from(awsSecret.data!['aws-secret-access-key'], 'base64').toString() + }, + region: Buffer.from(awsSecret.data!['aws-region'], 'base64').toString(), + endpoint: Buffer.from(awsSecret.data!['aws-endpoint'], 'base64').toString(), forcePathStyle: true }; @@ -104,7 +107,10 @@ async function setupAzureBuckets(k8s: KubernetesClient, options: BucketsOptions) logger.info('Creating Azure test containers and queues'); // Get Azure credentials from mock service - const azureSecret = await k8s.coreApi.readNamespacedSecret('azure-mock-credentials', options.namespace); + const azureSecret = await k8s.coreApi.readNamespacedSecret({ + name: 'azure-mock-credentials', + namespace: options.namespace, + }); const accountName = Buffer.from(azureSecret.data!['account-name'], 'base64').toString(); const accountKey = Buffer.from(azureSecret.data!['account-key'], 'base64').toString(); const blobEndpoint = Buffer.from(azureSecret.data!['blob-endpoint'], 'base64').toString(); diff --git a/tests/@setup/src/cli.ts b/tests/@setup/src/cli.ts index 2668e111c4..edd802ce26 100644 --- a/tests/@setup/src/cli.ts +++ b/tests/@setup/src/cli.ts @@ -4,7 +4,6 @@ import { Command } from 'commander'; import { setupMocks } from './mocks'; import { setupBuckets } from './buckets'; import { setupLocations } from './locations'; -import { setupKeycloak } from './keycloak'; import { setupDNS } from './dns'; import { setupRBAC } from './rbac'; import { logger } from './utils/logger'; @@ -88,18 +87,6 @@ program }); }); -program - .command('keycloak') - .description('Setup Keycloak realm, users, and roles') - .action(async () => { - const globalOptions = program.opts(); - await setupKeycloak({ - namespace: globalOptions.namespace || 'default', - instanceId: globalOptions.instanceId, - dryRun: globalOptions.dryRun, - }); - }); - program .command('dns') .description('Configure CoreDNS for test domains') @@ -125,7 +112,7 @@ program async function runSetup(options: any) { try { - logger.info('🚀 Starting Zenko test environment setup'); + logger.info('🚀 Starting Zenko test environment setup'); const tasks = []; @@ -178,39 +165,29 @@ async function runSetup(options: any) { }); } - if (options.keycloak) { - tasks.push({ - name: 'Keycloak', fn: () => setupKeycloak({ - namespace: options.namespace || 'default', - instanceId: options.instanceId, - dryRun: options.dryRun, - }) - }); - } - for (const task of tasks) { - logger.info(`📝 Setting up ${task.name}...`); - - if (options.dryRun) { - logger.info(` [DRY RUN] Would execute ${task.name} setup`); - continue; - } - - try { - await task.fn(); - logger.info(` ✅ ${task.name} setup completed`); - } catch (error) { - logger.error(` ❌ ${task.name} setup failed`, { error: error instanceof Error ? error.message : String(error) }); - throw error; - } + logger.info(`📝 Setting up ${task.name}...`); + + if (options.dryRun) { + logger.info(` [DRY RUN] Would execute ${task.name} setup`); + continue; + } + + try { + await task.fn(); + logger.info(` ✅ ${task.name} setup completed`); + } catch (error) { + logger.error(` ❌ ${task.name} setup failed`, { error: error instanceof Error ? error.message : String(error) }); + throw error; + } } - logger.info('🎉 Zenko test environment setup completed successfully!'); - - } catch (error) { - logger.error('💥 Setup failed', { error: error instanceof Error ? error.message : String(error) }); - process.exit(1); - } + logger.info('🎉 Zenko test environment setup completed successfully!'); + + } catch (error) { + logger.error('💥 Setup failed', { error: error instanceof Error ? error.message : String(error) }); + process.exit(1); + } } program.parse(); \ No newline at end of file diff --git a/tests/@setup/src/dns.ts b/tests/@setup/src/dns.ts index 582193da39..2827477893 100644 --- a/tests/@setup/src/dns.ts +++ b/tests/@setup/src/dns.ts @@ -15,7 +15,10 @@ export async function setupDNS(options: DNSOptions): Promise { // Get the current CoreDNS ConfigMap let coreDnsConfigMap; try { - coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap('coredns', 'kube-system'); + coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap({ + name: 'coredns', + namespace: 'kube-system', + }); } catch (error: any) { if (error.response?.statusCode === 404) { logger.warn('CoreDNS ConfigMap not found, attempting to find alternative'); @@ -27,7 +30,10 @@ export async function setupDNS(options: DNSOptions): Promise { for (const alt of alternatives) { try { - coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap(alt.name, alt.namespace); + coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap({ + name: alt.name, + namespace: alt.namespace, + }); break; } catch (e) { continue; @@ -61,14 +67,18 @@ export async function setupDNS(options: DNSOptions): Promise { // Update the ConfigMap const updatedConfigMap = { - ...coreDnsConfigMap.body, + ...coreDnsConfigMap, data: { ...coreDnsConfigMap.data, 'Corefile': newCorefile } }; - await k8s.coreApi.replaceNamespacedConfigMap('coredns', 'kube-system', updatedConfigMap); + await k8s.coreApi.replaceNamespacedConfigMap({ + name: 'coredns', + namespace: 'kube-system', + body: updatedConfigMap, + }); // Restart CoreDNS deployment to pick up changes await restartCoreDNS(k8s); @@ -176,7 +186,10 @@ function addRewriteRules(currentCorefile: string, rewriteRules: string, subdomai async function restartCoreDNS(k8s: KubernetesClient): Promise { try { // Get CoreDNS deployment - const deployment = await k8s.appsApi.readNamespacedDeployment('coredns', 'kube-system'); + const deployment = await k8s.appsApi.readNamespacedDeployment({ + name: 'coredns', + namespace: 'kube-system', + }); // Add/update restart annotation to trigger rolling restart const annotations = deployment.spec?.template.metadata?.annotations || {}; @@ -184,7 +197,11 @@ async function restartCoreDNS(k8s: KubernetesClient): Promise { deployment.spec!.template.metadata!.annotations = annotations; - await k8s.appsApi.replaceNamespacedDeployment('coredns', 'kube-system', deployment.body); + await k8s.appsApi.replaceNamespacedDeployment({ + name: 'coredns', + namespace: 'kube-system', + body: deployment, + }); logger.debug('CoreDNS deployment restart triggered'); diff --git a/tests/@setup/src/keycloak.ts b/tests/@setup/src/keycloak.ts deleted file mode 100644 index 5d3eb5b569..0000000000 --- a/tests/@setup/src/keycloak.ts +++ /dev/null @@ -1,320 +0,0 @@ -import axios from 'axios'; -import { KubernetesClient } from './utils/k8s'; -import { logger } from './utils/logger'; - -export interface KeycloakOptions { - namespace: string; - instanceId?: string; - dryRun?: boolean; -} - -interface KeycloakConfig { - endpoint: string; - adminUsername: string; - adminPassword: string; - realm: string; -} - -export async function setupKeycloak(options: KeycloakOptions): Promise { - logger.info('Setting up Keycloak realm, users, and roles'); - - const k8s = new KubernetesClient(); - const keycloakConfig = await getKeycloakConfig(k8s, options.namespace); - - if (!keycloakConfig) { - logger.warn('Keycloak not found or not configured, skipping Keycloak setup'); - return; - } - - const adminToken = await getAdminToken(keycloakConfig); - - await createRealm(keycloakConfig, adminToken); - await createRoles(keycloakConfig, adminToken, options.instanceId); - await createUsers(keycloakConfig, adminToken, options.instanceId); - - logger.info('Keycloak setup completed'); -} - -async function getKeycloakConfig(k8s: KubernetesClient, namespace: string): Promise { - try { - // Look for Keycloak service - const services = await k8s.coreApi.listNamespacedService(namespace); - const keycloakService = services.items.find(svc => - svc.metadata?.name?.toLowerCase().includes('keycloak') || - svc.metadata?.name?.toLowerCase().includes('auth') - ); - - if (!keycloakService) { - return null; - } - - // Look for Keycloak admin credentials - const secrets = await k8s.coreApi.listNamespacedSecret(namespace); - const keycloakSecret = secrets.items.find(secret => - secret.metadata?.name?.toLowerCase().includes('keycloak') && - (secret.metadata?.name?.toLowerCase().includes('admin') || - secret.metadata?.name?.toLowerCase().includes('credentials')) - ); - - let adminUsername = 'admin'; - let adminPassword = 'admin'; - - if (keycloakSecret?.data) { - adminUsername = keycloakSecret.data['username'] ? - Buffer.from(keycloakSecret.data['username'], 'base64').toString() : 'admin'; - adminPassword = keycloakSecret.data['password'] ? - Buffer.from(keycloakSecret.data['password'], 'base64').toString() : 'admin'; - } - - const serviceName = keycloakService.metadata!.name; - const port = keycloakService.spec?.ports?.[0]?.port || 8080; - const endpoint = `http://${serviceName}.${namespace}.svc.cluster.local:${port}`; - - return { - endpoint, - adminUsername, - adminPassword, - realm: 'zenko' - }; - - } catch (error) { - logger.debug(`Error getting Keycloak config: ${error}`); - return null; - } -} - -async function getAdminToken(config: KeycloakConfig): Promise { - try { - const response = await axios.post( - `${config.endpoint}/auth/realms/master/protocol/openid-connect/token`, - new URLSearchParams({ - grant_type: 'password', - client_id: 'admin-cli', - username: config.adminUsername, - password: config.adminPassword - }), - { - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - timeout: 30000 - } - ); - - return response.data.access_token; - } catch (error: any) { - logger.error(`Failed to get Keycloak admin token: ${error.message}`); - throw error; - } -} - -async function createRealm(config: KeycloakConfig, token: string): Promise { - const realmData = { - realm: config.realm, - enabled: true, - displayName: 'Zenko Test Realm', - registrationAllowed: false, - resetPasswordAllowed: true, - editUsernameAllowed: false, - loginWithEmailAllowed: true, - duplicateEmailsAllowed: false, - verifyEmail: false, - loginTheme: 'keycloak', - accountTheme: 'keycloak', - adminTheme: 'keycloak', - emailTheme: 'keycloak' - }; - - try { - await axios.post( - `${config.endpoint}/auth/admin/realms`, - realmData, - { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - timeout: 30000 - } - ); - logger.debug(`Created realm: ${config.realm}`); - } catch (error: any) { - if (error.response?.status === 409) { - logger.debug(`Realm ${config.realm} already exists`); - } else { - throw error; - } - } -} - -async function createRoles(config: KeycloakConfig, token: string, instanceId?: string): Promise { - const baseRoles = [ - 'StorageManager', - 'StorageAccountOwner', - 'DataConsumer' - ]; - - // Add instance-specific account roles - const accountRoles = instanceId ? [ - `AccountTest::${instanceId}`, - `AccountTest::${instanceId}::StorageManager`, - `AccountTest::${instanceId}::DataConsumer` - ] : [ - 'AccountTest::xyz123', - 'AccountTest::xyz123::StorageManager', - 'AccountTest::xyz123::DataConsumer' - ]; - - const allRoles = [...baseRoles, ...accountRoles]; - - for (const roleName of allRoles) { - try { - await axios.post( - `${config.endpoint}/auth/admin/realms/${config.realm}/roles`, - { - name: roleName, - description: `Zenko test role: ${roleName}` - }, - { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - timeout: 30000 - } - ); - logger.debug(`Created role: ${roleName}`); - } catch (error: any) { - if (error.response?.status === 409) { - logger.debug(`Role ${roleName} already exists`); - } else { - logger.error(`Failed to create role ${roleName}: ${error.message}`); - } - } - } -} - -async function createUsers(config: KeycloakConfig, token: string, instanceId?: string): Promise { - const testUsers = [ - { - username: 'storage-manager', - email: 'storage-manager@test.local', - firstName: 'Storage', - lastName: 'Manager', - roles: ['StorageManager'], - password: 'password123' - }, - { - username: 'account-owner', - email: 'account-owner@test.local', - firstName: 'Account', - lastName: 'Owner', - roles: ['StorageAccountOwner'], - password: 'password123' - }, - { - username: 'data-consumer', - email: 'data-consumer@test.local', - firstName: 'Data', - lastName: 'Consumer', - roles: ['DataConsumer'], - password: 'password123' - } - ]; - - // Add instance-specific test user - if (instanceId) { - testUsers.push({ - username: `test-${instanceId}`, - email: `test-${instanceId}@test.local`, - firstName: 'Test', - lastName: 'User', - roles: [`AccountTest::${instanceId}`, `AccountTest::${instanceId}::StorageManager`], - password: 'password123' - }); - } - - for (const user of testUsers) { - try { - // Create user - const createUserResponse = await axios.post( - `${config.endpoint}/auth/admin/realms/${config.realm}/users`, - { - username: user.username, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - enabled: true, - emailVerified: true, - credentials: [{ - type: 'password', - value: user.password, - temporary: false - }] - }, - { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - timeout: 30000 - } - ); - - // Get user ID from Location header or by querying - let userId = ''; - if (createUserResponse.headers.location) { - userId = createUserResponse.headers.location.split('/').pop(); - } else { - // Query for user ID - const usersResponse = await axios.get( - `${config.endpoint}/auth/admin/realms/${config.realm}/users?username=${user.username}`, - { - headers: { 'Authorization': `Bearer ${token}` }, - timeout: 30000 - } - ); - userId = usersResponse.data[0]?.id; - } - - if (userId) { - // Assign roles to user - for (const roleName of user.roles) { - try { - // Get role details - const roleResponse = await axios.get( - `${config.endpoint}/auth/admin/realms/${config.realm}/roles/${roleName}`, - { - headers: { 'Authorization': `Bearer ${token}` }, - timeout: 30000 - } - ); - - // Assign role to user - await axios.post( - `${config.endpoint}/auth/admin/realms/${config.realm}/users/${userId}/role-mappings/realm`, - [roleResponse.data], - { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - timeout: 30000 - } - ); - logger.debug(`Assigned role ${roleName} to user ${user.username}`); - } catch (roleError) { - logger.warn(`Failed to assign role ${roleName} to user ${user.username}`); - } - } - } - - logger.debug(`Created user: ${user.username}`); - - } catch (error: any) { - if (error.response?.status === 409) { - logger.debug(`User ${user.username} already exists`); - } else { - logger.error(`Failed to create user ${user.username}: ${error.message}`); - } - } - } -} \ No newline at end of file diff --git a/tests/@setup/src/locations.ts b/tests/@setup/src/locations.ts index 6a77874436..c1536d9bf5 100644 --- a/tests/@setup/src/locations.ts +++ b/tests/@setup/src/locations.ts @@ -81,7 +81,7 @@ export async function setupLocations(options: LocationsOptions): Promise { async function getManagementEndpoint(k8s: KubernetesClient, namespace: string): Promise { try { // Try to find Management API service - const services = await k8s.coreApi.listNamespacedService(namespace); + const services = await k8s.coreApi.listNamespacedService({ namespace }); const mgmtService = services.items.find(svc => svc.metadata?.name?.includes('management') || svc.metadata?.name?.includes('api') || @@ -105,7 +105,7 @@ async function getManagementEndpoint(k8s: KubernetesClient, namespace: string): async function getManagementCredentials(k8s: KubernetesClient, namespace: string): Promise<{ accessKey: string; secretKey: string }> { try { // Look for admin credentials in secrets - const secrets = await k8s.coreApi.listNamespacedSecret(namespace); + const secrets = await k8s.coreApi.listNamespacedSecret({ namespace }); const adminSecret = secrets.items.find(secret => secret.metadata?.name?.includes('admin') || secret.metadata?.name?.includes('management') || diff --git a/tests/@setup/src/rbac.ts b/tests/@setup/src/rbac.ts index 65aaf7d5c4..f845be7826 100644 --- a/tests/@setup/src/rbac.ts +++ b/tests/@setup/src/rbac.ts @@ -29,7 +29,9 @@ export async function setupRBAC(options: RBACOptions): Promise { }; // Get all service accounts in the namespace - const serviceAccounts = await k8s.coreApi.listNamespacedServiceAccount(options.namespace); + const serviceAccounts = await k8s.coreApi.listNamespacedServiceAccount({ + namespace: options.namespace, + }); const zenkoServiceAccounts = serviceAccounts.items.filter(sa => sa.metadata?.name?.includes('zenko') || sa.metadata?.name?.includes('cloudserver') || @@ -39,11 +41,16 @@ export async function setupRBAC(options: RBACOptions): Promise { // Apply cluster role try { - await k8s.rbacApi.createClusterRole(clusterRole); + await k8s.rbacApi.createClusterRole({ + body: clusterRole, + }); } catch (error: any) { if (error.response?.statusCode === 409) { logger.debug('ClusterRole zenko-test-admin already exists'); - await k8s.rbacApi.replaceClusterRole('zenko-test-admin', clusterRole); + await k8s.rbacApi.replaceClusterRole({ + name: 'zenko-test-admin', + body: clusterRole, + }); } else { throw error; } @@ -73,12 +80,17 @@ export async function setupRBAC(options: RBACOptions): Promise { }; try { - await k8s.rbacApi.createClusterRoleBinding(clusterRoleBinding); + await k8s.rbacApi.createClusterRoleBinding({ + body: clusterRoleBinding, + }); logger.debug(`Created ClusterRoleBinding for ${saName}`); } catch (error: any) { if (error.response?.statusCode === 409) { logger.debug(`ClusterRoleBinding for ${saName} already exists`); - await k8s.rbacApi.replaceClusterRoleBinding(`zenko-test-admin-${saName}`, clusterRoleBinding); + await k8s.rbacApi.replaceClusterRoleBinding({ + name: `zenko-test-admin-${saName}`, + body: clusterRoleBinding, + }); } else { throw error; } @@ -105,11 +117,16 @@ export async function setupRBAC(options: RBACOptions): Promise { }; try { - await k8s.rbacApi.createClusterRoleBinding(defaultRoleBinding); + await k8s.rbacApi.createClusterRoleBinding({ + body: defaultRoleBinding, + }); } catch (error: any) { if (error.response?.statusCode === 409) { logger.debug(`Default ClusterRoleBinding already exists`); - await k8s.rbacApi.replaceClusterRoleBinding(`zenko-test-admin-default-${options.namespace}`, defaultRoleBinding); + await k8s.rbacApi.replaceClusterRoleBinding({ + name: `zenko-test-admin-default-${options.namespace}`, + body: defaultRoleBinding, + }); } else { throw error; } diff --git a/tests/@setup/src/utils/k8s.ts b/tests/@setup/src/utils/k8s.ts index e0fb4da326..9afb857033 100644 --- a/tests/@setup/src/utils/k8s.ts +++ b/tests/@setup/src/utils/k8s.ts @@ -33,15 +33,17 @@ export class KubernetesClient { async ensureNamespace(namespace: string): Promise { try { - await this.coreApi.readNamespace(namespace); + await this.coreApi.readNamespace({ name: namespace }); logger.debug(`Namespace ${namespace} exists`); } catch (error: any) { if (error.response?.statusCode === 404) { logger.info(`Creating namespace ${namespace}`); await this.coreApi.createNamespace({ - apiVersion: 'v1', - kind: 'Namespace', - metadata: { name: namespace } + body: { + apiVersion: 'v1', + kind: 'Namespace', + metadata: { name: namespace } + } }); } else { throw error; @@ -60,103 +62,109 @@ export class KubernetesClient { try { switch (kind) { - case 'Deployment': - try { - await this.appsApi.readNamespacedDeployment( - metadata.name, - metadata.namespace || 'default', - ); - await this.appsApi.replaceNamespacedDeployment( - metadata.name, - metadata.namespace || 'default', - manifest, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.appsApi.createNamespacedDeployment( - metadata.namespace || 'default', - manifest, + case 'Deployment': + try { + await this.appsApi.readNamespacedDeployment( + { name: metadata.name, namespace: metadata.namespace || 'default' } ); - } else { - throw error; + await this.appsApi.replaceNamespacedDeployment( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.appsApi.createNamespacedDeployment( + metadata.namespace || 'default', + manifest, + ); + } else { + throw error; + } } - } - break; + break; - case 'Service': - try { - await this.coreApi.readNamespacedService( - metadata.name, - metadata.namespace || 'default', - ); - await this.coreApi.replaceNamespacedService( - metadata.name, - metadata.namespace || 'default', - manifest, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.coreApi.createNamespacedService(metadata.namespace || 'default', manifest); - } else { - throw error; + case 'Service': + try { + await this.coreApi.readNamespacedService( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.coreApi.replaceNamespacedService( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedService(metadata.namespace || 'default', manifest); + } else { + throw error; + } } - } - break; + break; - case 'ConfigMap': - try { - await this.coreApi.readNamespacedConfigMap( - metadata.name, - metadata.namespace || 'default', - ); - await this.coreApi.replaceNamespacedConfigMap( - metadata.name, - metadata.namespace || 'default', - manifest, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.coreApi.createNamespacedConfigMap(metadata.namespace || 'default', manifest); - } else { - throw error; + case 'ConfigMap': + try { + await this.coreApi.readNamespacedConfigMap( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.coreApi.replaceNamespacedConfigMap( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedConfigMap(metadata.namespace || 'default', manifest); + } else { + throw error; + } } - } - break; + break; - case 'Secret': - try { - await this.coreApi.readNamespacedSecret( - metadata.name, - metadata.namespace || 'default', - ); - await this.coreApi.replaceNamespacedSecret( - metadata.name, - metadata.namespace || 'default', - manifest, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.coreApi.createNamespacedSecret( - metadata.namespace || 'default', - manifest, + case 'Secret': + try { + await this.coreApi.readNamespacedSecret( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.coreApi.replaceNamespacedSecret( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, ); - } else { - throw error; + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedSecret( + metadata.namespace || 'default', + manifest, + ); + } else { + throw error; + } } - } - break; - - default: - // Handle custom resources - // eslint-disable-next-line no-case-declarations - const [group, version] = apiVersion.split('/'); - await this.customObjectsApi.createNamespacedCustomObject( - group, - version, - metadata.namespace || 'default', - `${kind.toLowerCase()}s`, - manifest - ); + break; + + default: + // Handle custom resources + // eslint-disable-next-line no-case-declarations + const [group, version] = apiVersion.split('/'); + await this.customObjectsApi.createNamespacedCustomObject( + { + group, + version, + plural: `${kind.toLowerCase()}s`, + body: manifest, + namespace: metadata.namespace || 'default', + }, + ); } } catch (error: any) { if (error.response?.statusCode === 409) { @@ -172,7 +180,7 @@ export class KubernetesClient { while (Date.now() - startTime < timeoutMs) { try { - const deployment = await this.appsApi.readNamespacedDeployment(name, namespace); + const deployment = await this.appsApi.readNamespacedDeployment({ name, namespace }); const status = deployment.status; if (status?.readyReplicas === status?.replicas && status?.replicas && status.replicas > 0) { diff --git a/tests/@setup/yarn.lock b/tests/@setup/yarn.lock index 55a6d6492f..b12b6f446d 100644 --- a/tests/@setup/yarn.lock +++ b/tests/@setup/yarn.lock @@ -70,24 +70,24 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-iam@^3.400.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-iam/-/client-iam-3.887.0.tgz#e7ec62be99fe7742c7aa9b3cda6466d6c903f885" - integrity sha512-4T9mFcaGOdm590+KKJtEMG4WRVKziUC1+VhwE0Zyx5DnwjwebkGzl7RBU2zEXhvsclHgkOmuM0fGOFxWbcaZRg== +"@aws-sdk/client-iam@^3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-iam/-/client-iam-3.888.0.tgz#7736c8c988a015e8c6be324a9b59292ce7c90445" + integrity sha512-BYJVOji9jrYcOiUsWspS94viNK0XCGb7QyLcHc4cyq2BXqTy3HXINnPtk+xHvep1LqRaIuyfIOGlmIKKsJ1tGA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.887.0" - "@aws-sdk/credential-provider-node" "3.887.0" + "@aws-sdk/core" "3.888.0" + "@aws-sdk/credential-provider-node" "3.888.0" "@aws-sdk/middleware-host-header" "3.887.0" "@aws-sdk/middleware-logger" "3.887.0" "@aws-sdk/middleware-recursion-detection" "3.887.0" - "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.888.0" "@aws-sdk/region-config-resolver" "3.887.0" "@aws-sdk/types" "3.887.0" "@aws-sdk/util-endpoints" "3.887.0" "@aws-sdk/util-user-agent-browser" "3.887.0" - "@aws-sdk/util-user-agent-node" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.888.0" "@smithy/config-resolver" "^4.2.1" "@smithy/core" "^3.11.0" "@smithy/fetch-http-handler" "^5.2.1" @@ -116,32 +116,32 @@ "@smithy/util-waiter" "^4.1.1" tslib "^2.6.2" -"@aws-sdk/client-s3@^3.400.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.887.0.tgz#122bc244f740bf9819f056fd2a3e823f4d84c3ec" - integrity sha512-WEFiYbCgUBhd3OMj6Q3SCoJ5ekZduLPMnkLQ6czz3UGDuK2GCtdpscEGlbOyKSxm+BdLSV30+vU3gwjdtWUhCg== +"@aws-sdk/client-s3@^3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.888.0.tgz#6a030cc4cc98974062862bd6d490e1b72478057c" + integrity sha512-MgYyF/qpvCMYVSiOpRJ5C/EtdFxuYAeF5SprtMsbf71xBiiCH5GurB616i+ZxJqHlfhBQTTvR0qugnWvk1Wqvw== dependencies: "@aws-crypto/sha1-browser" "5.2.0" "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.887.0" - "@aws-sdk/credential-provider-node" "3.887.0" + "@aws-sdk/core" "3.888.0" + "@aws-sdk/credential-provider-node" "3.888.0" "@aws-sdk/middleware-bucket-endpoint" "3.887.0" "@aws-sdk/middleware-expect-continue" "3.887.0" - "@aws-sdk/middleware-flexible-checksums" "3.887.0" + "@aws-sdk/middleware-flexible-checksums" "3.888.0" "@aws-sdk/middleware-host-header" "3.887.0" "@aws-sdk/middleware-location-constraint" "3.887.0" "@aws-sdk/middleware-logger" "3.887.0" "@aws-sdk/middleware-recursion-detection" "3.887.0" - "@aws-sdk/middleware-sdk-s3" "3.887.0" + "@aws-sdk/middleware-sdk-s3" "3.888.0" "@aws-sdk/middleware-ssec" "3.887.0" - "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.888.0" "@aws-sdk/region-config-resolver" "3.887.0" - "@aws-sdk/signature-v4-multi-region" "3.887.0" + "@aws-sdk/signature-v4-multi-region" "3.888.0" "@aws-sdk/types" "3.887.0" "@aws-sdk/util-endpoints" "3.887.0" "@aws-sdk/util-user-agent-browser" "3.887.0" - "@aws-sdk/util-user-agent-node" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.888.0" "@aws-sdk/xml-builder" "3.887.0" "@smithy/config-resolver" "^4.2.1" "@smithy/core" "^3.11.0" @@ -180,23 +180,23 @@ tslib "^2.6.2" uuid "^9.0.1" -"@aws-sdk/client-sso@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.887.0.tgz#7b9bce78df539b5d7139d0ca1654981b05c63c43" - integrity sha512-ZKN8BxkRdC6vK6wlnuLSYBhj7uufg14GP5bxqiRaDEooN1y2WcuY95GP13I3brLvM0uboFGbObIVpVrbeHifng== +"@aws-sdk/client-sso@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.888.0.tgz#ab161ff13de9bf43b641df9d04172150761f8418" + integrity sha512-8CLy/ehGKUmekjH+VtZJ4w40PqDg3u0K7uPziq/4P8Q7LLgsy8YQoHNbuY4am7JU3HWrqLXJI9aaz1+vPGPoWA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/middleware-host-header" "3.887.0" "@aws-sdk/middleware-logger" "3.887.0" "@aws-sdk/middleware-recursion-detection" "3.887.0" - "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.888.0" "@aws-sdk/region-config-resolver" "3.887.0" "@aws-sdk/types" "3.887.0" "@aws-sdk/util-endpoints" "3.887.0" "@aws-sdk/util-user-agent-browser" "3.887.0" - "@aws-sdk/util-user-agent-node" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.888.0" "@smithy/config-resolver" "^4.2.1" "@smithy/core" "^3.11.0" "@smithy/fetch-http-handler" "^5.2.1" @@ -224,10 +224,10 @@ "@smithy/util-utf8" "^4.1.0" tslib "^2.6.2" -"@aws-sdk/core@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.887.0.tgz#2e03e3ee7c3c615f96a1f5ea2e4bc6d52b7f53e6" - integrity sha512-oiBsWhuuj1Lzh+FHY+gE0PyYuiDxqFf98F9Pd2WruY5Gu/+/xvDFEPEkIEOae8gWRaLZ5Eh8u+OY9LS4DXZhuQ== +"@aws-sdk/core@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.888.0.tgz#d0b9115d5b79b515a0435ff59ad721195bcb55a8" + integrity sha512-L3S2FZywACo4lmWv37Y4TbefuPJ1fXWyWwIJ3J4wkPYFJ47mmtUPqThlVrSbdTHkEjnZgJe5cRfxk0qCLsFh1w== dependencies: "@aws-sdk/types" "3.887.0" "@aws-sdk/xml-builder" "3.887.0" @@ -245,23 +245,23 @@ fast-xml-parser "5.2.5" tslib "^2.6.2" -"@aws-sdk/credential-provider-env@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.887.0.tgz#ac2ee94d850d5d8c0fdb19bca4f96b11f375304f" - integrity sha512-kv7L5E8mxlWTMhCK639wrQnFEmwUDfKvKzTMDo2OboXZ0iSbD+hBPoT0gkb49qHNetYnsl63BVOxc0VNiOA04w== +"@aws-sdk/credential-provider-env@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.888.0.tgz#20bd28d5ea79d5254829700d9230e0d1a360fdbd" + integrity sha512-shPi4AhUKbIk7LugJWvNpeZA8va7e5bOHAEKo89S0Ac8WDZt2OaNzbh/b9l0iSL2eEyte8UgIsYGcFxOwIF1VA== dependencies: - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/property-provider" "^4.0.5" "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-http@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.887.0.tgz#aebae84484a08412c03d540ef5f43bb035f7988c" - integrity sha512-siLttHxSFgJ5caDgS+BHYs9GBDX7J3pgge4OmJvIQeGO+KaJC12TerBNPJOp+qRaRC3yuVw3T9RpSZa8mmaiyA== +"@aws-sdk/credential-provider-http@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.888.0.tgz#e32ff8223dbe090bcf004bcc58ec1b676043ccac" + integrity sha512-Jvuk6nul0lE7o5qlQutcqlySBHLXOyoPtiwE6zyKbGc7RVl0//h39Lab7zMeY2drMn8xAnIopL4606Fd8JI/Hw== dependencies: - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/fetch-http-handler" "^5.2.1" "@smithy/node-http-handler" "^4.2.1" @@ -272,18 +272,18 @@ "@smithy/util-stream" "^4.3.1" tslib "^2.6.2" -"@aws-sdk/credential-provider-ini@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.887.0.tgz#94d7007f96b04ee76a521d9496f19ff9d237f4a1" - integrity sha512-Na9IjKdPuSNU/mBcCQ49HiIgomq/O7kZAuRyGwAXiRPbf86AacKv4dsUyPZY6lCgVIvVniRWgYlVaPgq22EIig== - dependencies: - "@aws-sdk/core" "3.887.0" - "@aws-sdk/credential-provider-env" "3.887.0" - "@aws-sdk/credential-provider-http" "3.887.0" - "@aws-sdk/credential-provider-process" "3.887.0" - "@aws-sdk/credential-provider-sso" "3.887.0" - "@aws-sdk/credential-provider-web-identity" "3.887.0" - "@aws-sdk/nested-clients" "3.887.0" +"@aws-sdk/credential-provider-ini@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.888.0.tgz#4a97261b8593c3c8c8e5bac974ba0e5e0a40d89f" + integrity sha512-M82ItvS5yq+tO6ZOV1ruaVs2xOne+v8HW85GFCXnz8pecrzYdgxh6IsVqEbbWruryG/mUGkWMbkBZoEsy4MgyA== + dependencies: + "@aws-sdk/core" "3.888.0" + "@aws-sdk/credential-provider-env" "3.888.0" + "@aws-sdk/credential-provider-http" "3.888.0" + "@aws-sdk/credential-provider-process" "3.888.0" + "@aws-sdk/credential-provider-sso" "3.888.0" + "@aws-sdk/credential-provider-web-identity" "3.888.0" + "@aws-sdk/nested-clients" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/credential-provider-imds" "^4.0.7" "@smithy/property-provider" "^4.0.5" @@ -291,17 +291,17 @@ "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-node@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.887.0.tgz#7f6e30e5bad736d5ff0afb243dd7ee24f330bfa8" - integrity sha512-iJdCq/brBWYpJzJcXY2UhEoW7aA28ixIpvLKjxh5QUBfjCj19cImpj1gGwTIs6/fVcjVUw1tNveTBfn1ziTzVg== - dependencies: - "@aws-sdk/credential-provider-env" "3.887.0" - "@aws-sdk/credential-provider-http" "3.887.0" - "@aws-sdk/credential-provider-ini" "3.887.0" - "@aws-sdk/credential-provider-process" "3.887.0" - "@aws-sdk/credential-provider-sso" "3.887.0" - "@aws-sdk/credential-provider-web-identity" "3.887.0" +"@aws-sdk/credential-provider-node@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.888.0.tgz#48f769d52d999088d4437dc1bc76af55afecad9b" + integrity sha512-KCrQh1dCDC8Y+Ap3SZa6S81kHk+p+yAaOQ5jC3dak4zhHW3RCrsGR/jYdemTOgbEGcA6ye51UbhWfrrlMmeJSA== + dependencies: + "@aws-sdk/credential-provider-env" "3.888.0" + "@aws-sdk/credential-provider-http" "3.888.0" + "@aws-sdk/credential-provider-ini" "3.888.0" + "@aws-sdk/credential-provider-process" "3.888.0" + "@aws-sdk/credential-provider-sso" "3.888.0" + "@aws-sdk/credential-provider-web-identity" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/credential-provider-imds" "^4.0.7" "@smithy/property-provider" "^4.0.5" @@ -309,39 +309,39 @@ "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-process@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.887.0.tgz#e562eda10ff42a144cf17a2d6a77fb6d94df3575" - integrity sha512-J5TIrQ/DUiyR65gXt1j3TEbLUwMcgYVB/G68/AVgBptPvb9kj+6zFG67bJJHwxtqJxRLVLTtTi9u/YDXTqGBpQ== +"@aws-sdk/credential-provider-process@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.888.0.tgz#34842315e44b4882c63eb92fa2151c2efaf5401f" + integrity sha512-+aX6piSukPQ8DUS4JAH344GePg8/+Q1t0+kvSHAZHhYvtQ/1Zek3ySOJWH2TuzTPCafY4nmWLcQcqvU1w9+4Lw== dependencies: - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/property-provider" "^4.0.5" "@smithy/shared-ini-file-loader" "^4.0.5" "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-sso@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.887.0.tgz#c6d5269e9713201f66826e6c1f200e1a2db4fee4" - integrity sha512-Bv9wUActLu6Kn0MK2s72bgbbNxSLPVop/If4MVbCyJ3n+prJnm5RsTF3isoWQVyyXA5g4tIrS8mE5FpejSbyPQ== +"@aws-sdk/credential-provider-sso@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.888.0.tgz#85956b3bcebbcb1aee096d07b4365e524dc1b985" + integrity sha512-b1ZJji7LJ6E/j1PhFTyvp51in2iCOQ3VP6mj5H6f5OUnqn7efm41iNMoinKr87n0IKZw7qput5ggXVxEdPhouA== dependencies: - "@aws-sdk/client-sso" "3.887.0" - "@aws-sdk/core" "3.887.0" - "@aws-sdk/token-providers" "3.887.0" + "@aws-sdk/client-sso" "3.888.0" + "@aws-sdk/core" "3.888.0" + "@aws-sdk/token-providers" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/property-provider" "^4.0.5" "@smithy/shared-ini-file-loader" "^4.0.5" "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-web-identity@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.887.0.tgz#e2c865fd9e347bc64c5c822dfd38b2be68162244" - integrity sha512-PRh0KRukY2euN9xvvQ3cqhCAlEkMDJIWDLIfxQ1hTbv7JA3hrcLVrV+Jg5FRWsStDhweHIvD/VzruSkhJQS80g== +"@aws-sdk/credential-provider-web-identity@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.888.0.tgz#03de49dca86649ae2061247ee4d2831642a4767f" + integrity sha512-7P0QNtsDzMZdmBAaY/vY1BsZHwTGvEz3bsn2bm5VSKFAeMmZqsHK1QeYdNsFjLtegnVh+wodxMq50jqLv3LFlA== dependencies: - "@aws-sdk/core" "3.887.0" - "@aws-sdk/nested-clients" "3.887.0" + "@aws-sdk/core" "3.888.0" + "@aws-sdk/nested-clients" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/property-provider" "^4.0.5" "@smithy/types" "^4.5.0" @@ -370,15 +370,15 @@ "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-flexible-checksums@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.887.0.tgz#f065450a858cb3aa9a19221cf9c8bc78937ee302" - integrity sha512-QaRGWeeHNxRvY+OUuiQ+4A7H+4HPCWCtfTiQRPzILd3C968r7EFNg2ZWyjoqITW8cj3ZJZp3p8VcH08WBzAhcQ== +"@aws-sdk/middleware-flexible-checksums@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.888.0.tgz#b66da7fe00fe980a9274bc36bd56190a218a4314" + integrity sha512-vdwd4wMAlXSg1bldhXyTsDSnyPP+bbEVihapejGKNd4gLfyyHwjTfbli+B/hEONGttQs5Dp54UMn8yW/UA189g== dependencies: "@aws-crypto/crc32" "5.2.0" "@aws-crypto/crc32c" "5.2.0" "@aws-crypto/util" "5.2.0" - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/is-array-buffer" "^4.0.0" "@smithy/node-config-provider" "^4.2.1" @@ -428,12 +428,12 @@ "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-sdk-s3@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.887.0.tgz#5ff7bdf2c7a03c2ec17d2b3b9d6955f675bd83bb" - integrity sha512-vWMfd8esmMX5YSenzgendh9OSIw7IcKLH46ajaNvDBdF/9X0h6eobgNX/liLzrnNHd6t7Lru2KZXSjrwYgu7pA== +"@aws-sdk/middleware-sdk-s3@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.888.0.tgz#9e561338d9d036a5bf8252b578222b351b635ea1" + integrity sha512-rKOFNfqgqOfrdcLGF8fcO75azWS2aq2ksRHFoIEFru5FJxzu/yDAhY4C2FKiP/X34xeIUS2SbE/gQgrgWHSN2g== dependencies: - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/types" "3.887.0" "@aws-sdk/util-arn-parser" "3.873.0" "@smithy/core" "^3.11.0" @@ -457,12 +457,12 @@ "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-user-agent@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.887.0.tgz#ecb60f439081bec36d2390832d6ee138f69d37f5" - integrity sha512-YjBz2J4l3uCeMv2g1natat5YSMRZYdEpEg60g3d7q6hoHUD10SmWy8M+Ca8djF0is70vPmF3Icm2cArK3mtoNA== +"@aws-sdk/middleware-user-agent@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.888.0.tgz#8b7f8ed11120fd1b931b09de12f7846f72bfe538" + integrity sha512-ZkcUkoys8AdrNNG7ATjqw2WiXqrhTvT+r4CIK3KhOqIGPHX0p0DQWzqjaIl7ZhSUToKoZ4Ud7MjF795yUr73oA== dependencies: - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/types" "3.887.0" "@aws-sdk/util-endpoints" "3.887.0" "@smithy/core" "^3.11.0" @@ -470,23 +470,23 @@ "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/nested-clients@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.887.0.tgz#b95906d63b0040680e7f46edcf7e699d44edde57" - integrity sha512-h6/dHuAJhJnhSDihcQd0wfJBZoPmPajASVqGk8qDxYDBWxIU9/mYcKvM+kTrKw3f9Wf3S/eR5B/rYHHuxFheSw== +"@aws-sdk/nested-clients@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.888.0.tgz#5c3ea2517bf05caf4bd699e731f97bf5e565f397" + integrity sha512-py4o4RPSGt+uwGvSBzR6S6cCBjS4oTX5F8hrHFHfPCdIOMVjyOBejn820jXkCrcdpSj3Qg1yUZXxsByvxc9Lyg== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.887.0" + "@aws-sdk/core" "3.888.0" "@aws-sdk/middleware-host-header" "3.887.0" "@aws-sdk/middleware-logger" "3.887.0" "@aws-sdk/middleware-recursion-detection" "3.887.0" - "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.888.0" "@aws-sdk/region-config-resolver" "3.887.0" "@aws-sdk/types" "3.887.0" "@aws-sdk/util-endpoints" "3.887.0" "@aws-sdk/util-user-agent-browser" "3.887.0" - "@aws-sdk/util-user-agent-node" "3.887.0" + "@aws-sdk/util-user-agent-node" "3.888.0" "@smithy/config-resolver" "^4.2.1" "@smithy/core" "^3.11.0" "@smithy/fetch-http-handler" "^5.2.1" @@ -526,25 +526,25 @@ "@smithy/util-middleware" "^4.1.1" tslib "^2.6.2" -"@aws-sdk/signature-v4-multi-region@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.887.0.tgz#aaaaec78c1f10d6b57041b40fcd2449d5b677478" - integrity sha512-xAmoHzSow3692IFeAblZKRIABp4Iv96XGQKMIlHE1LugSl4KuR/6M9+UfbNMfSdyfhRt0RkG6kMZ/7GwlxqoAQ== +"@aws-sdk/signature-v4-multi-region@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.888.0.tgz#27336bd763746daa3513a8a72436754a370fccce" + integrity sha512-FmOHUaJzEhqfcpyh0L7HLwYcYopK13Dbmuf+oUyu56/RoeB1nLnltH1VMQVj8v3Am2IwlGR+/JpFyrdkErN+cA== dependencies: - "@aws-sdk/middleware-sdk-s3" "3.887.0" + "@aws-sdk/middleware-sdk-s3" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/protocol-http" "^5.2.1" "@smithy/signature-v4" "^5.1.3" "@smithy/types" "^4.5.0" tslib "^2.6.2" -"@aws-sdk/token-providers@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.887.0.tgz#0825955ec3e518fea88649cf381839b9b07ea859" - integrity sha512-3e5fTPMPeJ5DphZ+OSqzw4ymCgDf8SQVBgrlKVo4Bch9ZwmmAoOHbuQrXVa9xQHklEHJg1Gz2pkjxNaIgx7quA== +"@aws-sdk/token-providers@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.888.0.tgz#db79f49c8999c93abab321fbab4e2e6920738b93" + integrity sha512-WA3NF+3W8GEuCMG1WvkDYbB4z10G3O8xuhT7QSjhvLYWQ9CPt3w4VpVIfdqmUn131TCIbhCzD0KN/1VJTjAjyw== dependencies: - "@aws-sdk/core" "3.887.0" - "@aws-sdk/nested-clients" "3.887.0" + "@aws-sdk/core" "3.888.0" + "@aws-sdk/nested-clients" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/property-provider" "^4.0.5" "@smithy/shared-ini-file-loader" "^4.0.5" @@ -594,12 +594,12 @@ bowser "^2.11.0" tslib "^2.6.2" -"@aws-sdk/util-user-agent-node@3.887.0": - version "3.887.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.887.0.tgz#e0c2ae5667197b8b830feb1c2cd05cc4735a6640" - integrity sha512-eqnx2FWAf40Nw6EyhXWjVT5WYYMz0rLrKEhZR3GdRQyOFzgnnEfq74TtG2Xji9k/ODqkcKqkiI52RYDEcdh8Jg== +"@aws-sdk/util-user-agent-node@3.888.0": + version "3.888.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.888.0.tgz#45c871429c7742cf73f570fbc39ef17f4d12e8de" + integrity sha512-rSB3OHyuKXotIGfYEo//9sU0lXAUrTY28SUUnxzOGYuQsAt0XR5iYwBAp+RjV6x8f+Hmtbg0PdCsy1iNAXa0UQ== dependencies: - "@aws-sdk/middleware-user-agent" "3.887.0" + "@aws-sdk/middleware-user-agent" "3.888.0" "@aws-sdk/types" "3.887.0" "@smithy/node-config-provider" "^4.2.1" "@smithy/types" "^4.5.0" @@ -718,7 +718,7 @@ "@typespec/ts-http-runtime" "^0.3.0" tslib "^2.6.2" -"@azure/storage-blob@^12.15.0": +"@azure/storage-blob@^12.28.0": version "12.28.0" resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.28.0.tgz#a64ce49f0fe9fe08f1f7c1b36164033678d38cf6" integrity sha512-VhQHITXXO03SURhDiGuHhvc/k/sD2WvJUS7hqhiVNbErVCuQoLtWql7r97fleBlIRKHJaa9R7DpBjfE0pfLYcA== @@ -753,7 +753,7 @@ events "^3.3.0" tslib "^2.8.1" -"@azure/storage-queue@^12.15.0": +"@azure/storage-queue@^12.27.0": version "12.27.0" resolved "https://registry.yarnpkg.com/@azure/storage-queue/-/storage-queue-12.27.0.tgz#cde0c3560b08320ed61e813132d27de716c45973" integrity sha512-GoviVZrJ1BkYCmsam0gOZFqAjH7bKbnbBIEVPkgzCz3RzsB/C05jumQep+3GavZoWw7Yw4iaCNPSyyS1lbN1Gg== @@ -890,27 +890,37 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@kubernetes/client-node@^0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.20.0.tgz#4447ae27fd6eef3d4830a5a039f3b84ffd5c5913" - integrity sha512-xxlv5GLX4FVR/dDKEsmi4SPeuB49aRc35stndyxcC73XnUEEwF39vXbROpHOirmDse8WE9vxOjABnSVS+jb7EA== +"@jsep-plugin/assignment@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@jsep-plugin/assignment/-/assignment-1.3.0.tgz#fcfc5417a04933f7ceee786e8ab498aa3ce2b242" + integrity sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ== + +"@jsep-plugin/regex@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@jsep-plugin/regex/-/regex-1.0.4.tgz#cb2fc423220fa71c609323b9ba7f7d344a755fcc" + integrity sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg== + +"@kubernetes/client-node@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-1.3.0.tgz#6088bc1c4f6bb01f52e6e933f35008feb39fe909" + integrity sha512-IE0yrIpOT97YS5fg2QpzmPzm8Wmcdf4ueWMn+FiJSI3jgTTQT1u+LUhoYpdfhdHAVxdrNsaBg2C0UXSnOgMoCQ== dependencies: "@types/js-yaml" "^4.0.1" - "@types/node" "^20.1.1" - "@types/request" "^2.47.1" - "@types/ws" "^8.5.3" - byline "^5.0.0" + "@types/node" "^22.0.0" + "@types/node-fetch" "^2.6.9" + "@types/stream-buffers" "^3.0.3" + form-data "^4.0.0" + hpagent "^1.2.0" isomorphic-ws "^5.0.0" js-yaml "^4.1.0" - jsonpath-plus "^7.2.0" - request "^2.88.0" + jsonpath-plus "^10.3.0" + node-fetch "^2.6.9" + openid-client "^6.1.3" rfc4648 "^1.3.0" + socks-proxy-agent "^8.0.4" stream-buffers "^3.0.2" - tar "^6.1.11" - tslib "^2.4.1" - ws "^8.11.0" - optionalDependencies: - openid-client "^5.3.0" + tar-fs "^3.0.8" + ws "^8.18.2" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1448,11 +1458,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/caseless@*": - version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" - integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== - "@types/estree@^1.0.6": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" @@ -1468,6 +1473,14 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/node-fetch@^2.6.9": + version "2.6.13" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.13.tgz#e0c9b7b5edbdb1b50ce32c127e85e880872d56ee" + integrity sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw== + dependencies: + "@types/node" "*" + form-data "^4.0.4" + "@types/node@*": version "24.3.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.1.tgz#b0a3fb2afed0ef98e8d7f06d46ef6349047709f3" @@ -1475,45 +1488,37 @@ dependencies: undici-types "~7.10.0" -"@types/node@^20.1.1", "@types/node@^20.5.0": +"@types/node@^20.5.0": version "20.19.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.13.tgz#b79004a05068e28fb2de281b3a44c5c993650e59" integrity sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g== dependencies: undici-types "~6.21.0" -"@types/request@^2.47.1": - version "2.48.13" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.13.tgz#abdf4256524e801ea8fdda54320f083edb5a6b80" - integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== +"@types/node@^22.0.0": + version "22.18.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.3.tgz#e1a4eb680a688141cdd369c5a035f48cf8ece9e3" + integrity sha512-gTVM8js2twdtqM+AE2PdGEe9zGQY4UvmFjan9rZcVb6FGdStfjWoWejdmy4CfWVO9rh5MiYQGZloKAGkJt8lMw== dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.5" + undici-types "~6.21.0" "@types/semver@^7.5.0": version "7.7.1" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== -"@types/tough-cookie@*": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" - integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== +"@types/stream-buffers@^3.0.3": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.7.tgz#0b719fa1bd2ca2cc0908205a440e5e569e1aa21e" + integrity sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw== + dependencies: + "@types/node" "*" "@types/uuid@^9.0.1": version "9.0.8" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== -"@types/ws@^8.5.3": - version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" - integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== - dependencies: - "@types/node" "*" - "@typescript-eslint/eslint-plugin@8.43.0": version "8.43.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz#4d730c2becd8e47ef76e59f68aee0fb560927cfc" @@ -1729,7 +1734,7 @@ agent-base@^7.1.0, agent-base@^7.1.2: resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== -ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1761,53 +1766,71 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.13.2" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" - integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== - -axios@^1.5.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3" - integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg== +axios@^1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7" + integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.4" proxy-from-env "^1.1.0" +b4a@^1.6.4: + version "1.7.1" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.7.1.tgz#6fd4ec2fb33ba7a4ff341a2869bbfc88a6e57850" + integrity sha512-ZovbrBV0g6JxK5cGUF1Suby1vLfKjv4RWi8IxoaO/Mon8BDD9I21RxjHFtgQ+kskJqLAVyQZly3uMBui+vhc8Q== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== +bare-events@^2.2.0, bare-events@^2.5.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.6.1.tgz#f793b28bdc3dcf147d7cf01f882a6f0b12ccc4a2" + integrity sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g== + +bare-fs@^4.0.1: + version "4.4.4" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-4.4.4.tgz#69e7da11d4d7d4a77e1dc9454e11f7ac13a93462" + integrity sha512-Q8yxM1eLhJfuM7KXVP3zjhBvtMJCYRByoTT+wHXjpdMELv0xICFJX+1w4c7csa+WZEOsq4ItJ4RGwvzid6m/dw== dependencies: - tweetnacl "^0.14.3" + bare-events "^2.5.4" + bare-path "^3.0.0" + bare-stream "^2.6.4" + bare-url "^2.2.2" + fast-fifo "^1.3.2" + +bare-os@^3.0.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.6.2.tgz#b3c4f5ad5e322c0fd0f3c29fc97d19009e2796e5" + integrity sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A== + +bare-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" + integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== + dependencies: + bare-os "^3.0.1" + +bare-stream@^2.6.4: + version "2.7.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.7.0.tgz#5b9e7dd0a354d06e82d6460c426728536c35d789" + integrity sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A== + dependencies: + streamx "^2.21.0" + +bare-url@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/bare-url/-/bare-url-2.2.2.tgz#1369d1972bbd7d9b358d0214d3f12abe2328d3e9" + integrity sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA== + dependencies: + bare-path "^3.0.0" bowser@^2.11.0: version "2.12.1" @@ -1836,11 +1859,6 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== - call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -1854,11 +1872,6 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -1867,11 +1880,6 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -1884,28 +1892,28 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" -commander@11.1.0, commander@^11.0.0: +commander@11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== +commander@^14.0.1: + version "14.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.1.tgz#2f9225c19e6ebd0dc4404dd45821b2caa17ea09b" + integrity sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -1920,13 +1928,6 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" @@ -1956,10 +1957,10 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dotenv@^16.3.1: - version "16.6.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" - integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== +dotenv@^17.2.2: + version "17.2.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.2.tgz#4010cfe1c2be4fc0f46fd3d951afb424bc067ac6" + integrity sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q== dunder-proto@^1.0.1: version "1.0.1" @@ -1970,13 +1971,12 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== +end-of-stream@^1.1.0: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" + once "^1.4.0" entities@~3.0.1: version "3.0.1" @@ -2119,26 +2119,16 @@ events@^3.0.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" @@ -2219,24 +2209,7 @@ follow-redirects@^1.15.6: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@^2.5.5: - version "2.5.5" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" - integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.35" - safe-buffer "^5.2.1" - -form-data@^4.0.4: +form-data@^4.0.0, form-data@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== @@ -2247,22 +2220,6 @@ form-data@^4.0.4: hasown "^2.0.2" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" @@ -2292,13 +2249,6 @@ get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -2340,19 +2290,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -2377,6 +2314,11 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hpagent@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== + http-proxy-agent@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" @@ -2385,15 +2327,6 @@ http-proxy-agent@^7.0.0: agent-base "^7.1.0" debug "^4.3.4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - https-proxy-agent@^7.0.0: version "7.0.6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" @@ -2425,6 +2358,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +ip-address@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.0.1.tgz#a8180b783ce7788777d796286d61bce4276818ed" + integrity sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -2442,11 +2380,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2457,15 +2390,10 @@ isomorphic-ws@^5.0.0: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -jose@^4.15.9: - version "4.15.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" - integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== +jose@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.1.0.tgz#96285365689d16f2845a353964d2284bf19f464c" + integrity sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA== js-yaml@^4.1.0: version "4.1.0" @@ -2474,10 +2402,10 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +jsep@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsep/-/jsep-1.4.0.tgz#19feccbfa51d8a79f72480b4b8e40ce2e17152f0" + integrity sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw== json-buffer@3.0.1: version "3.0.1" @@ -2489,35 +2417,19 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -jsonpath-plus@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz#7ad94e147b3ed42f7939c315d2b9ce490c5a3899" - integrity sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA== - -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== +jsonpath-plus@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz#59e22e4fa2298c68dfcd70659bb47f0cad525238" + integrity sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA== dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" + "@jsep-plugin/assignment" "^1.3.0" + "@jsep-plugin/regex" "^1.0.4" + jsep "^1.4.0" keyv@^4.5.4: version "4.5.4" @@ -2553,13 +2465,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -2617,7 +2522,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19: +mime-types@^2.1.12: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -2645,31 +2550,6 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -2680,30 +2560,32 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" -object-hash@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +oauth4webapi@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-3.8.1.tgz#a6e205570c09e33aa656982c85e78841a57a6fec" + integrity sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA== -oidc-token-hash@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz#d35e31ca26d3a26678f5e9bda100b095ab58011f" - integrity sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g== +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" -openid-client@^5.3.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.7.1.tgz#34cace862a3e6472ed7d0a8616ef73b7fb85a9c3" - integrity sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew== +openid-client@^6.1.3: + version "6.8.0" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-6.8.0.tgz#ae60540fc1aaf595ee9100004ebda722e1d80e60" + integrity sha512-oG1d1nAVhIIE+JSjLS+7E9wY1QOJpZltkzlJdbZ7kEn7Hp3hqur2TEeQ8gLOHoHkhbRAGZJKoOnEQcLOQJuIyg== dependencies: - jose "^4.15.9" - lru-cache "^6.0.0" - object-hash "^2.2.0" - oidc-token-hash "^5.0.3" + jose "^6.1.0" + oauth4webapi "^3.8.1" optionator@^0.9.3: version "0.9.4" @@ -2753,11 +2635,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -2773,54 +2650,24 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -psl@^1.1.28: - version "1.15.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" - integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== dependencies: - punycode "^2.3.1" + end-of-stream "^1.1.0" + once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: +punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -request@^2.88.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -2843,21 +2690,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-json-stringify@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - semver@^7.5.4, semver@^7.6.0: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" @@ -2880,26 +2717,43 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -sshpk@^1.7.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" - integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +socks-proxy-agent@^8.0.4: + version "8.0.5" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee" + integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== + dependencies: + agent-base "^7.1.2" + debug "^4.3.4" + socks "^2.8.3" + +socks@^2.8.3: + version "2.8.7" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea" + integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A== + dependencies: + ip-address "^10.0.1" + smart-buffer "^4.2.0" stream-buffers@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.3.tgz#9fc6ae267d9c4df1190a781e011634cac58af3cd" integrity sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw== +streamx@^2.15.0, streamx@^2.21.0: + version "2.22.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.1.tgz#c97cbb0ce18da4f4db5a971dc9ab68ff5dc7f5a5" + integrity sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA== + dependencies: + fast-fifo "^1.3.2" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -2917,17 +2771,32 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -tar@^6.1.11: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== +tar-fs@^3.0.8: + version "3.1.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.1.0.tgz#4675e2254d81410e609d91581a762608de999d25" + integrity sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w== dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^4.0.1" + bare-path "^3.0.0" + +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + +text-decoder@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + dependencies: + b4a "^1.6.4" to-regex-range@^5.0.1: version "5.0.1" @@ -2936,13 +2805,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== ts-api-utils@^1.0.1: version "1.4.3" @@ -2973,23 +2839,11 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2.4.1, tslib@^2.6.2, tslib@^2.8.1: +tslib@^2.6.2, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -3034,11 +2888,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -3049,14 +2898,10 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== werelogs@scality/werelogs#8.2.2: version "8.2.2" @@ -3065,6 +2910,14 @@ werelogs@scality/werelogs#8.2.2: fast-safe-stringify "^2.1.1" safe-json-stringify "^1.2.0" +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -3077,17 +2930,17 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -ws@^8.11.0: +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.18.2: version "8.18.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^2.3.2: +yaml@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== From 9ab33c528d12ff1da0e85302eab466ea417eb5ae Mon Sep 17 00:00:00 2001 From: williamlardier Date: Mon, 15 Sep 2025 13:47:57 +0200 Subject: [PATCH 10/14] keycloak setup idempotent --- tests/ctst/package.json | 2 +- tests/ctst/yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ctst/package.json b/tests/ctst/package.json index d79732d0c0..e21c522268 100644 --- a/tests/ctst/package.json +++ b/tests/ctst/package.json @@ -26,7 +26,7 @@ "@aws-sdk/client-s3": "^3.583.0", "@aws-sdk/client-sts": "^3.583.0", "@eslint/compat": "^1.1.1", - "cli-testing": "github:scality/cli-testing.git#a30c83ed93e14d38cd4397d9393ed64bfed5c369", + "cli-testing": "github:scality/cli-testing.git#bc237af6354d5d6e64606d61ea464356f68bad98", "eslint": "^9.9.1", "eslint-config-scality": "scality/Guidelines#8.3.0", "typescript-eslint": "^8.4.0" diff --git a/tests/ctst/yarn.lock b/tests/ctst/yarn.lock index 71b31a3513..3343dd8544 100644 --- a/tests/ctst/yarn.lock +++ b/tests/ctst/yarn.lock @@ -3350,9 +3350,9 @@ cli-table3@0.6.5, cli-table3@^0.6.0: optionalDependencies: "@colors/colors" "1.5.0" -"cli-testing@github:scality/cli-testing.git#a30c83ed93e14d38cd4397d9393ed64bfed5c369": +"cli-testing@github:scality/cli-testing.git#bc237af6354d5d6e64606d61ea464356f68bad98": version "1.3.0" - resolved "git+ssh://git@github.com/scality/cli-testing.git#a30c83ed93e14d38cd4397d9393ed64bfed5c369" + resolved "git+ssh://git@github.com/scality/cli-testing.git#bc237af6354d5d6e64606d61ea464356f68bad98" dependencies: "@aws-crypto/sha256-universal" "^5.2.0" "@aws-sdk/client-iam" "^3.879.0" From 6043b9dda76cc358035a5feaa4be3bd849855599 Mon Sep 17 00:00:00 2001 From: williamlardier Date: Mon, 15 Sep 2025 16:51:44 +0200 Subject: [PATCH 11/14] unified setup --- .devcontainer/README.md | 6 +- .devcontainer/setup.sh | 2 +- .github/actions/build-setup-image/action.yaml | 66 ++++ .github/actions/deploy/action.yaml | 21 +- .github/scripts/end2end/configure-e2e.sh | 122 ------- ...nfigure-e2e-ctst.sh => configure-hosts.sh} | 0 .github/scripts/end2end/deploy-metadata.sh | 47 --- .github/scripts/end2end/enable-https.sh | 87 ----- .../end2end/install-kind-dependencies.sh | 3 + .github/scripts/end2end/install-mocks.sh | 25 -- .github/scripts/end2end/keycloak-helper.sh | 67 ---- .github/scripts/end2end/run-e2e-ctst.sh | 99 ------ .github/scripts/end2end/setup-ctst-local.sh | 50 --- .github/workflows/end2end.yaml | 192 ++++++----- tests/@setup/Dockerfile | 83 ++--- tests/@setup/README.md | 307 ++++++------------ tests/@setup/configs/accounts.json | 24 ++ tests/@setup/configs/buckets.json | 122 +++++++ tests/@setup/configs/dns.conf | 20 ++ tests/@setup/configs/endpoints.json | 19 ++ tests/@setup/configs/locations.json | 129 ++++++++ tests/@setup/configs/setup.json | 46 +++ tests/@setup/configs/workflows.json | 71 ++++ tests/@setup/run-tests.sh | 300 +++++++++++++++++ tests/@setup/setup-tests.sh | 204 ++++++++++++ tests/@setup/src/accounts.ts | 176 ++++++++++ tests/@setup/src/buckets.ts | 203 +++++++----- tests/@setup/src/cli.ts | 301 +++++++++++++++-- tests/@setup/src/ctst-local.ts | 131 ++++++++ tests/@setup/src/dns.ts | 302 +++++++---------- tests/@setup/src/endpoints.ts | 150 +++++++++ tests/@setup/src/locations.ts | 83 ++--- tests/@setup/src/metadata.ts | 293 +++++++++++++++++ tests/@setup/src/mocks.ts | 1 - tests/@setup/src/rbac.ts | 1 - tests/@setup/src/tls.ts | 85 +++++ tests/@setup/src/utils/k8s.ts | 300 +++++++++++------ tests/@setup/src/utils/zenko-status.ts | 108 ++++++ tests/@setup/src/workflows.ts | 303 +++++++++++++++++ tests/ZENKO_SETUP_INVENTORY.md | 143 ++++++++ tests/ctst/MIGRATION_TO_CTST_ONLY.md | 2 +- tests/ctst/README.md | 2 +- tests/zenko_tests/configuration.py | 158 --------- tests/zenko_tests/create_buckets.py | 176 ---------- tests/zenko_tests/e2e-config.yaml.template | 119 ------- tests/zenko_tests/e2e_config/accounts.py | 96 ------ tests/zenko_tests/e2e_config/clients.py | 10 - tests/zenko_tests/e2e_config/endpoints.py | 30 -- tests/zenko_tests/e2e_config/locations.py | 38 --- tests/zenko_tests/e2e_config/schema.py | 54 --- tests/zenko_tests/e2e_config/workflows.py | 14 - 51 files changed, 3392 insertions(+), 1999 deletions(-) create mode 100644 .github/actions/build-setup-image/action.yaml delete mode 100755 .github/scripts/end2end/configure-e2e.sh rename .github/scripts/end2end/{configure-e2e-ctst.sh => configure-hosts.sh} (100%) delete mode 100755 .github/scripts/end2end/deploy-metadata.sh delete mode 100755 .github/scripts/end2end/enable-https.sh delete mode 100644 .github/scripts/end2end/install-mocks.sh delete mode 100755 .github/scripts/end2end/keycloak-helper.sh delete mode 100755 .github/scripts/end2end/run-e2e-ctst.sh delete mode 100755 .github/scripts/end2end/setup-ctst-local.sh create mode 100644 tests/@setup/configs/accounts.json create mode 100644 tests/@setup/configs/buckets.json create mode 100644 tests/@setup/configs/dns.conf create mode 100644 tests/@setup/configs/endpoints.json create mode 100644 tests/@setup/configs/locations.json create mode 100644 tests/@setup/configs/setup.json create mode 100644 tests/@setup/configs/workflows.json create mode 100755 tests/@setup/run-tests.sh create mode 100755 tests/@setup/setup-tests.sh create mode 100644 tests/@setup/src/accounts.ts create mode 100644 tests/@setup/src/ctst-local.ts create mode 100644 tests/@setup/src/endpoints.ts create mode 100644 tests/@setup/src/metadata.ts create mode 100644 tests/@setup/src/tls.ts create mode 100644 tests/@setup/src/utils/zenko-status.ts create mode 100644 tests/@setup/src/workflows.ts create mode 100644 tests/ZENKO_SETUP_INVENTORY.md delete mode 100644 tests/zenko_tests/configuration.py delete mode 100644 tests/zenko_tests/create_buckets.py delete mode 100644 tests/zenko_tests/e2e-config.yaml.template delete mode 100644 tests/zenko_tests/e2e_config/accounts.py delete mode 100644 tests/zenko_tests/e2e_config/clients.py delete mode 100644 tests/zenko_tests/e2e_config/endpoints.py delete mode 100644 tests/zenko_tests/e2e_config/locations.py delete mode 100644 tests/zenko_tests/e2e_config/schema.py delete mode 100644 tests/zenko_tests/e2e_config/workflows.py diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 871a4fad22..2a4c498765 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -3,11 +3,11 @@ ## Running CTST tests in the codespace -To run the CTST tests in the codespace, simply head to `.github/script/end2end/` and run `run-e2e-ctst.sh` script. +To run the CTST tests in the codespace, simply head to `tests/@setup/` and run the `run-tests.sh` script. ```bash - cd .github/scripts/end2end/ - bash run-e2e-ctst.sh + cd tests/@setup/ + ./run-tests.sh ~/.kube/config ctst ``` ## Accessing s3 service diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index 3421b3f37c..c8023e2a2d 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -53,7 +53,7 @@ done ( cd .github/scripts/end2end - bash configure-e2e-ctst.sh + bash configure-hosts.sh ) docker image prune -af diff --git a/.github/actions/build-setup-image/action.yaml b/.github/actions/build-setup-image/action.yaml new file mode 100644 index 0000000000..00a8b4054e --- /dev/null +++ b/.github/actions/build-setup-image/action.yaml @@ -0,0 +1,66 @@ +name: Build Setup Image +description: Build the Zenko setup Docker image for use in Kubernetes Jobs + +inputs: + registry: + description: 'Container registry to push to' + required: false + default: 'ghcr.io' + image-name: + description: 'Image name' + required: false + default: 'zenko-setup' + tag: + description: 'Image tag' + required: false + default: 'latest' + push: + description: 'Push image to registry' + required: false + default: 'true' + +outputs: + image: + description: 'Full image name with tag' + value: ${{ steps.meta.outputs.tags }} + digest: + description: 'Image digest' + value: ${{ steps.build.outputs.digest }} + +runs: + using: composite + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + if: ${{ inputs.push == 'true' }} + uses: docker/login-action@v3 + with: + registry: ${{ inputs.registry }} + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ inputs.registry }}/${{ github.repository_owner }}/${{ inputs.image-name }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix={{branch}}- + type=raw,value=${{ inputs.tag }} + + - name: Build and push + id: build + uses: docker/build-push-action@v5 + with: + context: tests/@setup + file: tests/@setup/Dockerfile + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 \ No newline at end of file diff --git a/.github/actions/deploy/action.yaml b/.github/actions/deploy/action.yaml index a3b013d212..6db94c9717 100644 --- a/.github/actions/deploy/action.yaml +++ b/.github/actions/deploy/action.yaml @@ -7,10 +7,6 @@ inputs: description: "The tag of the Zenko Operator image to use" required: false default: "" - deploy_metadata: - description: "Deploy a metadata cluster alongside Zenko" - required: false - default: "false" runs: using: composite steps: @@ -62,14 +58,6 @@ runs: shell: bash run: bash deploy-zenko.sh end2end default working-directory: ./.github/scripts/end2end - - name: Add Keycloak user and assign StorageManager role - shell: bash - run: bash keycloak-helper.sh set-user-instance-ids default - working-directory: ./.github/scripts/end2end - - name: Start external service mocks - shell: bash - run: bash install-mocks.sh "default" - working-directory: ./.github/scripts/end2end - name: Start mock sorbet service shell: bash run: sh tests/smoke/deploy-sorbet-resources.sh end2end @@ -84,14 +72,9 @@ runs: env: SORBETD_NAME: mock-miria working-directory: ./.github/scripts/end2end/operator - - name: Deploy metadata - shell: bash - run: ./deploy-metadata.sh - working-directory: ./.github/scripts/end2end - if: ${{ inputs.deploy_metadata == 'true' }} - - name: End-to-end configuration + - name: Configure DNS for CTST shell: bash - run: bash configure-e2e.sh "end2end" ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} "default" + run: bash configure-hosts.sh working-directory: ./.github/scripts/end2end - name: Linting shell: bash diff --git a/.github/scripts/end2end/configure-e2e.sh b/.github/scripts/end2end/configure-e2e.sh deleted file mode 100755 index 7a41195494..0000000000 --- a/.github/scripts/end2end/configure-e2e.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/sh - -set -exu - -. "$(dirname $0)/common.sh" - -ZENKO_NAME=${1:-end2end} -E2E_IMAGE=${2:-ghcr.io/scality/zenko/zenko-e2e:latest} -NAMESPACE=${3:-default} - -SERVICE_ACCOUNT="${ZENKO_NAME}-config" -POD_NAME="${ZENKO_NAME}-config" -MANAGEMENT_ENDPOINT="http://${ZENKO_NAME}-management-orbit-api:5001" -VAULT_ENDPOINT="http://${ZENKO_NAME}-connector-vault-sts-api" -UUID=$(kubectl get zenko ${ZENKO_NAME} --namespace ${NAMESPACE} -o jsonpath='{.status.instanceID}') -TOKEN=$(get_token) - -cat <> $GITHUB_ENV -echo "OIDC_ENDPOINT=https://keycloak.zenko.local" >> $GITHUB_ENV -echo "NAVBAR_ENDPOINT=https://shell-ui.zenko.local" >> $GITHUB_ENV -echo "OIDC_HOST=keycloak.zenko.local" >> $GITHUB_ENV -echo "ENABLE_KEYCLOAK_HTTPS=true" >> $GITHUB_ENV - -# Set the HTTPS ingress options for Keycloak -KEYCLOAK_INGRESS_OPTIONS="$DIR/configs/keycloak_ingress_https.yaml" -KEYCLOAK_OPTIONS="$DIR/configs/keycloak_options.yaml" -helm upgrade --install keycloak codecentric/keycloak -f "${KEYCLOAK_OPTIONS}" -f "${KEYCLOAK_INGRESS_OPTIONS}" --version ${KEYCLOAK_VERSION} -kubectl rollout status sts/keycloak --timeout=5m - -echo "HTTPS successfully enabled for Zenko deployment" diff --git a/.github/scripts/end2end/install-kind-dependencies.sh b/.github/scripts/end2end/install-kind-dependencies.sh index 1d3df78e6c..17f43d6d20 100755 --- a/.github/scripts/end2end/install-kind-dependencies.sh +++ b/.github/scripts/end2end/install-kind-dependencies.sh @@ -124,6 +124,9 @@ prom_url=https://raw.githubusercontent.com/coreos/prometheus-operator/${PROMETHE kubectl create -f $prom_url || kubectl replace -f $prom_url --wait # wait for the resource to exist kubectl wait --for=condition=established --timeout=10m crd/alertmanagers.monitoring.coreos.com + +# Set PROMETHEUS_NAME for envsubst +export PROMETHEUS_NAME="prometheus-test" envsubst < configs/prometheus.yaml | kubectl apply -f - # zookeeper diff --git a/.github/scripts/end2end/install-mocks.sh b/.github/scripts/end2end/install-mocks.sh deleted file mode 100644 index 12321cc14f..0000000000 --- a/.github/scripts/end2end/install-mocks.sh +++ /dev/null @@ -1,25 +0,0 @@ -#! /bin/sh - -set -exu - -NAMESPACE=${1:-default} - -kubectl create \ - configmap aws-mock \ - --from-file=../mocks/aws/mock-metadata.tar.gz \ - --namespace ${NAMESPACE} - -kubectl create \ - -f ../mocks/azure-mock.yaml \ - -f ../mocks/aws-mock.yaml \ - --namespace ${NAMESPACE} && \ - -kubectl wait \ - --for=condition=Ready \ - --timeout=10m \ - pod -l component=mock \ - --namespace ${NAMESPACE} - -kubectl get \ - pod -l component=mock \ - --namespace ${NAMESPACE} diff --git a/.github/scripts/end2end/keycloak-helper.sh b/.github/scripts/end2end/keycloak-helper.sh deleted file mode 100755 index ec0cde2f9f..0000000000 --- a/.github/scripts/end2end/keycloak-helper.sh +++ /dev/null @@ -1,67 +0,0 @@ -#! /bin/sh - -set -exu - -DIR=$(dirname "$0") - -COMMAND=${1:-''} -NAMESPACE=${2:-default} -ZENKO_NAME=${3:-end2end} - -KEYCLOAK_EXEC="kubectl -n ${NAMESPACE} exec -i keycloak-0 --" - -refresh_creds() { - ${KEYCLOAK_EXEC} \ - /opt/jboss/keycloak/bin/kcadm.sh config credentials \ - --server http://localhost:8080/auth \ - --realm master \ - --user admin \ - --password password -} - -case $COMMAND in - "setup-realm") - refresh_creds - envsubst < $DIR/configs/keycloak_config.json | \ - ${KEYCLOAK_EXEC} /opt/jboss/keycloak/bin/kcadm.sh create realms -f - - ;; - - "add-user") - refresh_creds - - export INSTANCE_ID=`kubectl -n ${NAMESPACE} get zenko ${ZENKO_NAME} -o jsonpath='{.status.instanceID}'` - - export OIDC_EMAIL=${OIDC_EMAIL:-"e2e@zenko.local"} - - envsubst < $DIR/configs/keycloak_user.json | \ - ${KEYCLOAK_EXEC} /opt/jboss/keycloak/bin/kcadm.sh create users -r ${OIDC_REALM} -f - - - ${KEYCLOAK_EXEC} /opt/jboss/keycloak/bin/kcadm.sh set-password \ - -r ${OIDC_REALM} \ - --username ${OIDC_USERNAME} \ - --new-password ${OIDC_PASSWORD} - - # attach StorageManager role to user - ${KEYCLOAK_EXEC} /opt/jboss/keycloak/bin/kcadm.sh add-roles \ - -r ${OIDC_REALM} \ - --uusername ${OIDC_USERNAME} \ - --rolename "StorageManager" - ;; - - "set-user-instance-ids") - refresh_creds - - export INSTANCE_ID=`kubectl -n ${NAMESPACE} get zenko -o jsonpath='{.items[0].status.instanceID}'` - - # get user id - USER_ID=$(${KEYCLOAK_EXEC} /opt/jboss/keycloak/bin/kcadm.sh get users -r ${OIDC_REALM} -q "username=${OIDC_USERNAME}" | jq -r '.[0].id') - # set instanceIds array attribute for user - ${KEYCLOAK_EXEC} /opt/jboss/keycloak/bin/kcadm.sh update users/${USER_ID} -r ${OIDC_REALM} -s 'attributes={"instanceIds":["'"${INSTANCE_ID}"'"],"role":"user"}' - - - ;; - *) - echo "Invalid action. Available actions are [setup, add-user]" - exit 1 - ;; -esac diff --git a/.github/scripts/end2end/run-e2e-ctst.sh b/.github/scripts/end2end/run-e2e-ctst.sh deleted file mode 100755 index 6f3031ae2f..0000000000 --- a/.github/scripts/end2end/run-e2e-ctst.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash -set -exu - -ZENKO_NAME=${1:-end2end} -COMMAND=${2:-"premerge"} -PARALLEL_RUNS=${PARALLEL_RUNS:-$(( ( $(nproc) + 1 ) / 2 ))} -RETRIES=${4:-3} - -shift 4 - -JUNIT_REPORT_PATH=${JUNIT_REPORT_PATH:-"ctst-junit.xml"} - -# Zenko Version -VERSION=$(cat ../../../VERSION | grep -Po 'VERSION="\K[^"]*') - -# Minimal environment setup - CTST will handle all Kubernetes discovery - -# Minimal CTST world params - CTST handles all Kubernetes discovery -WORLD_PARAMETERS="$(jq -c </dev/null; then - echo "Patching CoreDNS for mock service resolution..." - bash patch-coredns.sh -else - echo "CoreDNS already configured" -fi - -# 3. Setup /etc/hosts for local development (requires sudo) -echo "Checking /etc/hosts configuration..." -if ! grep -q "zenko.local" /etc/hosts 2>/dev/null; then - echo "Setting up /etc/hosts (requires sudo)..." - echo "127.0.0.1 iam.zenko.local ui.zenko.local s3-local-file.zenko.local keycloak.zenko.local \ - sts.zenko.local management.zenko.local s3.zenko.local website.mywebsite.com utilization.zenko.local" | sudo tee -a /etc/hosts -else - echo "/etc/hosts already configured" -fi - -# 4. Wait for Zenko to be ready -echo "Waiting for Zenko deployment to be ready..." -kubectl wait --for condition=DeploymentFailure=false --timeout 10m zenko/end2end -n $NAMESPACE 2>/dev/null || echo "Zenko wait failed or not found" -kubectl wait --for condition=DeploymentInProgress=false --timeout 10m zenko/end2end -n $NAMESPACE 2>/dev/null || echo "Zenko wait failed or not found" - -echo "CTST local environment ready!" -echo "" -echo "Usage:" -echo " cd tests/ctst" -echo " npm test # Run all CTST tests" -echo " npm run test -- --tags @PRA # Run specific test tags" -echo "" -echo "Note: CTST will handle all Kubernetes setup (mocks, topics, deployments, etc.) automatically" \ No newline at end of file diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index e716dd2dc1..cb90ad1535 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -12,74 +12,75 @@ on: - 'q/*' env: - # kind-env + # Core infrastructure WORKER_COUNT: '2' - OPERATOR_REPO: git@github.com:scality/zenko-operator.git - OPERATOR_IMAGE: "" KIND_NODE_IMAGE: "kindest/node:v1.31.9@sha256:b94a3a6c06198d17f59cca8c6f486236fa05e2fb359cbd75dabbfc348a10b211" VOLUME_ROOT: /artifacts + SHELL_UI_NAME: "shell-ui" + SHELL_UI_IMAGE: "ghcr.io/scality/metalk8s/shell-ui:v127.0.0" + PROMETHEUS_NAME: "prometheus" + + # Test images + E2E_IMAGE_NAME: ghcr.io/scality/zenko/zenko-e2e + E2E_CTST_IMAGE_NAME: ghcr.io/scality/zenko/zenko-e2e-ctst + E2E_IMAGE_TAG: ${{ github.sha }} + + # External provider secrets (for real backends) + GCP_ACCESS_KEY: ${{ secrets.AWS_GCP_BACKEND_ACCESS_KEY }} + GCP_SECRET_KEY: ${{ secrets.AWS_GCP_BACKEND_SECRET_KEY }} + GCP_BACKEND_SERVICE_KEY: ${{ secrets.GCP_BACKEND_SERVICE_KEY }} + GCP_BACKEND_SERVICE_EMAIL: ${{ secrets.GCP_BACKEND_SERVICE_EMAIL }} + + # Feature flags + ENABLE_RING_TESTS: "false" + + # Domains + SUBDOMAIN: "zenko.local" + DR_SUBDOMAIN: "dr.zenko.local" + + # OIDC/Keycloak (still needed for test execution) OIDC_REALM: "zenko" OIDC_CLIENT_ID: "zenko-ui" OIDC_USERNAME: 'storage_manager' OIDC_PASSWORD: '123' OIDC_FIRST_NAME: 'hello' OIDC_LAST_NAME: 'world' - SHELL_UI_NAME: "shell-ui" - SHELL_UI_IMAGE: "ghcr.io/scality/metalk8s/shell-ui:v127.0.0" - HTTP_PROXY: "" - HTTPS_PROXY: "" - BACKBEAT_BUCKET_CHECK_TIMEOUT_S: "" - BACKBEAT_LCC_CRON_RULE: "" - # e2e-env - E2E_IMAGE_NAME: ghcr.io/scality/zenko/zenko-e2e - E2E_CTST_IMAGE_NAME: ghcr.io/scality/zenko/zenko-e2e-ctst - E2E_IMAGE_TAG: ${{ github.sha }} - VAULT_TEST_IMAGE_NAME: "" - VAULT_TEST_IMAGE_TAG: "" - # http-env - UI_ENDPOINT: 'http://ui.zenko.local' OIDC_ENDPOINT: 'http://keycloak.zenko.local' OIDC_HOST: 'keycloak.zenko.local' - NAVBAR_ENDPOINT: 'http://shell-ui.zenko.local' - ENABLE_KEYCLOAK_HTTPS: 'false' - KEYCLOAK_VERSION: '18.4.4' - PROMETHEUS_NAME: "prometheus" - # mock-env + + # Test environment endpoints (dynamically configured but need base values) + UI_ENDPOINT: 'http://ui.zenko.local' + + # Mock service configurations (from our setup tool configs) AZURE_ACCOUNT_NAME: devstoreaccount1 AZURE_BACKEND_ENDPOINT: https://devstoreaccount1.blob.azure-mock.zenko.local - AZURE_BACKEND_QUEUE_ENDPOINT: https://devstoreaccount1.queue.azure-mock.zenko.local AZURE_SECRET_KEY: Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw== AWS_ENDPOINT: https://aws-mock.zenko.local AWS_ACCESS_KEY: accessKey1 AWS_SECRET_KEY: verySecretKey1 VERIFY_CERTIFICATES: "false" - # secrets-env + + # Backend locations (from our locations.ts config) AWS_BACKEND_SOURCE_LOCATION: awsbackend AWS_BACKEND_DESTINATION_LOCATION: awsbackendmismatch AWS_BACKEND_DESTINATION_FAIL_LOCATION: awsbackendfail - AWS_BACKEND_DESTINATION_REPLICATION_FAIL_CTST_LOCATION: awsbackendreplicationctstfail GCP_BACKEND_DESTINATION_LOCATION: gcpbackendmismatch AZURE_BACKEND_DESTINATION_LOCATION: azurebackendmismatch COLD_BACKEND_DESTINATION_LOCATION: e2e-cold AZURE_ARCHIVE_BACKEND_DESTINATION_LOCATION: e2e-azure-archive MIRIA_BACKEND_DESTINATION_LOCATION: e2e-miria-archive LOCATION_QUOTA_BACKEND: quotabackend + + # Bucket names (from our buckets.json config) AWS_BUCKET_NAME: ci-zenko-aws-target-bucket AWS_CRR_BUCKET_NAME: ci-zenko-aws-crr-target-bucket AWS_FAIL_BUCKET_NAME: ci-zenko-aws-fail-target-bucket - AWS_REPLICATION_FAIL_CTST_BUCKET_NAME: ci-zenko-aws-replication-fail-ctst-bucket AZURE_CRR_BUCKET_NAME: ci-zenko-azure-crr-target-bucket AZURE_ARCHIVE_BUCKET_NAME: ci-zenko-azure-archive-target-bucket - AZURE_ARCHIVE_BUCKET_NAME_2: ci-zenko-azure-archive-target-bucket-2 - AZURE_ARCHIVE_QUEUE_NAME: ci-zenko-azure-archive-target-queue GCP_CRR_BUCKET_NAME: ci-zenko-gcp-crr-target-bucket GCP_CRR_MPU_BUCKET_NAME: ci-zenko-gcp-crr-mpu-bucket - GCP_ACCESS_KEY: ${{ secrets.AWS_GCP_BACKEND_ACCESS_KEY }} - GCP_SECRET_KEY: ${{ secrets.AWS_GCP_BACKEND_SECRET_KEY }} - GCP_BACKEND_SERVICE_KEY: ${{ secrets.GCP_BACKEND_SERVICE_KEY }} - GCP_BACKEND_SERVICE_EMAIL: ${{ secrets.GCP_BACKEND_SERVICE_EMAIL }} - # Enable this for Ring tests - ENABLE_RING_TESTS: "false" + + # Ring/S3C test configuration RING_S3C_ACCESS_KEY: accessKey1 RING_S3C_SECRET_KEY: verySecretKey1 RING_S3C_ENDPOINT: http://s3c.local:8000 @@ -88,13 +89,32 @@ env: RING_S3C_BACKEND_SOURCE_NON_VERSIONED_LOCATION: rings3cbackendingestionnonversioned RING_S3C_INGESTION_SRC_NON_VERSIONED_BUCKET_NAME: ingestion-test-src-non-versioned-bucket RING_S3C_INGESTION_NON_VERSIONED_OBJECT_COUNT_PER_TYPE: 2 - # CTST end2end tests - NOTIF_DEST_NAME: "destination1" - NOTIF_DEST_TOPIC: "destination-topic-1" - NOTIF_ALT_DEST_NAME: "destination2" - NOTIF_ALT_DEST_TOPIC: "destination-topic-2" - SUBDOMAIN: "zenko.local" - DR_SUBDOMAIN: "dr.zenko.local" + + # Test timeouts + BACKBEAT_BUCKET_CHECK_TIMEOUT_S: "" + + # MongoDB configuration (for tests that need direct DB access) + MONGO_DATABASE: "" + MONGO_READ_PREFERENCE: "" + MONGO_REPLICA_SET_HOSTS: "" + MONGO_SHARD_COLLECTION: "" + MONGO_WRITE_CONCERN: "" + MONGO_AUTH_USERNAME: "" + MONGO_AUTH_PASSWORD: "" + + # Keycloak test configuration (for OAuth tests) + KEYCLOAK_TEST_USER: ${OIDC_USERNAME} + KEYCLOAK_TEST_PASSWORD: ${OIDC_PASSWORD} + KEYCLOAK_TEST_HOST: keycloak.zenko.local + KEYCLOAK_TEST_PORT: "80" + KEYCLOAK_TEST_REALM_NAME: ${OIDC_REALM} + KEYCLOAK_TEST_CLIENT_ID: ${OIDC_CLIENT_ID} + KEYCLOAK_TEST_GRANT_TYPE: "password" + + # Test framework configuration + STAGE: "dev" + + # Test reporting SKOPEO_PATH: "/tmp" HELM_VERSION: "v3.15.4" YQ_VERSION: "v4.44.3" @@ -137,6 +157,22 @@ jobs: uses: ./.github/workflows/alerts.yaml secrets: inherit + build-setup-image: + runs-on: ubuntu-24.04 + outputs: + image: ${{ steps.build-image.outputs.image }} + digest: ${{ steps.build-image.outputs.digest }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build setup image + id: build-image + uses: ./.github/actions/build-setup-image + with: + registry: ghcr.io + image-name: zenko-setup + tag: ${{ github.sha }} + check-workflows: runs-on: ubuntu-24.04 steps: @@ -354,12 +390,6 @@ jobs: username: "${{ github.repository_owner }}" password: "${{ github.token }}" registry: ghcr.io - - name: Generate end2end config yaml - run: |- - cd tests/zenko_tests - envsubst < 'e2e-config.yaml.template' > 'e2e-config.yaml' - cat e2e-config.yaml - echo 'Generated e2e-config.yaml file' - name: Build and push CI image uses: docker/build-push-action@v5 with: @@ -439,7 +469,7 @@ jobs: cache-to: type=gha,mode=max,scope=end2end-ctst end2end-pra: - needs: [build-kafka, lint-and-build-ctst] + needs: [build-kafka, lint-and-build-ctst, build-setup-image] runs-on: ubuntu-24.04-16core steps: - name: Checkout @@ -475,24 +505,22 @@ jobs: env: ZENKO_MONGODB_DATABASE: pradb working-directory: ./.github/scripts/end2end - - name: Add Keycloak pra user and assign StorageManager role + - name: Configure PRA test users (handled by CTST) shell: bash - run: bash keycloak-helper.sh add-user default end2end-pra - env: - OIDC_USERNAME: 'zenko-end2end-pra' - OIDC_EMAIL: 'e2e-pra@zenko.local' - working-directory: ./.github/scripts/end2end - - name: Configure E2E PRA test environment - run: bash configure-e2e.sh end2end-pra ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} default - working-directory: ./.github/scripts/end2end + run: echo "PRA users configured by CTST setup" + - name: Setup PRA test environment env: - OIDC_USERNAME: 'zenko-end2end-pra' - - name: Configure E2E CTST test environment - run: bash configure-e2e-ctst.sh + INSTANCE_ID: "end2end-pra" + SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} + GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} + run: ./setup-tests.sh ~/.kube/config + working-directory: ./tests/@setup + - name: Configure hosts file + run: bash configure-hosts.sh working-directory: ./.github/scripts/end2end - name: Run CTST end to end tests - run: bash run-e2e-ctst.sh "" "" "" "" --tags @PRA - working-directory: ./.github/scripts/end2end + run: ./run-tests.sh ~/.kube/config ctst --tags @PRA + working-directory: ./tests/@setup - name: Debug wait uses: ./.github/actions/debug-wait timeout-minutes: 60 @@ -508,7 +536,7 @@ jobs: run: kind delete cluster end2end-2-shards-http: - needs: [build-kafka, build-test-image] + needs: [build-kafka, build-test-image, build-setup-image] runs-on: - ubuntu-24.04-8core env: @@ -539,6 +567,12 @@ jobs: uses: ./.github/actions/deploy env: GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} + - name: Setup test environment + env: + SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} + GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} + run: ./setup-tests.sh ~/.kube/config + working-directory: ./tests/@setup - name: Run init CI test run: bash run-e2e-test.sh "end2end" ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} "end2end" "default" working-directory: ./.github/scripts/end2end @@ -551,9 +585,6 @@ jobs: - name: Run smoke tests run: bash run-e2e-test.sh "end2end" ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} "smoke" "default" working-directory: ./.github/scripts/end2end - - name: Enable HTTPS - run: bash enable-https.sh - working-directory: ./.github/scripts/end2end - name: Run smoke tests run: bash run-e2e-test.sh "end2end" ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} "smoke" "default" working-directory: ./.github/scripts/end2end @@ -572,7 +603,7 @@ jobs: run: kind delete cluster end2end-sharded: - needs: [build-kafka, build-test-image] + needs: [build-kafka, build-test-image, build-setup-image] runs-on: - ubuntu-24.04-8core env: @@ -602,10 +633,17 @@ jobs: zenko-operator - name: Deploy Zenko uses: ./.github/actions/deploy + - name: Setup test environment env: + SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} - with: - deploy_metadata: ${{ env.ENABLE_RING_TESTS }} + run: | + if [[ "${{ env.ENABLE_RING_TESTS }}" == "true" ]]; then + ./setup-tests.sh ~/.kube/config setup --metadata + else + ./setup-tests.sh ~/.kube/config setup --rbac --dns --buckets --locations --accounts --endpoints --workflows --tls + fi + working-directory: ./tests/@setup - name: Run backbeat end to end tests run: bash run-e2e-test.sh "end2end" ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} "backbeat" "default" working-directory: ./.github/scripts/end2end @@ -624,7 +662,7 @@ jobs: run: kind delete cluster ctst-end2end-sharded: - needs: [build-kafka, lint-and-build-ctst] + needs: [build-kafka, lint-and-build-ctst, build-setup-image] runs-on: - ubuntu-24.04-8core steps: @@ -659,12 +697,18 @@ jobs: TIME_PROGRESSION_FACTOR: 86400 TRANSITION_ONE_DAY_EARLIER: false EXPIRE_ONE_DAY_EARLIER: false - - name: Configure E2E test environment - run: bash configure-e2e-ctst.sh + - name: Setup test environment + env: + SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} + GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} + run: ./setup-tests.sh ~/.kube/config + working-directory: ./tests/@setup + - name: Configure hosts file + run: bash configure-hosts.sh working-directory: ./.github/scripts/end2end - name: Run CTST end to end tests - run: bash run-e2e-ctst.sh "" "" "" "" --tags 'not @PRA' - working-directory: ./.github/scripts/end2end + run: ./run-tests.sh ~/.kube/config ctst --tags 'not @PRA' + working-directory: ./tests/@setup - name: Debug wait uses: ./.github/actions/debug-wait timeout-minutes: 60 diff --git a/tests/@setup/Dockerfile b/tests/@setup/Dockerfile index cd18bdeb2a..1554085313 100644 --- a/tests/@setup/Dockerfile +++ b/tests/@setup/Dockerfile @@ -1,63 +1,50 @@ -# Multi-stage build for smaller final image -FROM node:18-alpine AS builder +FROM node:22-alpine + +# Install system dependencies +RUN apk add --no-cache \ + curl \ + git \ + bash \ + jq \ + openssl \ + ca-certificates + +# Install TypeScript globally +RUN yarn global add typescript@5.9.2 + +# Install kubectl +RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \ + chmod +x kubectl && \ + mv kubectl /usr/local/bin/ + +# Install helm +RUN curl -fsSL https://get.helm.sh/helm-v3.12.0-linux-amd64.tar.gz | tar -xzC /tmp && \ + mv /tmp/linux-amd64/helm /usr/local/bin/helm && \ + chmod +x /usr/local/bin/helm # Set working directory WORKDIR /app # Copy package files -COPY package.json yarn.lock tsconfig.json ./ +COPY package.json yarn.lock ./ # Install dependencies -RUN yarn install --production --frozen-lockfile +RUN yarn install --prod --network-concurrency=1 # Copy source code -COPY src/ ./src/ - -# Build the TypeScript code -RUN npm run build +COPY . . -# Final runtime image -FROM node:18-alpine +# Build TypeScript +RUN yarn build -# Install kubectl for Kubernetes API access -RUN apk add --no-cache curl && \ - curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \ - chmod +x kubectl && \ - mv kubectl /usr/local/bin/ +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nextjs -u 1001 -# Create non-root user -RUN addgroup -g 1001 -S zenko && \ - adduser -S zenko -u 1001 -G zenko - -# Set working directory -WORKDIR /app - -# Copy built application from builder stage -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/package.json ./ - -# Create directory for kubeconfig mount -RUN mkdir -p /home/zenko/.kube && \ - chown -R zenko:zenko /home/zenko && \ - chown -R zenko:zenko /app - -# Switch to non-root user -USER zenko - -# Set environment variables -ENV NODE_ENV=production -ENV LOG_LEVEL=info - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD node -e "console.log('Health check passed')" || exit 1 +# Set proper permissions +RUN chown -R nextjs:nodejs /app +USER nextjs # Default command ENTRYPOINT ["node", "dist/cli.js"] -CMD ["--help"] - -# Labels for metadata -LABEL maintainer="Scality " -LABEL description="Unified CLI tool for Zenko test environment setup" -LABEL version="1.0.0" \ No newline at end of file +CMD ["--help"] \ No newline at end of file diff --git a/tests/@setup/README.md b/tests/@setup/README.md index b5481a1f4e..5f9b3c5d2e 100644 --- a/tests/@setup/README.md +++ b/tests/@setup/README.md @@ -1,259 +1,146 @@ -# Zenko Test Setup CLI +# Zenko Test Environment Setup -Unified CLI tool for Zenko test environment setup, consolidating all scattered setup scripts into a single TypeScript-based containerized solution. +Simple scripts to setup and run tests against any Zenko cluster using Kubernetes Jobs. -## Overview - -This tool replaces 22+ setup scripts scattered across Bash, Python, and TypeScript by providing a single, consistent interface for setting up Zenko test environments. - -## Features - -- **Mock Services**: AWS S3 (CloudServer) and Azure (Azurite) mock deployments -- **Test Buckets**: Automated creation across AWS, Azure, and Ring providers -- **Storage Locations**: Management API configuration for all storage backends -- **DNS Configuration**: CoreDNS rewrite rules for test domains -- **RBAC Permissions**: Service account cluster-admin permissions - -## Installation - -### Container Usage (Recommended) - -```bash -docker run --rm -v ~/.kube:/root/.kube \ - -e NAMESPACE=default \ - -e SUBDOMAIN=zenko.local \ - -e INSTANCE_ID=xyz123 \ - ghcr.io/scality/zenko-test-setup:latest \ - all --verbose -``` - -### Local Development +## Quick Start +### 1. Setup Test Environment ```bash -# Clone and install -yarn install +# Using default kubeconfig +./setup-tests.sh -# Build -yarn build +# Using specific kubeconfig +./setup-tests.sh /path/to/kubeconfig -# Run locally -yarn dev -- all --namespace=default --subdomain=zenko.local +# With custom options +./setup-tests.sh ~/.kube/config --no-metadata --no-tls ``` -## Usage - -### Complete Setup - -Run all setup tasks: +### 2. Run Tests ```bash -zenko-setup all --namespace=my-namespace --subdomain=test.local -``` +# Run CTST tests (default) +./run-tests.sh -Skip specific components: -```bash -zenko-setup all --skip-mocks -``` - -### Individual Components - -Setup specific components: -```bash -# Mock services only -zenko-setup mocks --aws-only - -# Buckets for specific provider -zenko-setup buckets --provider=aws +# Run specific test type +./run-tests.sh ~/.kube/config ctst +./run-tests.sh ~/.kube/config e2e +./run-tests.sh ~/.kube/config smoke -# Storage locations -zenko-setup locations - -# DNS configuration -zenko-setup dns - -# RBAC permissions -zenko-setup rbac +# Run CTST with specific tags +./run-tests.sh ~/.kube/config ctst --tags @PRA +./run-tests.sh ~/.kube/config ctst --tags 'not @PRA' ``` -### Options - -Global options available for all commands: - -- `--namespace `: Kubernetes namespace (default: default) -- `--subdomain `: DNS subdomain (default: zenko.local) -- `--instance-id `: Zenko instance ID for role assignments -- `--kubeconfig `: Path to kubeconfig file -- `--dry-run`: Show what would be done without executing -- `--verbose`: Enable verbose logging - ## Environment Variables -Configure via environment variables: +Set these before running the scripts to customize behavior: +### Setup Configuration ```bash -export NAMESPACE=my-namespace -export SUBDOMAIN=test.local -export INSTANCE_ID=abc123 -export KUBECONFIG=/path/to/kubeconfig -export LOG_LEVEL=debug +export NAMESPACE="default" # Kubernetes namespace +export INSTANCE_ID="end2end" # Zenko instance name +export SUBDOMAIN="zenko.local" # Base domain for services +export GIT_ACCESS_TOKEN="your-token" # For metadata deployment +export METADATA_NAMESPACE="metadata" # Metadata service namespace +export SETUP_IMAGE="ghcr.io/scality/zenko-setup:latest" # Setup container image +export LOG_LEVEL="debug" # Logging verbosity +export JOB_TIMEOUT="1800" # Job timeout in seconds ``` -## Container Environment - -The container requires: - -1. **Kubernetes Access**: Mount kubeconfig or use in-cluster config -2. **Network Access**: Ability to reach Kubernetes API and services - -Example with kubeconfig mount: +### Test Configuration ```bash -docker run --rm \ - -v ~/.kube:/root/.kube:ro \ - -e NAMESPACE=zenko-test \ - -e SUBDOMAIN=test.local \ - ghcr.io/scality/zenko-test-setup:latest \ - all -``` +export E2E_IMAGE="ghcr.io/scality/zenko/zenko-e2e:latest" # E2E test container +export E2E_CTST_IMAGE="ghcr.io/scality/zenko/zenko-e2e-ctst:latest" # CTST test container +export OIDC_REALM="zenko" # Keycloak realm +export OIDC_USERNAME="storage_manager" # Test user +export OIDC_PASSWORD="123" # Test password +export OIDC_HOST="keycloak.zenko.local" # Keycloak hostname -Example with in-cluster config (when running in Kubernetes): -```yaml -apiVersion: v1 -kind: Pod -spec: - containers: - - name: setup - image: ghcr.io/scality/zenko-test-setup:latest - args: ["all", "--namespace=zenko-test"] - env: - - name: NAMESPACE - value: "zenko-test" - serviceAccountName: zenko-setup # with cluster-admin permissions +# CTST-specific configuration +export PARALLEL_RUNS="4" # Number of parallel test runs +export RETRIES="3" # Test retry count +export JUNIT_REPORT_PATH="ctst-junit.xml" # JUnit report path +export AZURE_ACCOUNT_NAME="devstoreaccount1" # Azure storage account +export AZURE_SECRET_KEY="..." # Azure storage key ``` -## Integration Examples - -### CTST Integration - -Replace existing setup logic: -```typescript -// Before: Complex setup in BeforeAll -beforeAll(async () => { - // Call container instead of individual scripts - await exec('docker run --rm -v ~/.kube:/root/.kube ghcr.io/scality/zenko-test-setup:latest all'); - - // Keep only info extraction - await extractInstanceInfo(); - await extractCredentials(); -}); -``` +## Requirements -### GitHub Workflows - -Replace multiple script calls: -```yaml -# Before: Multiple script executions -- name: Setup Mocks - run: .github/scripts/end2end/install-mocks.sh -- name: Setup Keycloak - run: .github/scripts/end2end/keycloak-helper.sh - -# After: Single container call -- name: Setup Test Environment - run: | - docker run --rm \ - -v ${{ env.KUBECONFIG }}:/root/.kube/config \ - -e NAMESPACE=${{ env.NAMESPACE }} \ - ghcr.io/scality/zenko-test-setup:latest \ - all --verbose -``` +- `kubectl` configured to access your cluster +- Appropriate RBAC permissions in the target cluster (created automatically) -### Node.js Test Integration +## How It Works -```bash -# Replace Python scripts -# Before: python tests/zenko_tests/create_buckets.py -# After: -docker run --rm -v ~/.kube:/root/.kube ghcr.io/scality/zenko-test-setup:latest buckets -``` - -## Development +1. **Setup Script** (`./setup-tests.sh`): + - Creates RBAC permissions (ServiceAccount + ClusterRoleBinding) + - Runs a Kubernetes Job with the zenko-setup container + - Configures buckets, accounts, endpoints, workflows, TLS, etc. + - Waits for Zenko to stabilize -### Project Structure +2. **Test Script** (`./run-tests.sh`): + - Verifies Zenko is deployed and ready + - Runs test containers as Kubernetes Jobs + - Streams logs and reports results -``` -src/ -├── cli.ts # Main CLI interface -├── mocks.ts # AWS/Azure mock deployment -├── buckets.ts # Bucket creation (all providers) -├── locations.ts # Storage locations via Management API -├── keycloak.ts # Realm/users/roles -├── dns.ts # CoreDNS configuration -├── rbac.ts # Service account permissions -└── utils/ - ├── logger.ts # Logging utilities - └── k8s.ts # Kubernetes client wrapper -``` +Both scripts use Kubernetes Jobs in the cluster - no local Docker required. -### Building +## Examples ```bash -# Install dependencies -yarn install +# Setup development environment +export NAMESPACE="dev-env" +export INSTANCE_ID="my-zenko" +./setup-tests.sh ~/.kube/dev-config -# Build TypeScript -yarn build +# Run CTST tests +./run-tests.sh ~/.kube/dev-config ctst -# Run tests -yarn test +# Run with custom metadata deployment +export GIT_ACCESS_TOKEN="ghp_xxx" +export METADATA_NAMESPACE="s3c" +./setup-tests.sh ~/.kube/prod-config -# Lint code -yarn lint -``` - -### Container Build - -```bash -# Build container image -docker build -t zenko-test-setup:latest . +# Skip TLS setup +./setup-tests.sh ~/.kube/config --no-tls -# Test container -docker run --rm zenko-test-setup:latest --help +# Setup for remote cluster with custom timeout +export JOB_TIMEOUT="3600" # 1 hour +./setup-tests.sh ~/.kube/remote-cluster-config +./run-tests.sh ~/.kube/remote-cluster-config e2e ``` ## Troubleshooting -### Common Issues +### Job Failed or Timed Out +The scripts now automatically show detailed logs and status when jobs fail, including: +- Job status and description +- Pod status and details +- Complete job logs -1. **Kubeconfig not found**: Ensure kubeconfig is mounted at `/root/.kube/config` -2. **Permission denied**: Service account needs cluster-admin permissions -3. **DNS changes not applied**: CoreDNS pods may need manual restart -4. **Management API unavailable**: Check Zenko deployment status +```bash +# Check job status manually if needed +kubectl get jobs -n ${NAMESPACE} -l app=zenko-setup -### Debug Mode +# Get job logs manually if needed +kubectl logs job/zenko-setup-123456 -n ${NAMESPACE} -Enable verbose logging: -```bash -zenko-setup all --verbose +# Delete failed jobs +kubectl delete jobs -n ${NAMESPACE} -l app=zenko-setup ``` -Check container logs: +### RBAC Issues ```bash -docker logs -``` - -### Dry Run +# Verify service account exists +kubectl get serviceaccount zenko-setup -n ${NAMESPACE} -Test what would be executed: -```bash -zenko-setup all --dry-run --verbose +# Check cluster role binding +kubectl get clusterrolebinding zenko-setup ``` -## Contributing - -1. Follow TypeScript best practices -2. Add tests for new functionality -3. Update documentation -4. Ensure container builds successfully - -## License +### Remote Cluster Setup +For remote clusters, ensure: +- Your kubeconfig has proper credentials +- Network connectivity to the cluster +- Sufficient RBAC permissions in the target namespace -Apache License 2.0 \ No newline at end of file +The scripts will automatically create the needed ServiceAccount and ClusterRoleBinding. \ No newline at end of file diff --git a/tests/@setup/configs/accounts.json b/tests/@setup/configs/accounts.json new file mode 100644 index 0000000000..90db9111d5 --- /dev/null +++ b/tests/@setup/configs/accounts.json @@ -0,0 +1,24 @@ +{ + "accounts": [ + { + "name": "account-test-1", + "email": "account-test-1@zenko.local", + "description": "Primary test account for integration testing" + }, + { + "name": "account-test-2", + "email": "account-test-2@zenko.local", + "description": "Secondary test account for multi-account scenarios" + }, + { + "name": "replication-source", + "email": "replication-source@zenko.local", + "description": "Source account for replication workflows" + }, + { + "name": "replication-target", + "email": "replication-target@zenko.local", + "description": "Target account for replication workflows" + } + ] +} \ No newline at end of file diff --git a/tests/@setup/configs/buckets.json b/tests/@setup/configs/buckets.json new file mode 100644 index 0000000000..e37c9f6b42 --- /dev/null +++ b/tests/@setup/configs/buckets.json @@ -0,0 +1,122 @@ +{ + "aws": { + "buckets": [ + { + "name": "ci-zenko-aws-source-bucket", + "versioning": false, + "objects": [ + { "key": "test-object-1.txt", "body": "Test content 1" }, + { "key": "test-object-2.txt", "body": "Test content 2" }, + { "key": "folder/nested-object.txt", "body": "Nested content" } + ] + }, + { + "name": "ci-zenko-aws-target-bucket", + "versioning": false, + "objects": [] + }, + { + "name": "ci-zenko-aws-crr-target-bucket", + "versioning": false, + "objects": [] + }, + { + "name": "ci-zenko-aws-fail-target-bucket", + "versioning": false, + "objects": [] + }, + { + "name": "ci-zenko-aws-versioned-bucket", + "versioning": true, + "objects": [] + }, + { + "name": "ci-zenko-aws-lifecycle-bucket", + "versioning": false, + "objects": [] + }, + { + "name": "ci-zenko-aws-replication-bucket", + "versioning": true, + "objects": [] + }, + { + "name": "ci-zenko-aws-notification-bucket", + "versioning": false, + "objects": [] + } + ] + }, + "azure": { + "containers": [ + { + "name": "ci-zenko-azure-source-container", + "blobs": [ + { "name": "test-blob-1.txt", "content": "Azure test content 1" }, + { "name": "test-blob-2.txt", "content": "Azure test content 2" }, + { "name": "folder/nested-blob.txt", "content": "Azure nested content" } + ] + }, + { + "name": "ci-zenko-azure-target-container", + "blobs": [] + }, + { + "name": "ci-zenko-azure-archive-container", + "blobs": [] + }, + { + "name": "ci-zenko-azure-lifecycle-container", + "blobs": [] + }, + { + "name": "ci-zenko-azure-crr-target-bucket", + "blobs": [] + }, + { + "name": "ci-zenko-azure-archive-target-bucket", + "blobs": [] + } + ], + "queues": [ + { "name": "ci-zenko-azure-notifications-queue" }, + { "name": "ci-zenko-azure-status-queue" } + ] + }, + "ring": { + "buckets": [ + { + "name": "ci-zenko-ring-source-bucket", + "objects": [] + }, + { + "name": "ci-zenko-ring-target-bucket", + "objects": [] + }, + { + "name": "ci-zenko-ring-archive-bucket", + "objects": [] + }, + { + "name": "ingestion-test-src-bucket", + "objects": [] + }, + { + "name": "ingestion-test-src-non-versioned-bucket", + "objects": [] + }, + { + "name": "ci-zenko-quota-test-bucket", + "objects": [] + }, + { + "name": "ci-zenko-gcp-crr-target-bucket", + "objects": [] + }, + { + "name": "ci-zenko-gcp-crr-mpu-bucket", + "objects": [] + } + ] + } +} \ No newline at end of file diff --git a/tests/@setup/configs/dns.conf b/tests/@setup/configs/dns.conf new file mode 100644 index 0000000000..0355557df5 --- /dev/null +++ b/tests/@setup/configs/dns.conf @@ -0,0 +1,20 @@ +.:53 { + errors + health + ready + # Zenko test rewrite rules for {{subdomain}} + rewrite name regex (.+\.)?aws-mock\.{{subdomain}} cloudserver-mock.{{namespace}}.svc.cluster.local + rewrite name regex (.+\.)?azure-mock\.{{subdomain}} azurite-mock.{{namespace}}.svc.cluster.local + rewrite name regex iam\.{{subdomain}} zenko-iam.{{namespace}}.svc.cluster.local + rewrite name regex ui\.{{subdomain}} zenko-ui.{{namespace}}.svc.cluster.local + rewrite name regex s3\.{{subdomain}} zenko-s3.{{namespace}}.svc.cluster.local + rewrite name regex management\.{{subdomain}} zenko-management.{{namespace}}.svc.cluster.local + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + log +} diff --git a/tests/@setup/configs/endpoints.json b/tests/@setup/configs/endpoints.json new file mode 100644 index 0000000000..cc2d99b1af --- /dev/null +++ b/tests/@setup/configs/endpoints.json @@ -0,0 +1,19 @@ +{ + "endpoints": [ + { + "hostname": "s3.test-location-1.zenko.local", + "locationName": "aws-test-location", + "description": "S3 endpoint for AWS test location" + }, + { + "hostname": "s3.test-location-2.zenko.local", + "locationName": "azure-test-location", + "description": "S3 endpoint for Azure test location" + }, + { + "hostname": "s3.ring-test.zenko.local", + "locationName": "ring-test-location", + "description": "S3 endpoint for Ring test location" + } + ] +} \ No newline at end of file diff --git a/tests/@setup/configs/locations.json b/tests/@setup/configs/locations.json new file mode 100644 index 0000000000..cabf42cc22 --- /dev/null +++ b/tests/@setup/configs/locations.json @@ -0,0 +1,129 @@ +{ + "locations": [ + { + "name": "awsbackend", + "locationType": "location-s3-v1", + "details": { + "endpoint": "http://cloudserver-mock.{namespace}.svc.cluster.local:8000", + "bucketName": "ci-zenko-aws-target-bucket", + "accessKey": "accessKey1", + "secretKey": "verySecretKey1", + "bucketMatch": true, + "pathStyle": true + } + }, + { + "name": "awsbackendmismatch", + "locationType": "location-s3-v1", + "details": { + "endpoint": "http://cloudserver-mock.{namespace}.svc.cluster.local:8000", + "bucketName": "ci-zenko-aws-crr-target-bucket", + "accessKey": "accessKey1", + "secretKey": "verySecretKey1", + "bucketMatch": false, + "pathStyle": true + } + }, + { + "name": "awsbackendfail", + "locationType": "location-s3-v1", + "details": { + "endpoint": "http://cloudserver-mock.{namespace}.svc.cluster.local:8000", + "bucketName": "ci-zenko-aws-fail-target-bucket", + "accessKey": "accessKey1", + "secretKey": "verySecretKey1", + "bucketMatch": false, + "pathStyle": true + } + }, + { + "name": "azurebackendmismatch", + "locationType": "location-azure-v1", + "details": { + "endpoint": "http://azurite-mock.{namespace}.svc.cluster.local:10000/devstoreaccount1", + "containerName": "ci-zenko-azure-crr-target-bucket", + "accountName": "devstoreaccount1", + "accountKey": "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" + } + }, + { + "name": "e2e-azure-archive", + "locationType": "location-azure-v1", + "details": { + "endpoint": "http://azurite-mock.{namespace}.svc.cluster.local:10000/devstoreaccount1", + "containerName": "ci-zenko-azure-archive-target-bucket", + "accountName": "devstoreaccount1", + "accountKey": "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" + } + }, + { + "name": "gcpbackendmismatch", + "locationType": "location-gcp-v1", + "details": { + "endpoint": "https://storage.googleapis.com", + "bucketName": "ci-zenko-gcp-crr-target-bucket", + "accessKey": "gcp-access-key", + "secretKey": "gcp-secret-key", + "bucketMatch": false + } + }, + { + "name": "e2e-cold", + "locationType": "location-dmf-v1", + "details": { + "endpoint": "http://dmf-service:7778", + "repoId": ["repoId"], + "nsId": "nsId", + "username": "username", + "password": "password" + } + }, + { + "name": "e2e-miria-archive", + "locationType": "location-dmf-v1", + "details": { + "endpoint": "http://mock-miria:7778", + "repoId": ["repoId"], + "nsId": "nsId", + "username": "username", + "password": "password" + } + }, + { + "name": "quotabackend", + "locationType": "location-s3-v1", + "details": { + "endpoint": "http://cloudserver-mock.{namespace}.svc.cluster.local:8000", + "bucketName": "ci-zenko-quota-test-bucket", + "accessKey": "accessKey1", + "secretKey": "verySecretKey1", + "bucketMatch": true, + "pathStyle": true + } + }, + { + "name": "rings3cbackendingestion", + "locationType": "location-s3-v1", + "details": { + "endpoint": "http://s3c.local:8000", + "bucketName": "ingestion-test-src-bucket", + "accessKey": "accessKey1", + "secretKey": "verySecretKey1", + "bucketMatch": false, + "pathStyle": true + } + }, + { + "name": "rings3cbackendingestionnonversioned", + "locationType": "location-s3-v1", + "details": { + "endpoint": "http://s3c.local:8000", + "bucketName": "ingestion-test-src-non-versioned-bucket", + "accessKey": "accessKey1", + "secretKey": "verySecretKey1", + "bucketMatch": false, + "pathStyle": true + } + } + ] +} \ No newline at end of file diff --git a/tests/@setup/configs/setup.json b/tests/@setup/configs/setup.json new file mode 100644 index 0000000000..78860e7860 --- /dev/null +++ b/tests/@setup/configs/setup.json @@ -0,0 +1,46 @@ +{ + "setup": { + "description": "Default Zenko test environment setup configuration", + "version": "1.0.0", + "tasks": { + "rbac": { + "enabled": true, + "description": "Setup RBAC permissions for service accounts" + }, + "dns": { + "enabled": true, + "description": "Configure CoreDNS for test domains" + }, + "mocks": { + "enabled": true, + "aws": true, + "azure": true, + "description": "Deploy AWS and Azure mock services" + }, + "locations": { + "enabled": true, + "description": "Create storage locations via Management API" + }, + "buckets": { + "enabled": true, + "configFile": "configs/buckets.json", + "description": "Create test buckets across all providers" + }, + "accounts": { + "enabled": true, + "configFile": "configs/accounts.json", + "description": "Create test accounts via Management API" + }, + "endpoints": { + "enabled": true, + "configFile": "configs/endpoints.json", + "description": "Create S3 endpoints via Management API" + }, + "workflows": { + "enabled": false, + "configFile": "configs/workflows.json", + "description": "Create replication/lifecycle/ingestion workflows" + } + } + } +} \ No newline at end of file diff --git a/tests/@setup/configs/workflows.json b/tests/@setup/configs/workflows.json new file mode 100644 index 0000000000..ef9300afbe --- /dev/null +++ b/tests/@setup/configs/workflows.json @@ -0,0 +1,71 @@ +{ + "replication": [ + { + "name": "aws-to-azure-replication", + "sourceBucket": "ci-zenko-aws-source-bucket", + "sourceLocation": "aws-test-location", + "targetBucket": "ci-zenko-azure-target-container", + "targetLocation": "azure-test-location", + "enabled": true, + "description": "Replicate from AWS mock to Azure mock" + }, + { + "name": "versioned-replication", + "sourceBucket": "ci-zenko-aws-versioned-bucket", + "sourceLocation": "aws-test-location", + "targetBucket": "ci-zenko-aws-replication-bucket", + "targetLocation": "aws-test-location", + "enabled": true, + "description": "Versioned bucket replication within AWS" + } + ], + "lifecycle": [ + { + "name": "aws-lifecycle-transition", + "bucketName": "ci-zenko-aws-lifecycle-bucket", + "rules": [ + { + "id": "transition-to-azure", + "status": "Enabled", + "filter": { "prefix": "archive/" }, + "transitions": [ + { + "days": 30, + "storageClass": "azure-test-location" + } + ] + }, + { + "id": "expire-old-objects", + "status": "Enabled", + "filter": { "prefix": "temp/" }, + "expiration": { "days": 90 } + } + ] + }, + { + "name": "azure-lifecycle-cleanup", + "bucketName": "ci-zenko-azure-lifecycle-container", + "rules": [ + { + "id": "cleanup-logs", + "status": "Enabled", + "filter": { "prefix": "logs/" }, + "expiration": { "days": 7 } + } + ] + } + ], + "ingestion": [ + { + "name": "ring-to-aws-ingestion", + "sourceBucket": "ci-zenko-ring-source-bucket", + "sourceLocation": "ring-test-location", + "targetBucket": "ci-zenko-aws-target-bucket", + "targetLocation": "aws-test-location", + "schedule": "0 2 * * *", + "enabled": true, + "description": "Daily ingestion from Ring to AWS at 2 AM" + } + ] +} \ No newline at end of file diff --git a/tests/@setup/run-tests.sh b/tests/@setup/run-tests.sh new file mode 100755 index 0000000000..84e2bc11b2 --- /dev/null +++ b/tests/@setup/run-tests.sh @@ -0,0 +1,300 @@ +#!/bin/bash +set -euo pipefail + +# Simple script to run tests against a Zenko cluster +# Usage: ./run-tests.sh [path-to-kubeconfig] [test-type] + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +KUBECONFIG_FILE="${1:-$HOME/.kube/config}" +TEST_TYPE="${2:-ctst}" # ctst, e2e, smoke, etc. + +# For CTST, capture additional arguments (like --tags @PRA) +shift 2 || true +ADDITIONAL_ARGS=("$@") + +# Default environment variables - modify these as needed +export NAMESPACE="${NAMESPACE:-default}" +export INSTANCE_ID="${INSTANCE_ID:-end2end}" +export SUBDOMAIN="${SUBDOMAIN:-zenko.local}" +export E2E_IMAGE="${E2E_IMAGE:-ghcr.io/scality/zenko/zenko-e2e:latest}" +export E2E_CTST_IMAGE="${E2E_CTST_IMAGE:-ghcr.io/scality/zenko/zenko-e2e-ctst:latest}" +export JOB_TIMEOUT="${JOB_TIMEOUT:-3600}" + +# Test environment endpoints +export OIDC_REALM="${OIDC_REALM:-zenko}" +export OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-zenko-ui}" +export OIDC_USERNAME="${OIDC_USERNAME:-storage_manager}" +export OIDC_PASSWORD="${OIDC_PASSWORD:-123}" +export OIDC_HOST="${OIDC_HOST:-keycloak.zenko.local}" +export OIDC_ENDPOINT="http://keycloak-http.${NAMESPACE}.svc.cluster.local:8080/realms/${OIDC_REALM}" +export MANAGEMENT_ENDPOINT="http://zenko-connector.${NAMESPACE}.svc.cluster.local:8000/api/v1" +export S3_ENDPOINT="http://cloudserver.${NAMESPACE}.svc.cluster.local:80" + +# CTST-specific environment variables +export PARALLEL_RUNS="${PARALLEL_RUNS:-$(( ( $(nproc) + 1 ) / 2 ))}" +export RETRIES="${RETRIES:-3}" +export JUNIT_REPORT_PATH="${JUNIT_REPORT_PATH:-ctst-junit.xml}" +export DR_SUBDOMAIN="${DR_SUBDOMAIN:-dr.zenko.local}" +export AZURE_ACCOUNT_NAME="${AZURE_ACCOUNT_NAME:-devstoreaccount1}" +export AZURE_SECRET_KEY="${AZURE_SECRET_KEY:-Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==}" +export AZURE_ARCHIVE_BUCKET_NAME="${AZURE_ARCHIVE_BUCKET_NAME:-archive-container}" +export AZURE_ARCHIVE_BUCKET_NAME_2="${AZURE_ARCHIVE_BUCKET_NAME_2:-archive-container-2}" +export AZURE_ARCHIVE_QUEUE_NAME="${AZURE_ARCHIVE_QUEUE_NAME:-archive-queue}" +export AZURE_BACKEND_ENDPOINT="${AZURE_BACKEND_ENDPOINT:-}" +export AZURE_BACKEND_QUEUE_ENDPOINT="${AZURE_BACKEND_QUEUE_ENDPOINT:-}" + +echo "=== Zenko Test Execution ===" +echo "Kubeconfig: ${KUBECONFIG_FILE}" +echo "Test Type: ${TEST_TYPE}" +echo "Namespace: ${NAMESPACE}" +echo "Instance ID: ${INSTANCE_ID}" +echo "Subdomain: ${SUBDOMAIN}" +echo + +# Verify kubeconfig exists and is accessible +if [[ ! -f "${KUBECONFIG_FILE}" ]]; then + echo "Error: Kubeconfig file not found: ${KUBECONFIG_FILE}" + exit 1 +fi + +# Test cluster connectivity +echo "Testing cluster connectivity..." +if ! kubectl --kubeconfig="${KUBECONFIG_FILE}" cluster-info >/dev/null 2>&1; then + echo "Error: Cannot connect to Kubernetes cluster" + exit 1 +fi +echo "Connected to cluster" + +# Check if Zenko is deployed and ready +echo "Checking Zenko deployment..." +if ! kubectl --kubeconfig="${KUBECONFIG_FILE}" get zenko "${INSTANCE_ID}" -n "${NAMESPACE}" >/dev/null 2>&1; then + echo "Error: Zenko instance '${INSTANCE_ID}' not found in namespace '${NAMESPACE}'" + echo "Please run setup first: ./setup-tests.sh ${KUBECONFIG_FILE}" + exit 1 +fi +echo "Zenko instance found" + +# Handle CTST-specific setup +if [[ "${TEST_TYPE}" == "ctst" ]]; then + echo "Setting up CTST cluster-admin permissions..." + cat </dev/null | grep -Po 'VERSION="\K[^"]*' || echo "unknown") + + # Build CTST world parameters + WORLD_PARAMETERS=$(jq -c --null-input \ + --arg namespace "${NAMESPACE}" \ + --arg subdomain "${SUBDOMAIN}" \ + --arg dr_subdomain "${DR_SUBDOMAIN}" \ + --arg keycloak_username "${OIDC_USERNAME}" \ + --arg keycloak_password "${OIDC_PASSWORD}" \ + --arg keycloak_host "${OIDC_HOST}" \ + --arg keycloak_realm "${OIDC_REALM}" \ + --arg keycloak_client_id "${OIDC_CLIENT_ID}" \ + --arg azure_account_name "${AZURE_ACCOUNT_NAME}" \ + --arg azure_account_key "${AZURE_SECRET_KEY}" \ + --arg azure_archive_container "${AZURE_ARCHIVE_BUCKET_NAME}" \ + --arg azure_archive_container2 "${AZURE_ARCHIVE_BUCKET_NAME_2}" \ + --arg azure_archive_queue "${AZURE_ARCHIVE_QUEUE_NAME}" \ + '{ + "Namespace": $namespace, + "subdomain": $subdomain, + "DRSubdomain": $dr_subdomain, + "KeycloakUsername": $keycloak_username, + "KeycloakPassword": $keycloak_password, + "KeycloakHost": $keycloak_host, + "KeycloakRealm": $keycloak_realm, + "KeycloakClientId": $keycloak_client_id, + "AzureAccountName": $azure_account_name, + "AzureAccountKey": $azure_account_key, + "AzureArchiveContainer": $azure_archive_container, + "AzureArchiveContainer2": $azure_archive_container2, + "AzureArchiveQueue": $azure_archive_queue + }') +fi + +# Select test image and command based on test type +case "${TEST_TYPE}" in + ctst) + TEST_IMAGE="${E2E_CTST_IMAGE}" + # CTST uses a custom command with world parameters + TEST_COMMAND=("./run" "premerge" "${WORLD_PARAMETERS}" "--parallel" "${PARALLEL_RUNS}" "--retry" "${RETRIES}" "--retry-tag-filter" "@Flaky" "--format" "junit:${JUNIT_REPORT_PATH}") + # Add any additional arguments passed to the script + TEST_COMMAND+=("${ADDITIONAL_ARGS[@]}") + ;; + e2e) + TEST_IMAGE="${E2E_IMAGE}" + TEST_COMMAND=("npm" "run" "test:e2e") + ;; + smoke) + TEST_IMAGE="${E2E_IMAGE}" + TEST_COMMAND=("npm" "run" "test:smoke") + ;; + *) + echo "Error: Unknown test type '${TEST_TYPE}'" + echo "Available types: ctst, e2e, smoke" + exit 1 + ;; +esac + +echo "Test Image: ${TEST_IMAGE}" +echo "Test Command: ${TEST_COMMAND[*]}" +echo + +# Create the test Job +JOB_NAME="zenko-test-${TEST_TYPE}-$(date +%s)" +echo "Creating test job: ${JOB_NAME}..." + +# Convert test command array to YAML format for kubectl +TEST_COMMAND_YAML="" +for cmd in "${TEST_COMMAND[@]}"; do + TEST_COMMAND_YAML+=" - \"${cmd}\"\\n" +done + +# Build the Job manifest based on test type +if [[ "${TEST_TYPE}" == "ctst" ]]; then + cat </dev/null 2>&1; then + echo "Error: Cannot connect to Kubernetes cluster" + echo "Please check your kubeconfig file: ${KUBECONFIG_FILE}" + exit 1 +fi +echo "Connected to cluster" + +# Setup RBAC if needed +echo "Setting up RBAC permissions..." +cat < { + const k8s = new KubernetesClient(); + const config = loadAccountsConfig(options.configFile); + + logger.info('Setting up test accounts via Management API'); + + // Get management API endpoint and credentials + const { managementEndpoint, authToken } = await getManagementCredentials(k8s, options); + + // Get instance ID from Zenko CR if not provided + const instanceId = options.instanceId || await getInstanceId(k8s, options); + + if (!instanceId) { + throw new Error('Instance ID is required for account creation. Either provide --instance-id or ensure Zenko CR exists'); + } + + for (const account of config.accounts) { + try { + await createAccount(managementEndpoint, authToken, instanceId, account, options); + logger.info(`Created account: ${account.name}`); + } catch (error) { + logger.error(`Failed to create account ${account.name}: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } + } + + logger.info(`Successfully created ${config.accounts.length} test accounts`); +} + +async function getManagementCredentials(k8s: KubernetesClient, options: AccountsOptions): Promise<{ managementEndpoint: string; authToken: string }> { + // Get management API endpoint from service + const managementService = await k8s.coreApi.readNamespacedService({ + name: 'zenko-management', + namespace: options.namespace, + }); + + const managementPort = managementService.spec?.ports?.find(p => p.name === 'http')?.port || 8080; + const managementEndpoint = `http://zenko-management.${options.namespace}.svc.cluster.local:${managementPort}`; + + // Get admin credentials for authentication + const adminSecret = await k8s.coreApi.readNamespacedSecret({ + name: 'zenko-admin', + namespace: options.namespace, + }); + + if (!adminSecret.data) { + throw new Error('Failed to retrieve admin credentials from zenko-admin secret'); + } + + const accessKey = Buffer.from(adminSecret.data['access-key'], 'base64').toString(); + const secretKey = Buffer.from(adminSecret.data['secret-key'], 'base64').toString(); + + // Create admin auth token (basic auth for management API) + const authToken = Buffer.from(`${accessKey}:${secretKey}`).toString('base64'); + + return { managementEndpoint, authToken }; +} + +async function getInstanceId(k8s: KubernetesClient, options: AccountsOptions): Promise { + try { + // Try to get instance ID from Zenko CR + const customObjects = k8s.customObjectsApi; + const zenkoList = await customObjects.listNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha1', + namespace: options.namespace, + plural: 'zenkos', + }); + + const zenkos = zenkoList.body as any; + if (zenkos.items && zenkos.items.length > 0) { + return zenkos.items[0].spec?.instanceId || zenkos.items[0].metadata?.name; + } + + return null; + } catch (error) { + logger.debug(`Failed to retrieve instance ID from Zenko CR: ${error instanceof Error ? error.message : String(error)}`); + return null; + } +} + +async function createAccount( + managementEndpoint: string, + authToken: string, + instanceId: string, + account: AccountConfig, + options: AccountsOptions +): Promise { + + const accountPayload = { + userName: account.name, + email: account.email, + }; + + const response = await axios.post( + `${managementEndpoint}/api/v1/config/${instanceId}/user`, + accountPayload, + { + headers: { + 'Authorization': `Basic ${authToken}`, + 'Content-Type': 'application/json', + }, + timeout: 30000, + } + ); + + if (response.status !== 201 && response.status !== 200) { + throw new Error(`Management API returned status ${response.status}: ${JSON.stringify(response.data)}`); + } + + // Store account credentials in a Kubernetes secret for later use in tests + const accountData = response.data; + const k8s = new KubernetesClient(); + + const secret = { + apiVersion: 'v1', + kind: 'Secret', + metadata: { + name: `end2end-account-${account.name}`, + namespace: options.namespace, + labels: { + 'app.kubernetes.io/name': 'zenko-test-setup', + 'app.kubernetes.io/component': 'account-credentials', + 'test.zenko.io/account-name': account.name, + }, + }, + data: { + 'account-id': Buffer.from(accountData.id || '').toString('base64'), + 'account-name': Buffer.from(account.name).toString('base64'), + 'account-email': Buffer.from(account.email).toString('base64'), + 'access-key': Buffer.from(accountData.accessKey || '').toString('base64'), + 'secret-key': Buffer.from(accountData.secretKey || '').toString('base64'), + }, + }; + + await k8s.applyManifest(secret, options.namespace); + logger.debug(`Created secret end2end-account-${account.name} with account credentials`); +} \ No newline at end of file diff --git a/tests/@setup/src/buckets.ts b/tests/@setup/src/buckets.ts index 5015198f13..0b7b77513a 100644 --- a/tests/@setup/src/buckets.ts +++ b/tests/@setup/src/buckets.ts @@ -3,30 +3,92 @@ import { BlobServiceClient, StorageSharedKeyCredential as BlobStorageSharedKeyCr import { QueueServiceClient, StorageSharedKeyCredential } from '@azure/storage-queue'; import { KubernetesClient } from './utils/k8s'; import { logger } from './utils/logger'; +import * as fs from 'fs'; +import * as path from 'path'; + +export interface BucketObject { + key: string; + body: string; +} + +export interface AWSBucket { + name: string; + versioning: boolean; + objects: BucketObject[]; +} + +export interface AzureBlob { + name: string; + content: string; +} + +export interface AzureContainer { + name: string; + blobs: AzureBlob[]; +} + +export interface AzureQueue { + name: string; +} + +export interface RingBucket { + name: string; + objects: BucketObject[]; +} + +export interface BucketsConfig { + aws: { + buckets: AWSBucket[]; + }; + azure: { + containers: AzureContainer[]; + queues: AzureQueue[]; + }; + ring: { + buckets: RingBucket[]; + }; +} export interface BucketsOptions { namespace: string; provider?: 'aws' | 'azure' | 'ring'; - dryRun?: boolean; + configFile?: string; +} + +function loadBucketsConfig(configFile?: string): BucketsConfig { + const defaultConfigPath = path.join(__dirname, '..', 'configs', 'buckets.json'); + const configPath = configFile ? path.resolve(configFile) : defaultConfigPath; + + if (!fs.existsSync(configPath)) { + throw new Error(`Buckets configuration file not found: ${configPath}`); + } + + try { + const configData = fs.readFileSync(configPath, 'utf-8'); + return JSON.parse(configData) as BucketsConfig; + } catch (error) { + throw new Error(`Failed to parse buckets configuration: ${error instanceof Error ? error.message : String(error)}`); + } } export async function setupBuckets(options: BucketsOptions): Promise { const k8s = new KubernetesClient(); + const config = loadBucketsConfig(options.configFile); if (!options.provider || options.provider === 'aws') { - await setupAWSBuckets(k8s, options); + await setupAWSBuckets(k8s, options, config.aws.buckets); } if (!options.provider || options.provider === 'azure') { - await setupAzureBuckets(k8s, options); + await setupAzureBuckets(k8s, options, config.azure); } if (!options.provider || options.provider === 'ring') { - await setupRingBuckets(k8s, options); + await setupRingBuckets(k8s, options, config.ring.buckets); } } -async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions): Promise { +async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions, buckets: AWSBucket[]): Promise { logger.info('Creating AWS test buckets'); // Get AWS credentials from mock service @@ -46,55 +108,40 @@ async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions): const s3Client = new S3Client(awsConfig); - // Standard test buckets - const buckets = [ - 'ci-zenko-aws-source-bucket', - 'ci-zenko-aws-target-bucket', - 'ci-zenko-aws-versioned-bucket', - 'ci-zenko-aws-lifecycle-bucket', - 'ci-zenko-aws-replication-bucket', - 'ci-zenko-aws-notification-bucket' - ]; - - for (const bucketName of buckets) { + for (const bucket of buckets) { try { - await s3Client.send(new CreateBucketCommand({ Bucket: bucketName })); - logger.debug(`Created bucket: ${bucketName}`); + await s3Client.send(new CreateBucketCommand({ Bucket: bucket.name })); + logger.debug(`Created bucket: ${bucket.name}`); - // Enable versioning on versioned and replication buckets - if (bucketName.includes('versioned') || bucketName.includes('replication')) { + // Enable versioning if specified + if (bucket.versioning) { await s3Client.send(new PutBucketVersioningCommand({ - Bucket: bucketName, + Bucket: bucket.name, VersioningConfiguration: { Status: 'Enabled' } })); - logger.debug(`Enabled versioning on: ${bucketName}`); + logger.debug(`Enabled versioning on: ${bucket.name}`); } - // Add test objects to source bucket - if (bucketName.includes('source')) { - const testObjects = [ - { Key: 'test-object-1.txt', Body: 'Test content 1' }, - { Key: 'test-object-2.txt', Body: 'Test content 2' }, - { Key: 'folder/nested-object.txt', Body: 'Nested content' } - ]; - - for (const obj of testObjects) { - await s3Client.send(new PutObjectCommand({ - Bucket: bucketName, - Key: obj.Key, - Body: obj.Body - })); - } - logger.debug(`Added test objects to: ${bucketName}`); + // Add test objects from configuration + for (const obj of bucket.objects) { + await s3Client.send(new PutObjectCommand({ + Bucket: bucket.name, + Key: obj.key, + Body: obj.body + })); + } + + if (bucket.objects.length > 0) { + logger.debug(`Added ${bucket.objects.length} test objects to: ${bucket.name}`); } } catch (error: any) { if (error.name === 'BucketAlreadyOwnedByYou' || error.name === 'BucketAlreadyExists') { - logger.debug(`Bucket ${bucketName} already exists`); + logger.debug(`Bucket ${bucket.name} already exists`); } else { - logger.error(`Failed to create bucket ${bucketName}: ${error.message}`); + logger.error(`Failed to create bucket ${bucket.name}: ${error.message}`); throw error; } } @@ -103,7 +150,7 @@ async function setupAWSBuckets(k8s: KubernetesClient, options: BucketsOptions): logger.info(`Created ${buckets.length} AWS test buckets`); } -async function setupAzureBuckets(k8s: KubernetesClient, options: BucketsOptions): Promise { +async function setupAzureBuckets(k8s: KubernetesClient, options: BucketsOptions, azureConfig: { containers: AzureContainer[]; queues: AzureQueue[] }): Promise { logger.info('Creating Azure test containers and queues'); // Get Azure credentials from mock service @@ -122,76 +169,59 @@ async function setupAzureBuckets(k8s: KubernetesClient, options: BucketsOptions) // Setup blob containers const blobServiceClient = new BlobServiceClient(blobEndpoint, blobSharedKeyCredential); - const containers = [ - 'ci-zenko-azure-source-container', - 'ci-zenko-azure-target-container', - 'ci-zenko-azure-archive-container', - 'ci-zenko-azure-lifecycle-container' - ]; - - for (const containerName of containers) { + for (const container of azureConfig.containers) { try { - const containerClient = blobServiceClient.getContainerClient(containerName); + const containerClient = blobServiceClient.getContainerClient(container.name); await containerClient.create(); - logger.debug(`Created container: ${containerName}`); - - // Add test blobs to source container - if (containerName.includes('source')) { - const testBlobs = [ - { name: 'test-blob-1.txt', content: 'Azure test content 1' }, - { name: 'test-blob-2.txt', content: 'Azure test content 2' }, - { name: 'folder/nested-blob.txt', content: 'Azure nested content' } - ]; - - for (const blob of testBlobs) { - const blockBlobClient = containerClient.getBlockBlobClient(blob.name); - await blockBlobClient.upload(blob.content, blob.content.length); - } - logger.debug(`Added test blobs to: ${containerName}`); + logger.debug(`Created container: ${container.name}`); + + // Add blobs from configuration + for (const blob of container.blobs) { + const blockBlobClient = containerClient.getBlockBlobClient(blob.name); + await blockBlobClient.upload(blob.content, blob.content.length); + } + + if (container.blobs.length > 0) { + logger.debug(`Added ${container.blobs.length} test blobs to: ${container.name}`); } } catch (error: any) { if (error.statusCode === 409) { - logger.debug(`Container ${containerName} already exists`); + logger.debug(`Container ${container.name} already exists`); } else { - logger.error(`Failed to create container ${containerName}: ${error.message}`); + logger.error(`Failed to create container ${container.name}: ${error.message}`); throw error; } } } - // Setup queues for notification testing + // Setup queues from configuration const queueServiceClient = new QueueServiceClient(queueEndpoint, queueSharedKeyCredential); - const queues = [ - 'ci-zenko-azure-notifications-queue', - 'ci-zenko-azure-status-queue' - ]; - - for (const queueName of queues) { + for (const queue of azureConfig.queues) { try { - const queueClient = queueServiceClient.getQueueClient(queueName); + const queueClient = queueServiceClient.getQueueClient(queue.name); await queueClient.create(); - logger.debug(`Created queue: ${queueName}`); + logger.debug(`Created queue: ${queue.name}`); } catch (error: any) { if (error.statusCode === 409) { - logger.debug(`Queue ${queueName} already exists`); + logger.debug(`Queue ${queue.name} already exists`); } else { - logger.error(`Failed to create queue ${queueName}: ${error.message}`); + logger.error(`Failed to create queue ${queue.name}: ${error.message}`); throw error; } } } - logger.info(`Created ${containers.length} Azure containers and ${queues.length} queues`); + logger.info(`Created ${azureConfig.containers.length} Azure containers and ${azureConfig.queues.length} queues`); } -async function setupRingBuckets(k8s: KubernetesClient, options: BucketsOptions): Promise { +async function setupRingBuckets(k8s: KubernetesClient, options: BucketsOptions, buckets: RingBucket[]): Promise { logger.info('Creating Ring/S3C test buckets'); // Ring buckets are typically created through S3 API against Ring storage // This would require Ring/S3C credentials and endpoint configuration - // For now, create a placeholder configuration + // For now, create a configuration based on the input buckets const ringConfig = { apiVersion: 'v1', @@ -201,15 +231,14 @@ async function setupRingBuckets(k8s: KubernetesClient, options: BucketsOptions): namespace: options.namespace }, data: { - 'buckets.json': JSON.stringify([ - 'ci-zenko-ring-source-bucket', - 'ci-zenko-ring-target-bucket', - 'ci-zenko-ring-archive-bucket' - ], null, 2) + 'buckets.json': JSON.stringify(buckets.map(bucket => ({ + name: bucket.name, + objects: bucket.objects + })), null, 2) } }; await k8s.applyManifest(ringConfig, options.namespace); - logger.info('Ring bucket configuration created (actual buckets require Ring/S3C setup)'); + logger.info(`Ring bucket configuration created for ${buckets.length} buckets (actual buckets require Ring/S3C setup)`); } \ No newline at end of file diff --git a/tests/@setup/src/cli.ts b/tests/@setup/src/cli.ts index edd802ce26..ee847ecd65 100644 --- a/tests/@setup/src/cli.ts +++ b/tests/@setup/src/cli.ts @@ -4,8 +4,15 @@ import { Command } from 'commander'; import { setupMocks } from './mocks'; import { setupBuckets } from './buckets'; import { setupLocations } from './locations'; +import { setupAccounts } from './accounts'; +import { setupEndpoints } from './endpoints'; +import { setupWorkflows } from './workflows'; +import { setupTLSWithOpenSSL } from './tls'; import { setupDNS } from './dns'; import { setupRBAC } from './rbac'; +import { setupMetadata } from './metadata'; +import { setupCTSTLocal } from './ctst-local'; +import { waitForZenkoToStabilize } from './utils/zenko-status'; import { logger } from './utils/logger'; const program = new Command(); @@ -20,28 +27,103 @@ program .option('-d, --subdomain ', 'DNS subdomain', 'zenko.local') .option('-i, --instance-id ', 'Zenko instance ID') .option('-k, --kubeconfig ', 'Path to kubeconfig file') - .option('--dry-run', 'Show what would be done without executing') .option('-v, --verbose', 'Enable verbose logging'); program .command('all') - .description('Run all setup tasks') - .option('--skip-mocks', 'Skip mock services setup') - .option('--skip-buckets', 'Skip bucket creation') - .option('--skip-locations', 'Skip storage locations setup') - .option('--skip-keycloak', 'Skip Keycloak realm/users setup') - .option('--skip-dns', 'Skip DNS configuration') - .option('--skip-rbac', 'Skip RBAC permissions setup') + .description('Run complete setup (all tasks, use --no- to exclude specific tasks)') + .option('--config ', 'Path to setup configuration file') + .option('--buckets-config ', 'Path to buckets configuration file') + .option('--accounts-config ', 'Path to accounts configuration file') + .option('--endpoints-config ', 'Path to endpoints configuration file') + .option('--workflows-config ', 'Path to workflows configuration file') + .option('--locations-config ', 'Path to locations configuration file') + .option('--git-access-token ', 'Git access token for metadata repository') + .option('--metadata-namespace ', 'Metadata service namespace', 'metadata') + .option('--no-rbac', 'Skip RBAC setup') + .option('--no-dns', 'Skip DNS setup') + .option('--no-mocks', 'Skip mock services setup') + .option('--no-locations', 'Skip storage locations setup') + .option('--no-accounts', 'Skip test accounts setup') + .option('--no-endpoints', 'Skip S3 endpoints setup') + .option('--no-workflows', 'Skip workflows setup') + .option('--no-buckets', 'Skip test buckets setup') + .option('--no-metadata', 'Skip metadata service setup') + .option('--no-ctst-local', 'Skip CTST local environment setup') + .option('--no-tls', 'Skip TLS certificates setup') + .action(async (options) => { + const globalOptions = program.opts(); + await runSetup({ + ...globalOptions, + // Run everything for 'all' command, unless specifically excluded + rbac: !options.noRbac, + dns: !options.noDns, + mocks: !options.noMocks, + locations: !options.noLocations, + accounts: !options.noAccounts, + endpoints: !options.noEndpoints, + workflows: !options.noWorkflows, + buckets: !options.noBuckets, + metadata: !options.noMetadata, + ctstLocal: !options.noCtstLocal, + tls: !options.noTls, + configFile: options.config, + bucketsConfig: options.bucketsConfig, + accountsConfig: options.accountsConfig, + endpointsConfig: options.endpointsConfig, + workflowsConfig: options.workflowsConfig, + locationsConfig: options.locationsConfig, + gitAccessToken: options.gitAccessToken, + metadataNamespace: options.metadataNamespace, + }); + }); + +program + .command('setup') + .description('Run selective setup tasks') + .option('--rbac', 'Setup RBAC permissions') + .option('--dns', 'Configure DNS') + .option('--mocks', 'Setup mock services') + .option('--locations', 'Setup storage locations') + .option('--accounts', 'Create test accounts') + .option('--endpoints', 'Create S3 endpoints') + .option('--workflows', 'Create workflows') + .option('--buckets', 'Create test buckets') + .option('--metadata', 'Deploy metadata service') + .option('--ctst-local', 'Setup CTST local development environment') + .option('--tls', 'Setup TLS certificates') + .option('--config ', 'Path to setup configuration file') + .option('--buckets-config ', 'Path to buckets configuration file') + .option('--accounts-config ', 'Path to accounts configuration file') + .option('--endpoints-config ', 'Path to endpoints configuration file') + .option('--workflows-config ', 'Path to workflows configuration file') + .option('--locations-config ', 'Path to locations configuration file') + .option('--git-access-token ', 'Git access token for metadata repository') + .option('--metadata-namespace ', 'Metadata service namespace', 'metadata') .action(async (options) => { const globalOptions = program.opts(); await runSetup({ ...globalOptions, - mocks: !options.skipMocks, - buckets: !options.skipBuckets, - locations: !options.skipLocations, - keycloak: !options.skipKeycloak, - dns: !options.skipDns, - rbac: !options.skipRbac, + // Only run tasks that are explicitly specified + rbac: options.rbac || false, + dns: options.dns || false, + mocks: options.mocks || false, + locations: options.locations || false, + accounts: options.accounts || false, + endpoints: options.endpoints || false, + workflows: options.workflows || false, + buckets: options.buckets || false, + metadata: options.metadata || false, + ctstLocal: options['ctst-local'] || false, + tls: options.tls || false, + configFile: options.config, + bucketsConfig: options.bucketsConfig, + accountsConfig: options.accountsConfig, + endpointsConfig: options.endpointsConfig, + workflowsConfig: options.workflowsConfig, + locationsConfig: options.locationsConfig, + gitAccessToken: options.gitAccessToken, + metadataNamespace: options.metadataNamespace, }); }); @@ -58,7 +140,6 @@ program instanceId: globalOptions.instanceId, awsOnly: options.awsOnly, azureOnly: options.azureOnly, - dryRun: globalOptions.dryRun, }); }); @@ -66,24 +147,80 @@ program .command('buckets') .description('Create test buckets across all providers') .option('--provider ', 'Specific provider (aws|azure|ring)') + .option('--config ', 'Path to buckets configuration file') .action(async (options) => { const globalOptions = program.opts(); await setupBuckets({ namespace: globalOptions.namespace || 'default', provider: options.provider, - dryRun: globalOptions.dryRun, + configFile: options.config, }); }); program .command('locations') .description('Setup storage locations via Management API') - .action(async () => { + .option('--config ', 'Path to locations configuration file') + .action(async (options) => { const globalOptions = program.opts(); await setupLocations({ namespace: globalOptions.namespace || 'default', instanceId: globalOptions.instanceId, - dryRun: globalOptions.dryRun, + configFile: options.config, + }); + }); + +program + .command('accounts') + .description('Create test accounts via Management API') + .option('--config ', 'Path to accounts configuration file') + .action(async (options) => { + const globalOptions = program.opts(); + await setupAccounts({ + namespace: globalOptions.namespace || 'default', + instanceId: globalOptions.instanceId, + configFile: options.config, + }); + }); + +program + .command('endpoints') + .description('Create S3 endpoints via Management API') + .option('--config ', 'Path to endpoints configuration file') + .action(async (options) => { + const globalOptions = program.opts(); + await setupEndpoints({ + namespace: globalOptions.namespace || 'default', + instanceId: globalOptions.instanceId, + configFile: options.config, + }); + }); + +program + .command('workflows') + .description('Create replication/lifecycle/ingestion workflows') + .option('--config ', 'Path to workflows configuration file') + .option('--type ', 'Specific workflow type (replication|lifecycle|ingestion)') + .action(async (options) => { + const globalOptions = program.opts(); + await setupWorkflows({ + namespace: globalOptions.namespace || 'default', + instanceId: globalOptions.instanceId, + configFile: options.config, + workflowType: options.type, + }); + }); + + +program + .command('tls') + .description('Setup TLS certificates for HTTPS testing') + .option('--domains ', 'Comma-separated list of domains to include in certificate') + .action(async (options) => { + const globalOptions = program.opts(); + await setupTLSWithOpenSSL({ + namespace: globalOptions.namespace || 'default', + domains: options.domains ? options.domains.split(',') : undefined, }); }); @@ -95,7 +232,6 @@ program await setupDNS({ namespace: globalOptions.namespace || 'default', subdomain: globalOptions.subdomain || 'zenko.local', - dryRun: globalOptions.dryRun, }); }); @@ -106,13 +242,55 @@ program const globalOptions = program.opts(); await setupRBAC({ namespace: globalOptions.namespace || 'default', - dryRun: globalOptions.dryRun, + }); + }); + +program + .command('metadata') + .description('Deploy metadata service (S3C)') + .option('--git-access-token ', 'Git access token for metadata repository') + .option('--metadata-namespace ', 'Metadata service namespace', 'metadata') + .option('--timeout ', 'Timeout in seconds for deployment', '300') + .action(async (options) => { + const globalOptions = program.opts(); + await setupMetadata({ + gitAccessToken: options.gitAccessToken || process.env.GIT_ACCESS_TOKEN, + namespace: options.metadataNamespace || 'metadata', + timeout: parseInt(options.timeout || '300'), + }); + }); + +program + .command('ctst-local') + .description('Setup CTST local development environment') + .option('--skip-hosts-file', 'Skip /etc/hosts file setup') + .option('--skip-rbac', 'Skip RBAC permissions setup') + .option('--skip-dns', 'Skip DNS configuration') + .option('--skip-zenko-wait', 'Skip waiting for Zenko to be ready') + .action(async (options) => { + const globalOptions = program.opts(); + await setupCTSTLocal({ + namespace: globalOptions.namespace || 'default', + instanceId: globalOptions.instanceId, + subdomain: globalOptions.subdomain || 'zenko.local', + skipHostsFile: options.skipHostsFile, + skipRBAC: options.skipRbac, + skipDNS: options.skipDns, + skipZenkoWait: options.skipZenkoWait, }); }); async function runSetup(options: any) { try { - logger.info('🚀 Starting Zenko test environment setup'); + logger.info('Starting Zenko test environment setup'); + + // First, wait for Zenko to be ready + logger.info('Checking Zenko readiness...'); + await waitForZenkoToStabilize({ + namespace: options.namespace || 'default', + instanceId: options.instanceId || 'end2end', + timeout: 10 * 60 * 1000, // 10 minutes + }); const tasks = []; @@ -120,7 +298,6 @@ async function runSetup(options: any) { tasks.push({ name: 'RBAC', fn: () => setupRBAC({ namespace: options.namespace || 'default', - dryRun: options.dryRun, }) }); } @@ -130,7 +307,6 @@ async function runSetup(options: any) { name: 'DNS', fn: () => setupDNS({ namespace: options.namespace || 'default', subdomain: options.subdomain || 'zenko.local', - dryRun: options.dryRun, }) }); } @@ -141,7 +317,6 @@ async function runSetup(options: any) { namespace: options.namespace || 'default', subdomain: options.subdomain || 'zenko.local', instanceId: options.instanceId, - dryRun: options.dryRun, }) }); } @@ -151,7 +326,37 @@ async function runSetup(options: any) { name: 'Storage Locations', fn: () => setupLocations({ namespace: options.namespace || 'default', instanceId: options.instanceId, - dryRun: options.dryRun, + configFile: options.locationsConfig, + }) + }); + } + + if (options.accounts) { + tasks.push({ + name: 'Test Accounts', fn: () => setupAccounts({ + namespace: options.namespace || 'default', + instanceId: options.instanceId, + configFile: options.accountsConfig, + }) + }); + } + + if (options.endpoints) { + tasks.push({ + name: 'S3 Endpoints', fn: () => setupEndpoints({ + namespace: options.namespace || 'default', + instanceId: options.instanceId, + configFile: options.endpointsConfig, + }) + }); + } + + if (options.workflows) { + tasks.push({ + name: 'Workflows', fn: () => setupWorkflows({ + namespace: options.namespace || 'default', + instanceId: options.instanceId, + configFile: options.workflowsConfig, }) }); } @@ -160,32 +365,56 @@ async function runSetup(options: any) { tasks.push({ name: 'Test Buckets', fn: () => setupBuckets({ namespace: options.namespace || 'default', - dryRun: options.dryRun, + configFile: options.bucketsConfig, }) }); } - for (const task of tasks) { - logger.info(`📝 Setting up ${task.name}...`); + if (options.metadata) { + tasks.push({ + name: 'Metadata Service', fn: () => setupMetadata({ + gitAccessToken: options.gitAccessToken || process.env.GIT_ACCESS_TOKEN, + namespace: options.metadataNamespace || 'metadata', + timeout: 300, + }) + }); + } - if (options.dryRun) { - logger.info(` [DRY RUN] Would execute ${task.name} setup`); - continue; - } + if (options.ctstLocal) { + tasks.push({ + name: 'CTST Local Environment', fn: () => setupCTSTLocal({ + namespace: options.namespace || 'default', + instanceId: options.instanceId, + subdomain: options.subdomain || 'zenko.local', + }) + }); + } + + if (options.tls) { + tasks.push({ + name: 'TLS Certificates', fn: () => setupTLSWithOpenSSL({ + namespace: options.namespace || 'default', + domains: ['*.zenko.local'], + }) + }); + } + + for (const task of tasks) { + logger.info(`Setting up ${task.name}...`); try { await task.fn(); - logger.info(` ✅ ${task.name} setup completed`); + logger.info(`${task.name} setup completed`); } catch (error) { - logger.error(` ❌ ${task.name} setup failed`, { error: error instanceof Error ? error.message : String(error) }); + logger.error(`${task.name} setup failed`, { error: error instanceof Error ? error.message : String(error) }); throw error; } } - logger.info('🎉 Zenko test environment setup completed successfully!'); + logger.info('Zenko test environment setup completed successfully!'); } catch (error) { - logger.error('💥 Setup failed', { error: error instanceof Error ? error.message : String(error) }); + logger.error('Setup failed', { error: error instanceof Error ? error.message : String(error) }); process.exit(1); } } diff --git a/tests/@setup/src/ctst-local.ts b/tests/@setup/src/ctst-local.ts new file mode 100644 index 0000000000..c64b7324f9 --- /dev/null +++ b/tests/@setup/src/ctst-local.ts @@ -0,0 +1,131 @@ +import { execSync } from 'child_process'; +import { readFileSync } from 'fs'; +import { logger } from './utils/logger'; +import { setupRBAC } from './rbac'; +import { setupDNS } from './dns'; +import { waitForZenkoToStabilize } from './utils/zenko-status'; + +export interface CTSTLocalOptions { + namespace: string; + instanceId?: string; + subdomain?: string; + skipHostsFile?: boolean; + skipRBAC?: boolean; + skipDNS?: boolean; + skipZenkoWait?: boolean; +} + +export async function setupCTSTLocal(options: CTSTLocalOptions): Promise { + logger.info('Setting up CTST local development environment...'); + + try { + // 1. Setup RBAC permissions for CTST + if (!options.skipRBAC) { + logger.info('Setting up CTST permissions...'); + await setupCTSTPermissions(options.namespace); + } + + // 2. Setup DNS/CoreDNS configuration + if (!options.skipDNS) { + logger.info('Checking CoreDNS configuration...'); + await setupDNS({ + namespace: options.namespace, + subdomain: options.subdomain || 'zenko.local', + }); + } + + // 3. Setup /etc/hosts for local development + if (!options.skipHostsFile) { + logger.info('Checking /etc/hosts configuration...'); + await setupHostsFile(options.subdomain || 'zenko.local'); + } + + // 4. Wait for Zenko to be ready + if (!options.skipZenkoWait) { + logger.info('Waiting for Zenko deployment to be ready...'); + await waitForZenkoToStabilize({ + namespace: options.namespace, + instanceId: options.instanceId || 'end2end', + timeout: 10 * 60 * 1000, // 10 minutes + }); + } + + logger.info('CTST local environment ready!'); + logger.info('Usage:'); + logger.info(' cd tests/ctst'); + logger.info(' npm test # Run all CTST tests'); + logger.info(' npm run test -- --tags @PRA # Run specific test tags'); + logger.info(''); + logger.info('Note: CTST will handle all Kubernetes setup (mocks, topics, deployments, etc.) automatically'); + + } catch (error) { + logger.error('Failed to setup CTST local environment:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function setupCTSTPermissions(namespace: string): Promise { + try { + logger.info('Creating cluster-admin permissions for CTST...'); + + // Create clusterrolebinding for CTST with cluster-admin permissions + const clusterRoleBindingYaml = ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: ctst-cluster-admin +subjects: +- kind: ServiceAccount + name: default + namespace: ${namespace} +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +`; + + // Apply the configuration using kubectl + execSync('kubectl apply -f -', { + input: clusterRoleBindingYaml, + stdio: ['pipe', 'inherit', 'inherit'], + }); + + logger.info('CTST cluster-admin permissions configured successfully'); + } catch (error) { + logger.error('Failed to setup CTST permissions:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function setupHostsFile(subdomain: string): Promise { + try { + // Check if /etc/hosts already contains our entries + let hostsContent = ''; + try { + hostsContent = readFileSync('/etc/hosts', 'utf8'); + } catch (error) { + logger.debug('Could not read /etc/hosts file, skipping hosts file setup'); + return; + } + + if (hostsContent.includes(subdomain)) { + logger.info('/etc/hosts already configured'); + return; + } + + logger.info('Setting up /etc/hosts (requires sudo)...'); + + const hostsEntry = `127.0.0.1 iam.${subdomain} ui.${subdomain} s3-local-file.${subdomain} keycloak.${subdomain} sts.${subdomain} management.${subdomain} s3.${subdomain} website.mywebsite.com utilization.${subdomain}`; + + // Use sudo to append to /etc/hosts + execSync(`echo "${hostsEntry}" | sudo tee -a /etc/hosts`, { + stdio: 'inherit', + }); + + logger.info('/etc/hosts configured successfully'); + } catch (error) { + logger.warn('Failed to setup /etc/hosts (this may require manual setup):', { error: error instanceof Error ? error.message : String(error) }); + logger.info(`Manual setup: Add this line to /etc/hosts:`); + logger.info(`127.0.0.1 iam.${subdomain} ui.${subdomain} s3-local-file.${subdomain} keycloak.${subdomain} sts.${subdomain} management.${subdomain} s3.${subdomain} website.mywebsite.com utilization.${subdomain}`); + } +} \ No newline at end of file diff --git a/tests/@setup/src/dns.ts b/tests/@setup/src/dns.ts index 2827477893..db34137ac6 100644 --- a/tests/@setup/src/dns.ts +++ b/tests/@setup/src/dns.ts @@ -1,215 +1,155 @@ +import * as fs from 'fs'; +import * as path from 'path'; import { KubernetesClient } from './utils/k8s'; import { logger } from './utils/logger'; export interface DNSOptions { namespace: string; - subdomain: string; - dryRun?: boolean; + subdomain?: string; } -export async function setupDNS(options: DNSOptions): Promise { - const k8s = new KubernetesClient(); - - logger.info('Setting up CoreDNS configuration for test domains'); - - // Get the current CoreDNS ConfigMap - let coreDnsConfigMap; - try { - coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap({ - name: 'coredns', - namespace: 'kube-system', - }); - } catch (error: any) { - if (error.response?.statusCode === 404) { - logger.warn('CoreDNS ConfigMap not found, attempting to find alternative'); - // Try different possible names/namespaces - const alternatives = [ - { name: 'coredns-custom', namespace: 'kube-system' }, - { name: 'coredns', namespace: 'kube-dns' } - ]; - - for (const alt of alternatives) { - try { - coreDnsConfigMap = await k8s.coreApi.readNamespacedConfigMap({ - name: alt.name, - namespace: alt.namespace, - }); - break; - } catch (e) { - continue; - } - } - - if (!coreDnsConfigMap) { - logger.warn('Could not find CoreDNS ConfigMap, creating custom DNS setup'); - await createCustomDNSSetup(k8s, options); - return; - } - } else { - throw error; - } - } - - // Parse current Corefile - const currentCorefile = coreDnsConfigMap.data?.['Corefile'] || ''; - - // Generate rewrite rules for test domains - const rewriteRules = generateRewriteRules(options.subdomain, options.namespace); - - // Check if our rules already exist - if (currentCorefile.includes(`# Zenko test rewrite rules for ${options.subdomain}`)) { - logger.debug('DNS rewrite rules already configured'); - return; - } - - // Add our rewrite rules to the Corefile - const newCorefile = addRewriteRules(currentCorefile, rewriteRules, options.subdomain); - - // Update the ConfigMap - const updatedConfigMap = { - ...coreDnsConfigMap, - data: { - ...coreDnsConfigMap.data, - 'Corefile': newCorefile - } +// Define interfaces for our JSON configuration files for type safety +interface Location { + details: { + endpoint: string; + bucketName?: string; }; - - await k8s.coreApi.replaceNamespacedConfigMap({ - name: 'coredns', - namespace: 'kube-system', - body: updatedConfigMap, - }); - - // Restart CoreDNS deployment to pick up changes - await restartCoreDNS(k8s); - - logger.info('CoreDNS configuration updated successfully'); } - -async function createCustomDNSSetup(k8s: KubernetesClient, options: DNSOptions): Promise { - logger.info('Creating custom DNS setup for test environment'); - - // Create a custom CoreDNS deployment for test domains - const customCorefile = ` -# Zenko test DNS configuration -${options.subdomain}:53 { - rewrite name regex (.+\\.)?aws-mock\\.${options.subdomain} cloudserver-mock.${options.namespace}.svc.cluster.local - rewrite name regex (.+\\.)?azure-mock\\.${options.subdomain} azurite-mock.${options.namespace}.svc.cluster.local - rewrite name regex iam\\.${options.subdomain} zenko-iam.${options.namespace}.svc.cluster.local - rewrite name regex ui\\.${options.subdomain} zenko-ui.${options.namespace}.svc.cluster.local - rewrite name regex s3\\.${options.subdomain} zenko-s3.${options.namespace}.svc.cluster.local - forward . /etc/resolv.conf - cache 30 - errors - log -} - -.:53 { - forward . /etc/resolv.conf - cache 30 - errors - log +interface Endpoint { + hostname: string; } -`; - const customDNSConfigMap = { - apiVersion: 'v1', - kind: 'ConfigMap', - metadata: { - name: 'zenko-test-coredns', - namespace: options.namespace - }, - data: { - 'Corefile': customCorefile.trim() - } +/** + * Generates rewrite rules from the provided JSON config files. + * @returns A string containing all the dynamic rewrite rules. + */ +function generateDynamicRules(): string { + const configDir = path.join(__dirname, '..', 'config'); + const locations: { locations: Location[] } = JSON.parse(fs.readFileSync(path.join(configDir, 'locations.json'), 'utf8')); + const endpoints: { endpoints: Endpoint[] } = JSON.parse(fs.readFileSync(path.join(configDir, 'endpoints.json'), 'utf8')); + + const rules: string[] = []; + const destination = 'ingress-nginx-controller.ingress-nginx.svc.cluster.local'; + + // This mapping helps create bucket-specific hostnames based on the endpoint. + const mockServiceMap: { [key: string]: string } = { + 'cloudserver-mock': 'aws-mock.zenko.local', + 'azurite-mock': 'azure-mock.zenko.local', }; - await k8s.applyManifest(customDNSConfigMap, options.namespace); - logger.info('Custom DNS ConfigMap created'); -} - -function generateRewriteRules(subdomain: string, namespace: string): string { - return ` -# Zenko test rewrite rules for ${subdomain} -rewrite name regex (.+\\.)?aws-mock\\.${subdomain} cloudserver-mock.${namespace}.svc.cluster.local -rewrite name regex (.+\\.)?azure-mock\\.${subdomain} azurite-mock.${namespace}.svc.cluster.local -rewrite name regex iam\\.${subdomain} zenko-iam.${namespace}.svc.cluster.local -rewrite name regex ui\\.${subdomain} zenko-ui.${namespace}.svc.cluster.local -rewrite name regex s3\\.${subdomain} zenko-s3.${namespace}.svc.cluster.local -rewrite name regex management\\.${subdomain} zenko-management.${namespace}.svc.cluster.local`; -} - -function addRewriteRules(currentCorefile: string, rewriteRules: string, subdomain: string): string { - // Find the main server block (.:53 or similar) - const lines = currentCorefile.split('\\n'); - const newLines = []; - let insideMainBlock = false; - let foundMainBlock = false; - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - - // Detect main server block - if (line.trim().match(/^\\.:53\\s*{/) || line.trim().match(/^\\. {/)) { - insideMainBlock = true; - foundMainBlock = true; - newLines.push(line); - // Add our rewrite rules right after the opening brace - newLines.push(rewriteRules); - continue; - } + // 1. Generate rules from locations.json for bucket-specific hostnames + for (const loc of locations.locations) { + if (!loc.details.bucketName) continue; - // Detect end of server block - if (insideMainBlock && line.trim() === '}') { - insideMainBlock = false; + for (const serviceKey in mockServiceMap) { + if (loc.details.endpoint.includes(serviceKey)) { + const publicDomain = mockServiceMap[serviceKey]; + const source = `${loc.details.bucketName}.${publicDomain}`; + rules.push(` rewrite name exact ${source} ${destination}`); + break; // Move to the next location once a match is found + } } + } - newLines.push(line); + // 2. Generate rules from endpoints.json + for (const ep of endpoints.endpoints) { + rules.push(` rewrite name exact ${ep.hostname} ${destination}`); } - // If no main block found, add our own - if (!foundMainBlock) { - newLines.push(''); - newLines.push(`# Zenko test server block`); - newLines.push(`.:53 {`); - newLines.push(rewriteRules); - newLines.push(' forward . /etc/resolv.conf'); - newLines.push(' cache 30'); - newLines.push(' errors'); - newLines.push(' log'); - newLines.push('}'); + if (rules.length > 0) { + return `# Dynamically generated rules\n` + rules.join('\n'); } + return '# No dynamic rules generated'; +} - return newLines.join('\\n'); +/** + * Reads the template and injects dynamic rules to create the final Corefile. + * @param options - Contains the namespace for placeholder replacement. + * @returns The complete and final Corefile content as a string. + */ +function generateCorefile(options: DNSOptions): string { + const templatePath = path.join(__dirname, '..', 'config', 'dns.conf'); + const corefileTemplate = fs.readFileSync(templatePath, 'utf8'); + + const dynamicRules = generateDynamicRules(); + + // Replace placeholders in the template + const finalCorefile = corefileTemplate + .replace('{{dynamic_rules}}', dynamicRules) + .replace(/{namespace}/g, options.namespace); // Replace any namespace placeholders if they exist + + return finalCorefile; } +/** + * Restarts the CoreDNS deployment to apply configuration changes. + */ async function restartCoreDNS(k8s: KubernetesClient): Promise { try { - // Get CoreDNS deployment - const deployment = await k8s.appsApi.readNamespacedDeployment({ + logger.debug('Attempting to restart CoreDNS deployment...'); + const patch = [ + { + op: 'add', + path: '/spec/template/metadata/annotations/kubectl.kubernetes.io~1restartedAt', + value: new Date().toISOString(), + }, + ]; + + await k8s.appsApi.patchNamespacedDeployment({ name: 'coredns', namespace: 'kube-system', + body: patch, }); - // Add/update restart annotation to trigger rolling restart - const annotations = deployment.spec?.template.metadata?.annotations || {}; - annotations['kubectl.kubernetes.io/restartedAt'] = new Date().toISOString(); - - deployment.spec!.template.metadata!.annotations = annotations; + logger.info('CoreDNS deployment restart triggered.'); + await k8s.waitForDeployment('coredns', 'kube-system', 60000); + logger.info('CoreDNS deployment is ready.'); + } catch (error: any) { + const errorBody = error.response ? JSON.stringify(error.response.body) : error.message; + logger.warn(`Could not restart CoreDNS deployment: ${errorBody}. A manual restart may be needed.`); + } +} - await k8s.appsApi.replaceNamespacedDeployment({ - name: 'coredns', - namespace: 'kube-system', - body: deployment, - }); +/** + * Main function to set up DNS by overwriting the CoreDNS ConfigMap. + */ +export async function setupDNS(options: DNSOptions): Promise { + const k8s = new KubernetesClient(); + const configMapName = 'coredns'; + const configMapNamespace = 'kube-system'; - logger.debug('CoreDNS deployment restart triggered'); + logger.info('Generating CoreDNS configuration...'); + const newCorefile = generateCorefile(options); - // Wait a bit for the restart to take effect - await new Promise(resolve => setTimeout(resolve, 10000)); + const configMapBody = { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { name: configMapName, namespace: configMapNamespace }, + data: { 'Corefile': newCorefile }, + }; + logger.info(`Applying CoreDNS ConfigMap to ${configMapNamespace}/${configMapName}...`); + try { + // This is the "create or replace" logic, equivalent to `kubectl apply` + await k8s.coreApi.replaceNamespacedConfigMap({ + name: configMapName, + namespace: configMapNamespace, + body: configMapBody, + }); + logger.info('CoreDNS ConfigMap successfully replaced.'); } catch (error: any) { - logger.warn(`Could not restart CoreDNS deployment: ${error.message}`); - logger.info('DNS changes will take effect when CoreDNS pods are restarted'); + if (error.response?.statusCode === 404) { + await k8s.coreApi.createNamespacedConfigMap({ + namespace: configMapNamespace, + body: configMapBody, + }); + logger.info('CoreDNS ConfigMap successfully created.'); + } else { + logger.error('Failed to apply CoreDNS ConfigMap:', error); + throw error; + } } -} \ No newline at end of file + + await restartCoreDNS(k8s); + logger.info('CoreDNS setup completed successfully.'); +} diff --git a/tests/@setup/src/endpoints.ts b/tests/@setup/src/endpoints.ts new file mode 100644 index 0000000000..bb75ca96a8 --- /dev/null +++ b/tests/@setup/src/endpoints.ts @@ -0,0 +1,150 @@ +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; +import * as fs from 'fs'; +import * as path from 'path'; +import axios from 'axios'; + +export interface EndpointConfig { + hostname: string; + locationName: string; + description?: string; +} + +export interface EndpointsConfig { + endpoints: EndpointConfig[]; +} + +export interface EndpointsOptions { + namespace: string; + instanceId?: string; + configFile?: string; +} + +function loadEndpointsConfig(configFile?: string): EndpointsConfig { + const defaultConfigPath = path.join(__dirname, '..', 'configs', 'endpoints.json'); + const configPath = configFile ? path.resolve(configFile) : defaultConfigPath; + + if (!fs.existsSync(configPath)) { + throw new Error(`Endpoints configuration file not found: ${configPath}`); + } + + try { + const configData = fs.readFileSync(configPath, 'utf-8'); + return JSON.parse(configData) as EndpointsConfig; + } catch (error) { + throw new Error(`Failed to parse endpoints configuration: ${error instanceof Error ? error.message : String(error)}`); + } +} + +export async function setupEndpoints(options: EndpointsOptions): Promise { + const k8s = new KubernetesClient(); + const config = loadEndpointsConfig(options.configFile); + + logger.info('Setting up S3 endpoints via Management API'); + + // Get management API endpoint and credentials + const { managementEndpoint, authToken } = await getManagementCredentials(k8s, options); + + // Get instance ID from Zenko CR if not provided + const instanceId = options.instanceId || await getInstanceId(k8s, options); + + if (!instanceId) { + throw new Error('Instance ID is required for endpoint creation. Either provide --instance-id or ensure Zenko CR exists'); + } + + for (const endpoint of config.endpoints) { + try { + await createEndpoint(managementEndpoint, authToken, instanceId, endpoint, options); + logger.info(`Created endpoint: ${endpoint.hostname} -> ${endpoint.locationName}`); + } catch (error) { + logger.error(`Failed to create endpoint ${endpoint.hostname}: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } + } + + logger.info(`Successfully created ${config.endpoints.length} S3 endpoints`); +} + +async function getManagementCredentials(k8s: KubernetesClient, options: EndpointsOptions): Promise<{ managementEndpoint: string; authToken: string }> { + // Get management API endpoint from service + const managementService = await k8s.coreApi.readNamespacedService({ + name: 'zenko-management', + namespace: options.namespace, + }); + + const managementPort = managementService.spec?.ports?.find(p => p.name === 'http')?.port || 8080; + const managementEndpoint = `http://zenko-management.${options.namespace}.svc.cluster.local:${managementPort}`; + + // Get admin credentials for authentication + const adminSecret = await k8s.coreApi.readNamespacedSecret({ + name: 'zenko-admin', + namespace: options.namespace, + }); + + if (!adminSecret.data) { + throw new Error('Failed to retrieve admin credentials from zenko-admin secret'); + } + + const accessKey = Buffer.from(adminSecret.data['access-key'], 'base64').toString(); + const secretKey = Buffer.from(adminSecret.data['secret-key'], 'base64').toString(); + + // Create admin auth token (basic auth for management API) + const authToken = Buffer.from(`${accessKey}:${secretKey}`).toString('base64'); + + return { managementEndpoint, authToken }; +} + +async function getInstanceId(k8s: KubernetesClient, options: EndpointsOptions): Promise { + try { + // Try to get instance ID from Zenko CR + const customObjects = k8s.customObjectsApi; + const zenkoList = await customObjects.listNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha1', + namespace: options.namespace, + plural: 'zenkos', + }); + + const zenkos = zenkoList.body as any; + if (zenkos.items && zenkos.items.length > 0) { + return zenkos.items[0].spec?.instanceId || zenkos.items[0].metadata?.name; + } + + return null; + } catch (error) { + logger.debug(`Failed to retrieve instance ID from Zenko CR: ${error instanceof Error ? error.message : String(error)}`); + return null; + } +} + +async function createEndpoint( + managementEndpoint: string, + authToken: string, + instanceId: string, + endpoint: EndpointConfig, + options: EndpointsOptions +): Promise { + + const endpointPayload = { + hostname: endpoint.hostname, + locationName: endpoint.locationName, + }; + + const response = await axios.post( + `${managementEndpoint}/api/v1/config/${instanceId}/endpoint`, + endpointPayload, + { + headers: { + 'Authorization': `Basic ${authToken}`, + 'Content-Type': 'application/json', + }, + timeout: 30000, + } + ); + + if (response.status !== 201 && response.status !== 200) { + throw new Error(`Management API returned status ${response.status}: ${JSON.stringify(response.data)}`); + } + + logger.debug(`Created S3 endpoint ${endpoint.hostname} pointing to location ${endpoint.locationName}`); +} \ No newline at end of file diff --git a/tests/@setup/src/locations.ts b/tests/@setup/src/locations.ts index c1536d9bf5..c043fd032f 100644 --- a/tests/@setup/src/locations.ts +++ b/tests/@setup/src/locations.ts @@ -1,11 +1,13 @@ import axios from 'axios'; +import * as fs from 'fs'; +import * as path from 'path'; import { KubernetesClient } from './utils/k8s'; import { logger } from './utils/logger'; export interface LocationsOptions { namespace: string; instanceId?: string; - dryRun?: boolean; + configFile?: string; } interface StorageLocation { @@ -14,62 +16,49 @@ interface StorageLocation { details: any; } +interface LocationsConfig { + locations: StorageLocation[]; +} + +function loadLocationsConfig(configFile?: string): LocationsConfig { + const defaultConfigPath = path.join(__dirname, '..', 'configs', 'locations.json'); + const configPath = configFile ? path.resolve(configFile) : defaultConfigPath; + + if (!fs.existsSync(configPath)) { + throw new Error(`Locations configuration file not found: ${configPath}`); + } + + try { + const configData = fs.readFileSync(configPath, 'utf-8'); + return JSON.parse(configData) as LocationsConfig; + } catch (error) { + throw new Error(`Failed to parse locations configuration: ${error instanceof Error ? error.message : String(error)}`); + } +} + export async function setupLocations(options: LocationsOptions): Promise { const k8s = new KubernetesClient(); logger.info('Setting up storage locations via Management API'); + // Load locations configuration + const config = loadLocationsConfig(options.configFile); + // Get Management API endpoint and credentials const managementEndpoint = await getManagementEndpoint(k8s, options.namespace); const credentials = await getManagementCredentials(k8s, options.namespace); - const locations: StorageLocation[] = [ - { - name: 'aws-s3-mock', - locationType: 'location-s3-v1', - details: { - endpoint: `http://cloudserver-mock.${options.namespace}.svc.cluster.local:8000`, - bucketName: 'ci-zenko-aws-target-bucket', - accessKey: 'accessKey1', - secretKey: 'verySecretKey1', - bucketMatch: false, - pathStyle: true - } - }, - { - name: 'azure-blob-mock', - locationType: 'location-azure-v1', - details: { - endpoint: `http://azurite-mock.${options.namespace}.svc.cluster.local:10000/devstoreaccount1`, - containerName: 'ci-zenko-azure-target-container', - accountName: 'devstoreaccount1', - accountKey: 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==' - } - }, - { - name: 'dmf-tape', - locationType: 'location-dmf-v1', - details: { - endpoint: 'http://dmf-service:7778', - repoId: ['repoId'], - nsId: 'nsId', - username: 'username', - password: 'password' - } - }, - { - name: 'ring-s3c', - locationType: 'location-s3-v1', - details: { - endpoint: 'http://ring-s3c:8080', - bucketName: 'ci-zenko-ring-target-bucket', - accessKey: 'ring-access-key', - secretKey: 'ring-secret-key', - bucketMatch: false, - pathStyle: true - } + // Process locations and replace namespace placeholders + const locations: StorageLocation[] = config.locations.map(location => ({ + ...location, + details: { + ...location.details, + endpoint: location.details.endpoint?.replace('{namespace}', options.namespace) } - ]; + })); + + + // Create locations via Management API for (const location of locations) { await createStorageLocation(managementEndpoint, credentials, location); diff --git a/tests/@setup/src/metadata.ts b/tests/@setup/src/metadata.ts new file mode 100644 index 0000000000..70dbe8dcc0 --- /dev/null +++ b/tests/@setup/src/metadata.ts @@ -0,0 +1,293 @@ +import { execSync } from 'child_process'; +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; +import { V1Job, V1ObjectMeta, V1JobSpec, V1PodTemplateSpec, V1PodSpec, V1Container, V1EnvVar } from '@kubernetes/client-node'; + +export interface MetadataOptions { + gitAccessToken: string; + namespace?: string; + timeout?: number; +} + +export async function setupMetadata(options: MetadataOptions): Promise { + const namespace = options.namespace || 'metadata'; + const timeout = options.timeout || 300; + + logger.info('Setting up metadata service...'); + + try { + await createNamespace(namespace); + await deployMetadataViaJob(options.gitAccessToken, namespace); + await waitForRepdReady(namespace, timeout); + await restartAndWaitForBucketd(namespace, timeout); + await patchCloudserverConfig(namespace); + await restartAndWaitForCloudserver(namespace, timeout); + + logger.info('Metadata service setup completed successfully'); + } catch (error) { + logger.error('Failed to setup metadata service:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function createNamespace(namespace: string): Promise { + logger.info(`Creating namespace: ${namespace}`); + + const k8s = new KubernetesClient(); + try { + await k8s.coreApi.createNamespace({ + body: { + metadata: { name: namespace } + } + }); + logger.info(`Namespace ${namespace} created`); + } catch (error: any) { + if (error.response?.statusCode === 409) { + logger.info(`Namespace ${namespace} already exists`); + } else { + throw error; + } + } +} + +async function deployMetadataViaJob(gitAccessToken: string, namespace: string): Promise { + logger.info('Deploying metadata service via Kubernetes Job...'); + + const k8s = new KubernetesClient(); + + try { + const jobName = `metadata-deploy-${Date.now()}`; + const job = createMetadataDeploymentJob(jobName, namespace, gitAccessToken); + + await k8s.createJobAndWaitForCompletion(job, 'default'); + + logger.info('Metadata deployment job completed successfully'); + + } catch (error) { + logger.error('Failed to deploy metadata via job:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +function createMetadataDeploymentJob(jobName: string, targetNamespace: string, gitAccessToken: string): V1Job { + const job = new V1Job(); + const metadata = new V1ObjectMeta(); + const jobSpec = new V1JobSpec(); + const podTemplate = new V1PodTemplateSpec(); + const podSpec = new V1PodSpec(); + const container = new V1Container(); + + // Job metadata + metadata.name = jobName; + metadata.labels = { + 'app': 'metadata-deploy', + 'managed-by': 'zenko-setup' + }; + + // Container specification + container.name = 'metadata-deploy'; + container.image = 'alpine/helm:3.12.0'; // Image with both git and helm + container.command = ['/bin/sh']; + container.args = ['-c', ` + set -ex + + # Install git and other dependencies + apk add --no-cache git jq + + # Clone metadata repository + git clone --depth 1 https://git:${gitAccessToken}@github.com/scality/metadata.git /workspace/metadata + cd /workspace/metadata/helm + + # Update helm dependencies + helm dependency update cloudserver/ + + # Install the chart + helm install -n ${targetNamespace} \\ + --create-namespace \\ + --set metadata.persistentVolume.storageClass='' \\ + --set metadata.sproxyd.persistentVolume.storageClass='' \\ + s3c cloudserver/ + + echo "Metadata chart installed successfully" + `]; + + // Environment variables + container.env = [ + { name: 'HELM_CACHE_HOME', value: '/tmp/.helm' } as V1EnvVar, + { name: 'HELM_CONFIG_HOME', value: '/tmp/.helm' } as V1EnvVar, + { name: 'HELM_DATA_HOME', value: '/tmp/.helm' } as V1EnvVar, + ]; + + // Pod specification + podSpec.containers = [container]; + podSpec.restartPolicy = 'Never'; + + // Pod template + podTemplate.spec = podSpec; + + // Job specification + jobSpec.template = podTemplate; + jobSpec.backoffLimit = 2; // Retry up to 2 times + jobSpec.activeDeadlineSeconds = 600; // 10 minute timeout + + // Job + job.apiVersion = 'batch/v1'; + job.kind = 'Job'; + job.metadata = metadata; + job.spec = jobSpec; + + return job; +} + +async function waitForRepdReady(namespace: string, timeout: number): Promise { + logger.info('Waiting for repd to be ready...'); + + try { + execSync(`kubectl -n ${namespace} rollout status --watch --timeout=${timeout}s statefulset/s3c-metadata-repd`, { + stdio: 'inherit' + }); + + await waitForAllPodsInService('metadata-repd', namespace, '91*', 60); + logger.info('Repd is ready'); + } catch (error) { + logger.error('Failed to wait for repd:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function restartAndWaitForBucketd(namespace: string, timeout: number): Promise { + logger.info('Restarting bucketd to fix reconnection issues...'); + + try { + execSync(`kubectl -n ${namespace} rollout restart deployment/s3c-metadata-bucketd`, { + stdio: 'inherit' + }); + + execSync(`kubectl -n ${namespace} rollout status --watch --timeout=${timeout}s deploy/s3c-metadata-bucketd`, { + stdio: 'inherit' + }); + + await waitForAllPodsInService('metadata-bucketd', namespace, '9000', 60); + logger.info('Bucketd is ready'); + } catch (error) { + logger.error('Failed to restart/wait for bucketd:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function patchCloudserverConfig(namespace: string): Promise { + logger.info('Patching cloudserver config to add s3c.local endpoint...'); + + try { + // Get current config + const getCurrentConfigCmd = `kubectl get configmap/s3c-cloudserver-config-json -n ${namespace} -o jsonpath='{.data.config\\.json}'`; + const currentConfig = execSync(getCurrentConfigCmd, { encoding: 'utf8' }); + + // Update config with jq + const updateConfigCmd = `echo '${currentConfig}' | jq '.restEndpoints["s3c.local"] = "us-east-1"'`; + const updatedConfig = execSync(updateConfigCmd, { encoding: 'utf8' }); + + // Patch configmap + const patchData = JSON.stringify({ + data: { + 'config.json': updatedConfig.trim() + } + }); + + execSync(`kubectl patch configmap/s3c-cloudserver-config-json -n ${namespace} --type='merge' -p='${patchData}'`, { + stdio: 'inherit' + }); + + logger.info('Cloudserver config patched successfully'); + } catch (error) { + logger.error('Failed to patch cloudserver config:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function restartAndWaitForCloudserver(namespace: string, timeout: number): Promise { + logger.info('Restarting cloudserver to apply new config...'); + + try { + execSync(`kubectl -n ${namespace} rollout restart deployment/s3c-cloudserver`, { + stdio: 'inherit' + }); + + execSync(`kubectl -n ${namespace} rollout status --watch --timeout=${timeout}s deployment/s3c-cloudserver`, { + stdio: 'inherit' + }); + + await waitForAllPodsInService('cloudserver', namespace, '8000', 60); + logger.info('Cloudserver is ready'); + } catch (error) { + logger.error('Failed to restart/wait for cloudserver:', { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function waitForAllPodsInService( + service: string, + namespace: string, + portRegex: string, + timeoutSeconds: number +): Promise { + logger.info(`Waiting for all pods behind service ${service} to be ready on port ${portRegex}...`); + + try { + // Get pods for the service + const getPodsCmd = `kubectl get pods -n ${namespace} -l app=${service} -o jsonpath='{range .items[*]}{.metadata.deletionTimestamp}:{.status.podIP}:{.spec.containers[*].ports[*].containerPort}{"\\n"}{end}'`; + const podsOutput = execSync(getPodsCmd, { encoding: 'utf8' }); + + const pods = podsOutput.trim().split('\n').filter(line => line); + + for (const podInfo of pods) { + const [deletionTimestamp, ip, ports] = podInfo.split(':'); + + // Skip pods that are terminating or don't have IP/ports + if (deletionTimestamp !== '' || !ip || !ports) { + continue; + } + + // Check each port that matches the regex + const portList = ports.split(' '); + for (const port of portList) { + if (port.match(new RegExp(portRegex.replace('*', '.*')))) { + await waitForEndpoint(ip, port, timeoutSeconds); + } + } + } + } catch (error) { + logger.error(`Failed to wait for pods in service ${service}:`, { error: error instanceof Error ? error.message : String(error) }); + throw error; + } +} + +async function waitForEndpoint(host: string, port: string, timeoutSeconds: number): Promise { + logger.debug(`Waiting for ${host}:${port} to be available...`); + + const startTime = Date.now(); + const timeoutMs = timeoutSeconds * 1000; + + return new Promise((resolve, reject) => { + const checkEndpoint = () => { + const elapsed = Date.now() - startTime; + if (elapsed > timeoutMs) { + reject(new Error(`Timeout waiting for ${host}:${port} after ${timeoutSeconds} seconds`)); + return; + } + + try { + execSync(`kubectl run wait-for-port-${Date.now()} --image=busybox --attach=True --rm --restart=Never --pod-running-timeout=5m --image-pull-policy=IfNotPresent -- sh -c 'nc -z -w 1 ${host} ${port}'`, { + stdio: 'pipe' + }); + logger.debug(`${host}:${port} is now available`); + resolve(); + } catch { + // Still not ready, try again + setTimeout(checkEndpoint, 1000); + } + }; + + checkEndpoint(); + }); +} \ No newline at end of file diff --git a/tests/@setup/src/mocks.ts b/tests/@setup/src/mocks.ts index 1d3ec10e4c..4a9044a9b0 100644 --- a/tests/@setup/src/mocks.ts +++ b/tests/@setup/src/mocks.ts @@ -7,7 +7,6 @@ export interface MocksOptions { instanceId?: string; awsOnly?: boolean; azureOnly?: boolean; - dryRun?: boolean; } export async function setupMocks(options: MocksOptions): Promise { diff --git a/tests/@setup/src/rbac.ts b/tests/@setup/src/rbac.ts index f845be7826..915054374c 100644 --- a/tests/@setup/src/rbac.ts +++ b/tests/@setup/src/rbac.ts @@ -3,7 +3,6 @@ import { logger } from './utils/logger'; export interface RBACOptions { namespace: string; - dryRun?: boolean; } export async function setupRBAC(options: RBACOptions): Promise { diff --git a/tests/@setup/src/tls.ts b/tests/@setup/src/tls.ts new file mode 100644 index 0000000000..153f4019dd --- /dev/null +++ b/tests/@setup/src/tls.ts @@ -0,0 +1,85 @@ +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; +import { execSync } from 'child_process'; +import { mkdtempSync, readFileSync, rmSync } from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; + +export interface TLSOptions { + namespace: string; + domains?: string[]; +} + +export async function setupTLSWithOpenSSL(options: TLSOptions): Promise { + const k8s = new KubernetesClient(); + const domains = options.domains || ['zenko.local']; + const mainDomain = domains[0] || 'zenko.local'; + + logger.info('Setting up TLS certificates for HTTPS testing'); + + // Create temporary directory + const tempDir = mkdtempSync(join(tmpdir(), 'zenko-tls-')); + + try { + // Generate self-signed certificate (simple approach like the original) + // Use wildcard domain if specified, otherwise use main domain + const certDomain = mainDomain.startsWith('*.') ? mainDomain : `*.${mainDomain.replace('*.', '')}`; + const opensslCmd = `openssl req -x509 -nodes -days 365 -newkey rsa:2048 ` + + `-keyout tls.key -out tls.crt -subj "/CN=${certDomain}"`; + + logger.info(`Generating self-signed certificate for ${mainDomain}...`); + execSync(opensslCmd, { cwd: tempDir, stdio: 'pipe' }); + + // Read generated files + const tlsKey = readFileSync(join(tempDir, 'tls.key'), 'utf8'); + const tlsCert = readFileSync(join(tempDir, 'tls.crt'), 'utf8'); + + // Create TLS secret using kubectl equivalent + await createTLSSecret(k8s, options.namespace, 'zenko-tls', tlsCert, tlsKey); + + logger.info(`Successfully created TLS secret 'zenko-tls' for domain: ${mainDomain}`); + + } catch (error) { + logger.error(`Failed to setup TLS certificates: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } finally { + // Clean up temporary directory + rmSync(tempDir, { recursive: true, force: true }); + } +} + +async function createTLSSecret(k8s: KubernetesClient, namespace: string, secretName: string, cert: string, key: string): Promise { + try { + // Check if secret already exists + try { + await k8s.coreApi.readNamespacedSecret({ name: secretName, namespace }); + logger.info(`TLS secret ${secretName} already exists, replacing...`); + await k8s.coreApi.deleteNamespacedSecret({ name: secretName, namespace }); + } catch (error) { + // Secret doesn't exist, which is fine + } + + // Create new TLS secret + await k8s.coreApi.createNamespacedSecret({ + namespace, + body: { + apiVersion: 'v1', + kind: 'Secret', + metadata: { + name: secretName, + namespace: namespace, + }, + type: 'kubernetes.io/tls', + data: { + 'tls.crt': Buffer.from(cert).toString('base64'), + 'tls.key': Buffer.from(key).toString('base64'), + }, + }, + }); + + logger.info(`Created TLS secret: ${secretName}`); + } catch (error) { + logger.error(`Failed to create TLS secret: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } +} \ No newline at end of file diff --git a/tests/@setup/src/utils/k8s.ts b/tests/@setup/src/utils/k8s.ts index 9afb857033..d487772091 100644 --- a/tests/@setup/src/utils/k8s.ts +++ b/tests/@setup/src/utils/k8s.ts @@ -7,6 +7,7 @@ export class KubernetesClient { public appsApi: k8s.AppsV1Api; public customObjectsApi: k8s.CustomObjectsApi; public rbacApi: k8s.RbacAuthorizationV1Api; + public batchApi: k8s.BatchV1Api; constructor(kubeconfig?: string) { this.kc = new k8s.KubeConfig(); @@ -17,11 +18,18 @@ export class KubernetesClient { this.kc.loadFromFile(process.env.KUBECONFIG); } else { try { - this.kc.loadFromDefault(); - } catch (error) { - logger.error( - 'Failed to load kubeconfig. Please provide --kubeconfig or set KUBECONFIG environment variable'); - throw error; + // Try in-cluster config first (when running as a Pod) + this.kc.loadFromCluster(); + logger.debug('Using in-cluster Kubernetes configuration'); + } catch (clusterError) { + try { + // Fallback to default (local kubeconfig) + this.kc.loadFromDefault(); + logger.debug('Using default Kubernetes configuration'); + } catch (defaultError) { + logger.error('Failed to load Kubernetes configuration. Tried in-cluster and default configurations.'); + throw defaultError; + } } } @@ -29,6 +37,7 @@ export class KubernetesClient { this.appsApi = this.kc.makeApiClient(k8s.AppsV1Api); this.customObjectsApi = this.kc.makeApiClient(k8s.CustomObjectsApi); this.rbacApi = this.kc.makeApiClient(k8s.RbacAuthorizationV1Api); + this.batchApi = this.kc.makeApiClient(k8s.BatchV1Api); } async ensureNamespace(namespace: string): Promise { @@ -62,109 +71,109 @@ export class KubernetesClient { try { switch (kind) { - case 'Deployment': - try { - await this.appsApi.readNamespacedDeployment( - { name: metadata.name, namespace: metadata.namespace || 'default' } - ); - await this.appsApi.replaceNamespacedDeployment( - { - name: metadata.name, - namespace: metadata.namespace || 'default', - body: manifest, - }, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.appsApi.createNamespacedDeployment( - metadata.namespace || 'default', - manifest, - ); - } else { - throw error; - } - } - break; - - case 'Service': - try { - await this.coreApi.readNamespacedService( - { name: metadata.name, namespace: metadata.namespace || 'default' } - ); - await this.coreApi.replaceNamespacedService( - { - name: metadata.name, - namespace: metadata.namespace || 'default', - body: manifest, - }, + case 'Deployment': + try { + await this.appsApi.readNamespacedDeployment( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.appsApi.replaceNamespacedDeployment( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.appsApi.createNamespacedDeployment( + metadata.namespace || 'default', + manifest, ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.coreApi.createNamespacedService(metadata.namespace || 'default', manifest); - } else { - throw error; - } + } else { + throw error; } - break; + } + break; - case 'ConfigMap': - try { - await this.coreApi.readNamespacedConfigMap( - { name: metadata.name, namespace: metadata.namespace || 'default' } - ); - await this.coreApi.replaceNamespacedConfigMap( - { - name: metadata.name, - namespace: metadata.namespace || 'default', - body: manifest, - }, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.coreApi.createNamespacedConfigMap(metadata.namespace || 'default', manifest); - } else { - throw error; - } + case 'Service': + try { + await this.coreApi.readNamespacedService( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.coreApi.replaceNamespacedService( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedService(metadata.namespace || 'default', manifest); + } else { + throw error; } - break; + } + break; - case 'Secret': - try { - await this.coreApi.readNamespacedSecret( - { name: metadata.name, namespace: metadata.namespace || 'default' } - ); - await this.coreApi.replaceNamespacedSecret( - { - name: metadata.name, - namespace: metadata.namespace || 'default', - body: manifest, - }, - ); - } catch (error: any) { - if (error.response?.statusCode === 404) { - await this.coreApi.createNamespacedSecret( - metadata.namespace || 'default', - manifest, - ); - } else { - throw error; - } + case 'ConfigMap': + try { + await this.coreApi.readNamespacedConfigMap( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.coreApi.replaceNamespacedConfigMap( + { + name: metadata.name, + namespace: metadata.namespace || 'default', + body: manifest, + }, + ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedConfigMap(metadata.namespace || 'default', manifest); + } else { + throw error; } - break; + } + break; - default: - // Handle custom resources - // eslint-disable-next-line no-case-declarations - const [group, version] = apiVersion.split('/'); - await this.customObjectsApi.createNamespacedCustomObject( + case 'Secret': + try { + await this.coreApi.readNamespacedSecret( + { name: metadata.name, namespace: metadata.namespace || 'default' } + ); + await this.coreApi.replaceNamespacedSecret( { - group, - version, - plural: `${kind.toLowerCase()}s`, - body: manifest, + name: metadata.name, namespace: metadata.namespace || 'default', + body: manifest, }, ); + } catch (error: any) { + if (error.response?.statusCode === 404) { + await this.coreApi.createNamespacedSecret( + metadata.namespace || 'default', + manifest, + ); + } else { + throw error; + } + } + break; + + default: + // Handle custom resources + // eslint-disable-next-line no-case-declarations + const [group, version] = apiVersion.split('/'); + await this.customObjectsApi.createNamespacedCustomObject( + { + group, + version, + plural: `${kind.toLowerCase()}s`, + body: manifest, + namespace: metadata.namespace || 'default', + }, + ); } } catch (error: any) { if (error.response?.statusCode === 409) { @@ -198,4 +207,101 @@ export class KubernetesClient { throw new Error(`Deployment ${name} did not become ready within ${timeoutMs}ms`); } + + /** + * Wait for a Kubernetes Job to complete (succeed or fail) + */ + async waitForJobCompletion(jobName: string, namespace: string, timeoutMs: number = 10 * 60 * 1000): Promise { + logger.info(`Waiting for job ${jobName} to complete...`); + + const pollInterval = 5000; // 5 seconds + const startTime = Date.now(); + + while (Date.now() - startTime < timeoutMs) { + try { + const jobResponse = await this.batchApi.readNamespacedJob({ + name: jobName, + namespace, + }); + + const job = jobResponse; + const status = job?.status; + + if (status?.succeeded && status.succeeded > 0) { + logger.info(`Job ${jobName} completed successfully`); + return; + } + + if (status?.failed && status.failed > 0) { + // Get job logs for debugging + const podsResponse = await this.coreApi.listNamespacedPod({ + namespace, + labelSelector: `job-name=${jobName}` + }); + + if (podsResponse?.items.length > 0) { + const podName = podsResponse.items[0].metadata?.name; + if (podName) { + try { + const logsResponse = await this.coreApi.readNamespacedPodLog({ + name: podName, + namespace + }); + logger.error(`Job ${jobName} failed. Pod logs:`, { logs: logsResponse }); + } catch (logError) { + logger.debug('Could not retrieve pod logs', { error: logError }); + } + } + } + throw new Error(`Job ${jobName} failed`); + } + + logger.debug(`Job ${jobName} still running...`, { status }); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } catch (error) { + if ((error as any)?.response?.statusCode === 404) { + throw new Error(`Job ${jobName} not found`); + } + throw error; + } + } + + throw new Error(`Timeout waiting for job ${jobName} to complete after ${timeoutMs}ms`); + } + + /** + * Create a Job and wait for its completion + */ + async createJobAndWaitForCompletion( + job: k8s.V1Job, + namespace: string, + timeoutMs: number = 10 * 60 * 1000 + ): Promise { + const jobResponse = await this.batchApi.createNamespacedJob({ + namespace, + body: job + }); + + const jobName = jobResponse?.metadata?.name; + if (!jobName) { + throw new Error('Failed to create job - no name returned'); + } + + logger.info(`Created job: ${jobName}`); + + try { + await this.waitForJobCompletion(jobName, namespace, timeoutMs); + } finally { + // Clean up the job + try { + await this.batchApi.deleteNamespacedJob({ + name: jobName, + namespace + }); + logger.debug(`Cleaned up job: ${jobName}`); + } catch (cleanupError) { + logger.debug(`Failed to cleanup job ${jobName}:`, { error: cleanupError }); + } + } + } } diff --git a/tests/@setup/src/utils/zenko-status.ts b/tests/@setup/src/utils/zenko-status.ts new file mode 100644 index 0000000000..771d47a488 --- /dev/null +++ b/tests/@setup/src/utils/zenko-status.ts @@ -0,0 +1,108 @@ +import { KubernetesClient } from './k8s'; +import { logger } from './logger'; + +interface ZenkoStatusValue { + lastTransitionTime: string; + message: string; + status: 'True' | 'False'; + reason?: string; + type: 'DeploymentFailure' | 'DeploymentInProgress' | 'Available'; +} + +type ZenkoStatus = ZenkoStatusValue[]; + +interface ZenkoStatusOptions { + namespace: string; + instanceId?: string; + timeout?: number; +} + +function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export async function waitForZenkoToStabilize(options: ZenkoStatusOptions): Promise { + const { namespace, instanceId = 'end2end', timeout = 15 * 60 * 1000 } = options; + const k8s = new KubernetesClient(); + + const startTime = Date.now(); + let status = false; + let deploymentFailure: ZenkoStatusValue = { + lastTransitionTime: '', + message: '', + status: 'False', + type: 'DeploymentFailure', + }; + let deploymentInProgress: ZenkoStatusValue = { + lastTransitionTime: '', + message: '', + status: 'False', + type: 'DeploymentInProgress', + }; + let available: ZenkoStatusValue = { + lastTransitionTime: '', + message: '', + status: 'False', + type: 'Available', + }; + + logger.info(`Waiting for Zenko instance '${instanceId}' to stabilize...`); + + while (!status && Date.now() - startTime < timeout) { + const zenkoCR = await k8s.customObjectsApi.getNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha2', + namespace, + plural: 'zenkos', + name: instanceId + }).catch(err => { + logger.debug('Error getting Zenko CR', { + error: err instanceof Error ? err.message : String(err), + }); + return null; + }); + + if (!zenkoCR) { + await sleep(5000); + continue; + } + + const conditions: ZenkoStatus = (zenkoCR as any)?.status?.conditions || []; + + conditions.forEach(condition => { + if (condition.type === 'DeploymentFailure') { + deploymentFailure = condition; + } else if (condition.type === 'DeploymentInProgress') { + deploymentInProgress = condition; + } else if (condition.type === 'Available') { + available = condition; + } + }); + + logger.debug('Checking Zenko CR status', { + deploymentFailure: `${deploymentFailure.type}=${deploymentFailure.status}`, + deploymentInProgress: `${deploymentInProgress.type}=${deploymentInProgress.status}`, + available: `${available.type}=${available.status}`, + }); + + if (deploymentFailure.status === 'False' && + deploymentInProgress.status === 'False' && + available.status === 'True' + ) { + status = true; + break; + } + + if (deploymentFailure.status === 'True') { + throw new Error(`Zenko deployment failed: ${deploymentFailure.message}`); + } + + await sleep(5000); + } + + if (!status) { + throw new Error(`Zenko instance '${instanceId}' did not stabilize within ${timeout / 1000} seconds`); + } + + logger.info(`Zenko instance '${instanceId}' is ready and stable`); +} diff --git a/tests/@setup/src/workflows.ts b/tests/@setup/src/workflows.ts new file mode 100644 index 0000000000..be921dd490 --- /dev/null +++ b/tests/@setup/src/workflows.ts @@ -0,0 +1,303 @@ +import { KubernetesClient } from './utils/k8s'; +import { logger } from './utils/logger'; +import * as fs from 'fs'; +import * as path from 'path'; +import axios from 'axios'; + +export interface ReplicationWorkflow { + name: string; + sourceBucket: string; + sourceLocation: string; + targetBucket: string; + targetLocation: string; + enabled: boolean; + description?: string; +} + +export interface LifecycleRule { + id: string; + status: 'Enabled' | 'Disabled'; + filter: { + prefix?: string; + tags?: { [key: string]: string }; + }; + transitions?: Array<{ + days: number; + storageClass: string; + }>; + expiration?: { + days: number; + }; +} + +export interface LifecycleWorkflow { + name: string; + bucketName: string; + rules: LifecycleRule[]; +} + +export interface IngestionWorkflow { + name: string; + sourceBucket: string; + sourceLocation: string; + targetBucket: string; + targetLocation: string; + schedule: string; + enabled: boolean; + description?: string; +} + +export interface WorkflowsConfig { + replication: ReplicationWorkflow[]; + lifecycle: LifecycleWorkflow[]; + ingestion: IngestionWorkflow[]; +} + +export interface WorkflowsOptions { + namespace: string; + instanceId?: string; + configFile?: string; + workflowType?: 'replication' | 'lifecycle' | 'ingestion'; +} + +function loadWorkflowsConfig(configFile?: string): WorkflowsConfig { + const defaultConfigPath = path.join(__dirname, '..', 'configs', 'workflows.json'); + const configPath = configFile ? path.resolve(configFile) : defaultConfigPath; + + if (!fs.existsSync(configPath)) { + throw new Error(`Workflows configuration file not found: ${configPath}`); + } + + try { + const configData = fs.readFileSync(configPath, 'utf-8'); + return JSON.parse(configData) as WorkflowsConfig; + } catch (error) { + throw new Error(`Failed to parse workflows configuration: ${error instanceof Error ? error.message : String(error)}`); + } +} + +export async function setupWorkflows(options: WorkflowsOptions): Promise { + const k8s = new KubernetesClient(); + const config = loadWorkflowsConfig(options.configFile); + + logger.info('Setting up workflows via Management API'); + + // Get management API endpoint and credentials + const { managementEndpoint, authToken } = await getManagementCredentials(k8s, options); + + // Get instance ID from Zenko CR if not provided + const instanceId = options.instanceId || await getInstanceId(k8s, options); + + if (!instanceId) { + throw new Error('Instance ID is required for workflow creation. Either provide --instance-id or ensure Zenko CR exists'); + } + + let totalCreated = 0; + + // Create replication workflows + if (!options.workflowType || options.workflowType === 'replication') { + for (const workflow of config.replication) { + try { + await createReplicationWorkflow(managementEndpoint, authToken, instanceId, workflow, options); + logger.info(`Created replication workflow: ${workflow.name}`); + totalCreated++; + } catch (error) { + logger.error(`Failed to create replication workflow ${workflow.name}: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } + } + } + + // Create lifecycle workflows + if (!options.workflowType || options.workflowType === 'lifecycle') { + for (const workflow of config.lifecycle) { + try { + await createLifecycleWorkflow(managementEndpoint, authToken, instanceId, workflow, options); + logger.info(`Created lifecycle workflow: ${workflow.name}`); + totalCreated++; + } catch (error) { + logger.error(`Failed to create lifecycle workflow ${workflow.name}: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } + } + } + + // Create ingestion workflows + if (!options.workflowType || options.workflowType === 'ingestion') { + for (const workflow of config.ingestion) { + try { + await createIngestionWorkflow(managementEndpoint, authToken, instanceId, workflow, options); + logger.info(`Created ingestion workflow: ${workflow.name}`); + totalCreated++; + } catch (error) { + logger.error(`Failed to create ingestion workflow ${workflow.name}: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } + } + } + + logger.info(`Successfully created ${totalCreated} workflows`); +} + +async function getManagementCredentials(k8s: KubernetesClient, options: WorkflowsOptions): Promise<{ managementEndpoint: string; authToken: string }> { + // Get management API endpoint from service + const managementService = await k8s.coreApi.readNamespacedService({ + name: 'zenko-management', + namespace: options.namespace, + }); + + const managementPort = managementService.spec?.ports?.find(p => p.name === 'http')?.port || 8080; + const managementEndpoint = `http://zenko-management.${options.namespace}.svc.cluster.local:${managementPort}`; + + // Get admin credentials for authentication + const adminSecret = await k8s.coreApi.readNamespacedSecret({ + name: 'zenko-admin', + namespace: options.namespace, + }); + + if (!adminSecret.data) { + throw new Error('Failed to retrieve admin credentials from zenko-admin secret'); + } + + const accessKey = Buffer.from(adminSecret.data['access-key'], 'base64').toString(); + const secretKey = Buffer.from(adminSecret.data['secret-key'], 'base64').toString(); + + // Create admin auth token (basic auth for management API) + const authToken = Buffer.from(`${accessKey}:${secretKey}`).toString('base64'); + + return { managementEndpoint, authToken }; +} + +async function getInstanceId(k8s: KubernetesClient, options: WorkflowsOptions): Promise { + try { + // Try to get instance ID from Zenko CR + const customObjects = k8s.customObjectsApi; + const zenkoList = await customObjects.listNamespacedCustomObject({ + group: 'zenko.io', + version: 'v1alpha1', + namespace: options.namespace, + plural: 'zenkos', + }); + + const zenkos = zenkoList.body as any; + if (zenkos.items && zenkos.items.length > 0) { + return zenkos.items[0].spec?.instanceId || zenkos.items[0].metadata?.name; + } + + return null; + } catch (error) { + logger.debug(`Failed to retrieve instance ID from Zenko CR: ${error instanceof Error ? error.message : String(error)}`); + return null; + } +} + +async function createReplicationWorkflow( + managementEndpoint: string, + authToken: string, + instanceId: string, + workflow: ReplicationWorkflow, + options: WorkflowsOptions +): Promise { + + const workflowPayload = { + workflowId: workflow.name, + type: 'replication', + enabled: workflow.enabled, + source: { + bucket: workflow.sourceBucket, + location: workflow.sourceLocation, + }, + destination: { + bucket: workflow.targetBucket, + location: workflow.targetLocation, + }, + }; + + const response = await axios.post( + `${managementEndpoint}/api/v1/config/${instanceId}/workflow`, + workflowPayload, + { + headers: { + 'Authorization': `Basic ${authToken}`, + 'Content-Type': 'application/json', + }, + timeout: 30000, + } + ); + + if (response.status !== 201 && response.status !== 200) { + throw new Error(`Management API returned status ${response.status}: ${JSON.stringify(response.data)}`); + } +} + +async function createLifecycleWorkflow( + managementEndpoint: string, + authToken: string, + instanceId: string, + workflow: LifecycleWorkflow, + options: WorkflowsOptions +): Promise { + + const workflowPayload = { + workflowId: workflow.name, + type: 'lifecycle', + bucketName: workflow.bucketName, + rules: workflow.rules, + }; + + const response = await axios.post( + `${managementEndpoint}/api/v1/config/${instanceId}/lifecycle`, + workflowPayload, + { + headers: { + 'Authorization': `Basic ${authToken}`, + 'Content-Type': 'application/json', + }, + timeout: 30000, + } + ); + + if (response.status !== 201 && response.status !== 200) { + throw new Error(`Management API returned status ${response.status}: ${JSON.stringify(response.data)}`); + } +} + +async function createIngestionWorkflow( + managementEndpoint: string, + authToken: string, + instanceId: string, + workflow: IngestionWorkflow, + options: WorkflowsOptions +): Promise { + + const workflowPayload = { + workflowId: workflow.name, + type: 'ingestion', + enabled: workflow.enabled, + schedule: workflow.schedule, + source: { + bucket: workflow.sourceBucket, + location: workflow.sourceLocation, + }, + destination: { + bucket: workflow.targetBucket, + location: workflow.targetLocation, + }, + }; + + const response = await axios.post( + `${managementEndpoint}/api/v1/config/${instanceId}/workflow`, + workflowPayload, + { + headers: { + 'Authorization': `Basic ${authToken}`, + 'Content-Type': 'application/json', + }, + timeout: 30000, + } + ); + + if (response.status !== 201 && response.status !== 200) { + throw new Error(`Management API returned status ${response.status}: ${JSON.stringify(response.data)}`); + } +} \ No newline at end of file diff --git a/tests/ZENKO_SETUP_INVENTORY.md b/tests/ZENKO_SETUP_INVENTORY.md new file mode 100644 index 0000000000..13e4f24f06 --- /dev/null +++ b/tests/ZENKO_SETUP_INVENTORY.md @@ -0,0 +1,143 @@ +# Zenko Test Setup Inventory + +Complete inventory of ALL non-standard Zenko setup tasks across the entire codebase. + +## Setup Tasks Inventory + +| Task | Current Implementation | Used By | What It Does | +|------|----------------------|---------|--------------| +| **Mock Services - AWS** | `.github/scripts/end2end/install-mocks.sh` + `tests/ctst/steps/setup/setup.ts` | CTST, Node Tests, Backbeat Tests | Creates AWS S3 mock (CloudServer) with pre-configured metadata | +| **Mock Services - Azure** | `.github/scripts/end2end/install-mocks.sh` + `tests/ctst/steps/setup/setup.ts` | CTST, Azure Archive Tests | Creates Azurite mock for blob/queue storage | +| **Buckets - AWS** | `tests/zenko_tests/create_buckets.py` | Node Tests, Backbeat Tests | Creates `ci-zenko-aws-*-bucket` buckets with versioning | +| **Buckets - Azure** | `tests/zenko_tests/create_buckets.py` | Node Tests, Azure Tests | Creates Azure containers and queues | +| **Buckets - Ring** | `tests/zenko_tests/create_buckets.py` | Ring/S3C Tests | Creates Ring buckets with pre-populated objects | +| **Storage Locations** | `tests/zenko_tests/configuration.py` + `tests/ctst/steps/setup/setup.ts` | All Tests | Creates AWS/Azure/DMF/Ring storage locations via Management API | +| **Accounts** | `tests/zenko_tests/configuration.py` | Node Tests, UI Tests | Creates test accounts via Management API | +| **Endpoints** | `tests/zenko_tests/configuration.py` | Node Tests | Creates S3 endpoints via Management API | +| **Workflows - Replication** | `tests/zenko_tests/configuration.py` | Replication Tests | Creates replication workflows via Management API | +| **Workflows - Lifecycle** | `tests/zenko_tests/configuration.py` | Lifecycle Tests | Creates lifecycle workflows via Management API | +| **Workflows - Ingestion** | `tests/zenko_tests/configuration.py` | Ingestion Tests | Creates ingestion workflows via Management API | +| **Keycloak Realm** | `.github/scripts/end2end/keycloak-helper.sh` | Auth Tests, UI Tests | Creates realm with roles: StorageManager, AccountTest::*, etc. | +| **Keycloak Users** | `.github/scripts/end2end/keycloak-helper.sh` | Auth Tests, UI Tests | Creates users with instance IDs and role assignments | +| **DNS - CoreDNS** | `.github/scripts/end2end/patch-coredns.sh` | All Tests | Adds rewrite rules for `*.aws-mock.zenko.local` → ingress | +| **DNS - /etc/hosts** | `.github/scripts/end2end/setup-ctst-local.sh` | Local Development | Adds `127.0.0.1 iam.zenko.local ui.zenko.local s3.zenko.local...` | +| **RBAC - Cluster Admin** | `.github/scripts/end2end/setup-ctst-local.sh` + `tests/ctst/steps/setup/setup.ts` | CTST, Node Tests | Grants cluster-admin to all service accounts | +| **Kafka Topics** | `tests/ctst/steps/setup/setup.ts` | CTST, Notification Tests | Creates notification and cold storage status topics | +| **Notification Targets** | `tests/ctst/steps/setup/setup.ts` | CTST, Notification Tests | Creates ZenkoNotificationTarget CRDs for Kafka | +| **Deployment Patches** | `tests/ctst/steps/setup/setup.ts` | CTST, Quota Tests | Sets `SCUBA_HEALTHCHECK_FREQUENCY=100` on cloudserver | +| **TLS Certificates** | `.github/scripts/end2end/enable-https.sh` | HTTPS Tests | Creates CA certificates and TLS secrets | +| **Metadata Service** | `.github/scripts/end2end/deploy-metadata.sh` | S3C/Ring Tests | Deploys separate metadata service (bucketd/repd) | +| **PRA Setup** | `.github/scripts/end2end/prepare-pra.sh` | PRA/DR Tests | Configures Point-in-time Recovery and Archive | +| **Shell UI** | `.github/scripts/end2end/deploy-shell-ui.sh` | UI Tests | Deploys shell-ui for navigation | +| **Admin Credentials** | `tests/ctst/steps/setup/setup.ts` | CTST | Extracts admin access/secret keys from secrets | +| **Service User Credentials** | `tests/ctst/steps/setup/setup.ts` | CTST | Extracts backbeat service user credentials | +| **PRA Credentials** | `tests/ctst/steps/setup/setup.ts` | CTST | Extracts DR/PRA admin credentials | +| **Kafka Configuration** | `tests/ctst/steps/setup/setup.ts` | CTST | Extracts Kafka hosts and topics from secrets | +| **Instance Information** | `tests/ctst/steps/setup/setup.ts` | CTST | Extracts InstanceID, time progression factor from Zenko CR | + +## Setup Script Locations + +### Shell Scripts (19 files) +``` +.github/scripts/end2end/ +├── install-mocks.sh # Mock services +├── keycloak-helper.sh # Keycloak realm/users +├── patch-coredns.sh # DNS configuration +├── setup-ctst-local.sh # Local dev setup +├── enable-https.sh # TLS certificates +├── deploy-metadata.sh # Metadata service +├── prepare-pra.sh # PRA configuration +├── deploy-shell-ui.sh # Shell UI +├── run-e2e-test.sh # Test runner (with setup) +├── run-e2e-ctst.sh # CTST runner (DEPRECATED - replaced by tests/@setup/run-tests.sh) +├── deploy-zenko.sh # Zenko deployment +├── deploy-zkop.sh # Zenko operator +├── bootstrap-kind.sh # Kind cluster setup +├── install-kind-dependencies.sh # Kind dependencies +├── common.sh # Shared utilities +├── requirements.sh # Prerequisite checks +└── create-pull-image-secret.sh # Image pull secrets +``` + +**Note:** The following files have been removed/migrated to tests/@setup/: +- configure-e2e.sh and configure-hosts.sh (migrated to TypeScript setup) + +### Python Scripts +``` +tests/zenko_tests/ +├── cleans3c.py # S3C cleanup utility +└── docker-entrypoint.sh # Test container entrypoint + +Note: configuration.py and create_buckets.py have been migrated to TypeScript in tests/@setup/src/ +``` + +### TypeScript/CTST Setup +``` +tests/ctst/steps/setup/setup.ts # CTST-specific setup (overlaps with shell scripts) +``` + +## Analysis + +**Total Setup Implementations:** 22+ files +**Duplicated Logic:** Mock services, RBAC, storage locations, credentials extraction +**Languages:** Bash (19 files), Python (2 files), TypeScript (1 file) +**Scope:** Some global (RBAC, DNS), some test-suite specific (credentials, buckets) + +**Key Insight:** Setup is scattered across 3 different languages and 22+ files, with significant duplication between CTST (TypeScript) and shell scripts (Bash) for the same functionality. + +--- + +# Centralization Strategy + +## Solution + +Create single TypeScript setup container that replaces all 22+ setup scripts. + +``` +tests/@setup/ +├── src/ +│ ├── mocks.ts # AWS/Azure mock deployment +│ ├── buckets.ts # Bucket creation (all providers) +│ ├── locations.ts # Storage locations via Management API +│ ├── keycloak.ts # Realm/users/roles +│ ├── dns.ts # CoreDNS configuration +│ ├── rbac.ts # Service account permissions +│ └── cli.ts # CLI interface +├── package.json # Dependencies +└── Dockerfile # Self-contained image +``` + +## Usage + +Single command replaces all setup scripts: + +```bash +docker run --rm -v ~/.kube:/root/.kube \ + -e NAMESPACE=default \ + -e SUBDOMAIN=zenko.local \ + -e INSTANCE_ID=xyz123 \ + ghcr.io/scality/zenko-test-setup:latest \ + --mocks --buckets --locations --keycloak +``` + +## Why Container Approach + +**Problem:** Setup logic scattered across 22+ files in 3 languages (Bash, Python, TypeScript) with no shared dependencies or runtime. + +**Container Solution:** +- Packages all setup logic in single consistent environment +- Provides TypeScript runtime with all dependencies (@kubernetes/client-node, @aws-sdk/*, @azure/*) +- Eliminates language/dependency conflicts between test suites +- Can be called identically from any test suite or CI/CD pipeline +- Kubernetes API access via mounted kubeconfig + +**Alternative approaches failed:** +- Shell scripts: Limited, hard to maintain, no type safety +- Python integration: Would require Python runtime in TypeScript test suites +- Direct TypeScript import: Would force all test suites to adopt TypeScript dependencies + +## Integration + +**CTST:** Call container in BeforeAll, keep only info retrieval functions +**Node tests:** Replace create_buckets.py + configuration.py with container call +**GitHub workflows:** Replace install-mocks.sh + keycloak-helper.sh + other scripts with container call diff --git a/tests/ctst/MIGRATION_TO_CTST_ONLY.md b/tests/ctst/MIGRATION_TO_CTST_ONLY.md index 7481365619..9e39061062 100644 --- a/tests/ctst/MIGRATION_TO_CTST_ONLY.md +++ b/tests/ctst/MIGRATION_TO_CTST_ONLY.md @@ -93,7 +93,7 @@ steps: - name: Setup CTST Prerequisites run: bash setup-ctst-local.sh - name: Run All E2E Tests via CTST - run: bash run-e2e-ctst.sh + run: ./run-tests.sh ~/.kube/config ctst ``` ## **Local Development Support** diff --git a/tests/ctst/README.md b/tests/ctst/README.md index c8fed82e24..a1d5c2b136 100644 --- a/tests/ctst/README.md +++ b/tests/ctst/README.md @@ -59,5 +59,5 @@ this can be used to check if CTST is working properly > > Some tests may require some additional configuration before running the tests. > -> Please refer to `.github/scripts/end2end/configure-e2e-ctst.sh` to see the configuration applied +> Please refer to `.github/scripts/end2end/configure-hosts.sh` to see the configuration applied in the CI for CTST tests. diff --git a/tests/zenko_tests/configuration.py b/tests/zenko_tests/configuration.py deleted file mode 100644 index 6dd94dcfaa..0000000000 --- a/tests/zenko_tests/configuration.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python - -import logging -import os -import sys -import time - -import yaml -import jwt -from bravado.client import SwaggerClient -from bravado.requests_client import RequestsClient -from bravado_core.spec import Spec -from kubernetes import client, config -from kubernetes.config.config_exception import ConfigException -from jsonschema import validate - -from e2e_config import accounts, endpoints, locations, workflows, schema -import create_buckets - -logging.basicConfig(level=logging.INFO) -_log = logging.getLogger("end2end configuration") - - -def load_k8s_config(config_file): - """ - Loads K8S config. - - :param config_file: Name of the kube-config file. - """ - try: - config.load_incluster_config() - return - except: - _log.info("unable to load incluster config") - - try: - config.load_kube_config(config_file=config_file) - return - except: - _log.info("unable to load k8s config file") - - raise Exception("Failed to load k8s config") - - -def load_end2end_config(config_file): - """ - Loads end-to-end test setup file - - :param config_file: Name of the end-to-end config file. - """ - with open(config_file) as file: - return yaml.full_load(file) - - -def create_swagger_client(swagger_conf_url, api_url, token=""): - """ - Create swagger client - - :param swagger_conf_url: url to swagger.conf - :param api_url: - :param token: jwt string - """ - # to view available clients - # print(dir(client)) - - # to use a client - # ui_client = client.ui_facing - - # to list methods available - # print(dir(client.ui_facing)) - - # describe method - # print(client.ui_facing.createConfigurationOverlayUser.__doc__) - - # to view list of models for request/response - # for model in client.swagger_spec.definitions: - # print(model) - - # retrieve model for request/response - # client.get_model('user-v1') - - # print the properties of the model - # print(client.get_model('user-v1').__doc__) - - if api_url.startswith("http://"): - api_url = api_url.lstrip("http://") - elif api_url.startswith("https://"): - api_url = api_url.lstrip("https://") - - http_client = RequestsClient() - http_client.set_api_key(api_url, - token, - param_name="X-Authentication-Token", - param_in="header") - client = SwaggerClient.from_url(swagger_conf_url, http_client=http_client, config={ - 'validate_swagger_spec': False, - }) - - return client - - -def main(): - TOKEN = os.getenv("TOKEN") - UUID = os.getenv("UUID") - KUBECONFIG = os.getenv("KUBECONFIG") - CONFIG_FILE = os.getenv("CONFIG_FILE", "./e2e-config.yaml") - MANAGEMENT_ENDPOINT = os.getenv("MANAGEMENT_ENDPOINT", - "http://managementapi.zenko.local") - MANAGEMENT_ENDPOINT = MANAGEMENT_ENDPOINT.rstrip('/') - NAMESPACE = os.getenv("NAMESPACE", "default") - - try: - load_k8s_config(config_file=KUBECONFIG) - e2e_config = load_end2end_config(config_file=CONFIG_FILE) - - validate(e2e_config, yaml.full_load(schema.e2e_config_schema)) - - client = create_swagger_client(MANAGEMENT_ENDPOINT + "/swagger.json", - MANAGEMENT_ENDPOINT, - token=TOKEN) - - # create buckets - create_buckets.create_aws_buckets() - create_buckets.create_ring_buckets() - create_buckets.create_azure_containers() - create_buckets.create_azure_queues() - - # create zenko resources - for account in e2e_config["accounts"]: - accounts.create_account(client, - TOKEN, - UUID, - account, - namespace=NAMESPACE) - - for endpoint in e2e_config["endpoints"]: - endpoints.create_endpoint(client, UUID, endpoint["hostname"], - endpoint["locationName"]) - - for location in e2e_config["locations"]: - locations.create_location(client, UUID, location) - - for wf in e2e_config["workflows"]["replication"]: - workflows.create_replication_workflow(client, UUID, wf) - - for wf in e2e_config["workflows"]["lifecycle"]: - workflows.create_lifecycle_workflow(client, UUID, wf) - - for wf in e2e_config["workflows"]["ingestion"]: - workflows.create_ingestion_workflow(client, UUID, wf) - - except Exception as e: - _log.error("Unable to run set up: %s", e) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/tests/zenko_tests/create_buckets.py b/tests/zenko_tests/create_buckets.py deleted file mode 100644 index 5c12b27022..0000000000 --- a/tests/zenko_tests/create_buckets.py +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env python -from boto3 import Session -from azure.storage.blob import BlobServiceClient -from azure.core.credentials import AzureNamedKeyCredential -from azure.core.exceptions import ResourceExistsError -from azure.storage.queue import QueueServiceClient -import os -import logging - -logging.basicConfig(level=logging.INFO) -_log = logging.getLogger('create_buckets') - -VERIFY_CERTIFICATES = os.environ.get('VERIFY_CERTIFICATES', 'false').lower() == 'true' - -def get_env(key, default=None, error=False): - if not error: - return os.environ.get(key, default) - return os.environ[key] - -def bucket_safe_create(bucket): - try: - _log.info('Creating bucket %s' % bucket.name) - bucket.create() - except bucket.meta.client.exceptions.BucketAlreadyOwnedByYou: - _log.info('Bucket %s already exists!' % bucket.name) - except Exception as exp: # pylint: disable=broad-except - _log.info('Error creating bucket %s - %s' % (bucket.name, str(exp))) - raise exp - -def put_file(bucket, object_name, body): - """ - Put an object in a bucket - """ - try: - _log.info('Putting object %s' % object_name) - obj = bucket.Object(object_name) - obj.put(Body=body) - except Exception as exp: - _log.info('Error putting object %s - %s' % (bucket.name, object_name, str(exp))) - raise exp - -def put_singlepart_mpu(bucket, object_name, body): - """ - Put an object in a bucket using a single part multipart upload - """ - try: - _log.info('Putting object %s using multipart upload' % object_name) - mpu = bucket.meta.client.create_multipart_upload( - Bucket=bucket.name, - Key=object_name - ) - response = bucket.meta.client.upload_part( - Body=body, - Bucket=bucket.name, - Key=object_name, - PartNumber=1, - UploadId=mpu['UploadId'] - ) - bucket.meta.client.complete_multipart_upload( - Bucket=bucket.name, - Key=object_name, - UploadId=mpu['UploadId'], - MultipartUpload={ - 'Parts': [{ - 'PartNumber': 1, - 'ETag': response['ETag'] - }] - } - ) - except Exception as exp: - _log.error('Error in multipart upload for object %s - %s' % (object_name, str(exp))) - raise exp - -def create_ring_buckets(): - RING_S3C_ACCESS_KEY = get_env('RING_S3C_ACCESS_KEY') - RING_S3C_SECRET_KEY = get_env('RING_S3C_SECRET_KEY') - RING_S3C_ENDPOINT = get_env('RING_S3C_ENDPOINT') - ENABLE_RING_TESTS = get_env('ENABLE_RING_TESTS') - ## test bucket names - RING_S3C_INGESTION_SRC_BUCKET_NAME = get_env('RING_S3C_INGESTION_SRC_BUCKET_NAME') - RING_S3C_INGESTION_SRC_NON_VERSIONED_BUCKET_NAME = get_env('RING_S3C_INGESTION_SRC_NON_VERSIONED_BUCKET_NAME') - RING_S3C_INGESTION_NON_VERSIONED_OBJECT_COUNT_PER_TYPE = get_env('RING_S3C_INGESTION_NON_VERSIONED_OBJECT_COUNT_PER_TYPE') - - # Disable if Ring is not enabled - if ENABLE_RING_TESTS == "false": - return - - s3c = Session(aws_access_key_id=RING_S3C_ACCESS_KEY, - aws_secret_access_key=RING_S3C_SECRET_KEY) - ring_s3c_client = s3c.resource('s3', endpoint_url=RING_S3C_ENDPOINT, - verify=VERIFY_CERTIFICATES) - - versioned_bucket = ring_s3c_client.Bucket(RING_S3C_INGESTION_SRC_BUCKET_NAME) - non_versioned_bucket = ring_s3c_client.Bucket(RING_S3C_INGESTION_SRC_NON_VERSIONED_BUCKET_NAME) - - ## Creating S3C buckets - _log.info('Creating S3C buckets...') - bucket_safe_create(versioned_bucket) - bucket_safe_create(non_versioned_bucket) - - ## Adding non versioned objects before test execution to avoid - ## having to create the location mid tests which might cause flakiness. - ## A RING location can only be created if the bucket is versioned, and - ## once versioning is enabled it cannot be disabled. - _log.info('Putting non versioned objects...') - for i in range(int(RING_S3C_INGESTION_NON_VERSIONED_OBJECT_COUNT_PER_TYPE)): - put_file(non_versioned_bucket, 'simple-%d' % i, b'data') - put_file(non_versioned_bucket, 'zerobyte-%d' % i, b'') - put_singlepart_mpu(non_versioned_bucket, 'mpu-singlepart-%d' % i, b'mpudata') - - ## Enabling versioning - _log.info('Enabling versioning on buckets...') - versioned_bucket.Versioning().enable() - non_versioned_bucket.Versioning().enable() - -def create_aws_buckets(): - AWS_ACCESS_KEY = get_env('AWS_ACCESS_KEY') - AWS_SECRET_KEY = get_env('AWS_SECRET_KEY') - AWS_ENDPOINT = get_env('AWS_ENDPOINT') - AWS_FAIL_BUCKET_NAME = get_env('AWS_FAIL_BUCKET_NAME') - AWS_REPLICATION_FAIL_CTST_BUCKET_NAME = get_env('AWS_REPLICATION_FAIL_CTST_BUCKET_NAME') - - s3c = Session(aws_access_key_id=AWS_ACCESS_KEY, - aws_secret_access_key=AWS_SECRET_KEY) - aws_s3c_client = s3c.resource('s3', endpoint_url=AWS_ENDPOINT, - verify=VERIFY_CERTIFICATES) - - ## Creating AWS buckets - _log.info('Creating AWS buckets...') - bucket_safe_create(aws_s3c_client.Bucket(AWS_FAIL_BUCKET_NAME)) - bucket_safe_create(aws_s3c_client.Bucket(AWS_REPLICATION_FAIL_CTST_BUCKET_NAME)) - aws_s3c_client.Bucket(AWS_FAIL_BUCKET_NAME).Versioning().enable() - aws_s3c_client.Bucket(AWS_REPLICATION_FAIL_CTST_BUCKET_NAME).Versioning().enable() - -def create_azure_containers(): - AZURE_BACKEND_ENDPOINT = get_env("AZURE_BACKEND_ENDPOINT") - AZURE_ACCOUNT_NAME = get_env("AZURE_ACCOUNT_NAME") - AZURE_SECRET_KEY = get_env("AZURE_SECRET_KEY") - AZURE_CRR_BUCKET_NAME = get_env("AZURE_CRR_BUCKET_NAME") - AZURE_ARCHIVE_BUCKET_NAME = get_env("AZURE_ARCHIVE_BUCKET_NAME") - AZURE_ARCHIVE_BUCKET_NAME_2 = get_env("AZURE_ARCHIVE_BUCKET_NAME_2") - - credential = AzureNamedKeyCredential(name=AZURE_ACCOUNT_NAME, - key=AZURE_SECRET_KEY) - blob_service_client = BlobServiceClient(account_url=AZURE_BACKEND_ENDPOINT, - credential=credential, - connection_verify=VERIFY_CERTIFICATES) - ## Creating Azure buckets - _log.info('Creating Azure buckets...') - for bucket_name in [AZURE_CRR_BUCKET_NAME, AZURE_ARCHIVE_BUCKET_NAME, AZURE_ARCHIVE_BUCKET_NAME_2]: - try: - _log.info('Creating bucket %s' % bucket_name) - blob_service_client.create_container(name=bucket_name) - except ResourceExistsError: - _log.info('Container %s already exists!' % bucket_name) - -def create_azure_queues(): - AZURE_BACKEND_QUEUE_ENDPOINT = get_env("AZURE_BACKEND_QUEUE_ENDPOINT") - AZURE_ACCOUNT_NAME = get_env("AZURE_ACCOUNT_NAME") - AZURE_SECRET_KEY = get_env("AZURE_SECRET_KEY") - AZURE_ARCHIVE_QUEUE_NAME = get_env("AZURE_ARCHIVE_QUEUE_NAME") - - credential = AzureNamedKeyCredential(name=AZURE_ACCOUNT_NAME, - key=AZURE_SECRET_KEY) - - queue_client = QueueServiceClient(account_url=AZURE_BACKEND_QUEUE_ENDPOINT, - credential=credential, - connection_verify=VERIFY_CERTIFICATES) - - ## Creating Azure queue - _log.info('Creating Azure queues...') - try: - _log.info('Creating queue %s' % AZURE_ARCHIVE_QUEUE_NAME) - queue_client.create_queue(name=AZURE_ARCHIVE_QUEUE_NAME) - except ResourceExistsError: - _log.info('Queue %s already exists!' % AZURE_ARCHIVE_QUEUE_NAME) diff --git a/tests/zenko_tests/e2e-config.yaml.template b/tests/zenko_tests/e2e-config.yaml.template deleted file mode 100644 index ded5c90396..0000000000 --- a/tests/zenko_tests/e2e-config.yaml.template +++ /dev/null @@ -1,119 +0,0 @@ -accounts: - - "zenko" -endpoints: [] -locations: - - name: "${AWS_BACKEND_DESTINATION_LOCATION}" - locationType: "location-aws-s3-v1" - legacyAwsBehavior: yes - details: - bucketName: "${AWS_CRR_BUCKET_NAME}" - endpoint: "${AWS_ENDPOINT}" - accessKey: "${AWS_ACCESS_KEY}" - secretKey: "${AWS_SECRET_KEY}" - bucketMatch: no - repoId: [] - - name: "${AWS_BACKEND_DESTINATION_FAIL_LOCATION}" - locationType: "location-aws-s3-v1" - details: - bucketName: "${AWS_FAIL_BUCKET_NAME}" - endpoint: "${AWS_ENDPOINT}" - accessKey: "${AWS_ACCESS_KEY}" - secretKey: "${AWS_SECRET_KEY}" - bucketMatch: yes - repoId: [] - - name: "${AWS_BACKEND_SOURCE_LOCATION}" - locationType: "location-aws-s3-v1" - details: - bucketName: "${AWS_BUCKET_NAME}" - endpoint: "${AWS_ENDPOINT}" - accessKey: "${AWS_ACCESS_KEY}" - secretKey: "${AWS_SECRET_KEY}" - bucketMatch: yes - repoId: [] - - name: "${AWS_BACKEND_DESTINATION_REPLICATION_FAIL_CTST_LOCATION}" - # This location is used for a specific test that temporarily removes its bucket - # Consider using other locations first - locationType: "location-aws-s3-v1" - details: - bucketName: "${AWS_REPLICATION_FAIL_CTST_BUCKET_NAME}" - endpoint: "${AWS_ENDPOINT}" - accessKey: "${AWS_ACCESS_KEY}" - secretKey: "${AWS_SECRET_KEY}" - bucketMatch: no - repoId: [] - - name: ${AZURE_BACKEND_DESTINATION_LOCATION} - locationType: location-azure-v1 - details: - bucketName: ${AZURE_CRR_BUCKET_NAME} - endpoint: ${AZURE_BACKEND_ENDPOINT} - accessKey: ${AZURE_ACCOUNT_NAME} - secretKey: ${AZURE_SECRET_KEY} - bucketMatch: no - repoId: [] - - name: "${RING_S3C_BACKEND_SOURCE_LOCATION}" - locationType: "location-scality-ring-s3-v1" - details: - bucketName: "${RING_S3C_INGESTION_SRC_BUCKET_NAME}" - endpoint: "${RING_S3C_ENDPOINT}" - accessKey: "${RING_S3C_ACCESS_KEY}" - secretKey: "${RING_S3C_SECRET_KEY}" - bucketMatch: yes - repoId: [] - - name: "${RING_S3C_BACKEND_SOURCE_NON_VERSIONED_LOCATION}" - locationType: "location-scality-ring-s3-v1" - details: - bucketName: "${RING_S3C_INGESTION_SRC_NON_VERSIONED_BUCKET_NAME}" - endpoint: "${RING_S3C_ENDPOINT}" - accessKey: "${RING_S3C_ACCESS_KEY}" - secretKey: "${RING_S3C_SECRET_KEY}" - bucketMatch: yes - repoId: [] - - name: "${GCP_BACKEND_DESTINATION_LOCATION}" - locationType: "location-gcp-v1" - details: - bucketName: "${GCP_CRR_BUCKET_NAME}" - mpuBucketName: "${GCP_CRR_MPU_BUCKET_NAME}" - accessKey: "${GCP_ACCESS_KEY}" - secretKey: "${GCP_SECRET_KEY}" - bucketMatch: no - repoId: [] - - name: "${COLD_BACKEND_DESTINATION_LOCATION}" - locationType: "location-dmf-v1" - isCold: true - details: - endpoint: "ws://mock-sorbet:5001/session" - username: "user1" - password: "pass1" - repoId: - - 233aead6-1d7b-4647-a7cf-0d3280b5d1d7 - - 81e78de8-df11-4acd-8ad1-577ff05a68db - nsId: 65f9fd61-42fe-4a68-9ac0-6ba25311cc85 - - name: "${AZURE_ARCHIVE_BACKEND_DESTINATION_LOCATION}" - locationType: "location-azure-archive-v1" - isCold: true - details: - endpoint: "${AZURE_BACKEND_ENDPOINT}" - bucketName: "${AZURE_ARCHIVE_BUCKET_NAME}" - queue: - type: "location-azure-storage-queue-v1" - queueName: "${AZURE_ARCHIVE_QUEUE_NAME}" - endpoint: "${AZURE_BACKEND_QUEUE_ENDPOINT}" - auth: - type: "location-azure-shared-key" - accountName: "${AZURE_ACCOUNT_NAME}" - accountKey: "${AZURE_SECRET_KEY}" - repoId: - - 233aead6-1d7b-4647-a7cf-0d3280b5d1d7 - - name: "${MIRIA_BACKEND_DESTINATION_LOCATION}" - locationType: "location-miria-v1" - isCold: true - details: - endpoint: "ws://mock-miria:5001/session" - username: "user1" - password: "pass1" - repoId: - - 65f9fd61-42fe-4a68-9ac0-6ba25311cc85 -workflows: - replication: [] - lifecycle: [] - ingestion: [] diff --git a/tests/zenko_tests/e2e_config/accounts.py b/tests/zenko_tests/e2e_config/accounts.py deleted file mode 100644 index b484a16b0b..0000000000 --- a/tests/zenko_tests/e2e_config/accounts.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python - -import logging - -from kubernetes import client, config -from kubernetes.client.rest import ApiException - -from e2e_config import clients - -_log = logging.getLogger("end2end configuration") - -def get_credentials(token, account_id): - """ - Retrieve credentials from sts service - - :param token: oidc id token - :param account_id: account id - """ - _log.info("getting account credentials") - - # TODO: either use account credentials by generating it using pensieve-api - # or create a role and then use assume role to generate credential/token - # do not use hard coded role, this will be removed in the future versions! - res = clients.stsclient.assume_role_with_web_identity( - RoleArn="arn:aws:iam::%s:role/scality-internal/storage-manager-role" % (account_id), - RoleSessionName='end2end', - WebIdentityToken=token, - DurationSeconds=60 * 60 * 12, # 12 hrs - ) - - return res - - -def create_account_secret(name, credentials, namespace="default"): - """ - Create a k8s secret resource for account - - :param name: secret name - :param credentials: sts assume role credentials - :param namespace: k8s namespace - """ - _log.info("creating account secret") - - core = client.CoreV1Api() - secret = client.V1Secret( - api_version="v1", - metadata=client.V1ObjectMeta( - name=name, - labels={ - "type": "end2end", - }, - ), - string_data=credentials, - ) - - try: - res = core.create_namespaced_secret(namespace, body=secret) - except ApiException as e: - if e.status == 409: - _log.warning("secret already exists") - else: - raise e - - _log.info("created account secret") - - -def create_account(client, token, uuid, account_name, namespace="default"): - """ - Creates a user account and save its accessKey and secretKey as k8s secret - - :param client: swagger client - :param uuid: zenko instance uuid - :param account_name: account_name - :param namespace: k8s namespace - """ - try: - User_V1 = client.get_model('user-v1') - u = User_V1(userName=account_name, - email="%s@zenko.local" % (account_name)) - - res = ( - client.ui_facing - .createConfigurationOverlayUser(user=u, uuid=uuid) - .response() - .result - ) - - creds = get_credentials(token, res.id) - create_account_secret(name="end2end-account-%s" % (res.userName), - credentials=creds["Credentials"], - namespace=namespace) - - _log.info("created account") - except Exception as e: - raise Exception( - "Failed to create account '%s': %s" % (account_name, e)) diff --git a/tests/zenko_tests/e2e_config/clients.py b/tests/zenko_tests/e2e_config/clients.py deleted file mode 100644 index 17b6152e39..0000000000 --- a/tests/zenko_tests/e2e_config/clients.py +++ /dev/null @@ -1,10 +0,0 @@ -import os -import boto3 - -VAULT_ENDPOINT = os.getenv("VAULT_ENDPOINT") -session = boto3.session.Session() - -stsclient = session.client( - service_name='sts', - endpoint_url=VAULT_ENDPOINT, -) diff --git a/tests/zenko_tests/e2e_config/endpoints.py b/tests/zenko_tests/e2e_config/endpoints.py deleted file mode 100644 index 4775c0d107..0000000000 --- a/tests/zenko_tests/e2e_config/endpoints.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python - -import logging - -_log = logging.getLogger("end2end configuration") - -def create_endpoint(client, uuid, host, location): - """ - Creates an endpoint for a location - :param client: swagger client - :param uuid: zenko instance uuid - :param host: hostname - :param location: location name - """ - try: - Endpoint_V1 = client.get_model('endpoint-v1') - ep = Endpoint_V1(hostname=host, locationName=location) - - res = ( - client.ui_facing - .createConfigurationOverlayEndpoint(endpoint=ep, uuid=uuid) - .response() - .result - ) - - _log.info(res) - _log.info("endpoint created") - except Exception as e: - raise Exception( - "Failed to create endpoint for location '%s': %s" % (location, e)) diff --git a/tests/zenko_tests/e2e_config/locations.py b/tests/zenko_tests/e2e_config/locations.py deleted file mode 100644 index 578f4e5057..0000000000 --- a/tests/zenko_tests/e2e_config/locations.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -import logging -import os - -_log = logging.getLogger("end2end configuration") - -def create_location(client, uuid, location): - """ - Creates a location - :param client: swagger client - :param uuid: zenko instance uuid - :param location: location details - """ - - ENABLE_RING_TESTS = os.environ['ENABLE_RING_TESTS'] - if ENABLE_RING_TESTS == "false" and location["locationType"] == "location-scality-ring-s3-v1": - return - - try: - Location_V1 = client.get_model('location-v1') - if "bootstrapList" not in location["details"]: - location["details"]["bootstrapList"] = [] - loc = Location_V1(name=location["name"], - locationType=location["locationType"], - details=location["details"]) - - res = ( - client.ui_facing - .createConfigurationOverlayLocation(location=loc, uuid=uuid) - .response() - .result - ) - - _log.info("location created") - except Exception as e: - raise Exception( - "Failed to create location '%s': %s" % (location["name"], e)) diff --git a/tests/zenko_tests/e2e_config/schema.py b/tests/zenko_tests/e2e_config/schema.py deleted file mode 100644 index 4b518b9b2a..0000000000 --- a/tests/zenko_tests/e2e_config/schema.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python - -e2e_config_schema = """ -type: object -properties: - accounts: - type: array - items: - type: string - endpoints: - type: array - items: - type: object - properties: - hostname: - type: string - locationName: - type: string - locations: - type: array - items: - type: object - properties: - name: - type: string - locationType: - type: string - details: - type: object - properties: - bucketName: - type: string - endpoint: - type: string - accessKey: - type: string - secretKey: - type: string - workflows: - type: object - properties: - replication: - type: array - items: - type: object - lifecycle: - type: array - items: - type: object - ingestion: - type: array - items: - type: object -""" diff --git a/tests/zenko_tests/e2e_config/workflows.py b/tests/zenko_tests/e2e_config/workflows.py deleted file mode 100644 index 8dea787396..0000000000 --- a/tests/zenko_tests/e2e_config/workflows.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -import logging - -_log = logging.getLogger("end2end configuration") - -def create_replication_workflow(client, UUID, workflow): - pass - -def create_lifecycle_workflow(client, UUID, workflow): - pass - -def create_ingestion_workflow(client, UUID, workflow): - pass From d3e416981a58503dace8a46571ebee04dfce5ec4 Mon Sep 17 00:00:00 2001 From: williamlardier Date: Tue, 16 Sep 2025 08:51:47 +0200 Subject: [PATCH 12/14] run scripts --- tests/@setup/README.md | 133 ++-------- tests/@setup/eslint.config.mjs | 2 - tests/@setup/run-tests.sh | 455 ++++++++++++++++----------------- tests/@setup/setup-tests.sh | 310 +++++++++++----------- tests/@setup/src/utils/k8s.ts | 4 +- 5 files changed, 419 insertions(+), 485 deletions(-) diff --git a/tests/@setup/README.md b/tests/@setup/README.md index 5f9b3c5d2e..c0aaa8021c 100644 --- a/tests/@setup/README.md +++ b/tests/@setup/README.md @@ -18,13 +18,8 @@ Simple scripts to setup and run tests against any Zenko cluster using Kubernetes ### 2. Run Tests ```bash -# Run CTST tests (default) -./run-tests.sh - -# Run specific test type -./run-tests.sh ~/.kube/config ctst -./run-tests.sh ~/.kube/config e2e -./run-tests.sh ~/.kube/config smoke +# Run CTST tests +./run-tests.sh ctst # Run CTST with specific tags ./run-tests.sh ~/.kube/config ctst --tags @PRA @@ -37,110 +32,38 @@ Set these before running the scripts to customize behavior: ### Setup Configuration ```bash -export NAMESPACE="default" # Kubernetes namespace -export INSTANCE_ID="end2end" # Zenko instance name -export SUBDOMAIN="zenko.local" # Base domain for services -export GIT_ACCESS_TOKEN="your-token" # For metadata deployment -export METADATA_NAMESPACE="metadata" # Metadata service namespace -export SETUP_IMAGE="ghcr.io/scality/zenko-setup:latest" # Setup container image -export LOG_LEVEL="debug" # Logging verbosity -export JOB_TIMEOUT="1800" # Job timeout in seconds +export NAMESPACE="default" +export INSTANCE_ID="end2end" +export SUBDOMAIN="zenko.local" +export GIT_ACCESS_TOKEN="your-token" +export METADATA_NAMESPACE="metadata" +export SETUP_IMAGE="ghcr.io/scality/zenko-setup:latest" +export LOG_LEVEL="debug" +export JOB_TIMEOUT="1800" ``` ### Test Configuration ```bash -export E2E_IMAGE="ghcr.io/scality/zenko/zenko-e2e:latest" # E2E test container -export E2E_CTST_IMAGE="ghcr.io/scality/zenko/zenko-e2e-ctst:latest" # CTST test container -export OIDC_REALM="zenko" # Keycloak realm -export OIDC_USERNAME="storage_manager" # Test user -export OIDC_PASSWORD="123" # Test password -export OIDC_HOST="keycloak.zenko.local" # Keycloak hostname - -# CTST-specific configuration -export PARALLEL_RUNS="4" # Number of parallel test runs -export RETRIES="3" # Test retry count -export JUNIT_REPORT_PATH="ctst-junit.xml" # JUnit report path -export AZURE_ACCOUNT_NAME="devstoreaccount1" # Azure storage account -export AZURE_SECRET_KEY="..." # Azure storage key +# CTST and test images +export E2E_IMAGE="ghcr.io/scality/zenko/zenko-e2e:latest" +export E2E_CTST_IMAGE="ghcr.io/scality/zenko/zenko-e2e-ctst:latest" + +# Keycloak configuration +export OIDC_REALM="zenko" +export OIDC_USERNAME="storage_manager" +export OIDC_PASSWORD="123" +export OIDC_HOST="keycloak.zenko.local" + +# CTST specific configuration +export PARALLEL_RUNS="4" +export RETRIES="3" +export JUNIT_REPORT_PATH="ctst-junit.xml" + +# Azure storage configuration +export AZURE_ACCOUNT_NAME="devstoreaccount1" +export AZURE_SECRET_KEY="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" ``` ## Requirements - `kubectl` configured to access your cluster -- Appropriate RBAC permissions in the target cluster (created automatically) - -## How It Works - -1. **Setup Script** (`./setup-tests.sh`): - - Creates RBAC permissions (ServiceAccount + ClusterRoleBinding) - - Runs a Kubernetes Job with the zenko-setup container - - Configures buckets, accounts, endpoints, workflows, TLS, etc. - - Waits for Zenko to stabilize - -2. **Test Script** (`./run-tests.sh`): - - Verifies Zenko is deployed and ready - - Runs test containers as Kubernetes Jobs - - Streams logs and reports results - -Both scripts use Kubernetes Jobs in the cluster - no local Docker required. - -## Examples - -```bash -# Setup development environment -export NAMESPACE="dev-env" -export INSTANCE_ID="my-zenko" -./setup-tests.sh ~/.kube/dev-config - -# Run CTST tests -./run-tests.sh ~/.kube/dev-config ctst - -# Run with custom metadata deployment -export GIT_ACCESS_TOKEN="ghp_xxx" -export METADATA_NAMESPACE="s3c" -./setup-tests.sh ~/.kube/prod-config - -# Skip TLS setup -./setup-tests.sh ~/.kube/config --no-tls - -# Setup for remote cluster with custom timeout -export JOB_TIMEOUT="3600" # 1 hour -./setup-tests.sh ~/.kube/remote-cluster-config -./run-tests.sh ~/.kube/remote-cluster-config e2e -``` - -## Troubleshooting - -### Job Failed or Timed Out -The scripts now automatically show detailed logs and status when jobs fail, including: -- Job status and description -- Pod status and details -- Complete job logs - -```bash -# Check job status manually if needed -kubectl get jobs -n ${NAMESPACE} -l app=zenko-setup - -# Get job logs manually if needed -kubectl logs job/zenko-setup-123456 -n ${NAMESPACE} - -# Delete failed jobs -kubectl delete jobs -n ${NAMESPACE} -l app=zenko-setup -``` - -### RBAC Issues -```bash -# Verify service account exists -kubectl get serviceaccount zenko-setup -n ${NAMESPACE} - -# Check cluster role binding -kubectl get clusterrolebinding zenko-setup -``` - -### Remote Cluster Setup -For remote clusters, ensure: -- Your kubeconfig has proper credentials -- Network connectivity to the cluster -- Sufficient RBAC permissions in the target namespace - -The scripts will automatically create the needed ServiceAccount and ClusterRoleBinding. \ No newline at end of file diff --git a/tests/@setup/eslint.config.mjs b/tests/@setup/eslint.config.mjs index 517b7bbe41..566d8ae19d 100644 --- a/tests/@setup/eslint.config.mjs +++ b/tests/@setup/eslint.config.mjs @@ -20,8 +20,6 @@ export default tseslint.config( includeIgnoreFile(gitignorePath), { rules: { - // CucumberJS steps start with an uppercase - 'new-cap': 'off', '@typescript-eslint/no-explicit-any': 'off', }, }, diff --git a/tests/@setup/run-tests.sh b/tests/@setup/run-tests.sh index 84e2bc11b2..43bdbfb40f 100755 --- a/tests/@setup/run-tests.sh +++ b/tests/@setup/run-tests.sh @@ -1,87 +1,75 @@ #!/bin/bash set -euo pipefail -# Simple script to run tests against a Zenko cluster -# Usage: ./run-tests.sh [path-to-kubeconfig] [test-type] +NAMESPACE="${NAMESPACE:-default}" +INSTANCE_ID="${INSTANCE_ID:-end2end}" +SUBDOMAIN="${SUBDOMAIN:-zenko.local}" +E2E_IMAGE="${E2E_IMAGE:-ghcr.io/scality/zenko/zenko-e2e:latest}" +E2E_CTST_IMAGE="${E2E_CTST_IMAGE:-ghcr.io/scality/zenko/zenko-e2e-ctst:latest}" +JOB_TIMEOUT="${JOB_TIMEOUT:-3600}" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -KUBECONFIG_FILE="${1:-$HOME/.kube/config}" -TEST_TYPE="${2:-ctst}" # ctst, e2e, smoke, etc. +PARALLEL_RUNS="${PARALLEL_RUNS:-$(( ( $(nproc || echo 2) + 1 ) / 2 ))}" +JUNIT_REPORT_PATH="${JUNIT_REPORT_PATH:-/reports/ctst-junit.xml}" -# For CTST, capture additional arguments (like --tags @PRA) -shift 2 || true -ADDITIONAL_ARGS=("$@") +MANAGED_BY_LABEL="zenko-run-tests-script" +CLUSTER_ROLE_BINDING_NAME="ctst-cluster-admin-for-${NAMESPACE}" -# Default environment variables - modify these as needed -export NAMESPACE="${NAMESPACE:-default}" -export INSTANCE_ID="${INSTANCE_ID:-end2end}" -export SUBDOMAIN="${SUBDOMAIN:-zenko.local}" -export E2E_IMAGE="${E2E_IMAGE:-ghcr.io/scality/zenko/zenko-e2e:latest}" -export E2E_CTST_IMAGE="${E2E_CTST_IMAGE:-ghcr.io/scality/zenko/zenko-e2e-ctst:latest}" -export JOB_TIMEOUT="${JOB_TIMEOUT:-3600}" +usage() { + cat < Required. The type of test to run (e2e, smoke, ctst). + --kubeconfig Path to the kubeconfig file. Defaults to ~/.kube/config. + --cleanup Remove all resources created by this script and exit. + --help Display this help message and exit. -# CTST-specific environment variables -export PARALLEL_RUNS="${PARALLEL_RUNS:-$(( ( $(nproc) + 1 ) / 2 ))}" -export RETRIES="${RETRIES:-3}" -export JUNIT_REPORT_PATH="${JUNIT_REPORT_PATH:-ctst-junit.xml}" -export DR_SUBDOMAIN="${DR_SUBDOMAIN:-dr.zenko.local}" -export AZURE_ACCOUNT_NAME="${AZURE_ACCOUNT_NAME:-devstoreaccount1}" -export AZURE_SECRET_KEY="${AZURE_SECRET_KEY:-Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==}" -export AZURE_ARCHIVE_BUCKET_NAME="${AZURE_ARCHIVE_BUCKET_NAME:-archive-container}" -export AZURE_ARCHIVE_BUCKET_NAME_2="${AZURE_ARCHIVE_BUCKET_NAME_2:-archive-container-2}" -export AZURE_ARCHIVE_QUEUE_NAME="${AZURE_ARCHIVE_QUEUE_NAME:-archive-queue}" -export AZURE_BACKEND_ENDPOINT="${AZURE_BACKEND_ENDPOINT:-}" -export AZURE_BACKEND_QUEUE_ENDPOINT="${AZURE_BACKEND_QUEUE_ENDPOINT:-}" +ADDITIONAL_TEST_ARGS: + Any arguments placed after '--' will be passed directly to the test command. + Example for ctst: -- --tags @PRA --tags ~@Flaky +EOF + exit 1 +} -echo "=== Zenko Test Execution ===" -echo "Kubeconfig: ${KUBECONFIG_FILE}" -echo "Test Type: ${TEST_TYPE}" -echo "Namespace: ${NAMESPACE}" -echo "Instance ID: ${INSTANCE_ID}" -echo "Subdomain: ${SUBDOMAIN}" -echo +check_deps() { + echo "Checking for required dependencies..." + command -v kubectl >/dev/null || { echo "Error: kubectl is not installed. Please install it to continue." >&2; exit 1; } + command -v jq >/dev/null || { echo "Error: jq is not installed. Please install it to continue." >&2; exit 1; } + echo "All dependencies are satisfied." +} -# Verify kubeconfig exists and is accessible -if [[ ! -f "${KUBECONFIG_FILE}" ]]; then - echo "Error: Kubeconfig file not found: ${KUBECONFIG_FILE}" - exit 1 -fi +cleanup() { + echo "Starting cleanup of script-managed resources..." + echo "Deleting ClusterRoleBinding '${CLUSTER_ROLE_BINDING_NAME}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" delete clusterrolebinding "${CLUSTER_ROLE_BINDING_NAME}" --ignore-not-found=true -# Test cluster connectivity -echo "Testing cluster connectivity..." -if ! kubectl --kubeconfig="${KUBECONFIG_FILE}" cluster-info >/dev/null 2>&1; then - echo "Error: Cannot connect to Kubernetes cluster" - exit 1 -fi -echo "Connected to cluster" + echo "Deleting test Jobs in namespace '${NAMESPACE}' with label 'managed-by=${MANAGED_BY_LABEL}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" delete job -n "${NAMESPACE}" -l "managed-by=${MANAGED_BY_LABEL}" --ignore-not-found=true + echo "Cleanup complete." +} -# Check if Zenko is deployed and ready -echo "Checking Zenko deployment..." -if ! kubectl --kubeconfig="${KUBECONFIG_FILE}" get zenko "${INSTANCE_ID}" -n "${NAMESPACE}" >/dev/null 2>&1; then - echo "Error: Zenko instance '${INSTANCE_ID}' not found in namespace '${NAMESPACE}'" - echo "Please run setup first: ./setup-tests.sh ${KUBECONFIG_FILE}" - exit 1 -fi -echo "Zenko instance found" +run_checks() { + echo "Performing prerequisite checks..." + [[ -f "${KUBECONFIG_FILE}" ]] || { echo "Error: Kubeconfig file not found: ${KUBECONFIG_FILE}" >&2; exit 1; } + + echo "Testing cluster connectivity..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" cluster-info >/dev/null || { echo "Error: Cannot connect to Kubernetes cluster." >&2; exit 1; } + + echo "Checking for Zenko instance '${INSTANCE_ID}' in namespace '${NAMESPACE}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" get zenko "${INSTANCE_ID}" -n "${NAMESPACE}" >/dev/null || \ + { echo "Error: Zenko instance not found. Please ensure Zenko is deployed." >&2; exit 1; } + echo "All checks passed." +} -# Handle CTST-specific setup -if [[ "${TEST_TYPE}" == "ctst" ]]; then - echo "Setting up CTST cluster-admin permissions..." +setup_ctst_permissions() { + echo "Applying CTST cluster-admin permissions for ServiceAccount 'default' in namespace '${NAMESPACE}'..." cat </dev/null | grep -Po 'VERSION="\K[^"]*' || echo "unknown") - - # Build CTST world parameters - WORLD_PARAMETERS=$(jq -c --null-input \ - --arg namespace "${NAMESPACE}" \ - --arg subdomain "${SUBDOMAIN}" \ - --arg dr_subdomain "${DR_SUBDOMAIN}" \ - --arg keycloak_username "${OIDC_USERNAME}" \ - --arg keycloak_password "${OIDC_PASSWORD}" \ - --arg keycloak_host "${OIDC_HOST}" \ - --arg keycloak_realm "${OIDC_REALM}" \ - --arg keycloak_client_id "${OIDC_CLIENT_ID}" \ - --arg azure_account_name "${AZURE_ACCOUNT_NAME}" \ - --arg azure_account_key "${AZURE_SECRET_KEY}" \ - --arg azure_archive_container "${AZURE_ARCHIVE_BUCKET_NAME}" \ - --arg azure_archive_container2 "${AZURE_ARCHIVE_BUCKET_NAME_2}" \ - --arg azure_archive_queue "${AZURE_ARCHIVE_QUEUE_NAME}" \ - '{ - "Namespace": $namespace, - "subdomain": $subdomain, - "DRSubdomain": $dr_subdomain, - "KeycloakUsername": $keycloak_username, - "KeycloakPassword": $keycloak_password, - "KeycloakHost": $keycloak_host, - "KeycloakRealm": $keycloak_realm, - "KeycloakClientId": $keycloak_client_id, - "AzureAccountName": $azure_account_name, - "AzureAccountKey": $azure_account_key, - "AzureArchiveContainer": $azure_archive_container, - "AzureArchiveContainer2": $azure_archive_container2, - "AzureArchiveQueue": $azure_archive_queue - }') -fi +# Converts a bash array into a YAML sequence for the Job manifest. +# Usage: array_to_yaml_list "my_array[@]" +array_to_yaml_list() { + local -n arr=$1 + for item in "${arr[@]}"; do + echo " - \"${item}\"" + done +} -# Select test image and command based on test type -case "${TEST_TYPE}" in - ctst) - TEST_IMAGE="${E2E_CTST_IMAGE}" - # CTST uses a custom command with world parameters - TEST_COMMAND=("./run" "premerge" "${WORLD_PARAMETERS}" "--parallel" "${PARALLEL_RUNS}" "--retry" "${RETRIES}" "--retry-tag-filter" "@Flaky" "--format" "junit:${JUNIT_REPORT_PATH}") - # Add any additional arguments passed to the script - TEST_COMMAND+=("${ADDITIONAL_ARGS[@]}") - ;; - e2e) - TEST_IMAGE="${E2E_IMAGE}" - TEST_COMMAND=("npm" "run" "test:e2e") - ;; - smoke) - TEST_IMAGE="${E2E_IMAGE}" - TEST_COMMAND=("npm" "run" "test:smoke") - ;; - *) - echo "Error: Unknown test type '${TEST_TYPE}'" - echo "Available types: ctst, e2e, smoke" - exit 1 - ;; -esac - -echo "Test Image: ${TEST_IMAGE}" -echo "Test Command: ${TEST_COMMAND[*]}" -echo +create_job() { + local test_type="$1" + local job_name="zenko-${test_type}-test-$(date +%s)" + local test_image="" + local -a test_command + local env_vars_yaml="" + local volumes_yaml="" + local volume_mounts_yaml="" + + echo "Configuring Job for test type: ${test_type}" -# Create the test Job -JOB_NAME="zenko-test-${TEST_TYPE}-$(date +%s)" -echo "Creating test job: ${JOB_NAME}..." + case "${test_type}" in + ctst) + local version + version=$(grep -Po 'VERSION="\K[^"]*' ../../../VERSION 2>/dev/null || echo "unknown") + + test_image="${E2E_CTST_IMAGE}" + + local world_parameters + world_parameters=$(jq -cn \ + --arg namespace "${NAMESPACE}" \ + --arg subdomain "${SUBDOMAIN}" \ + --arg dr_subdomain "${DR_SUBDOMAIN:-dr.zenko.local}" \ + --arg keycloak_username "${OIDC_USERNAME:-storage_manager}" \ + --arg keycloak_password "${OIDC_PASSWORD:-123}" \ + --arg keycloak_host "${OIDC_HOST:-keycloak.zenko.local}" \ + --arg keycloak_realm "${OIDC_REALM:-zenko}" \ + --arg keycloak_client_id "${OIDC_CLIENT_ID:-zenko-ui}" \ + '{ "Namespace": $namespace, "subdomain": $subdomain, "DRSubdomain": $dr_subdomain, "KeycloakUsername": $keycloak_username, "KeycloakPassword": $keycloak_password, "KeycloakHost": $keycloak_host, "KeycloakRealm": $keycloak_realm, "KeycloakClientId": $keycloak_client_id }') -# Convert test command array to YAML format for kubectl -TEST_COMMAND_YAML="" -for cmd in "${TEST_COMMAND[@]}"; do - TEST_COMMAND_YAML+=" - \"${cmd}\"\\n" -done + test_command=( + "./run" "premerge" "${world_parameters}" + "--parallel" "${PARALLEL_RUNS}" + "--retry" "${RETRIES:-3}" + "--retry-tag-filter" "@Flaky" + "--format" "junit:${JUNIT_REPORT_PATH}" + ) + test_command+=("${ADDITIONAL_ARGS[@]}") -# Build the Job manifest based on test type -if [[ "${TEST_TYPE}" == "ctst" ]]; then - cat <&2 + exit 1 + ;; + esac + + local command_yaml + command_yaml=$(array_to_yaml_list test_command) + + echo "Creating test job: ${job_name}" + echo "Test Image: ${test_image}" + echo "Test Command: ${test_command[*]}" + cat <&2 + exit 1 + fi +} + +main() { + KUBECONFIG_FILE="${HOME}/.kube/config" + TEST_TYPE="" + ACTION="run" + ADDITIONAL_ARGS=() + + while [[ $# -gt 0 ]]; do + case $1 in + --type) + TEST_TYPE="$2" + shift 2 + ;; + --kubeconfig) + KUBECONFIG_FILE="$2" + shift 2 + ;; + --cleanup) + ACTION="cleanup" + shift 1 + ;; + --help) + usage + ;; + --) + shift + ADDITIONAL_ARGS=("$@") + break + ;; + *) + echo "Error: Unknown option: $1" >&2 + exit 1 + ;; + esac + done + + if [[ "${ACTION}" == "cleanup" ]]; then + cleanup + exit 0 + fi + + if [[ -z "${TEST_TYPE}" ]]; then + echo "Error: Test type is required. Use --type ." >&2 + exit 1 + fi + + check_deps + run_checks + + if [[ "${TEST_TYPE}" == "ctst" ]]; then + setup_ctst_permissions + fi + + create_job "${TEST_TYPE}" +} + +main "$@" diff --git a/tests/@setup/setup-tests.sh b/tests/@setup/setup-tests.sh index 53384dfb15..d5e928130b 100755 --- a/tests/@setup/setup-tests.sh +++ b/tests/@setup/setup-tests.sh @@ -1,157 +1,150 @@ #!/bin/bash -set -euox pipefail - -# Simple script to setup test environment on any Zenko cluster -# Usage: ./setup-tests.sh [path-to-kubeconfig] [additional-options] - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -KUBECONFIG_FILE="${1:-$HOME/.kube/config}" -shift || true # Remove first argument, keep rest as additional options - -# Default environment variables - modify these as needed -export NAMESPACE="${NAMESPACE:-default}" -export INSTANCE_ID="${INSTANCE_ID:-end2end}" -export SUBDOMAIN="${SUBDOMAIN:-zenko.local}" -export SETUP_IMAGE="${SETUP_IMAGE:-ghcr.io/scality/zenko-setup:latest}" -export LOG_LEVEL="${LOG_LEVEL:-info}" -export METADATA_NAMESPACE="${METADATA_NAMESPACE:-metadata}" -export JOB_TIMEOUT="${JOB_TIMEOUT:-1800}" - -# Optional environment variables (set them before running this script if needed) -# export GIT_ACCESS_TOKEN="your-token-here" - -echo "=== Zenko Test Environment Setup ===" -echo "Kubeconfig: ${KUBECONFIG_FILE}" -echo "Namespace: ${NAMESPACE}" -echo "Instance ID: ${INSTANCE_ID}" -echo "Subdomain: ${SUBDOMAIN}" -echo "Setup Image: ${SETUP_IMAGE}" -echo "Additional options: $*" -echo - -# Verify kubeconfig exists and is accessible -if [[ ! -f "${KUBECONFIG_FILE}" ]]; then - echo "Error: Kubeconfig file not found: ${KUBECONFIG_FILE}" +set -euo pipefail + +NAMESPACE="${NAMESPACE:-default}" +INSTANCE_ID="${INSTANCE_ID:-end2end}" +SUBDOMAIN="${SUBDOMAIN:-zenko.local}" +SETUP_IMAGE="${SETUP_IMAGE:-ghcr.io/scality/zenko-setup:latest}" +LOG_LEVEL="${LOG_LEVEL:-info}" +METADATA_NAMESPACE="${METADATA_NAMESPACE:-metadata}" +JOB_TIMEOUT="${JOB_TIMEOUT:-1800}" +KUBECONFIG_FILE="${KUBECONFIG_FILE:-${HOME}/.kube/config}" + +SERVICE_ACCOUNT_NAME="zenko-setup" +CLUSTER_ROLE_NAME="zenko-setup-role" +CLUSTER_ROLE_BINDING_NAME="zenko-setup-binding" +MANAGED_BY_LABEL="zenko-setup-script" + +usage() { + cat < Path to the kubeconfig file. Defaults to ~/.kube/config. + --cleanup Remove all resources created by this script and exit. + --help Display this help message and exit. + +ADDITIONAL_SETUP_ARGS: + Any arguments placed after '--' will be passed directly to the setup container. +EOF exit 1 -fi +} -# Test cluster connectivity -echo "Testing cluster connectivity..." -if ! kubectl --kubeconfig="${KUBECONFIG_FILE}" cluster-info >/dev/null 2>&1; then - echo "Error: Cannot connect to Kubernetes cluster" - echo "Please check your kubeconfig file: ${KUBECONFIG_FILE}" - exit 1 -fi -echo "Connected to cluster" +check_deps() { + command -v kubectl >/dev/null || { echo "Error: kubectl is not installed. Please install it to continue." >&2; exit 1; } +} + +array_to_yaml_list() { + local -n arr=$1 + for item in "${arr[@]}"; do + echo " - \"${item}\"" + done +} + +cleanup() { + echo "Starting cleanup of all resources managed by this script..." + + echo "Deleting ClusterRoleBinding '${CLUSTER_ROLE_BINDING_NAME}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" delete clusterrolebinding "${CLUSTER_ROLE_BINDING_NAME}" --ignore-not-found=true + + echo "Deleting ClusterRole '${CLUSTER_ROLE_NAME}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" delete clusterrole "${CLUSTER_ROLE_NAME}" --ignore-not-found=true + + echo "Deleting ServiceAccount '${SERVICE_ACCOUNT_NAME}' in namespace '${NAMESPACE}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" delete serviceaccount "${SERVICE_ACCOUNT_NAME}" -n "${NAMESPACE}" --ignore-not-found=true + + echo "Deleting setup Jobs in namespace '${NAMESPACE}'..." + kubectl --kubeconfig="${KUBECONFIG_FILE}" delete job -n "${NAMESPACE}" -l "managed-by=${MANAGED_BY_LABEL}" --ignore-not-found=true + + echo "Cleanup complete." +} -# Setup RBAC if needed -echo "Setting up RBAC permissions..." -cat < rbac.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: zenko-setup + name: ${SERVICE_ACCOUNT_NAME} namespace: ${NAMESPACE} + labels: + managed-by: ${MANAGED_BY_LABEL} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: zenko-setup + name: ${CLUSTER_ROLE_NAME} + labels: + managed-by: ${MANAGED_BY_LABEL} rules: -# Core API permissions -- apiGroups: [""] - resources: ["*"] - verbs: ["*"] -# Apps API permissions -- apiGroups: ["apps"] - resources: ["*"] - verbs: ["*"] -# Batch API permissions -- apiGroups: ["batch"] - resources: ["*"] - verbs: ["*"] -# RBAC permissions -- apiGroups: ["rbac.authorization.k8s.io"] - resources: ["*"] - verbs: ["*"] -# Custom resources (Zenko CRDs) -- apiGroups: ["zenko.io"] - resources: ["*"] - verbs: ["*"] -# Networking -- apiGroups: ["networking.k8s.io"] +- apiGroups: ["", "apps", "batch", "rbac.authorization.k8s.io", "zenko.io", "networking.k8s.io"] resources: ["*"] verbs: ["*"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: zenko-setup + name: ${CLUSTER_ROLE_BINDING_NAME} + labels: + managed-by: ${MANAGED_BY_LABEL} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: zenko-setup + name: ${CLUSTER_ROLE_NAME} subjects: - kind: ServiceAccount - name: zenko-setup + name: ${SERVICE_ACCOUNT_NAME} namespace: ${NAMESPACE} EOF + cat rbac.yaml + kubectl --kubeconfig="${KUBECONFIG_FILE}" apply -f rbac.yaml +} + +create_job() { + local -n additional_args_ref=$1 + local job_name="zenko-setup-$(date +%s)" + + local -a setup_args + setup_args=( + "all" + "--namespace" "${NAMESPACE}" + "--subdomain" "${SUBDOMAIN}" + "--instance-id" "${INSTANCE_ID}" + "--metadata-namespace" "${METADATA_NAMESPACE}" + ) + if [[ -n "${GIT_ACCESS_TOKEN:-}" ]]; then + setup_args+=("--git-access-token" "${GIT_ACCESS_TOKEN}") + fi + setup_args+=("${additional_args_ref[@]}") + + local setup_args_yaml + setup_args_yaml=$(array_to_yaml_list setup_args) -# Build the setup command arguments -SETUP_ARGS=("all") -SETUP_ARGS+=("--namespace" "${NAMESPACE}") -SETUP_ARGS+=("--subdomain" "${SUBDOMAIN}") - -# Add instance ID if provided -if [[ -n "${INSTANCE_ID}" ]]; then - SETUP_ARGS+=("--instance-id" "${INSTANCE_ID}") -fi - -# Add git access token if provided -if [[ -n "${GIT_ACCESS_TOKEN:-}" ]]; then - SETUP_ARGS+=("--git-access-token" "${GIT_ACCESS_TOKEN}") -fi - -# Add metadata namespace -SETUP_ARGS+=("--metadata-namespace" "${METADATA_NAMESPACE}") - -# Add any additional command line arguments passed to this script -SETUP_ARGS+=("$@") - -# Convert args array to YAML format for kubectl -SETUP_ARGS_YAML="" -for arg in "${SETUP_ARGS[@]}"; do - SETUP_ARGS_YAML+=" - \"${arg}\" -" -done - -# Create the Job -JOB_NAME="zenko-setup-$(date +%s)" -echo "Creating setup job: ${JOB_NAME}..." -echo "Setup args: ${SETUP_ARGS[*]}" + echo "Creating setup job: ${job_name}..." + echo "Setup container args: ${setup_args[*]}" -cat < job.yaml apiVersion: batch/v1 kind: Job metadata: - name: ${JOB_NAME} + name: ${job_name} namespace: ${NAMESPACE} labels: app: zenko-setup - managed-by: local-script + managed-by: ${MANAGED_BY_LABEL} spec: ttlSecondsAfterFinished: 300 backoffLimit: 1 activeDeadlineSeconds: ${JOB_TIMEOUT} template: spec: - serviceAccountName: zenko-setup + serviceAccountName: ${SERVICE_ACCOUNT_NAME} restartPolicy: Never containers: - name: setup image: ${SETUP_IMAGE} args: -${SETUP_ARGS_YAML} +${setup_args_yaml} env: - name: LOG_LEVEL value: "${LOG_LEVEL}" @@ -165,40 +158,71 @@ ${SETUP_ARGS_YAML} memory: "1Gi" cpu: "500m" EOF - -# Wait for job completion -echo "Waiting for job ${JOB_NAME} to complete (timeout: ${JOB_TIMEOUT}s)..." -if kubectl --kubeconfig="${KUBECONFIG_FILE}" wait --for=condition=complete "job/${JOB_NAME}" -n "${NAMESPACE}" --timeout="${JOB_TIMEOUT}s"; then - echo - echo "Setup completed successfully!" - echo - echo "Your Zenko test environment is ready." - echo "You can now run tests with: ./run-tests.sh ${KUBECONFIG_FILE}" - echo - - # Show logs for debugging even on success - echo "Setup job logs:" - kubectl --kubeconfig="${KUBECONFIG_FILE}" logs "job/${JOB_NAME}" -n "${NAMESPACE}" --tail=50 || true - echo -else - echo - echo "Setup job failed or timed out!" - echo + cat job.yaml + kubectl --kubeconfig="${KUBECONFIG_FILE}" apply -f job.yaml + + echo "Waiting for job '${job_name}' to complete (timeout: ${JOB_TIMEOUT}s)..." + if kubectl --kubeconfig="${KUBECONFIG_FILE}" wait --for=condition=complete "job/${job_name}" -n "${NAMESPACE}" --timeout="${JOB_TIMEOUT}s"; then + echo "Setup completed successfully." + echo "Setup job logs (last 50 lines):" + kubectl --kubeconfig="${KUBECONFIG_FILE}" logs "job/${job_name}" -n "${NAMESPACE}" --tail=50 || true + else + echo "Error: Setup job failed or timed out." >&2 + echo "--- Job Description ---" + kubectl --kubeconfig="${KUBECONFIG_FILE}" describe "job/${job_name}" -n "${NAMESPACE}" || true + echo "--- Pod Status ---" + kubectl --kubeconfig="${KUBECONFIG_FILE}" get pods -l job-name="${job_name}" -n "${NAMESPACE}" -o wide || true + echo "--- Full Job Logs ---" + kubectl --kubeconfig="${KUBECONFIG_FILE}" logs "job/${job_name}" -n "${NAMESPACE}" || true + exit 1 + fi +} + +main() { + ACTION="run" + ADDITIONAL_ARGS=() + + while [[ $# -gt 0 ]]; do + case $1 in + --kubeconfig) + KUBECONFIG_FILE="$2" + shift 2 + ;; + --cleanup) + ACTION="cleanup" + shift 1 + ;; + --help) + usage + ;; + --) + shift + ADDITIONAL_ARGS=("$@") + break + ;; + *) + echo "Error: Unknown option: $1" >&2 + usage + ;; + esac + done + + check_deps + + if [[ "${ACTION}" == "cleanup" ]]; then + cleanup + exit 0 + fi - # Get detailed job status - echo "Job status:" - kubectl --kubeconfig="${KUBECONFIG_FILE}" describe "job/${JOB_NAME}" -n "${NAMESPACE}" || true - echo - - # Get pod status and logs - echo "Pod status:" - kubectl --kubeconfig="${KUBECONFIG_FILE}" get pods -l job-name="${JOB_NAME}" -n "${NAMESPACE}" -o wide || true - echo + echo "Starting Zenko test environment setup..." + echo "Using kubeconfig: ${KUBECONFIG_FILE}" + + cleanup - echo "Setup job logs:" - kubectl --kubeconfig="${KUBECONFIG_FILE}" logs "job/${JOB_NAME}" -n "${NAMESPACE}" || true - echo + apply_rbac + create_job ADDITIONAL_ARGS - echo "Setup failed!" - exit 1 -fi \ No newline at end of file + echo "Zenko test environment is ready." +} + +main "$@" diff --git a/tests/@setup/src/utils/k8s.ts b/tests/@setup/src/utils/k8s.ts index d487772091..0ed733e692 100644 --- a/tests/@setup/src/utils/k8s.ts +++ b/tests/@setup/src/utils/k8s.ts @@ -21,13 +21,13 @@ export class KubernetesClient { // Try in-cluster config first (when running as a Pod) this.kc.loadFromCluster(); logger.debug('Using in-cluster Kubernetes configuration'); - } catch (clusterError) { + } catch { try { // Fallback to default (local kubeconfig) this.kc.loadFromDefault(); logger.debug('Using default Kubernetes configuration'); } catch (defaultError) { - logger.error('Failed to load Kubernetes configuration. Tried in-cluster and default configurations.'); + logger.error('Failed to load Kubernetes configuration.'); throw defaultError; } } From ee1a9a1888232e2b3694ab74204e9ace80522b05 Mon Sep 17 00:00:00 2001 From: williamlardier Date: Tue, 16 Sep 2025 09:31:03 +0200 Subject: [PATCH 13/14] run new scripts --- .github/workflows/end2end.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index cb90ad1535..6e9ea51e53 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -513,13 +513,13 @@ jobs: INSTANCE_ID: "end2end-pra" SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} - run: ./setup-tests.sh ~/.kube/config + run: ./setup-tests.sh --kubeconfig ~/.kube/config working-directory: ./tests/@setup - name: Configure hosts file run: bash configure-hosts.sh working-directory: ./.github/scripts/end2end - name: Run CTST end to end tests - run: ./run-tests.sh ~/.kube/config ctst --tags @PRA + run: ./run-tests.sh --kubeconfig ~/.kube/config --type ctst -- --tags @PRA working-directory: ./tests/@setup - name: Debug wait uses: ./.github/actions/debug-wait @@ -571,7 +571,7 @@ jobs: env: SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} - run: ./setup-tests.sh ~/.kube/config + run: ./setup-tests.sh --kubeconfig ~/.kube/config working-directory: ./tests/@setup - name: Run init CI test run: bash run-e2e-test.sh "end2end" ${E2E_IMAGE_NAME}:${E2E_IMAGE_TAG} "end2end" "default" @@ -639,9 +639,9 @@ jobs: GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} run: | if [[ "${{ env.ENABLE_RING_TESTS }}" == "true" ]]; then - ./setup-tests.sh ~/.kube/config setup --metadata + ./setup-tests.sh --kubeconfig ~/.kube/config -- setup --metadata else - ./setup-tests.sh ~/.kube/config setup --rbac --dns --buckets --locations --accounts --endpoints --workflows --tls + ./setup-tests.sh --kubeconfig ~/.kube/config -- setup --rbac --dns --buckets --locations --accounts --endpoints --workflows --tls fi working-directory: ./tests/@setup - name: Run backbeat end to end tests @@ -701,13 +701,13 @@ jobs: env: SETUP_IMAGE: ${{ needs.build-setup-image.outputs.image }} GIT_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} - run: ./setup-tests.sh ~/.kube/config + run: ./setup-tests.sh --kubeconfig ~/.kube/config working-directory: ./tests/@setup - name: Configure hosts file run: bash configure-hosts.sh working-directory: ./.github/scripts/end2end - name: Run CTST end to end tests - run: ./run-tests.sh ~/.kube/config ctst --tags 'not @PRA' + run: ./run-tests.sh --kubeconfig ~/.kube/config --type ctst -- --tags 'not @PRA' working-directory: ./tests/@setup - name: Debug wait uses: ./.github/actions/debug-wait From fcaba1155d0d5175c65282c1aec3795509b8b4dd Mon Sep 17 00:00:00 2001 From: williamlardier Date: Tue, 16 Sep 2025 09:50:41 +0200 Subject: [PATCH 14/14] fix metadata tag --- .github/actions/build-setup-image/action.yaml | 2 +- .github/workflows/end2end.yaml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/actions/build-setup-image/action.yaml b/.github/actions/build-setup-image/action.yaml index 00a8b4054e..3bd7532dbe 100644 --- a/.github/actions/build-setup-image/action.yaml +++ b/.github/actions/build-setup-image/action.yaml @@ -22,7 +22,7 @@ inputs: outputs: image: description: 'Full image name with tag' - value: ${{ steps.meta.outputs.tags }} + value: ${{ inputs.registry }}/${{ github.repository_owner }}/${{ inputs.image-name }}:${{ inputs.tag }} digest: description: 'Image digest' value: ${{ steps.build.outputs.digest }} diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 6e9ea51e53..5610a0382a 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -505,9 +505,6 @@ jobs: env: ZENKO_MONGODB_DATABASE: pradb working-directory: ./.github/scripts/end2end - - name: Configure PRA test users (handled by CTST) - shell: bash - run: echo "PRA users configured by CTST setup" - name: Setup PRA test environment env: INSTANCE_ID: "end2end-pra"