diff --git a/index.js b/index.js index f5fa36c2e2..fe68a8a1b1 100644 --- a/index.js +++ b/index.js @@ -7,4 +7,25 @@ require('werelogs').stderrUtils.catchAndTimestampStderr( require('cluster').isPrimary ? 1 : null, ); +const tracing = require('arsenal/build/lib/tracing'); + +// Gated on isEnabled() so the OTEL-off path doesn't load Config early. +if (tracing.isEnabled() && !(require('./lib/Config').config.isCluster && require('cluster').isPrimary)) { + tracing.init({ + serviceName: 'cloudserver', + serviceVersion: require('./package.json').version, + instrumentations: () => { + const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); + const { IORedisInstrumentation } = require('@opentelemetry/instrumentation-ioredis'); + const { MongoDBInstrumentation } = require('@opentelemetry/instrumentation-mongodb'); + const healthPaths = ['/live', '/ready', '/_/healthcheck', '/_/healthcheck/deep', '/metrics']; + return [ + new HttpInstrumentation(tracing.makeHttpInstrumentationConfig({ healthPaths })), + new IORedisInstrumentation({ requireParentSpan: true }), + new MongoDBInstrumentation({ enhancedDatabaseReporting: false }), + ]; + }, + }); +} + require('./lib/server.js')(); diff --git a/lib/api/api.js b/lib/api/api.js index 75c6e0e158..299a11a30c 100644 --- a/lib/api/api.js +++ b/lib/api/api.js @@ -69,8 +69,7 @@ const objectPutPart = require('./objectPutPart'); const objectPutCopyPart = require('./objectPutCopyPart'); const objectPutRetention = require('./objectPutRetention'); const objectRestore = require('./objectRestore'); -const prepareRequestContexts - = require('./apiUtils/authorization/prepareRequestContexts'); +const prepareRequestContexts = require('./apiUtils/authorization/prepareRequestContexts'); const serviceGet = require('./serviceGet'); const vault = require('../auth/vault'); const website = require('./website'); @@ -80,6 +79,7 @@ const parseCopySource = require('./apiUtils/object/parseCopySource'); const { tagConditionKeyAuth } = require('./apiUtils/authorization/tagConditionKeys'); const { isRequesterASessionUser } = require('./apiUtils/authorization/permissionChecks'); const checkHttpHeadersSize = require('./apiUtils/object/checkHttpHeadersSize'); +const { instrumentApiMethod } = require('arsenal/build/lib/tracing'); const constants = require('../../constants'); const { config } = require('../Config.js'); const metadata = require('../metadata/wrapper'); @@ -102,8 +102,7 @@ auth.setHandler(vault); // scan the args instead of relying on a fixed position. function hasCorsHeaders(callbackArgs) { for (const arg of callbackArgs) { - if (!arg || typeof arg !== 'object' || Array.isArray(arg) - || Buffer.isBuffer(arg)) { + if (!arg || typeof arg !== 'object' || Array.isArray(arg) || Buffer.isBuffer(arg)) { continue; } if ('access-control-allow-origin' in arg) { @@ -137,22 +136,21 @@ function wrapCallbackWithErrorCorsHeaders(callback, request, response, log) { if (!bucket || response.headersSent) { return; } - const headers = collectCorsHeaders( - request.headers.origin, request.method, bucket); + const headers = collectCorsHeaders(request.headers.origin, request.method, bucket); Object.keys(headers).forEach(key => { try { response.setHeader(key, headers[key]); } catch (e) { log.debug('could not set CORS header on error', { - header: key, error: e.message, + header: key, + error: e.message, }); } }); } return (err, ...callbackArgs) => { - if (!err || !request.headers || !request.headers.origin - || !request.bucketName) { + if (!err || !request.headers || !request.headers.origin || !request.bucketName) { return callback(err, ...callbackArgs); } // Fast path: most post-auth failures come back with corsHeaders @@ -194,8 +192,7 @@ function checkAuthResults(authResults, apiMethod, log) { isImplicitDeny[authResults[0].action] = authResults[0].isImplicit; // second item checks s3:GetObject(Version)Tagging action if (!authResults[1].isAllowed) { - log.trace('get tagging authorization denial ' + - 'from Vault'); + log.trace('get tagging authorization denial ' + 'from Vault'); returnTagCount = false; } } else { @@ -256,14 +253,15 @@ function callApiHandler(apiMethod, apiHandler, request, response, log, callback) } // no need to check auth on website or cors preflight requests - if (apiMethod === 'websiteGet' || apiMethod === 'websiteHead' || - apiMethod === 'corsPreflight') { + if (apiMethod === 'websiteGet' || apiMethod === 'websiteHead' || apiMethod === 'corsPreflight') { request.actionImplicitDenies = false; return apiHandler(request, log, callback); } - const { sourceBucket, sourceObject, sourceVersionId, parsingError } = - parseCopySource(apiMethod, request.headers['x-amz-copy-source']); + const { sourceBucket, sourceObject, sourceVersionId, parsingError } = parseCopySource( + apiMethod, + request.headers['x-amz-copy-source'], + ); if (parsingError) { log.debug('error parsing copy source', { error: parsingError, @@ -279,177 +277,190 @@ function callApiHandler(apiMethod, apiHandler, request, response, log, callback) return process.nextTick(callback, httpHeadersSizeError); } - const requestContexts = prepareRequestContexts(apiMethod, request, - sourceBucket, sourceObject, sourceVersionId); + const requestContexts = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); // Extract all the _apiMethods and store them in an array const apiMethods = requestContexts ? requestContexts.map(context => context._apiMethod) : []; // Attach the names to the current request request.apiMethods = apiMethods; - return async.waterfall([ - next => auth.server.doAuth( - request, log, (err, userInfo, authorizationResults, streamingV4Params, infos) => { - if (request.serverAccessLog) { - request.serverAccessLog.authInfo = userInfo; + return async.waterfall( + [ + next => + auth.server.doAuth( + request, + log, + (err, userInfo, authorizationResults, streamingV4Params, infos) => { + if (request.serverAccessLog) { + request.serverAccessLog.authInfo = userInfo; + } + if (err) { + // VaultClient returns standard errors, but the route requires + // Arsenal errors + const arsenalError = err.metadata ? err : errors[err.code] || errors.InternalError; + log.trace('authentication error', { error: err }); + return next(arsenalError); + } + return next(null, userInfo, authorizationResults, streamingV4Params, infos); + }, + 's3', + requestContexts, + ), + (userInfo, authorizationResults, streamingV4Params, infos, next) => { + const authNames = { accountName: userInfo.getAccountDisplayName() }; + if (userInfo.isRequesterAnIAMUser()) { + authNames.userName = userInfo.getIAMdisplayName(); } - if (err) { - // VaultClient returns standard errors, but the route requires - // Arsenal errors - const arsenalError = err.metadata ? err : errors[err.code] || errors.InternalError; - log.trace('authentication error', { error: err }); - return next(arsenalError); + if (isRequesterASessionUser(userInfo)) { + authNames.sessionName = userInfo.getShortid().split(':')[1]; } - return next(null, userInfo, authorizationResults, streamingV4Params, infos); - }, 's3', requestContexts), - (userInfo, authorizationResults, streamingV4Params, infos, next) => { - const authNames = { accountName: userInfo.getAccountDisplayName() }; - if (userInfo.isRequesterAnIAMUser()) { - authNames.userName = userInfo.getIAMdisplayName(); - } - if (isRequesterASessionUser(userInfo)) { - authNames.sessionName = userInfo.getShortid().split(':')[1]; - } - log.addDefaultFields(authNames); - if (request.serverAccessLog) { - request.serverAccessLog.analyticsAccountName = authNames.accountName; - request.serverAccessLog.analyticsUserName = authNames.userName; - } - if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') { - const setStartTurnAroundTime = () => { - if (request.serverAccessLog) { - request.serverAccessLog.startTurnAroundTime = process.hrtime.bigint(); - } - }; - // For 0-byte uploads, downstream handlers do not consume - // the request stream, so 'end' never fires. Set - // startTurnAroundTime synchronously in that case. - if (request.headers['content-length'] === '0') { - setStartTurnAroundTime(); - } else { - request.on('end', setStartTurnAroundTime); + log.addDefaultFields(authNames); + if (request.serverAccessLog) { + request.serverAccessLog.analyticsAccountName = authNames.accountName; + request.serverAccessLog.analyticsUserName = authNames.userName; } - return next(null, userInfo, authorizationResults, streamingV4Params, infos); - } - // issue 100 Continue to the client - writeContinue(request, response); - - const defaultMaxBodyLength = request.method === 'POST' ? - constants.oneMegaBytes : constants.halfMegaBytes; - const MAX_BODY_LENGTH = config.apiBodySizeLimits[apiMethod] || defaultMaxBodyLength; - const post = []; - let bodyLength = 0; - request.on('data', chunk => { - bodyLength += chunk.length; - // Sanity check on post length - if (bodyLength <= MAX_BODY_LENGTH) { - post.push(chunk); + if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') { + const setStartTurnAroundTime = () => { + if (request.serverAccessLog) { + request.serverAccessLog.startTurnAroundTime = process.hrtime.bigint(); + } + }; + // For 0-byte uploads, downstream handlers do not consume + // the request stream, so 'end' never fires. Set + // startTurnAroundTime synchronously in that case. + if (request.headers['content-length'] === '0') { + setStartTurnAroundTime(); + } else { + request.on('end', setStartTurnAroundTime); + } + return next(null, userInfo, authorizationResults, streamingV4Params, infos); } - }); + // issue 100 Continue to the client + writeContinue(request, response); + + const defaultMaxBodyLength = + request.method === 'POST' ? constants.oneMegaBytes : constants.halfMegaBytes; + const MAX_BODY_LENGTH = config.apiBodySizeLimits[apiMethod] || defaultMaxBodyLength; + const post = []; + let bodyLength = 0; + request.on('data', chunk => { + bodyLength += chunk.length; + // Sanity check on post length + if (bodyLength <= MAX_BODY_LENGTH) { + post.push(chunk); + } + }); - request.on('error', err => { - log.trace('error receiving request', { - error: err, + request.on('error', err => { + log.trace('error receiving request', { + error: err, + }); + return next(errors.InternalError); }); - return next(errors.InternalError); - }); - request.on('end', () => { - if (request.serverAccessLog) { - request.serverAccessLog.startTurnAroundTime = process.hrtime.bigint(); - } + request.on('end', () => { + if (request.serverAccessLog) { + request.serverAccessLog.startTurnAroundTime = process.hrtime.bigint(); + } - if (bodyLength > MAX_BODY_LENGTH) { - log.error('body length is too long for request type', - { bodyLength }); - return next(errors.InvalidRequest); - } + if (bodyLength > MAX_BODY_LENGTH) { + log.error('body length is too long for request type', { bodyLength }); + return next(errors.InvalidRequest); + } - const buff = Buffer.concat(post, bodyLength); - return validateMethodChecksumNoChunking(request, buff, log) - .then(error => { - if (error) { - return next(error); + const buff = Buffer.concat(post, bodyLength); + return validateMethodChecksumNoChunking(request, buff, log) + .then(error => { + if (error) { + return next(error); + } + + // Convert array of post buffers into one string + request.post = buff.toString(); + return next(null, userInfo, authorizationResults, streamingV4Params, infos); + }) + .catch(error => { + log.error('error validating checksums', { error }); + next(error); + }); + }); + return undefined; + }, + // Tag condition keys require information from CloudServer for evaluation + (userInfo, authorizationResults, streamingV4Params, infos, next) => + tagConditionKeyAuth( + authorizationResults, + request, + requestContexts, + apiMethod, + log, + (err, authResultsWithTags) => { + if (err) { + log.trace('tag authentication error', { error: err }); + return next(err); + } + return next(null, userInfo, authResultsWithTags, streamingV4Params, infos); + }, + ), + (userInfo, authorizationResults, streamingV4Params, infos, next) => + handleAuthorizationResults( + request, + authorizationResults, + apiMethod, + returnTagCount, + log, + (err, res) => { + request.accountQuotas = infos?.accountQuota; + request.accountLimits = infos?.limits; + if (err) { + return next(err); } + returnTagCount = res.returnTagCount; + return next(null, userInfo, authorizationResults, streamingV4Params); + }, + ), + ], + (err, userInfo, authorizationResults, streamingV4Params) => { + if (err) { + return callback(err); + } + const methodCallback = (err, ...results) => + async.forEachLimit( + request.finalizerHooks, + 5, + (hook, done) => hook(err, done), + () => callback(err, ...results), + ); - // Convert array of post buffers into one string - request.post = buff.toString(); - return next(null, userInfo, authorizationResults, streamingV4Params, infos); - }) - .catch(error => { - log.error('error validating checksums', { error }); - next(error); - }); - }); - return undefined; + if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') { + request._response = response; + return apiHandler(userInfo, request, streamingV4Params, log, methodCallback, authorizationResults); + } + if (apiMethod === 'objectCopy' || apiMethod === 'objectPutCopyPart') { + return apiHandler(userInfo, request, sourceBucket, sourceObject, sourceVersionId, log, methodCallback); + } + if (apiMethod === 'objectGet') { + // remove objectGetTagging/objectGetTaggingVersion from apiMethods, these were added by + // prepareRequestContexts to determine the value of returnTagCount. + request.apiMethods = request.apiMethods.filter(methodName => !methodName.includes('Tagging')); + return apiHandler(userInfo, request, returnTagCount, log, callback); + } + return apiHandler(userInfo, request, log, methodCallback); }, - // Tag condition keys require information from CloudServer for evaluation - (userInfo, authorizationResults, streamingV4Params, infos, next) => tagConditionKeyAuth( - authorizationResults, - request, - requestContexts, - apiMethod, - log, - (err, authResultsWithTags) => { - if (err) { - log.trace('tag authentication error', { error: err }); - return next(err); - } - return next(null, userInfo, authResultsWithTags, streamingV4Params, infos); - }, - ), - (userInfo, authorizationResults, streamingV4Params, infos, next) => handleAuthorizationResults( - request, authorizationResults, apiMethod, returnTagCount, log, (err, res) => { - request.accountQuotas = infos?.accountQuota; - request.accountLimits = infos?.limits; - if (err) { - return next(err); - } - returnTagCount = res.returnTagCount; - return next(null, userInfo, authorizationResults, streamingV4Params); - }), - ], (err, userInfo, authorizationResults, streamingV4Params) => { - if (err) { - return callback(err); - } - const methodCallback = (err, ...results) => async.forEachLimit(request.finalizerHooks, 5, - (hook, done) => hook(err, done), - () => callback(err, ...results)); - - if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') { - request._response = response; - return apiHandler(userInfo, request, streamingV4Params, - log, methodCallback, authorizationResults); - } - if (apiMethod === 'objectCopy' || apiMethod === 'objectPutCopyPart') { - return apiHandler(userInfo, request, sourceBucket, - sourceObject, sourceVersionId, log, methodCallback); - } - if (apiMethod === 'objectGet') { - // remove objectGetTagging/objectGetTaggingVersion from apiMethods, these were added by - // prepareRequestContexts to determine the value of returnTagCount. - request.apiMethods = request.apiMethods.filter(methodName => !methodName.includes('Tagging')); - return apiHandler(userInfo, request, returnTagCount, log, callback); - } - return apiHandler(userInfo, request, log, methodCallback); - }); + ); } const api = { callApiMethod(apiMethod, request, response, log, callback) { // Attach the apiMethod method to the request, so it can used by monitoring in the server request.apiMethod = apiMethod; - callback = wrapCallbackWithErrorCorsHeaders( - callback, request, response, log); + callback = wrapCallbackWithErrorCorsHeaders(callback, request, response, log); // Array of end of API callbacks, used to perform some logic // at the end of an API. request.finalizerHooks = []; const actionLog = monitoringMap[apiMethod]; - if (!actionLog && - apiMethod !== 'websiteGet' && - apiMethod !== 'websiteHead' && - apiMethod !== 'corsPreflight') { + if (!actionLog && apiMethod !== 'websiteGet' && apiMethod !== 'websiteHead' && apiMethod !== 'corsPreflight') { log.error('callApiMethod(): No actionLog for this api method', { apiMethod, }); @@ -591,4 +602,11 @@ const api = { handleAuthorizationResults, }; +const NON_INSTRUMENTED_KEYS = new Set(['callApiMethod', 'checkAuthResults', 'handleAuthorizationResults']); +for (const [name, handler] of Object.entries(api)) { + if (typeof handler === 'function' && !NON_INSTRUMENTED_KEYS.has(name)) { + api[name] = instrumentApiMethod(handler, name); + } +} + module.exports = api; diff --git a/lib/server.js b/lib/server.js index a5bb364b61..a3ccdc29fa 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,11 +1,13 @@ const http = require('http'); const https = require('https'); const cluster = require('cluster'); +const { promisify } = require('util'); const { series } = require('async'); const arsenal = require('arsenal'); const { setServerHeader } = arsenal.s3routes.routesUtils; const { RedisClient, StatsClient } = arsenal.metrics; const monitoringClient = require('./utilities/monitoringHandler'); +const tracing = require('arsenal/build/lib/tracing'); const logger = require('./utilities/logger'); const { internalHandlers } = require('./utilities/internalHandlers'); @@ -15,15 +17,11 @@ const { blacklistedPrefixes } = require('../constants'); const api = require('./api/api'); const dataWrapper = require('./data/wrapper'); const kms = require('./kms/wrapper'); -const locationStorageCheck = - require('./api/apiUtils/object/locationStorageCheck'); +const locationStorageCheck = require('./api/apiUtils/object/locationStorageCheck'); const vault = require('./auth/vault'); const metadata = require('./metadata/wrapper'); const { initManagement } = require('./management'); -const { - initManagementClient, - isManagementAgentUsed, -} = require('./management/agentClient'); +const { initManagementClient, isManagementAgentUsed } = require('./management/agentClient'); const { startCleanupJob } = require('./api/apiUtils/rateLimit/cleanup'); const { startRefillJob, stopRefillJob } = require('./api/apiUtils/rateLimit/refillJob'); @@ -46,8 +44,7 @@ updateAllEndpoints(); _config.on('location-constraints-update', () => { if (implName === 'multipleBackends') { const clients = parseLC(_config, vault); - client = new MultipleBackendGateway( - clients, metadata, locationStorageCheck); + client = new MultipleBackendGateway(clients, metadata, locationStorageCheck); } }); @@ -59,8 +56,7 @@ if (_config.localCache) { // stats client const STATS_INTERVAL = 5; // 5 seconds const STATS_EXPIRY = 30; // 30 seconds -const statsClient = new StatsClient(localCacheClient, STATS_INTERVAL, - STATS_EXPIRY); +const statsClient = new StatsClient(localCacheClient, STATS_INTERVAL, STATS_EXPIRY); const enableRemoteManagement = true; class S3Server { @@ -84,7 +80,7 @@ class S3Server { process.on('SIGHUP', this.cleanUp.bind(this)); process.on('SIGQUIT', this.cleanUp.bind(this)); process.on('SIGTERM', this.cleanUp.bind(this)); - process.on('SIGPIPE', () => { }); + process.on('SIGPIPE', () => {}); // This will pick up exceptions up the stack process.on('uncaughtException', err => { // If just send the error object results in empty @@ -95,7 +91,7 @@ class S3Server { workerId: this.worker ? this.worker.id : undefined, workerPid: this.worker ? this.worker.process.pid : undefined, }); - this.caughtExceptionShutdown(); + void this.caughtExceptionShutdown(); }); this.started = false; } @@ -130,9 +126,10 @@ class S3Server { const requestStartTime = process.hrtime.bigint(); // Skip server access logs for heartbeat. - const isLoggingEnabled = _config.serverAccessLogs - && (_config.serverAccessLogs.mode === serverAccessLogsModes.LOG_ONLY - || _config.serverAccessLogs.mode === serverAccessLogsModes.ENABLED); + const isLoggingEnabled = + _config.serverAccessLogs && + (_config.serverAccessLogs.mode === serverAccessLogsModes.LOG_ONLY || + _config.serverAccessLogs.mode === serverAccessLogsModes.ENABLED); const isInternalRoute = req.url.startsWith('/_'); const isBackbeatRoute = req.url.startsWith('/_/backbeat/'); if (isLoggingEnabled && (!isInternalRoute || isBackbeatRoute)) { @@ -176,9 +173,7 @@ class S3Server { labels.action = req.apiMethod; } monitoringClient.httpRequestsTotal.labels(labels).inc(); - monitoringClient.httpRequestDurationSeconds - .labels(labels) - .observe(responseTimeInNs / 1e9); + monitoringClient.httpRequestDurationSeconds.labels(labels).observe(responseTimeInNs / 1e9); monitoringClient.httpActiveRequests.dec(); }; res.on('close', monitorEndOfRequest); @@ -231,14 +226,13 @@ class S3Server { }; let reqUids = req.headers['x-scal-request-uids']; - if (reqUids !== undefined && !/*isValidReqUids*/(reqUids.length < 128)) { + if (reqUids !== undefined && !(/*isValidReqUids*/ (reqUids.length < 128))) { // simply ignore invalid id (any user can provide an // invalid request ID through a crafted header) reqUids = undefined; } - const log = (reqUids !== undefined ? - logger.newRequestLoggerFromSerializedUids(reqUids) : - logger.newRequestLogger()); + const log = + reqUids !== undefined ? logger.newRequestLoggerFromSerializedUids(reqUids) : logger.newRequestLogger(); log.end().addDefaultFields(clientInfo); log.debug('received admin request', clientInfo); @@ -292,8 +286,7 @@ class S3Server { server.requestTimeout = 0; // disabling request timeout server.on('connection', socket => { - socket.on('error', err => logger.info('request rejected', - { error: err })); + socket.on('error', err => logger.info('request rejected', { error: err })); }); // https://nodejs.org/dist/latest-v6.x/ @@ -309,8 +302,11 @@ class S3Server { }; const { address } = addr; logger.info('server started', { - address, port, - pid: process.pid, serverIP: address, serverPort: port + address, + port, + pid: process.pid, + serverIP: address, + serverPort: port, }); }); @@ -323,32 +319,43 @@ class S3Server { this.servers.push(server); } - /* - * This exits the running process properly. - */ - cleanUp() { + async cleanUp() { logger.info('server shutting down'); - // Stop token refill job if running if (this.config.rateLimiting?.enabled) { stopRefillJob(logger); } - Promise.all(this.servers.map(server => - new Promise(resolve => server.close(resolve)) - )).then(() => process.exit(0)); + try { + await Promise.all(this.servers.map(server => promisify(server.close.bind(server))())); + } finally { + try { + await tracing.close(); + } finally { + process.exit(0); + } + } } - caughtExceptionShutdown() { + async caughtExceptionShutdown() { if (!this.cluster) { - process.exit(1); + try { + await tracing.close(); + } finally { + process.exit(1); + } + return; } logger.error('shutdown of worker due to exception', { workerId: this.worker ? this.worker.id : undefined, workerPid: this.worker ? this.worker.process.pid : undefined, }); - // Will close all servers, cause disconnect event on primary and kill - // worker process with 'SIGTERM'. if (this.worker) { - this.worker.kill(); + try { + await tracing.close(); + } finally { + // Will close all servers, cause disconnect event on primary and kill + // worker process with 'SIGTERM'. + this.worker.kill(); + } } } @@ -363,10 +370,7 @@ class S3Server { } initiateStartup(log) { - series([ - next => metadata.setup(next), - next => clientCheck(true, log, next), - ], (err, results) => { + series([next => metadata.setup(next), next => clientCheck(true, log, next)], (err, results) => { if (err) { log.warn('initial health check failed, delaying startup', { error: err, @@ -417,8 +421,10 @@ class S3Server { try { logger.info('ServerAccessLogger config', { config: _config.serverAccessLogs }); - if (_config.serverAccessLogs.mode === serverAccessLogsModes.LOG_ONLY - || _config.serverAccessLogs.mode === serverAccessLogsModes.ENABLED) { + if ( + _config.serverAccessLogs.mode === serverAccessLogsModes.LOG_ONLY || + _config.serverAccessLogs.mode === serverAccessLogsModes.ENABLED + ) { var serverAccessLogger = new ServerAccessLogger( _config.serverAccessLogs.outputFile, _config.serverAccessLogs.highWaterMarkBytes, @@ -434,7 +440,6 @@ class S3Server { logger.error('ServerAccessLogger creation error', error); } - this.started = true; }); } @@ -490,8 +495,7 @@ function main() { }); const metricServer = new S3Server(_config); - metricServer.startServer(_config.metricsListenOn, - _config.metricsPort, metricServer.routeAdminRequest); + metricServer.startServer(_config.metricsListenOn, _config.metricsPort, metricServer.routeAdminRequest); } if (_config.isCluster && cluster.isWorker) { const server = new S3Server(_config, cluster.worker); diff --git a/package.json b/package.json index 999993962b..4764110fdf 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,12 @@ "@aws-sdk/crc64-nvme-crt": "^3.989.0", "@azure/storage-blob": "^12.28.0", "@hapi/joi": "^17.1.1", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/instrumentation-http": "~0.218.0", + "@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.4", + "arsenal": "git+https://github.com/scality/Arsenal#improvement/ARSN-586/otel-tracing-module", "async": "2.6.4", "bucketclient": "scality/bucketclient#8.2.7", "bufferutil": "^4.0.8", diff --git a/tests/unit/server.js b/tests/unit/server.js index e0d8604e30..402a0bb989 100644 --- a/tests/unit/server.js +++ b/tests/unit/server.js @@ -8,6 +8,7 @@ const arsenal = require('arsenal'); const uuid = require('uuid'); const logger = require('../../lib/utilities/logger'); const { config: defaultConfig } = require('../../lib/Config'); +const tracing = require('arsenal/build/lib/tracing'); const { S3Server } = require('../../lib/server'); describe('S3Server', () => { @@ -26,7 +27,7 @@ describe('S3Server', () => { internalPort: undefined, internalListenOn: [], metricsListenOn: [], - metricsPort: 8002 + metricsPort: 8002, }; server = new S3Server(config); @@ -38,14 +39,15 @@ describe('S3Server', () => { sinon.restore(); }); - const waitReady = () => new Promise(resolve => { - const interval = setInterval(() => { - if (server.started) { - clearInterval(interval); - resolve(); - } - }, 100); - }); + const waitReady = () => + new Promise(resolve => { + const interval = setInterval(() => { + if (server.started) { + clearInterval(interval); + resolve(); + } + }, 100); + }); describe('initiateStartup', () => { beforeEach(() => { @@ -56,12 +58,13 @@ describe('S3Server', () => { // `sinon` matcher to match when the callback argument actually invokes the expected // function - const wrapperFor = expected => sinon.match(actual => { - const req = uuid.v4(); - const res = uuid.v4(); - actual(req, res); - return expected.calledWith(req, res); - }); + const wrapperFor = expected => + sinon.match(actual => { + const req = uuid.v4(); + const res = uuid.v4(); + actual(req, res); + return expected.calledWith(req, res); + }); it('should start API server with default port if no listenOn is provided', async () => { config.port = 8000; @@ -73,13 +76,12 @@ describe('S3Server', () => { assert.strictEqual(startServerStub.callCount, 2); assert(startServerStub.calledWith(wrapperFor(server.routeRequest), 8000)); assert(startServerStub.calledWith(wrapperFor(server.routeAdminRequest))); - }); - + it('should start API servers from listenOn array', async () => { config.listenOn = [ { port: 8000, ip: '127.0.0.1' }, - { port: 8001, ip: '0.0.0.0' } + { port: 8001, ip: '0.0.0.0' }, ]; config.port = 9999; // Should be ignored since listenOn is provided @@ -93,7 +95,7 @@ describe('S3Server', () => { assert(startServerStub.calledWith(wrapperFor(server.routeAdminRequest))); assert.strictEqual(startServerStub.neverCalledWith(sinon.any, 9999), true); }); - + it('should start internal API server with internalPort if no internalListenOn is provided', async () => { config.internalPort = 9000; @@ -104,11 +106,11 @@ describe('S3Server', () => { assert.strictEqual(startServerStub.callCount, 2); assert(startServerStub.calledWith(wrapperFor(server.internalRouteRequest), 9000)); }); - + it('should start internal API servers from internalListenOn array', async () => { config.internalListenOn = [ { port: 9000, ip: '127.0.0.1' }, - { port: 9001, ip: '0.0.0.0' } + { port: 9001, ip: '0.0.0.0' }, ]; config.internalPort = 9999; // Should be ignored since internalListenOn is provided @@ -122,29 +124,29 @@ describe('S3Server', () => { assert(startServerStub.calledWith(wrapperFor(server.routeAdminRequest))); assert.strictEqual(startServerStub.neverCalledWith(sinon.any, 9999), true); }); - + it('should start metrics server with metricsPort if no metricsListenOn is provided', async () => { config.metricsPort = 8012; server.initiateStartup(log); await waitReady(); - + assert.strictEqual(startServerStub.callCount, 1); assert(startServerStub.calledWith(wrapperFor(server.routeAdminRequest), 8012)); }); - + it('should start metrics servers from metricsListenOn array', async () => { config.metricsListenOn = [ { port: 8002, ip: '127.0.0.1' }, - { port: 8003, ip: '0.0.0.0' } + { port: 8003, ip: '0.0.0.0' }, ]; config.metricsPort = 9999; // Should be ignored since metricsListenOn is provided server.initiateStartup(log); await waitReady(); - + assert.strictEqual(startServerStub.callCount, 2); assert(startServerStub.calledWith(wrapperFor(server.routeAdminRequest), 8002, '127.0.0.1')); assert(startServerStub.calledWith(wrapperFor(server.routeAdminRequest), 8003, '0.0.0.0')); @@ -169,10 +171,10 @@ describe('S3Server', () => { describe('internalRouteRequest', () => { const resp = { - on: () => { }, - setHeader: () => { }, - writeHead: () => { }, - end: () => { }, + on: () => {}, + setHeader: () => {}, + writeHead: () => {}, + end: () => {}, }; let req; @@ -181,7 +183,7 @@ describe('S3Server', () => { req = { headers: {}, socket: { - setNoDelay: () => { }, + setNoDelay: () => {}, }, url: 'http://localhost:8000', }; @@ -219,7 +221,7 @@ describe('S3Server request timeout', () => { beforeEach(() => { sandbox = sinon.createSandbox(); - + // Create a mock server to capture the requestTimeout setting mockServer = { requestTimeout: null, @@ -227,7 +229,7 @@ describe('S3Server request timeout', () => { listen: sandbox.stub(), address: sandbox.stub().returns({ address: '127.0.0.1', port: 8000 }), }; - + // Mock server creation to return our mock sandbox.stub(http, 'createServer').returns(mockServer); sandbox.stub(https, 'createServer').returns(mockServer); @@ -240,13 +242,93 @@ describe('S3Server request timeout', () => { it('should set server.requestTimeout to 0 when starting server', () => { const server = new S3Server({ ...defaultConfig, - https: false + https: false, }); - + // Call _startServer which should set requestTimeout = 0 server._startServer(() => {}, 8000, '127.0.0.1'); - + // Verify that requestTimeout was set to 0 assert.strictEqual(mockServer.requestTimeout, 0); }); }); + +describe('S3Server shutdown', () => { + let server; + let tracingCloseStub; + let exitStub; + + beforeEach(() => { + server = new S3Server({ + ...defaultConfig, + port: undefined, + listenOn: [], + internalPort: undefined, + internalListenOn: [], + metricsListenOn: [], + metricsPort: 8002, + }); + // S3Server.cleanUp iterates this.servers and closes each — empty + // array makes the Promise.all in cleanUp resolve immediately. + server.servers = []; + // Avoid touching the rateLimiting refill-job teardown path. + server.config = { ...server.config, rateLimiting: { enabled: false } }; + + tracingCloseStub = sinon.stub(tracing, 'close').resolves(); + exitStub = sinon.stub(process, 'exit'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should flush OTEL via tracing.close before process.exit(0) on cleanUp', async () => { + await server.cleanUp(); + sinon.assert.callOrder(tracingCloseStub, exitStub); + assert.strictEqual(tracingCloseStub.callCount, 1); + assert.strictEqual(exitStub.callCount, 1); + assert.strictEqual(exitStub.firstCall.args[0], 0); + }); + + it('should flush OTEL on cleanUp even when a server.close errors', async () => { + const erroringServer = { + close(cb) { + cb(new Error('socket already closed')); + }, + }; + server.servers = [erroringServer]; + await assert.rejects(() => server.cleanUp(), /socket already closed/); + assert.strictEqual(tracingCloseStub.callCount, 1); + assert.strictEqual(exitStub.callCount, 1); + assert.strictEqual(exitStub.firstCall.args[0], 0); + }); + + it('should still exit(0) on cleanUp even if tracing.close rejects', async () => { + tracingCloseStub.rejects(new Error('flush failed')); + await assert.rejects(() => server.cleanUp(), /flush failed/); + assert.strictEqual(tracingCloseStub.callCount, 1); + assert.strictEqual(exitStub.firstCall.args[0], 0); + }); + + it('should flush OTEL before process.exit(1) on caughtExceptionShutdown (non-cluster)', async () => { + server.cluster = false; + await server.caughtExceptionShutdown(); + sinon.assert.callOrder(tracingCloseStub, exitStub); + assert.strictEqual(tracingCloseStub.callCount, 1); + assert.strictEqual(exitStub.firstCall.args[0], 1); + }); + + it('should flush OTEL before worker.kill on caughtExceptionShutdown (cluster worker)', async () => { + const killStub = sinon.stub(); + server.cluster = true; + server.worker = { id: 1, process: { pid: 12345 }, kill: killStub }; + + await server.caughtExceptionShutdown(); + + sinon.assert.callOrder(tracingCloseStub, killStub); + assert.strictEqual(tracingCloseStub.callCount, 1); + assert.strictEqual(killStub.callCount, 1); + // The non-cluster process.exit branch is skipped in this path. + assert.strictEqual(exitStub.callCount, 0); + }); +}); diff --git a/yarn.lock b/yarn.lock index 6a6ab403de..38101ab35c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2987,6 +2987,24 @@ "@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" @@ -3175,6 +3193,11 @@ "@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" @@ -3212,21 +3235,409 @@ dependencies: semver "^7.3.5" +"@opentelemetry/api-logs@0.216.0": + version "0.216.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.216.0.tgz#7aa4b485ea2f2e21ffcb94120136c72f718c2eaf" + integrity sha512-KmGTgvxTJ0J01d4mOeX1wMV5NUTNf9HebIuOOGDfIn0a/IrnXIQbOnlylDyl9tkDv4h0DUpdI/GqCdLzfTkUXg== + dependencies: + "@opentelemetry/api" "^1.3.0" + +"@opentelemetry/api-logs@0.218.0": + version "0.218.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz#7b9818e8dfdf1d3dcab88bfe4d6724f2f831f7ec" + integrity sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw== + dependencies: + "@opentelemetry/api" "^1.3.0" + +"@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.1.tgz#c1b0346de336ba55af2d5a7970882037baedec05" + integrity sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q== + "@opentelemetry/api@^1.4.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/api@^1.9.0": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.1.tgz#c1b0346de336ba55af2d5a7970882037baedec05" - integrity sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q== +"@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" + integrity sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw== + 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" + integrity sha512-x9djaqdzpT8WAboep1H9nCAQ1E+MMsm08TNfA02TqM3bNNddZeiim+E3KMWVQFaX6JpUy7V0nm/wfN/K2Em+Zw== + dependencies: + "@opentelemetry/core" "2.7.1" + "@opentelemetry/instrumentation" "0.218.0" + "@opentelemetry/semantic-conventions" "^1.29.0" + forwarded-parse "2.1.2" + +"@opentelemetry/instrumentation-ioredis@~0.64.0": + version "0.64.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.64.0.tgz#b02a214263d5f3a6848f6911fe23f505ff6a9181" + integrity sha512-GQ36/amPdO1rVPXgrRZNnd6MktqwDcYalzpMRe9m55b3EwX4pazq8VB3qfTH67xboElqm/B9J1tBEnbQmcvaww== + dependencies: + "@opentelemetry/instrumentation" "^0.216.0" + "@opentelemetry/redis-common" "^0.38.3" + "@opentelemetry/semantic-conventions" "^1.33.0" + +"@opentelemetry/instrumentation-mongodb@~0.69.0": + version "0.69.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.69.0.tgz#b03010de06c816f973038165cfe4cea852b3d43f" + integrity sha512-kj8w2FN2/z0VIXMqcdAdJYtc0udH41Sb485jC7tLl0X4+OD3KLjyhjVoZOXH/gxp+N+BQY6SKgMNC0yi8nok9A== + dependencies: + "@opentelemetry/instrumentation" "^0.216.0" + "@opentelemetry/semantic-conventions" "^1.33.0" + +"@opentelemetry/instrumentation@0.218.0": + version "0.218.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.218.0.tgz#fcceb4ffb45f99c0d292600769150fc5944dc3e9" + integrity sha512-mIZil8Es+sYDK5m+DQiwAwF57F14TF2YlEqvIjZ/RQWcxDBwRGsKfdK2Tv65OU9meQKCMzSIFS9mxAcnAb6Bkg== + dependencies: + "@opentelemetry/api-logs" "0.218.0" + import-in-the-middle "^3.0.0" + require-in-the-middle "^8.0.0" + +"@opentelemetry/instrumentation@^0.216.0": + version "0.216.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.216.0.tgz#048777113d0cb7f6b6613025055fc0d2f822bf49" + integrity sha512-BrY0b2K81OLgwBcFxY2wKgPFhq4DpindT+S83++zquc5Rtb2SuYLMkujgDRWMgZQDz+OT+dfvPnMGADPuw4FDw== + dependencies: + "@opentelemetry/api-logs" "0.216.0" + 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" + integrity sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA== "@pkgjs/parseargs@^0.11.0": version "0.11.0" 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" @@ -5739,6 +6150,13 @@ 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" @@ -5864,6 +6282,11 @@ accesscontrol@^2.2.1: dependencies: notation "^1.3.6" +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -5874,6 +6297,11 @@ acorn@^8.14.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== +acorn@^8.15.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -6148,9 +6576,9 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.4.4": +"arsenal@git+https://github.com/scality/Arsenal#improvement/ARSN-586/otel-tracing-module": version "8.4.4" - resolved "git+https://github.com/scality/Arsenal#a1fa5b412c0f634fa87fc7ccd7b086973d2f8d87" + resolved "git+https://github.com/scality/Arsenal#15c51a2585b26a0f4afb291ea7539ef093b9e40e" dependencies: "@aws-sdk/client-kms" "^3.975.0" "@aws-sdk/client-s3" "^3.975.0" @@ -6191,6 +6619,10 @@ 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: @@ -6689,6 +7121,11 @@ chownr@^3.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== +cjs-module-lexer@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca" + integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -8036,6 +8473,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +forwarded-parse@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325" + integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw== + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -8568,6 +9010,16 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-in-the-middle@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz#8a0a1230c9b865c0e12698171646ae1e3fff691d" + integrity sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA== + dependencies: + acorn "^8.15.0" + acorn-import-attributes "^1.9.5" + cjs-module-lexer "^2.2.0" + module-details-from-path "^1.0.4" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -9552,6 +10004,11 @@ 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" @@ -9669,6 +10126,11 @@ 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" @@ -10019,6 +10481,11 @@ mocha@^11.7.5: yargs-parser "^21.1.1" yargs-unparser "^2.0.0" +module-details-from-path@^1.0.3, module-details-from-path@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz#b662fdcd93f6c83d3f25289da0ce81c8d9685b94" + integrity sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w== + moment@^2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" @@ -10762,6 +11229,24 @@ 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" @@ -11046,6 +11531,14 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-in-the-middle@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz#dbde2587f669398626d56b20c868ab87bf01cce4" + integrity sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ== + dependencies: + debug "^4.3.5" + module-details-from-path "^1.0.3" + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -12138,6 +12631,11 @@ 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" @@ -12616,6 +13114,11 @@ 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"