diff --git a/Dockerfile b/Dockerfile index 4124f69fc8..15e6dd4ad3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,7 @@ ENV PYTHON=python3 RUN npm install -g \ node-gyp \ typescript@4.9.5 -COPY package.json yarn.lock /usr/src/app/ +COPY package.json yarn.lock scality-cloudserverclient-v1.0.9.tgz /usr/src/app/ RUN yarn install --production --ignore-optional --frozen-lockfile --ignore-engines --network-concurrency 1 diff --git a/lib/api/apiUtils/object/versioning.js b/lib/api/apiUtils/object/versioning.js index 418224a4a3..a090f82e84 100644 --- a/lib/api/apiUtils/object/versioning.js +++ b/lib/api/apiUtils/object/versioning.js @@ -523,7 +523,6 @@ function restoreMetadata(objMD, metadataStoreParams) { * version id of the null version */ function overwritingVersioning(objMD, metadataStoreParams) { - metadataStoreParams.updateMicroVersionId = true; metadataStoreParams.amzStorageClass = objMD['x-amz-storage-class']; // set correct originOp @@ -566,6 +565,21 @@ function overwritingVersioning(objMD, metadataStoreParams) { return options; } +/** + * @param {Object} objectMD - plain object metadata (not an ObjectMD instance) +*/ +function prepareMetadataForCascadedCrr(objectMD) { + // Bump microVersionId so cascade CRR detects the change as a new revision. + // eslint-disable-next-line no-param-reassign + objectMD.microVersionId = versionIdUtils.generateVersionId( + config.instanceId, config.replicationGroupId); + if (objectMD.replicationInfo) { + // Clear isReplica, as a user modification is no longer purely a replica + // eslint-disable-next-line no-param-reassign + objectMD.replicationInfo.isReplica = false; + } +} + module.exports = { decodeVersionId, getVersionIdResHeader, @@ -577,4 +591,5 @@ module.exports = { preprocessingVersioningDelete, overwritingVersioning, decodeVID, + prepareMetadataForCascadedCrr, }; diff --git a/lib/api/objectDeleteTagging.js b/lib/api/objectDeleteTagging.js index 71115ffe5a..6c42b8573e 100644 --- a/lib/api/objectDeleteTagging.js +++ b/lib/api/objectDeleteTagging.js @@ -1,7 +1,8 @@ const async = require('async'); const { errors } = require('arsenal'); -const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions } +const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions, + prepareMetadataForCascadedCrr } = require('./apiUtils/object/versioning'); const { standardMetadataValidateBucketAndObj } = require('../metadata/metadataUtils'); @@ -85,6 +86,7 @@ function objectDeleteTagging(authInfo, request, log, callback) { } // eslint-disable-next-line no-param-reassign objectMD.originOp = 's3:ObjectTagging:Delete'; + prepareMetadataForCascadedCrr(objectMD); metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, err => next(err, bucket, objectMD)); diff --git a/lib/api/objectPutLegalHold.js b/lib/api/objectPutLegalHold.js index c16f2c84e8..fd9b40fff8 100644 --- a/lib/api/objectPutLegalHold.js +++ b/lib/api/objectPutLegalHold.js @@ -2,7 +2,8 @@ const async = require('async'); const { errors, errorInstances, s3middleware } = require('arsenal'); const collectCorsHeaders = require('../utilities/collectCorsHeaders'); -const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions } = +const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions, + prepareMetadataForCascadedCrr } = require('./apiUtils/object/versioning'); const getReplicationInfo = require('./apiUtils/object/getReplicationInfo'); const metadata = require('../metadata/wrapper'); @@ -96,6 +97,7 @@ function objectPutLegalHold(authInfo, request, log, callback) { } // eslint-disable-next-line no-param-reassign objectMD.originOp = 's3:ObjectLegalHold:Put'; + prepareMetadataForCascadedCrr(objectMD); metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, err => next(err, bucket, objectMD)); }, diff --git a/lib/api/objectPutRetention.js b/lib/api/objectPutRetention.js index 6a7a2c8441..b5a1e6fa92 100644 --- a/lib/api/objectPutRetention.js +++ b/lib/api/objectPutRetention.js @@ -1,7 +1,8 @@ const async = require('async'); const { errors, errorInstances, s3middleware } = require('arsenal'); -const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions } = +const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions, + prepareMetadataForCascadedCrr } = require('./apiUtils/object/versioning'); const { ObjectLockInfo, hasGovernanceBypassHeader } = require('./apiUtils/object/objectLockHelpers'); @@ -119,6 +120,7 @@ function objectPutRetention(authInfo, request, log, callback) { objectMD.replicationInfo, replicationInfo); } objectMD.originOp = 's3:ObjectRetention:Put'; + prepareMetadataForCascadedCrr(objectMD); /* eslint-enable no-param-reassign */ metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, err => next(err, bucket, objectMD)); diff --git a/lib/api/objectPutTagging.js b/lib/api/objectPutTagging.js index ef23dcf64d..b5d25a4d20 100644 --- a/lib/api/objectPutTagging.js +++ b/lib/api/objectPutTagging.js @@ -1,7 +1,8 @@ const async = require('async'); const { errors, s3middleware } = require('arsenal'); -const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions } = +const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions, + prepareMetadataForCascadedCrr } = require('./apiUtils/object/versioning'); const { standardMetadataValidateBucketAndObj } = require('../metadata/metadataUtils'); @@ -89,6 +90,7 @@ function objectPutTagging(authInfo, request, log, callback) { } // eslint-disable-next-line no-param-reassign objectMD.originOp = 's3:ObjectTagging:Put'; + prepareMetadataForCascadedCrr(objectMD); metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, err => next(err, bucket, objectMD)); diff --git a/lib/metadata/acl.js b/lib/metadata/acl.js index f48ab7aa42..41e47b56f2 100644 --- a/lib/metadata/acl.js +++ b/lib/metadata/acl.js @@ -1,6 +1,7 @@ const { errors } = require('arsenal'); const getReplicationInfo = require('../api/apiUtils/object/getReplicationInfo'); +const { prepareMetadataForCascadedCrr } = require('../api/apiUtils/object/versioning'); const aclUtils = require('../utilities/aclUtils'); const constants = require('../../constants'); const metadata = require('../metadata/wrapper'); @@ -56,7 +57,7 @@ const acl = { ...replicationInfo, }; } - + prepareMetadataForCascadedCrr(objectMD); return metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, cb); } return cb(); diff --git a/lib/routes/routeBackbeat.js b/lib/routes/routeBackbeat.js index 476d3e755d..4147b544cb 100644 --- a/lib/routes/routeBackbeat.js +++ b/lib/routes/routeBackbeat.js @@ -7,7 +7,7 @@ const joi = require('@hapi/joi'); const backbeatProxy = httpProxy.createProxyServer({ ignorePath: true, }); -const { auth, errors, errorInstances, s3middleware, s3routes, models, storage } = +const { auth, errors, errorInstances, s3middleware, s3routes, models, storage, versioning } = require('arsenal'); const { responseJSONBody } = s3routes.routesUtils; @@ -25,6 +25,8 @@ const { dataStore } = require('../api/apiUtils/object/storeObject'); const prepareRequestContexts = require( '../api/apiUtils/authorization/prepareRequestContexts'); const { decodeVersionId } = require('../api/apiUtils/object/versioning'); +const writeContinue = require('../utilities/writeContinue'); +const { decode, encode, checkCrrCascadeEvent } = versioning.VersionID; const locationKeysHaveChanged = require('../api/apiUtils/object/locationKeysHaveChanged'); const { standardMetadataValidateBucketAndObj, @@ -47,6 +49,8 @@ const quotaUtils = require('../api/apiUtils/quotas/quotaUtils'); const { handleAuthorizationResults } = require('../api/api'); const { versioningPreprocessing } = require('../api/apiUtils/object/versioning'); +const getReplicationInfo = require('../api/apiUtils/object/getReplicationInfo'); +const { VersionIdCollisionException, StaleMicroVersionIdException } = require('@scality/cloudserverclient'); const {promisify} = require('util'); const versioningPreprocessingPromised = promisify(versioningPreprocessing); @@ -106,7 +110,7 @@ function _isObjectRequest(req) { ].includes(req.resourceType); } -function _respondWithHeaders(response, payload, extraHeaders, log, callback) { +function _respondWithHeaders(response, payload, extraHeaders, log, callback, statusCode = 200) { let body = ''; if (typeof payload === 'string') { body = payload; @@ -125,10 +129,10 @@ function _respondWithHeaders(response, payload, extraHeaders, log, callback) { // eslint-disable-next-line no-param-reassign response.serverAccessLog.endTurnAroundTime = process.hrtime.bigint(); } - response.writeHead(200, httpHeaders); + response.writeHead(statusCode, httpHeaders); response.end(body, 'utf8', () => { log.end().info('responded with payload', { - httpCode: 200, + httpCode: statusCode, contentLength: Buffer.byteLength(body), }); callback(); @@ -430,6 +434,27 @@ function putData(request, response, bucketInfo, objMd, log, callback) { log.error(errMessage); return callback(errorInstances.BadRequest.customizeDescription(errMessage)); } + + const incomingVersionIdEncoded = request.headers['x-scal-source-version-id']; + const decoded = incomingVersionIdEncoded ? decode(incomingVersionIdEncoded) : null; + const incomingVersionIdDecoded = decoded instanceof Error ? null : decoded; + if (incomingVersionIdDecoded && objMd && objMd.versionId === incomingVersionIdDecoded) { + // Skip the write if data is already at destination for this version id + // Return 409 with the existing microVersionId so backbeat can + // decide if putMetadata is still needed + log.debug('crr cascade putData: version already at destination', { + method: 'putData', + bucketName: request.bucketName, + objectKey: request.objectKey, + hasMicroVersionId: !!objMd.microVersionId, + }); + return _respondWithHeaders(response, + { code: VersionIdCollisionException.name, message: 'version id already at destination' }, + { 'x-scal-micro-version-id': objMd.microVersionId ? encode(objMd.microVersionId) : '' }, + log, callback, 409); + } + + writeContinue(request, response); const context = { bucketName: request.bucketName, owner: canonicalID, @@ -541,6 +566,31 @@ function getCanonicalIdsByAccountId(accountId, log, cb) { } function putMetadata(request, response, bucketInfo, objMd, log, callback) { + const { bucketName, objectKey } = request; + + const encodedMicroVersionId = request.headers['x-scal-micro-version-id']; + const decoded = encodedMicroVersionId ? decode(encodedMicroVersionId) : null; + const incomingRaw = decoded instanceof Error ? null : decoded; + if (incomingRaw) { + const event = checkCrrCascadeEvent(incomingRaw, objMd && objMd.microVersionId); + if (event === 'loop') { + log.debug('crr cascade putMetadata: loop detected, skipping write', { + method: 'putMetadata', bucketName, objectKey, + }); + return _respondWithHeaders(response, {}, + { 'x-scal-replication-loop': 'true' }, log, callback); + } + if (event === 'stale') { + log.debug('crr cascade putMetadata: stale event, rejecting', { + method: 'putMetadata', bucketName, objectKey, + }); + return _respondWithHeaders(response, + { code: StaleMicroVersionIdException.name, message: 'incoming revision is older than destination' }, + {}, log, callback, 409); + } + } + + writeContinue(request, response); return _getRequestPayload(request, (err, payload) => { if (err) { return callback(err); @@ -554,14 +604,15 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { return callback(errors.MalformedPOSTRequest); } - const { headers, bucketName, objectKey } = request; + const { headers } = request; // Destination-side delete-marker replication. // We need the REPLICA status to distinguish from // source-side replication status updates that also carry isDeleteMarker=true. if (omVal.isDeleteMarker && omVal.replicationInfo - && omVal.replicationInfo.status === 'REPLICA' + && (omVal.replicationInfo.isReplica === true + || omVal.replicationInfo.status === 'REPLICA') && request.serverAccessLog) { // eslint-disable-next-line no-param-reassign request.serverAccessLog.replication = true; @@ -575,7 +626,8 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { // URI shape. // The REPLICA status excludes source-side replication-status updates. if (omVal.replicationInfo - && omVal.replicationInfo.status === 'REPLICA' + && (omVal.replicationInfo.isReplica === true + || omVal.replicationInfo.status === 'REPLICA') && (omVal.originOp === 's3:ObjectTagging:Put' || omVal.originOp === 's3:ObjectTagging:Delete') && request.serverAccessLog) { @@ -591,7 +643,8 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { // populates the aclRequired field. // The REPLICA status excludes source-side replication-status updates. if (omVal.replicationInfo - && omVal.replicationInfo.status === 'REPLICA' + && (omVal.replicationInfo.isReplica === true + || omVal.replicationInfo.status === 'REPLICA') && omVal.originOp === 's3:ObjectAcl:Put' && request.serverAccessLog) { // eslint-disable-next-line no-param-reassign @@ -669,7 +722,8 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { // then we want to create a version for the replica object even though // none was provided in the object metadata value. if (omVal.replicationInfo.isNFS) { - const isReplica = omVal.replicationInfo.status === 'REPLICA'; + const isReplica = omVal.replicationInfo.isReplica === true + || omVal.replicationInfo.status === 'REPLICA'; versioning = isReplica; omVal.replicationInfo.isNFS = !isReplica; } @@ -721,6 +775,53 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { options.isNull = isNull; } + // Cascade triggering + // If the bucket receiving this replica has its own CRR rules, set + // status to PENDING so the queue populator here picks it up for the + // next hop. If not, clear the source-side replicationInfo fields + // Always mark isReplica=true. + if (incomingRaw) { + const isMDOnly = headers['x-scal-replication-content'] === 'METADATA'; + const objSize = omVal['content-length'] || 0; + + // These S3-compatible Scality locations are excluded + // as cascade targets because they use the MultiBackend S3 path which + // bypasses the putData/putMetadata routes, so loop detection cannot fire + // on those destinations. + const BLOCKED_LOCATION_TYPES = [ + 'location-scality-ring-s3-v1', + 'location-scality-artesca-s3-v1', + ]; + + const nextReplInfo = getReplicationInfo( + config, objectKey, bucketInfo, isMDOnly, objSize, + null, null, null); + + if (nextReplInfo) { + nextReplInfo.backends = nextReplInfo.backends.filter(b => { + const loc = config.locationConstraints[b.site]; + return !loc || !BLOCKED_LOCATION_TYPES.includes(loc.type); + }); + } + + if (nextReplInfo && nextReplInfo.backends.length > 0) { + omVal.replicationInfo = nextReplInfo; + } else { + omVal.replicationInfo = { + status: '', + backends: [], + content: [], + destination: '', + storageClass: '', + role: '', + storageType: '', + dataStoreVersionId: '', + }; + } + + omVal.replicationInfo.isReplica = true; + } + return async.series([ // Zenko's CRR delegates replacing the account // information to the destination's Cloudserver, as diff --git a/lib/routes/utilities/pushReplicationMetric.js b/lib/routes/utilities/pushReplicationMetric.js index c81027a494..8bec307855 100644 --- a/lib/routes/utilities/pushReplicationMetric.js +++ b/lib/routes/utilities/pushReplicationMetric.js @@ -4,7 +4,7 @@ const { pushMetric } = require('../../utapi/utilities'); function getMetricToPush(prevObjectMD, newObjectMD) { // We only want to update metrics for a destination bucket. - if (newObjectMD.getReplicationStatus() !== 'REPLICA') { + if (!newObjectMD.getReplicationIsReplica()) { return null; } diff --git a/lib/services.js b/lib/services.js index 97116329a4..a8485018d9 100644 --- a/lib/services.js +++ b/lib/services.js @@ -109,9 +109,9 @@ const services = { lastModifiedDate, versioning, versionId, uploadId, tagging, taggingCopy, replicationInfo, defaultRetention, dataStoreName, creationTime, retentionMode, retentionDate, - legalHold, originOp, updateMicroVersionId, archive, oldReplayId, - deleteNullKey, amzStorageClass, overheadField, needOplogUpdate, - restoredEtag, bucketOwnerId } = params; + legalHold, originOp, archive, + oldReplayId, deleteNullKey, amzStorageClass, overheadField, + needOplogUpdate, restoredEtag, bucketOwnerId } = params; log.trace('storing object in metadata'); assert.strictEqual(typeof bucketName, 'string'); const md = new ObjectMD(); @@ -189,10 +189,7 @@ const services = { md.setUploadId(uploadId); options.replayId = uploadId; } - // update microVersionId when overwriting metadata. - if (updateMicroVersionId) { - md.updateMicroVersionId(); - } + md.updateMicroVersionId(config.instanceId, config.replicationGroupId); // update restore if (archive) { md.setAmzStorageClass(amzStorageClass); diff --git a/lib/utilities/collectResponseHeaders.js b/lib/utilities/collectResponseHeaders.js index a754300c62..111ce12f2c 100644 --- a/lib/utilities/collectResponseHeaders.js +++ b/lib/utilities/collectResponseHeaders.js @@ -100,9 +100,13 @@ function collectResponseHeaders(objectMD, corsHeaders, versioningCfg, responseMetaHeaders['x-amz-object-lock-legal-hold'] = objectMD.legalHold ? 'ON' : 'OFF'; } - if (objectMD.replicationInfo && objectMD.replicationInfo.status) { - responseMetaHeaders['x-amz-replication-status'] = - objectMD.replicationInfo.status; + const replInfo = objectMD.replicationInfo; + if (replInfo) { + if (replInfo.isReplica === true || replInfo.status === 'REPLICA') { + responseMetaHeaders['x-amz-replication-status'] = 'REPLICA'; + } else if (replInfo.status) { + responseMetaHeaders['x-amz-replication-status'] = replInfo.status; + } } if (Array.isArray(objectMD?.replicationInfo?.backends)) { objectMD.replicationInfo.backends.forEach(backend => { diff --git a/package.json b/package.json index c1cd9d2b21..211385873e 100644 --- a/package.json +++ b/package.json @@ -19,17 +19,17 @@ }, "homepage": "https://github.com/scality/S3#readme", "dependencies": { + "@aws-crypto/crc32": "^5.2.0", + "@aws-crypto/crc32c": "^5.2.0", "@aws-sdk/client-iam": "^3.930.0", "@aws-sdk/client-s3": "^3.1013.0", "@aws-sdk/client-sts": "^3.930.0", + "@aws-sdk/crc64-nvme-crt": "^3.989.0", "@aws-sdk/credential-providers": "^3.864.0", "@aws-sdk/middleware-retry": "^3.374.0", "@aws-sdk/protocol-http": "^3.374.0", "@aws-sdk/s3-request-presigner": "^3.901.0", "@aws-sdk/signature-v4": "^3.374.0", - "@aws-crypto/crc32": "^5.2.0", - "@aws-crypto/crc32c": "^5.2.0", - "@aws-sdk/crc64-nvme-crt": "^3.989.0", "@azure/storage-blob": "^12.28.0", "@hapi/joi": "^17.1.1", "@opentelemetry/api": "^1.9.0", @@ -37,7 +37,7 @@ "@opentelemetry/instrumentation-ioredis": "~0.64.0", "@opentelemetry/instrumentation-mongodb": "~0.69.0", "@smithy/node-http-handler": "^3.0.0", - "arsenal": "git+https://github.com/scality/Arsenal#8.4.5", + "arsenal": "git+https://github.com/scality/Arsenal#83682d1ae77705877b81414f39aacce4e27e0ceb", "async": "2.6.4", "bucketclient": "scality/bucketclient#8.2.7", "bufferutil": "^4.0.8", @@ -65,11 +65,11 @@ "vaultclient": "scality/vaultclient#8.5.3", "werelogs": "scality/werelogs#8.2.2", "ws": "^8.18.0", + "@scality/cloudserverclient": "./scality-cloudserverclient-v1.0.9.tgz", "xml2js": "^0.6.2" }, "devDependencies": { "@eslint/compat": "^1.2.2", - "@scality/cloudserverclient": "1.0.7", "@scality/eslint-config-scality": "scality/Guidelines#8.3.1", "eslint": "^9.14.0", "eslint-plugin-import": "^2.31.0", @@ -88,10 +88,10 @@ "nodemon": "^3.1.10", "nyc": "^15.1.0", "pino-pretty": "^13.1.3", + "prettier": "^3.4.2", "sinon": "^13.0.1", "ts-morph": "^28.0.0", - "tv4": "^1.3.0", - "prettier": "^3.4.2" + "tv4": "^1.3.0" }, "resolutions": { "jsonwebtoken": "^9.0.0", diff --git a/scality-cloudserverclient-v1.0.9.tgz b/scality-cloudserverclient-v1.0.9.tgz new file mode 100644 index 0000000000..361a849d53 Binary files /dev/null and b/scality-cloudserverclient-v1.0.9.tgz differ diff --git a/tests/functional/backbeat/crrCascade.js b/tests/functional/backbeat/crrCascade.js new file mode 100644 index 0000000000..4c93b92735 --- /dev/null +++ b/tests/functional/backbeat/crrCascade.js @@ -0,0 +1,296 @@ +'use strict'; + +const assert = require('assert'); +const { createHash } = require('crypto'); +const { v4: uuidv4 } = require('uuid'); +const { + CreateBucketCommand, + PutBucketReplicationCommand, + PutBucketVersioningCommand, + PutObjectCommand, +} = require('@aws-sdk/client-s3'); + +const { versioning, models } = require('arsenal'); +const { ObjectMD } = models; +const BucketUtility = require('../aws-node-sdk/lib/utility/bucket-util'); + +const { + BackbeatRoutesClient, + GetMetadataCommand, + PutMetadataCommand, + PutDataCommand, + VersionIdCollisionException, + StaleMicroVersionIdException, +} = require('@scality/cloudserverclient'); + +const { generateVersionId, encode: encodeVersionId } = versioning.VersionID; + +const TEST_BUCKET = `bucket-crr-cascade-${uuidv4().split('-')[0]}`; +const TEST_BUCKET_CRR = `bucket-crr-cascade-crr-${uuidv4().split('-')[0]}`; +const DEST_BUCKET = `bucket-crr-cascade-dest-${uuidv4().split('-')[0]}`; +const OBJECT_BODY = 'imAboutToBeCascadedWitNoParachuteInMyBack'; +const OBJECT_MD5_HEX = createHash('md5').update(OBJECT_BODY).digest('hex'); +const CANONICAL_ID = '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be'; +const bucketUtil = new BucketUtility('default', {}); +const s3 = bucketUtil.s3; + +let backbeatClient; + +function makeMicroVersionId() { + const raw = generateVersionId('test-instance', 'RG001'); + return { raw, encoded: encodeVersionId(raw) }; +} + +function buildMetadataBody(overrides) { + const obj = Object.assign({ + 'content-length': Buffer.byteLength(OBJECT_BODY), + 'content-type': 'text/plain', + 'last-modified': new Date().toISOString(), + 'x-amz-version-id': 'null', + 'owner-id': CANONICAL_ID, + 'owner-display-name': 'test', + 'content-md5': OBJECT_MD5_HEX, + replicationInfo: { + status: 'REPLICA', + isReplica: true, + backends: [], + content: [], + destination: '', + storageClass: '', + role: '', + storageType: '', + dataStoreVersionId: '', + }, + }, overrides || {}); + return Buffer.from(JSON.stringify(obj)); +} + +async function putMetadata(key, mvId) { + const bodyOverrides = mvId + ? { microVersionId: mvId.raw } + : {}; + return backbeatClient.send(new PutMetadataCommand({ + Bucket: TEST_BUCKET, + Key: key, + MicroVersionId: mvId ? mvId.encoded : undefined, + Body: buildMetadataBody(bodyOverrides), + })); +} + +async function putData(key, { versionId } = {}) { + return backbeatClient.send(new PutDataCommand({ + Bucket: TEST_BUCKET, + Key: key, + ContentMD5: OBJECT_MD5_HEX, + CanonicalID: CANONICAL_ID, + VersioningRequired: true, + SourceVersionId: versionId || undefined, + Body: Buffer.from(OBJECT_BODY), + })); +} + +before(async () => { + const creds = await s3.config.credentials(); + backbeatClient = new BackbeatRoutesClient({ + endpoint: `http://${process.env.IP || '127.0.0.1'}:8000`, + region: 'us-east-1', + credentials: { + accessKeyId: creds.accessKeyId, + secretAccessKey: creds.secretAccessKey, + }, + forcePathStyle: true, + }); + + await s3.send(new CreateBucketCommand({ Bucket: TEST_BUCKET })); + await s3.send(new PutBucketVersioningCommand({ + Bucket: TEST_BUCKET, + VersioningConfiguration: { Status: 'Enabled' }, + })); + + await s3.send(new CreateBucketCommand({ Bucket: DEST_BUCKET })); + await s3.send(new PutBucketVersioningCommand({ + Bucket: DEST_BUCKET, + VersioningConfiguration: { Status: 'Enabled' }, + })); + await s3.send(new CreateBucketCommand({ Bucket: TEST_BUCKET_CRR })); + await s3.send(new PutBucketVersioningCommand({ + Bucket: TEST_BUCKET_CRR, + VersioningConfiguration: { Status: 'Enabled' }, + })); + await s3.send(new PutBucketReplicationCommand({ + Bucket: TEST_BUCKET_CRR, + ReplicationConfiguration: { + Role: 'arn:aws:iam::account-id:role/src-resource,' + + 'arn:aws:iam::account-id:role/dest-resource', + Rules: [{ + Status: 'Enabled', + Prefix: '', + Destination: { + Bucket: `arn:aws:s3:::${DEST_BUCKET}`, + StorageClass: 'zenko', + }, + }], + }, + })); +}); + +describe('CRR cascade — putMetadata', () => { + it('should return loop-detected on second write with the same microVersionId', async () => { + const key = 'crr-cascade-md-loop'; + const mvId = makeMicroVersionId(); + const first = await putMetadata(key, mvId); + assert.ok(!first.ReplicationLoop, + 'should not be flagged as loop on first write'); + const second = await putMetadata(key, mvId); + assert.strictEqual(second.ReplicationLoop, true, + 'second write with same id should be flagged as loop'); + }); + + it('should return 409 when writing with an older microVersionId', async () => { + const key = 'crr-cascade-md-stale'; + const olderMvId = makeMicroVersionId(); + const newerMvId = makeMicroVersionId(); + await putMetadata(key, newerMvId); + try { + await putMetadata(key, olderMvId); + assert.fail('expected StaleMicroVersionIdException'); + } catch (err) { + assert.ok(err instanceof StaleMicroVersionIdException, + `expected StaleMicroVersionIdException, got ${err.constructor.name}`); + } + }); +}); + +describe('CRR cascade — putData', () => { + it('should throw VersionIdCollisionException with MicroVersionId when versionId matches current master', async () => { + const key = 'crr-cascade-data-version-collision'; + + // Create a versioned object so it has both a versionId and a + // microVersionId in its metadata. + const putResult = await s3.send(new PutObjectCommand({ + Bucket: TEST_BUCKET, + Key: key, + Body: Buffer.from(OBJECT_BODY), + ContentType: 'text/plain', + })); + const encodedVersionId = putResult.VersionId; + assert.ok(encodedVersionId, 'PutObject should return a VersionId'); + + try { + await putData(key, { versionId: encodedVersionId }); + assert.fail('expected VersionIdCollisionException'); + } catch (err) { + assert.ok(err instanceof VersionIdCollisionException, + `expected VersionIdCollisionException, got ${err.constructor.name}`); + + const { Body } = await backbeatClient.send(new GetMetadataCommand({ + Bucket: TEST_BUCKET, + Key: key, + })); + const storedMd = new ObjectMD(JSON.parse(Body)); + const { decode } = versioning.VersionID; + const decoded = decode(err.MicroVersionId); + assert.ok(!(decoded instanceof Error), + 'MicroVersionId should be a decodable versionId'); + assert.strictEqual(decoded, storedMd.getMicroVersionId(), + 'decoded MicroVersionId should match the stored microVersionId'); + } + }); + + it('should write data normally when VersionId does not match the current master', async () => { + const key = 'crr-cascade-data-version-no-match'; + + const putResult = await s3.send(new PutObjectCommand({ + Bucket: TEST_BUCKET, + Key: key, + Body: Buffer.from(OBJECT_BODY), + ContentType: 'text/plain', + })); + assert.ok(putResult.VersionId, 'PutObject should return a VersionId'); + + const output = await putData(key, { versionId: 'totally-different-version-id' }); + assert.ok(output.Location, + 'should return a Location because data was written normally'); + }); +}); + +describe('CRR cascade : no errors', () => { + it('should succeed and store updated metadata when putMetadata microVersionId is newer', async () => { + const key = 'crr-cascade-md-forward'; + const olderMvId = makeMicroVersionId(); + const newerMvId = makeMicroVersionId(); + + await putMetadata(key, olderMvId); + const output = await putMetadata(key, newerMvId); + assert.ok(!output.ReplicationLoop, + 'newer write should not be flagged as loop'); + + const { Body } = await backbeatClient.send(new GetMetadataCommand({ + Bucket: TEST_BUCKET, + Key: key, + })); + const storedMd = new ObjectMD(JSON.parse(Body)); + assert.strictEqual(storedMd.getMicroVersionId(), newerMvId.raw, + 'stored microVersionId should be the newer one'); + + assert.strictEqual(storedMd.getReplicationStatus(), '', + 'replication status should be cleared when no further replication is configured'); + assert.deepStrictEqual(storedMd.getReplicationBackends(), [], + 'replication backends should be empty when no further replication is configured'); + assert.strictEqual(storedMd.getReplicationIsReplica(), true, + 'isReplica should be preserved regardless of further replication config'); + }); + +}); + +describe('CRR cascade : cascade setup for next location', () => { + it('should set replication status to PENDING and preserve isReplica when bucket has CRR rules', async () => { + const key = 'crr-cascade-next-hop'; + const olderMvId = makeMicroVersionId(); + const newerMvId = makeMicroVersionId(); + + await backbeatClient.send(new PutMetadataCommand({ + Bucket: TEST_BUCKET_CRR, + Key: key, + MicroVersionId: olderMvId.encoded, + Body: buildMetadataBody({ microVersionId: olderMvId.raw }), + })); + await backbeatClient.send(new PutMetadataCommand({ + Bucket: TEST_BUCKET_CRR, + Key: key, + MicroVersionId: newerMvId.encoded, + Body: buildMetadataBody({ microVersionId: newerMvId.raw }), + })); + + const { Body } = await backbeatClient.send(new GetMetadataCommand({ + Bucket: TEST_BUCKET_CRR, + Key: key, + })); + const storedMd = new ObjectMD(JSON.parse(Body)); + assert.strictEqual(storedMd.getMicroVersionId(), newerMvId.raw, + 'stored microVersionId should be the newer one'); + + assert.strictEqual(storedMd.getReplicationStatus(), 'PENDING', + 'replication status should be PENDING when a CRR rule matches'); + assert.ok(storedMd.getReplicationBackends().length > 0, + 'replication backends should be populated when a CRR rule matches'); + assert.strictEqual(storedMd.getReplicationIsReplica(), true, + 'isReplica should be preserved regardless of further replication config'); + }); +}); + +describe('CRR cascade : baseline (no cascade headers)', () => { + it('should succeed normally when putData has no VersionId header', async () => { + const key = 'crr-cascade-baseline-data'; + const output = await putData(key); + assert.ok(output.Location, + 'putData without VersionId should return a Location'); + }); + + it('should succeed normally when putMetadata has no MicroVersionId header', async () => { + const key = 'crr-cascade-baseline-md'; + const output = await putMetadata(key, null); + assert.ok(!output.ReplicationLoop, + 'putMetadata without MicroVersionId should not set ReplicationLoop'); + }); +}); diff --git a/tests/unit/routes/routeBackbeat.js b/tests/unit/routes/routeBackbeat.js index 5d21d61a70..e0915db98f 100644 --- a/tests/unit/routes/routeBackbeat.js +++ b/tests/unit/routes/routeBackbeat.js @@ -20,6 +20,7 @@ const bucketPutVersioning = require('../../../lib/api/bucketPutVersioning'); const objectPut = require('../../../lib/api/objectPut'); const { objectDelete } = require('../../../lib/api/objectDelete'); const bucketPutPolicy = require('../../../lib/api/bucketPutPolicy'); +const { versioning } = require('arsenal'); const log = new DummyRequestLogger(); @@ -174,6 +175,47 @@ describe('routeBackbeat', () => { assert.deepStrictEqual(mockResponse.body, [{}]); }); + it('should return 409 VersionIdCollisionException when versionId matches master, no microVersionId', async () => { + // Old objects without microVersionId: data is already at destination. + // Cloudserver returns 409 VersionIdCollisionException with empty x-scal-micro-version-id. + // Backbeat sees empty MicroVersionId and proceeds to putMetadata (partAlreadyAtDest). + const rawVersionId = versioning.VersionID.generateVersionId('test', 'RG001'); + const encodedVersionId = versioning.VersionID.encode(rawVersionId); + + mockRequest = prepareDummyRequest({ + 'x-scal-canonical-id': 'id', + 'content-md5': '1234', + 'content-length': '0', + 'x-scal-versioning-required': 'true', + 'x-scal-source-version-id': encodedVersionId, + }); + mockRequest.method = 'PUT'; + mockRequest.url = '/_/backbeat/data/bucket0/key0'; + mockRequest.destroy = () => {}; + + metadataUtils.standardMetadataValidateBucketAndObj.callsFake( + (params, denies, log, callback) => { + callback(null, { + getVersioningConfiguration: () => ({ Status: 'Enabled' }), + isVersioningEnabled: () => true, + getLocationConstraint: () => undefined, + }, { + versionId: rawVersionId, + // no microVersionId — old-format object + }); + }); + + routeBackbeat('127.0.0.1', mockRequest, mockResponse, log); + void await endPromise; + + sinon.assert.notCalled(storeObject.dataStore); + assert.strictEqual(mockResponse.statusCode, 409); + assert.strictEqual(mockResponse.body.code, 'VersionIdCollisionException'); + const [, responseHeaders] = mockResponse.writeHead.firstCall.args; + assert.strictEqual(responseHeaders['x-scal-micro-version-id'], '', + 'should have empty x-scal-micro-version-id header for old-format objects'); + }); + describe('putMetadata', () => { const bucketInfo = { getVersioningConfiguration: () => ({ Status: 'Enabled' }), diff --git a/yarn.lock b/yarn.lock index e942b928fd..68dfb6d86f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2987,24 +2987,6 @@ "@eslint/core" "^0.12.0" levn "^0.4.1" -"@grpc/grpc-js@^1.14.3": - version "1.14.4" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.14.4.tgz#e73ff57d97802f063999545f43ebb2b1eca65d9d" - integrity sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ== - dependencies: - "@grpc/proto-loader" "^0.8.0" - "@js-sdsl/ordered-map" "^4.4.2" - -"@grpc/proto-loader@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.8.1.tgz#5a6b290ccbfb1ae2f6775afb74e9898bd8c5d4e8" - integrity sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg== - dependencies: - lodash.camelcase "^4.3.0" - long "^5.0.0" - protobufjs "^7.5.5" - yargs "^17.7.2" - "@hapi/address@^4.0.1": version "4.1.0" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-4.1.0.tgz#d60c5c0d930e77456fdcde2598e77302e2955e1d" @@ -3193,11 +3175,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@js-sdsl/ordered-map@^4.4.2": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" - integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== - "@js-sdsl/ordered-set@^4.4.2": version "4.4.2" resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-set/-/ordered-set-4.4.2.tgz#ab857eb63cf358b5a0f74fdd458b4601423779b7" @@ -3259,19 +3236,6 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/configuration@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/configuration/-/configuration-0.218.0.tgz#a5fdd50ec9cfa0adb3bb41202cb39f79c5f4e7d9" - integrity sha512-W8wIz7H2R1pufR5jfjb3gU2XkMpm2x/7b1RJcsuzvd70Il/rWWE+g5/Od7hQKrxRTSrTrOWlru101PWXz5I1EQ== - dependencies: - "@opentelemetry/core" "2.7.1" - yaml "^2.0.0" - -"@opentelemetry/context-async-hooks@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.7.1.tgz#1555a6fb269596416d8c626fd020c3f2c38e071f" - integrity sha512-OPFBYuXEn1E4ja3Y6eeA7O+ZnLBNcXTV5Cgsn1VaqBZ6hC5FnpZPLBNme1LJY8ZtF4aOujPKFoeWN4ik487KuQ== - "@opentelemetry/core@2.7.1": version "2.7.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.7.1.tgz#162bfab46d6ff4da1bef240ea52e23a926b0fdbc" @@ -3279,134 +3243,6 @@ dependencies: "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/exporter-logs-otlp-grpc@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.218.0.tgz#7e5a7e624074d6449590c851d58efe60096314b3" - integrity sha512-hoxrNH1l/Xy6F9WTJ5IK+6j1r9nQFlPOmrnTlhYHTySdunfXLmUCPv3bQtKYntxag9h3wLYBZQ2HI6FOx+BT2g== - dependencies: - "@grpc/grpc-js" "^1.14.3" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-grpc-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/sdk-logs" "0.218.0" - -"@opentelemetry/exporter-logs-otlp-http@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.218.0.tgz#0996cb45e0ebc6c7465445430a018edcac9871b4" - integrity sha512-Qx+4rpVHzgg89dawcWRHyt+XRXeLnhFz/qBtvggmjkcgPUdr+NAB0/u/eIPA8yAeJV0J80Vz43JZCh/XFvZFGw== - dependencies: - "@opentelemetry/api-logs" "0.218.0" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/sdk-logs" "0.218.0" - -"@opentelemetry/exporter-logs-otlp-proto@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.218.0.tgz#e539720b2e145e85a7a4901a1eabb0b2e77990f8" - integrity sha512-1/noQNsp9gXD75HPzgjBrcF1+XTtry7pFAUfxVEJgg7mPv2AawKQuYkhMmJ8qjxz4Ubc3Y8bwvfxevXsKTq4cg== - dependencies: - "@opentelemetry/api-logs" "0.218.0" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-logs" "0.218.0" - "@opentelemetry/sdk-trace-base" "2.7.1" - -"@opentelemetry/exporter-metrics-otlp-grpc@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.218.0.tgz#6d66c2638702489d063b8857741eee9cea1d21d6" - integrity sha512-YapQ9vNMX0NSZF6LK5pWAFfjpJleV2O9uYWfYGeb/5F1Kb9rPGK8tZDMJFa/sOksgdFuflDvYuA0B4qjDB4fjQ== - dependencies: - "@grpc/grpc-js" "^1.14.3" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/exporter-metrics-otlp-http" "0.218.0" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-grpc-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-metrics" "2.7.1" - -"@opentelemetry/exporter-metrics-otlp-http@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.218.0.tgz#c205581585d04c5106d1ca766e23632006e46a2c" - integrity sha512-bV7d2OuMpZu2+gAaxUAhzfZ0h3WVZk8ETQUEE3DNSntbTaMpuITjtm8I0rNyHFdm7Ax57K6ty7SgFXlBmOLIvQ== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-metrics" "2.7.1" - -"@opentelemetry/exporter-metrics-otlp-proto@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.218.0.tgz#66642b8bb5e654ca7a5944a4f0b490814fc875fd" - integrity sha512-ubLddKjWULhla9YZRCj/rTBeppjJYE4e9w0icx5mTu3eFhWjQzbV75NYjXuIlEG+NJsBl6d+sTFw5Qu+oej4oQ== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/exporter-metrics-otlp-http" "0.218.0" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-metrics" "2.7.1" - -"@opentelemetry/exporter-prometheus@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.218.0.tgz#41bddc97d34cd9d9993382b9c10fed19a8de63f4" - integrity sha512-RT5oEyu1kddZJ1vt7/BUo5wV+P7hpNAESsR3dUd3+8deHuX7gWNoCOZn+SfDT+hJHlIJ5h/AxiCLXIrutswDJg== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-metrics" "2.7.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/exporter-trace-otlp-grpc@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.218.0.tgz#5d5532ab94d5514bec8aedc19ce3170b6381eed1" - integrity sha512-3fXxVQEj9TNAFaCi79JeFKfeLd0sDtInaR3gaZDVlzNSPHtz8PZuCV34JKWjD4XXzT20IdMe8IpX6mRVNDA4Tw== - dependencies: - "@grpc/grpc-js" "^1.14.3" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-grpc-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - -"@opentelemetry/exporter-trace-otlp-http@0.218.0", "@opentelemetry/exporter-trace-otlp-http@^0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.218.0.tgz#36d6abf6d639b9ea861603c61f434751c0b1a0ea" - integrity sha512-8dqezsmPhtKitIK/eTipZhYl9EX2/gNQ5zUMhaz3uxEURwfkNf8IPvo6yNfrzbxdtpAOybS/+h7wmIWYqFSpiw== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - -"@opentelemetry/exporter-trace-otlp-proto@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.218.0.tgz#de0b2b3545149dafedd2b948fb891f9bf962940c" - integrity sha512-r1Msf8SNLRmwh9J6XQ5uh82D7CdDWMNHnPB7LAVHjzut0TkSeKc5KcIvr4SvHvfk/xwN5gxC+VLKQ1k0o8PSPw== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - -"@opentelemetry/exporter-zipkin@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.7.1.tgz#3b79d223adc8c097ba3323e4de4ed8abb83c789e" - integrity sha512-mfsD9bKAxcKrh5+y08TPodvClBO0CznBE3p79YAGnO81WI4LrdsGA65T53e4iTSbCalW4WaUpkbeJcbpyIUHfg== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - "@opentelemetry/instrumentation-http@~0.218.0": version "0.218.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.218.0.tgz#0b26b702a6288fa11bdea48ff4a3635385a7d587" @@ -3452,130 +3288,11 @@ import-in-the-middle "^3.0.0" require-in-the-middle "^8.0.0" -"@opentelemetry/otlp-exporter-base@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz#f33e3217ce568756baa132fabe30f0c9345db086" - integrity sha512-ZwqpkNL5W7RyGJPDZ9g06DvKp8KFTWPJPN12anpMQYSKpTSU0z3EIZuPq9vPGpS8siFyOqDYDAuCwlNO9FqgbA== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-transformer" "0.218.0" - -"@opentelemetry/otlp-grpc-exporter-base@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.218.0.tgz#dc5a1fec716245d84dc4167de9b1c607e07fb6a7" - integrity sha512-H/lCGJ536N98VpYJOaWTQOkv4Dx6TnmStK6Rqfu1W7KkFbPAx04hjdYEMZF/YbnHzPUSIK4kM6OE2GKGBTpV9A== - dependencies: - "@grpc/grpc-js" "^1.14.3" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/otlp-transformer" "0.218.0" - -"@opentelemetry/otlp-transformer@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.218.0.tgz#6784d0ddd13803c63a1b24072606c02fc21b9071" - integrity sha512-CFaKH87WAzjuJ4awowTTLzUvMfaRfiOFG5+qm5S5ncyalRtN4ecQ+YmuANJSCrVPuvZFEkUgKhBPBndxi3rHsQ== - dependencies: - "@opentelemetry/api-logs" "0.218.0" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-logs" "0.218.0" - "@opentelemetry/sdk-metrics" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - -"@opentelemetry/propagator-b3@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-b3/-/propagator-b3-2.7.1.tgz#107fe3e16d0728c489edbad221c402ee197514a4" - integrity sha512-RJid6E2CKyeGfKBzXKF21ejabGMHypFkPAh3qZ+NvI+SGjuIye79t3PmiqcDgtRzdKH6ynXzbfslQ8DfpRUg2A== - dependencies: - "@opentelemetry/core" "2.7.1" - -"@opentelemetry/propagator-jaeger@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.7.1.tgz#e8ebf3f6c0e9aa525cf041893425889cf3e69125" - integrity sha512-KMjVBHzP4N60bOzxja76M1F1hZZ43lGPga5ix+mkv9+kk1nx9SbkxSvJsMbuVUxdPQmsPTqGShmhN8ulrMOg6Q== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/redis-common@^0.38.3": version "0.38.3" resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz#31a0464a48a991c29408614e3725d94db7c11aee" integrity sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw== -"@opentelemetry/resources@2.7.1", "@opentelemetry/resources@^2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.7.1.tgz#3b2a9179f6119bb1f2cddefe41ba9b2855504a5d" - integrity sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/sdk-logs@0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz#78886fe300b82802cee9963208b2af326b928af5" - integrity sha512-QvnNdugatFTVCJXH0Mcu7GOOJSylA9j127kIezOE4YwTI4YbowRons2K4WZTv5FMS8T4q9P0NdaRHdkSmeAIag== - dependencies: - "@opentelemetry/api-logs" "0.218.0" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/sdk-metrics@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz#b713f69dd67933ecc9c61357f1d452cdc9f4e281" - integrity sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/resources" "2.7.1" - -"@opentelemetry/sdk-node@^0.218.0": - version "0.218.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-node/-/sdk-node-0.218.0.tgz#cf8bdc0c993387189c7c474612e0f801505feb97" - integrity sha512-tPMjHrLV5gsfNdYqoRHjeGbCAZBXXD9c1Qo/2ut7VwnUABDNh76xNxrT0SEhkIIJuCN45bbN1vZnYL1gY0IkOg== - dependencies: - "@opentelemetry/api-logs" "0.218.0" - "@opentelemetry/configuration" "0.218.0" - "@opentelemetry/context-async-hooks" "2.7.1" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/exporter-logs-otlp-grpc" "0.218.0" - "@opentelemetry/exporter-logs-otlp-http" "0.218.0" - "@opentelemetry/exporter-logs-otlp-proto" "0.218.0" - "@opentelemetry/exporter-metrics-otlp-grpc" "0.218.0" - "@opentelemetry/exporter-metrics-otlp-http" "0.218.0" - "@opentelemetry/exporter-metrics-otlp-proto" "0.218.0" - "@opentelemetry/exporter-prometheus" "0.218.0" - "@opentelemetry/exporter-trace-otlp-grpc" "0.218.0" - "@opentelemetry/exporter-trace-otlp-http" "0.218.0" - "@opentelemetry/exporter-trace-otlp-proto" "0.218.0" - "@opentelemetry/exporter-zipkin" "2.7.1" - "@opentelemetry/instrumentation" "0.218.0" - "@opentelemetry/otlp-exporter-base" "0.218.0" - "@opentelemetry/propagator-b3" "2.7.1" - "@opentelemetry/propagator-jaeger" "2.7.1" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/sdk-logs" "0.218.0" - "@opentelemetry/sdk-metrics" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - "@opentelemetry/sdk-trace-node" "2.7.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/sdk-trace-base@2.7.1", "@opentelemetry/sdk-trace-base@^2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz#9160c3af9ef2219c26563abd136e22fb7d19b34f" - integrity sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw== - dependencies: - "@opentelemetry/core" "2.7.1" - "@opentelemetry/resources" "2.7.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/sdk-trace-node@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.7.1.tgz#54dedb8e77fa51a6d02fc2192097739266c82168" - integrity sha512-pCpQxU68lV+I9s9svqMyVu5iHdDDUnqUpSxqwyCU8A9ejEsSnMPCbearwsUO4yk08ZJzAIUCFuReMdVQvHrdvg== - dependencies: - "@opentelemetry/context-async-hooks" "2.7.1" - "@opentelemetry/core" "2.7.1" - "@opentelemetry/sdk-trace-base" "2.7.1" - "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.33.0": version "1.41.1" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz#b04e7151c5913a7a006d4f465479da75efb98a7a" @@ -3586,71 +3303,19 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.5.tgz#d9315ad7cf3f30aac70bda3c068443dc6f143659" - integrity sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g== - -"@protobufjs/eventemitter@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz#d512cb26c0ae026091ee2c1167f1be6faf5c842a" - integrity sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg== - -"@protobufjs/fetch@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.1.tgz#4d6fc00c8fb64016a5c81b469d549046350f1065" - integrity sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.2.tgz#ae64fbc014ff44c8bfad03dd4c93cd2d6a4c82db" - integrity sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.1.tgz#eaee5900122c110a3dbcb728c0597014a2621774" - integrity sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg== - "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@scality/cloudserverclient@1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@scality/cloudserverclient/-/cloudserverclient-1.0.7.tgz#ee9eed09cc7da5e97d5ad8359f429218a0a30859" - integrity sha512-gMDtI/ufRDVqWJlYkvqxXRfZOChBCw9uXt2lsaIvMuiqx/pDTNZMVLfPyRN5vx0tb6HP+5ZAe2FyPdtKXG24ng== +"@scality/cloudserverclient@./scality-cloudserverclient-v1.0.9.tgz": + version "1.0.9" + resolved "./scality-cloudserverclient-v1.0.9.tgz" dependencies: "@aws-sdk/client-s3" "^3.1009.0" + "@aws-sdk/middleware-expect-continue" "^3.972.8" JSONStream "^1.3.5" - fast-xml-parser "^4.3.2" + fast-xml-parser "^5.5.7" "@scality/eslint-config-scality@scality/Guidelines#8.3.1": version "8.3.1" @@ -6150,13 +5815,6 @@ dependencies: undici-types "~6.20.0" -"@types/node@>=13.7.0": - version "25.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.1.tgz#3bda556db500ae4319c08e7fc9ab94f19013ba0b" - integrity sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg== - dependencies: - undici-types ">=7.24.0 <7.24.7" - "@types/triple-beam@^1.3.2": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" @@ -6493,32 +6151,38 @@ arraybuffer.prototype.slice@^1.0.4: get-intrinsic "^1.2.6" is-array-buffer "^3.0.4" -"arsenal@git+https://github.com/scality/Arsenal#8.2.28": - version "8.2.28" - resolved "git+https://github.com/scality/Arsenal#7df5088715bb26a62ff1db2045e611029ff17de1" +"arsenal@git+https://github.com/scality/Arsenal#83682d1ae77705877b81414f39aacce4e27e0ceb": + version "8.4.5" + resolved "git+https://github.com/scality/Arsenal#83682d1ae77705877b81414f39aacce4e27e0ceb" dependencies: - "@azure/identity" "^4.10.2" - "@azure/storage-blob" "^12.27.0" + "@aws-sdk/client-kms" "^3.975.0" + "@aws-sdk/client-s3" "^3.975.0" + "@aws-sdk/credential-providers" "^3.975.0" + "@aws-sdk/lib-storage" "^3.975.0" + "@azure/identity" "^4.13.0" + "@azure/storage-blob" "^12.31.0" "@js-sdsl/ordered-set" "^4.4.2" - "@scality/hdclient" "^1.3.1" + "@opentelemetry/api" "^1.9.0" + "@scality/hdclient" "^1.3.2" + "@smithy/node-http-handler" "^4.3.0" + "@smithy/protocol-http" "^5.3.5" JSONStream "^1.3.5" agentkeepalive "^4.6.0" ajv "6.12.3" async "~2.6.4" - aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - debug "^4.4.1" + debug "^4.4.3" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 https-proxy-agent "^7.0.6" - ioredis "^5.6.1" + ioredis "^5.8.1" ipaddr.js "^2.2.0" - joi "^17.13.3" + joi "^18.0.1" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.17.0" + mongodb "^6.20.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" @@ -6532,37 +6196,32 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.2.4": - version "8.2.4" - resolved "git+https://github.com/scality/Arsenal#96ef6a3e26d7528f877300606586759f1da6d0cd" +"arsenal@git+https://github.com/scality/Arsenal#8.2.28": + version "8.2.28" + resolved "git+https://github.com/scality/Arsenal#7df5088715bb26a62ff1db2045e611029ff17de1" dependencies: - "@azure/identity" "^4.5.0" - "@azure/storage-blob" "^12.25.0" - "@eslint/plugin-kit" "^0.2.3" + "@azure/identity" "^4.10.2" + "@azure/storage-blob" "^12.27.0" "@js-sdsl/ordered-set" "^4.4.2" "@scality/hdclient" "^1.3.1" - "@types/async" "^3.2.24" - "@types/utf8" "^3.0.3" JSONStream "^1.3.5" - agentkeepalive "^4.5.0" + agentkeepalive "^4.6.0" ajv "6.12.3" async "~2.6.4" aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - bson "^6.8.0" - debug "^4.3.7" - diskusage "^1.2.0" + debug "^4.4.1" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 - https-proxy-agent "^7.0.5" - ioredis "^5.4.1" + https-proxy-agent "^7.0.6" + ioredis "^5.6.1" ipaddr.js "^2.2.0" joi "^17.13.3" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.11.0" + mongodb "^6.17.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" @@ -6576,38 +6235,37 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.4.5": - version "8.4.5" - resolved "git+https://github.com/scality/Arsenal#594473628d622c6b0a9ae1b270fee27ef99e5f85" +"arsenal@git+https://github.com/scality/Arsenal#8.2.4": + version "8.2.4" + resolved "git+https://github.com/scality/Arsenal#96ef6a3e26d7528f877300606586759f1da6d0cd" dependencies: - "@aws-sdk/client-kms" "^3.975.0" - "@aws-sdk/client-s3" "^3.975.0" - "@aws-sdk/credential-providers" "^3.975.0" - "@aws-sdk/lib-storage" "^3.975.0" - "@azure/identity" "^4.13.0" - "@azure/storage-blob" "^12.31.0" + "@azure/identity" "^4.5.0" + "@azure/storage-blob" "^12.25.0" + "@eslint/plugin-kit" "^0.2.3" "@js-sdsl/ordered-set" "^4.4.2" - "@opentelemetry/api" "^1.9.0" - "@scality/hdclient" "^1.3.2" - "@smithy/node-http-handler" "^4.3.0" - "@smithy/protocol-http" "^5.3.5" + "@scality/hdclient" "^1.3.1" + "@types/async" "^3.2.24" + "@types/utf8" "^3.0.3" JSONStream "^1.3.5" - agentkeepalive "^4.6.0" + agentkeepalive "^4.5.0" ajv "6.12.3" async "~2.6.4" + aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - debug "^4.4.3" + bson "^6.8.0" + debug "^4.3.7" + diskusage "^1.2.0" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 - https-proxy-agent "^7.0.6" - ioredis "^5.8.1" + https-proxy-agent "^7.0.5" + ioredis "^5.4.1" ipaddr.js "^2.2.0" - joi "^18.0.1" + joi "^17.13.3" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.20.0" + mongodb "^6.11.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" @@ -6619,10 +6277,6 @@ arraybuffer.prototype.slice@^1.0.4: werelogs scality/werelogs#8.2.2 xml2js "^0.6.2" optionalDependencies: - "@opentelemetry/exporter-trace-otlp-http" "^0.218.0" - "@opentelemetry/resources" "^2.7.1" - "@opentelemetry/sdk-node" "^0.218.0" - "@opentelemetry/sdk-trace-base" "^2.7.1" ioctl "^2.0.2" asn1@~0.2.3: @@ -8293,7 +7947,7 @@ fast-xml-builder@^1.1.4: dependencies: path-expression-matcher "^1.1.3" -fast-xml-parser@5.2.5, fast-xml-parser@5.3.6, fast-xml-parser@5.5.6, fast-xml-parser@^4.3.2, fast-xml-parser@^5.0.7, fast-xml-parser@^5.5.6: +fast-xml-parser@5.2.5, fast-xml-parser@5.3.6, fast-xml-parser@5.5.6, fast-xml-parser@^5.0.7, fast-xml-parser@^5.5.6, fast-xml-parser@^5.5.7: version "5.5.7" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz#e1ddc86662d808450a19cf2fb6ccc9c3c9933c5d" integrity sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg== @@ -10004,11 +9658,6 @@ lodash-compat@^3.10.2: resolved "https://registry.yarnpkg.com/lodash-compat/-/lodash-compat-3.10.2.tgz#c6940128a9d30f8e902cd2cf99fd0cba4ecfc183" integrity sha512-k8SE/OwvWfYZqx3MA/Ry1SHBDWre8Z8tCs0Ba0bF5OqVNvymxgFZ/4VDtbTxzTvcoG11JpTMFsaeZp/yGYvFnA== -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -10126,11 +9775,6 @@ long-timeout@0.1.1: resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514" integrity sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w== -long@^5.0.0, long@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" - integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== - looper@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec" @@ -11229,24 +10873,6 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -protobufjs@^7.5.5: - version "7.6.2" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.6.2.tgz#2659f77bd8d54778814c274dc0df808f54c88918" - integrity sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.5" - "@protobufjs/eventemitter" "^1.1.1" - "@protobufjs/fetch" "^1.1.1" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.2" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.1" - "@types/node" ">=13.7.0" - long "^5.3.2" - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -12631,11 +12257,6 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -"undici-types@>=7.24.0 <7.24.7": - version "7.24.6" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91" - integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg== - undici-types@~6.20.0: version "6.20.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" @@ -13114,11 +12735,6 @@ yallist@^5.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== -yaml@^2.0.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4" - integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA== - yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"