diff --git a/.github/actions/node/21/action.yml b/.github/actions/node/21/action.yml deleted file mode 100644 index 0792701558f..00000000000 --- a/.github/actions/node/21/action.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: Node 21 -runs: - using: composite - steps: - - uses: actions/setup-node@v3 - with: - node-version: '21' diff --git a/.github/workflows/appsec.yml b/.github/workflows/appsec.yml index 1f59c0ecd79..aabc5be4d29 100644 --- a/.github/workflows/appsec.yml +++ b/.github/workflows/appsec.yml @@ -228,6 +228,6 @@ jobs: - run: yarn install - uses: ./.github/actions/node/oldest - run: yarn test:appsec:plugins:ci - - uses: ./.github/actions/node/21 + - uses: ./.github/actions/node/latest - run: yarn test:appsec:plugins:ci - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/profiling.yml b/.github/workflows/profiling.yml index 52e900dc27d..1f54c5a4c51 100644 --- a/.github/workflows/profiling.yml +++ b/.github/workflows/profiling.yml @@ -34,7 +34,7 @@ jobs: - uses: ./.github/actions/node/20 - run: yarn test:profiler:ci - run: yarn test:integration:profiler - - uses: ./.github/actions/node/21 + - uses: ./.github/actions/node/latest - run: yarn test:profiler:ci - run: yarn test:integration:profiler - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/tracing.yml b/.github/workflows/tracing.yml index 5bdf8e4a2cc..cd54e25eb7e 100644 --- a/.github/workflows/tracing.yml +++ b/.github/workflows/tracing.yml @@ -31,7 +31,7 @@ jobs: - run: yarn test:trace:core:ci - uses: ./.github/actions/node/20 - run: yarn test:trace:core:ci - - uses: ./.github/actions/node/21 + - uses: ./.github/actions/node/latest - run: yarn test:trace:core:ci - uses: codecov/codecov-action@v3 diff --git a/README.md b/README.md index 021604873f2..349b0a51478 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,11 @@ If you would like to trace your bundled application then please read this page o ## Security Vulnerabilities -Please refer to the [SECURITY.md](https://github.com/DataDog/dd-trace-js/blob/master/SECURITY.md) document if you have found a security issue. \ No newline at end of file +Please refer to the [SECURITY.md](https://github.com/DataDog/dd-trace-js/blob/master/SECURITY.md) document if you have found a security issue. + +## Datadog With OpenTelemetery + +Please refer to the [Node.js Custom Instrumentation using OpenTelemetry API](https://docs.datadoghq.com/tracing/trace_collection/custom_instrumentation/nodejs/otel/) document. It includes information on how to use the OpenTelemetry API with dd-trace-js + +Note that our internal implementation of the OpenTelemetry API is currently set within the version range `>=1.0.0 <1.9.0`. This range will be updated at a regular cadence therefore, we recommend updating your tracer to the latest release to ensure up to date support. + diff --git a/docs/test.ts b/docs/test.ts index b30ed25b98f..abdac7a7daf 100644 --- a/docs/test.ts +++ b/docs/test.ts @@ -260,6 +260,7 @@ tracer.use('aws-sdk', awsSdkOptions); tracer.use('bunyan'); tracer.use('couchbase'); tracer.use('cassandra-driver'); +tracer.use('child_process'); tracer.use('connect'); tracer.use('connect', httpServerOptions); tracer.use('cypress'); diff --git a/index.d.ts b/index.d.ts index 68959b6268a..6684011efd3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -144,6 +144,7 @@ interface Plugins { "aws-sdk": tracer.plugins.aws_sdk; "bunyan": tracer.plugins.bunyan; "cassandra-driver": tracer.plugins.cassandra_driver; + "child_process": tracer.plugins.child_process; "connect": tracer.plugins.connect; "couchbase": tracer.plugins.couchbase; "cucumber": tracer.plugins.cucumber; @@ -1207,6 +1208,12 @@ declare namespace tracer { */ interface cassandra_driver extends Instrumentation {} + /** + * This plugin automatically instruments the + * [child_process](https://nodejs.org/api/child_process.html) module. + */ + interface child_process extends Instrumentation {} + /** * This plugin automatically instruments the * [connect](https://github.com/senchalabs/connect) module. diff --git a/init.js b/init.js index 3e0e977da36..328e287f506 100644 --- a/init.js +++ b/init.js @@ -1,7 +1,31 @@ 'use strict' -const tracer = require('.') +const path = require('path') +const Module = require('module') -tracer.init() +let initBailout = false -module.exports = tracer +if (process.env.DD_INJECTION_ENABLED) { + // If we're running via single-step install, and we're not in the app's + // node_modules, then we should not initialize the tracer. This prevents + // single-step-installed tracer from clobbering the manually-installed tracer. + let resolvedInApp + const entrypoint = process.argv[1] + try { + resolvedInApp = Module.createRequire(entrypoint).resolve('dd-trace') + } catch (e) { + // Ignore. If we can't resolve the module, we assume it's not in the app. + } + if (resolvedInApp) { + const ourselves = path.join(__dirname, 'index.js') + if (ourselves !== resolvedInApp) { + initBailout = true + } + } +} + +if (!initBailout) { + const tracer = require('.') + tracer.init() + module.exports = tracer +} diff --git a/integration-tests/ci-visibility.spec.js b/integration-tests/ci-visibility.spec.js index 8918e6e63c1..e4c2eb65e70 100644 --- a/integration-tests/ci-visibility.spec.js +++ b/integration-tests/ci-visibility.spec.js @@ -45,7 +45,7 @@ const mochaCommonOptions = { const jestCommonOptions = { name: 'jest', - dependencies: ['jest', 'chai@v4', 'jest-jasmine2'], + dependencies: ['jest', 'chai@v4', 'jest-jasmine2', 'jest-environment-jsdom'], expectedStdout: 'Test Suites: 2 passed', expectedCoverageFiles: [ 'ci-visibility/test/sum.js', @@ -328,6 +328,7 @@ testFrameworks.forEach(({ done() }).catch(done) }) + it('reports tests when using agentless', (done) => { childProcess = fork(testFile, { cwd, @@ -347,6 +348,7 @@ testFrameworks.forEach(({ done() }).catch(done) }) + it('reports tests when using evp proxy', (done) => { childProcess = fork(testFile, { cwd, @@ -1414,6 +1416,88 @@ testFrameworks.forEach(({ eventsPromise.then(() => done()).catch(done) }) }) + + it('works with jsdom', (done) => { + const envVars = reportingOption === 'agentless' + ? getCiVisAgentlessConfig(receiver.port) + : getCiVisEvpProxyConfig(receiver.port) + if (reportingOption === 'evp proxy') { + receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) + } + // Tests from ci-visibility/test/ci-visibility-test-2.js will be considered new + receiver.setKnownTests({ + [name]: { + 'ci-visibility/test/ci-visibility-test.js': ['ci visibility can report tests'] + } + }) + const NUM_RETRIES_EFD = 3 + receiver.setSettings({ + itr_enabled: false, + code_coverage: false, + tests_skipping: false, + early_flake_detection: { + enabled: true, + slow_test_retries: { + '5s': NUM_RETRIES_EFD + }, + faulty_session_threshold: 100 + } + }) + + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const tests = events.filter(event => event.type === 'test').map(event => event.content) + + // no other tests are considered new + const oldTests = tests.filter(test => + test.meta[TEST_SUITE] === 'ci-visibility/test/ci-visibility-test.js' + ) + oldTests.forEach(test => { + assert.notProperty(test.meta, TEST_IS_NEW) + }) + assert.equal(oldTests.length, 1) + + const newTests = tests.filter(test => + test.meta[TEST_SUITE] === 'ci-visibility/test/ci-visibility-test-2.js' + ) + newTests.forEach(test => { + assert.propertyVal(test.meta, TEST_IS_NEW, 'true') + }) + const retriedTests = newTests.filter(test => test.meta[TEST_IS_RETRY] === 'true') + // all but one has been retried + assert.equal( + newTests.length - 1, + retriedTests.length + ) + assert.equal(retriedTests.length, NUM_RETRIES_EFD) + // Test name does not change + newTests.forEach(test => { + assert.equal(test.meta[TEST_NAME], 'ci visibility 2 can report tests 2') + }) + }) + + childProcess = exec( + runTestsWithCoverageCommand, + { + cwd, + env: { + ...envVars, + TESTS_TO_RUN: 'test/ci-visibility-test', + ENABLE_JSDOM: true, + DD_TRACE_DEBUG: 1, + DD_TRACE_LOG_LEVEL: 'warn' + }, + stdio: 'inherit' + } + ) + childProcess.on('exit', () => { + eventsPromise.then(() => { + done() + }).catch(done) + }) + }) } }) }) diff --git a/integration-tests/ci-visibility/run-jest.js b/integration-tests/ci-visibility/run-jest.js index c8a740ea331..a1e21ebd488 100644 --- a/integration-tests/ci-visibility/run-jest.js +++ b/integration-tests/ci-visibility/run-jest.js @@ -20,6 +20,10 @@ if (process.env.OLD_RUNNER) { options.testRunner = 'jest-jasmine2' } +if (process.env.ENABLE_JSDOM) { + options.testEnvironment = 'jsdom' +} + jest.runCLI( options, options.projects diff --git a/integration-tests/ci-visibility/run-jest.mjs b/integration-tests/ci-visibility/run-jest.mjs index a35ddda382c..a9ecb24d0c6 100644 --- a/integration-tests/ci-visibility/run-jest.mjs +++ b/integration-tests/ci-visibility/run-jest.mjs @@ -22,6 +22,10 @@ if (process.env.OLD_RUNNER) { options.testRunner = 'jest-jasmine2' } +if (process.env.ENABLE_JSDOM) { + options.testEnvironment = 'jsdom' +} + jest.runCLI( options, options.projects diff --git a/integration-tests/init.spec.js b/integration-tests/init.spec.js new file mode 100644 index 00000000000..d093d1d53df --- /dev/null +++ b/integration-tests/init.spec.js @@ -0,0 +1,57 @@ +const { + createSandbox, + spawnProc +} = require('./helpers') +const { assert } = require('chai') +const path = require('path') + +const DD_INJECTION_ENABLED = 'tracing' + +describe('init.js', () => { + let cwd, proc, sandbox + + async function runTest (cwd, env, expected) { + return new Promise((resolve, reject) => { + spawnProc(path.join(cwd, 'init/index.js'), { cwd, env }, data => { + try { + assert.strictEqual(data.toString(), expected) + resolve() + } catch (e) { + reject(e) + } + }).then(subproc => { + proc = subproc + }) + }) + } + + before(async () => { + sandbox = await createSandbox() + cwd = sandbox.folder + }) + afterEach(() => { + proc && proc.kill() + }) + after(() => { + return sandbox.remove() + }) + + context('when dd-trace is not in the app dir', () => { + const NODE_OPTIONS = `--require ${path.join(__dirname, '..', 'init.js')}` + it('should initialize the tracer, if no DD_INJECTION_ENABLED', () => { + return runTest(cwd, { NODE_OPTIONS }, 'true\n') + }) + it('should not initialize the tracer, if DD_INJECTION_ENABLED', () => { + return runTest(cwd, { NODE_OPTIONS, DD_INJECTION_ENABLED }, 'false\n') + }) + }) + context('when dd-trace in the app dir', () => { + const NODE_OPTIONS = '--require dd-trace/init.js' + it('should initialize the tracer, if no DD_INJECTION_ENABLED', () => { + return runTest(cwd, { NODE_OPTIONS }, 'true\n') + }) + it('should initialize the tracer, if DD_INJECTION_ENABLED', () => { + return runTest(cwd, { NODE_OPTIONS, DD_INJECTION_ENABLED }, 'true\n') + }) + }) +}) diff --git a/integration-tests/init/index.js b/integration-tests/init/index.js new file mode 100644 index 00000000000..9c9c5d731d9 --- /dev/null +++ b/integration-tests/init/index.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line no-console +console.log(!!global._ddtrace) +process.exit() diff --git a/package.json b/package.json index 4ff6dcdf759..39ff5019f94 100644 --- a/package.json +++ b/package.json @@ -74,9 +74,9 @@ "@datadog/native-iast-rewriter": "2.3.1", "@datadog/native-iast-taint-tracking": "2.1.0", "@datadog/native-metrics": "^2.0.0", - "@datadog/pprof": "5.2.0", + "@datadog/pprof": "5.3.0", "@datadog/sketches-js": "^2.1.0", - "@opentelemetry/api": "^1.0.0", + "@opentelemetry/api": ">=1.0.0 <1.9.0", "@opentelemetry/core": "^1.14.0", "crypto-randomuuid": "^1.0.0", "dc-polyfill": "^0.1.4", diff --git a/packages/datadog-esbuild/index.js b/packages/datadog-esbuild/index.js index ad1a0d1efdf..95a0e8ddd16 100644 --- a/packages/datadog-esbuild/index.js +++ b/packages/datadog-esbuild/index.js @@ -4,6 +4,7 @@ const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations.js') const hooks = require('../datadog-instrumentations/src/helpers/hooks.js') +const extractPackageAndModulePath = require('../datadog-instrumentations/src/utils/src/extract-package-and-module-path') for (const hook of Object.values(hooks)) { hook() @@ -21,7 +22,6 @@ for (const instrumentation of Object.values(instrumentations)) { } } -const NM = 'node_modules/' const INSTRUMENTED = Object.keys(instrumentations) const RAW_BUILTINS = require('module').builtinModules const CHANNEL = 'dd-trace:bundler:load' @@ -181,33 +181,3 @@ function dotFriendlyResolve (path, directory) { return require.resolve(path, { paths: [directory] }) } - -/** - * For a given full path to a module, - * return the package name it belongs to and the local path to the module - * input: '/foo/node_modules/@co/stuff/foo/bar/baz.js' - * output: { pkg: '@co/stuff', path: 'foo/bar/baz.js' } - */ -function extractPackageAndModulePath (fullPath) { - const nm = fullPath.lastIndexOf(NM) - if (nm < 0) { - return { pkg: null, path: null } - } - - const subPath = fullPath.substring(nm + NM.length) - const firstSlash = subPath.indexOf('/') - - if (subPath[0] === '@') { - const secondSlash = subPath.substring(firstSlash + 1).indexOf('/') - - return { - pkg: subPath.substring(0, firstSlash + 1 + secondSlash), - path: subPath.substring(firstSlash + 1 + secondSlash + 1) - } - } - - return { - pkg: subPath.substring(0, firstSlash), - path: subPath.substring(firstSlash + 1) - } -} diff --git a/packages/datadog-instrumentations/src/check_require_cache.js b/packages/datadog-instrumentations/src/check_require_cache.js new file mode 100644 index 00000000000..b33ed59f18b --- /dev/null +++ b/packages/datadog-instrumentations/src/check_require_cache.js @@ -0,0 +1,41 @@ +'use strict' + +/* eslint-disable no-console */ + +const extractPackageAndModulePath = require('./utils/src/extract-package-and-module-path') + +/** + * The lowest hanging fruit to debug an app that isn't tracing + * properly is to check that it is loaded before any modules + * that need to be instrumented. This function checks the + * `require.cache` to see if any supported packages have + * already been required and prints a warning. + * + * Note that this only going to work for modules within npm + * packages, like `express`, and not internal modules, like + * `http`. + * + * The output isn't necessarily 100% perfect. For example if the + * app loads a package we instrument but outside of an + * unsupported version then a warning would still be displayed. + * This is OK as the tracer should be loaded earlier anyway. + */ +module.exports = function () { + const packages = require('../../datadog-instrumentations/src/helpers/hooks') + const naughties = new Set() + let didWarn = false + + for (const pathToModule of Object.keys(require.cache)) { + const { pkg } = extractPackageAndModulePath(pathToModule) + + if (naughties.has(pkg)) continue + if (!(pkg in packages)) continue + + console.error(`Warning: Package '${pkg}' was loaded before dd-trace! This may break instrumentation.`) + + naughties.add(pkg) + didWarn = true + } + + if (didWarn) console.error('Warning: Please ensure dd-trace is loaded before other modules.') +} diff --git a/packages/datadog-instrumentations/src/helpers/hooks.js b/packages/datadog-instrumentations/src/helpers/hooks.js index 08da0e81b6b..930ac2aed6c 100644 --- a/packages/datadog-instrumentations/src/helpers/hooks.js +++ b/packages/datadog-instrumentations/src/helpers/hooks.js @@ -57,7 +57,6 @@ module.exports = { 'jest-config': () => require('../jest'), 'jest-environment-node': () => require('../jest'), 'jest-environment-jsdom': () => require('../jest'), - 'jest-jasmine2': () => require('../jest'), 'jest-runtime': () => require('../jest'), 'jest-worker': () => require('../jest'), knex: () => require('../knex'), diff --git a/packages/datadog-instrumentations/src/helpers/register.js b/packages/datadog-instrumentations/src/helpers/register.js index 9fc22ca45f1..c4a2a3f7dde 100644 --- a/packages/datadog-instrumentations/src/helpers/register.js +++ b/packages/datadog-instrumentations/src/helpers/register.js @@ -6,8 +6,12 @@ const semver = require('semver') const Hook = require('./hook') const requirePackageJson = require('../../../dd-trace/src/require-package-json') const log = require('../../../dd-trace/src/log') +const checkRequireCache = require('../check_require_cache') -const { DD_TRACE_DISABLED_INSTRUMENTATIONS = '' } = process.env +const { + DD_TRACE_DISABLED_INSTRUMENTATIONS = '', + DD_TRACE_DEBUG = '' +} = process.env const hooks = require('./hooks') const instrumentations = require('./instrumentations') @@ -27,6 +31,8 @@ if (!disabledInstrumentations.has('fetch')) { const HOOK_SYMBOL = Symbol('hookExportsMap') // TODO: make this more efficient +if (DD_TRACE_DEBUG && DD_TRACE_DEBUG.toLowerCase() !== 'false') checkRequireCache() + for (const packageName of names) { if (disabledInstrumentations.has(packageName)) continue diff --git a/packages/datadog-instrumentations/src/jest.js b/packages/datadog-instrumentations/src/jest.js index e775fe7ad8b..d68a292b26c 100644 --- a/packages/datadog-instrumentations/src/jest.js +++ b/packages/datadog-instrumentations/src/jest.js @@ -19,7 +19,6 @@ const { getJestTestName, getJestSuitesToRun } = require('../../datadog-plugin-jest/src/util') -const { DD_MAJOR } = require('../../../version') const testSessionStartCh = channel('ci:jest:session:start') const testSessionFinishCh = channel('ci:jest:session:finish') @@ -68,14 +67,6 @@ let hasFilteredSkippableSuites = false const sessionAsyncResource = new AsyncResource('bound-anonymous-fn') -const specStatusToTestStatus = { - pending: 'skip', - disabled: 'skip', - todo: 'skip', - passed: 'pass', - failed: 'fail' -} - const asyncResources = new WeakMap() const originalTestFns = new WeakMap() const retriedTestsToNumAttempts = new Map() @@ -157,7 +148,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) { } let hasSnapshotTests = true try { - const { _snapshotData } = this.context.expect.getState().snapshotState + const { _snapshotData } = this.getVmContext().expect.getState().snapshotState hasSnapshotTests = Object.keys(_snapshotData).length > 0 } catch (e) { // if we can't be sure, we'll err on the side of caution and assume it has snapshots @@ -837,45 +828,6 @@ addHook({ versions: ['24.8.0 - 24.9.0'] }, jestConfigSyncWrapper) -function jasmineAsyncInstallWraper (jasmineAsyncInstallExport, jestVersion) { - log.warn('jest-jasmine2 support is removed from dd-trace@v4. Consider changing to jest-circus as `testRunner`.') - return function (globalConfig, globalInput) { - globalInput._ddtrace = global._ddtrace - shimmer.wrap(globalInput.jasmine.Spec.prototype, 'execute', execute => function (onComplete) { - const asyncResource = new AsyncResource('bound-anonymous-fn') - asyncResource.runInAsyncScope(() => { - const testSuite = getTestSuitePath(this.result.testPath, globalConfig.rootDir) - testStartCh.publish({ - name: this.getFullName(), - suite: testSuite, - runner: 'jest-jasmine2', - frameworkVersion: jestVersion - }) - const spec = this - const callback = asyncResource.bind(function () { - if (spec.result.failedExpectations && spec.result.failedExpectations.length) { - const formattedError = formatJestError(spec.result.failedExpectations[0].error) - testErrCh.publish(formattedError) - } - testRunFinishCh.publish({ status: specStatusToTestStatus[spec.result.status] }) - onComplete.apply(this, arguments) - }) - arguments[0] = callback - execute.apply(this, arguments) - }) - }) - return jasmineAsyncInstallExport.default(globalConfig, globalInput) - } -} - -if (DD_MAJOR < 4) { - addHook({ - name: 'jest-jasmine2', - versions: ['>=24.8.0'], - file: 'build/jasmineAsyncInstall.js' - }, jasmineAsyncInstallWraper) -} - const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = [ 'selenium-webdriver' ] diff --git a/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js b/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js new file mode 100644 index 00000000000..176c3c618ff --- /dev/null +++ b/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js @@ -0,0 +1,33 @@ +'use strict' + +const NM = 'node_modules/' + +/** + * For a given full path to a module, + * return the package name it belongs to and the local path to the module + * input: '/foo/node_modules/@co/stuff/foo/bar/baz.js' + * output: { pkg: '@co/stuff', path: 'foo/bar/baz.js' } + */ +module.exports = function extractPackageAndModulePath (fullPath) { + const nm = fullPath.lastIndexOf(NM) + if (nm < 0) { + return { pkg: null, path: null } + } + + const subPath = fullPath.substring(nm + NM.length) + const firstSlash = subPath.indexOf('/') + + if (subPath[0] === '@') { + const secondSlash = subPath.substring(firstSlash + 1).indexOf('/') + + return { + pkg: subPath.substring(0, firstSlash + 1 + secondSlash), + path: subPath.substring(firstSlash + 1 + secondSlash + 1) + } + } + + return { + pkg: subPath.substring(0, firstSlash), + path: subPath.substring(firstSlash + 1) + } +} diff --git a/packages/datadog-instrumentations/test/check_require_cache.spec.js b/packages/datadog-instrumentations/test/check_require_cache.spec.js new file mode 100644 index 00000000000..168eac97d78 --- /dev/null +++ b/packages/datadog-instrumentations/test/check_require_cache.spec.js @@ -0,0 +1,34 @@ +'use strict' + +const { exec } = require('node:child_process') + +describe('check_require_cache', () => { + const opts = { + cwd: __dirname, + env: { + DD_TRACE_DEBUG: 'true' + } + } + + it('should be no warnings when tracer is loaded first', (done) => { + exec(`${process.execPath} ./check_require_cache/good-order.js`, opts, (error, stdout, stderr) => { + expect(error).to.be.null + expect(stdout).to.be.empty + expect(stderr).to.be.empty + done() + }) + }) + + // stderr is empty on Windows + if (process.platform !== 'windows') { + it('should find warnings when tracer loaded late', (done) => { + exec(`${process.execPath} ./check_require_cache/bad-order.js`, opts, (error, stdout, stderr) => { + expect(error).to.be.null + expect(stdout).to.be.empty + expect(stderr).to.not.be.empty + expect(stderr).to.include("Package 'express' was loaded") + done() + }) + }) + } +}) diff --git a/packages/datadog-instrumentations/test/check_require_cache/bad-order.js b/packages/datadog-instrumentations/test/check_require_cache/bad-order.js new file mode 100755 index 00000000000..a5fab991153 --- /dev/null +++ b/packages/datadog-instrumentations/test/check_require_cache/bad-order.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +require('express') // package required before tracer +const tracer = require('../../../../') +tracer.init() + +process.exit() diff --git a/packages/datadog-instrumentations/test/check_require_cache/good-order.js b/packages/datadog-instrumentations/test/check_require_cache/good-order.js new file mode 100755 index 00000000000..72bd2c666b9 --- /dev/null +++ b/packages/datadog-instrumentations/test/check_require_cache/good-order.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +const tracer = require('../../../../') +require('express') // package required after tracer +tracer.init() + +process.exit() diff --git a/packages/datadog-plugin-jest/test/jasmine2.spec.js b/packages/datadog-plugin-jest/test/jasmine2.spec.js deleted file mode 100644 index 7ea4b80000b..00000000000 --- a/packages/datadog-plugin-jest/test/jasmine2.spec.js +++ /dev/null @@ -1,228 +0,0 @@ -'use strict' -const fs = require('fs') -const path = require('path') -const nock = require('nock') - -const { ORIGIN_KEY, COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants') -const agent = require('../../dd-trace/test/plugins/agent') -const { - TEST_FRAMEWORK, - TEST_TYPE, - TEST_NAME, - TEST_SUITE, - TEST_SOURCE_FILE, - TEST_STATUS, - CI_APP_ORIGIN, - TEST_FRAMEWORK_VERSION, - JEST_TEST_RUNNER, - TEST_CODE_OWNERS, - LIBRARY_VERSION -} = require('../../dd-trace/src/plugins/util/test') - -const { version: ddTraceVersion } = require('../../../package.json') -const { DD_MAJOR } = require('../../../version') - -const describeFunction = DD_MAJOR < 4 ? describe : describe.skip - -describeFunction('Plugin', function () { - this.retries(2) - let jestExecutable - - const jestCommonOptions = { - projects: [__dirname], - testPathIgnorePatterns: ['/node_modules/'], - coverageReporters: [], - reporters: [], - cache: false, - maxWorkers: '50%', - testEnvironment: 'node' - } - - withVersions('jest', ['jest-jasmine2'], (version) => { - afterEach(() => { - const jestTestFile = fs.readdirSync(__dirname).filter(name => name.startsWith('jest-')) - jestTestFile.forEach((testFile) => { - delete require.cache[require.resolve(path.join(__dirname, testFile))] - }) - return agent.close() - }) - beforeEach(() => { - // for http integration tests - nock('http://test:123') - .get('/') - .reply(200, 'OK') - - agent.setAvailableEndpoints([]) - - return agent.load( - ['jest', 'http'], { service: 'test' }, { experimental: { exporter: 'agent_proxy' } } - ).then(() => { - jestCommonOptions.testRunner = - require(`../../../versions/jest@${version}`).getPath('jest-jasmine2') - - jestExecutable = require(`../../../versions/jest@${version}`).get() - }) - }) - describe('jest with jasmine', function () { - this.timeout(20000) - it('instruments async, sync and integration tests', function (done) { - const tests = [ - { - name: 'jest-test-suite tracer and active span are available', - status: 'pass', - extraTags: { 'test.add.stuff': 'stuff' } - }, - { name: 'jest-test-suite done', status: 'pass' }, - { name: 'jest-test-suite done fail', status: 'fail' }, - { name: 'jest-test-suite done fail uncaught', status: 'fail' }, - { name: 'jest-test-suite can do integration http', status: 'pass' }, - { name: 'jest-test-suite promise passes', status: 'pass' }, - { name: 'jest-test-suite promise fails', status: 'fail' }, - { name: 'jest-test-suite timeout', status: 'fail' }, - { name: 'jest-test-suite passes', status: 'pass' }, - { name: 'jest-test-suite fails', status: 'fail' }, - { name: 'jest-test-suite does not crash with missing stack', status: 'fail' }, - { name: 'jest-test-suite skips', status: 'skip' }, - { name: 'jest-test-suite skips todo', status: 'skip' } - ] - const assertionPromises = tests.map(({ name, status, error, extraTags }) => { - return agent.use(trace => { - const testSpan = trace[0][0] - expect(testSpan.parent_id.toString()).to.equal('0') - expect(testSpan.meta).to.contain({ - language: 'javascript', - service: 'test', - [ORIGIN_KEY]: CI_APP_ORIGIN, - [TEST_FRAMEWORK]: 'jest', - [TEST_NAME]: name, - [TEST_STATUS]: status, - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-test.js', - [TEST_SOURCE_FILE]: 'packages/datadog-plugin-jest/test/jest-test.js', - [TEST_TYPE]: 'test', - [JEST_TEST_RUNNER]: 'jest-jasmine2', - [LIBRARY_VERSION]: ddTraceVersion, - [COMPONENT]: 'jest' - }) - // reads from dd-trace-js' CODEOWNERS - expect(testSpan.meta[TEST_CODE_OWNERS]).to.contain('@DataDog') - if (extraTags) { - expect(testSpan.meta).to.contain(extraTags) - } - if (error) { - expect(testSpan.meta[ERROR_MESSAGE]).to.include(error) - } - // TODO: add assertions on http spans when stealthy-require issue is resolved - if (name === 'jest-test-suite can do integration http') { - const httpSpan = trace[0].find(span => span.name === 'http.request') - expect(httpSpan.meta[ORIGIN_KEY]).to.equal(CI_APP_ORIGIN) - expect(httpSpan.meta['http.url']).to.equal('http://test:123/') - expect(httpSpan.parent_id.toString()).to.equal(testSpan.span_id.toString()) - } - expect(testSpan.type).to.equal('test') - expect(testSpan.name).to.equal('jest.test') - expect(testSpan.service).to.equal('test') - expect(testSpan.resource).to.equal(`packages/datadog-plugin-jest/test/jest-test.js.${name}`) - expect(testSpan.meta[TEST_FRAMEWORK_VERSION]).not.to.be.undefined - }, { timeoutMs: 10000, spanResourceMatch: new RegExp(`${name}$`) }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-test.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - - it('works when there is a hook error', (done) => { - const tests = [ - { name: 'jest-hook-failure will not run', error: 'hey, hook error before' }, - { name: 'jest-hook-failure-after will not run', error: 'hey, hook error after' } - ] - - const assertionPromises = tests.map(({ name, error }) => { - return agent.use(trace => { - const testSpan = trace[0][0] - expect(testSpan.parent_id.toString()).to.equal('0') - expect(testSpan.meta).to.contain({ - language: 'javascript', - service: 'test', - [ORIGIN_KEY]: CI_APP_ORIGIN, - [TEST_FRAMEWORK]: 'jest', - [TEST_NAME]: name, - [TEST_STATUS]: 'fail', - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-hook-failure.js', - [TEST_SOURCE_FILE]: 'packages/datadog-plugin-jest/test/jest-hook-failure.js', - [TEST_TYPE]: 'test', - [JEST_TEST_RUNNER]: 'jest-jasmine2', - [COMPONENT]: 'jest' - }) - expect(testSpan.meta[ERROR_MESSAGE]).to.equal(error) - expect(testSpan.type).to.equal('test') - expect(testSpan.name).to.equal('jest.test') - expect(testSpan.service).to.equal('test') - expect(testSpan.resource).to.equal( - `packages/datadog-plugin-jest/test/jest-hook-failure.js.${name}` - ) - expect(testSpan.meta[TEST_FRAMEWORK_VERSION]).not.to.be.undefined - }, { timeoutMs: 10000, spanResourceMatch: new RegExp(`${name}$`) }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-hook-failure.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - - it('should work with focused tests', (done) => { - const tests = [ - { name: 'jest-test-focused will be skipped', status: 'skip' }, - { name: 'jest-test-focused-2 will be skipped too', status: 'skip' }, - { name: 'jest-test-focused can do focused test', status: 'pass' } - ] - - const assertionPromises = tests.map(({ name, status }) => { - return agent.use(trace => { - const testSpan = trace[0].find(span => span.type === 'test') - expect(testSpan.parent_id.toString()).to.equal('0') - expect(testSpan.meta[ORIGIN_KEY]).to.equal(CI_APP_ORIGIN) - expect(testSpan.meta).to.contain({ - language: 'javascript', - service: 'test', - [TEST_NAME]: name, - [TEST_STATUS]: status, - [TEST_FRAMEWORK]: 'jest', - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-focus.js', - [TEST_SOURCE_FILE]: 'packages/datadog-plugin-jest/test/jest-focus.js', - [COMPONENT]: 'jest' - }) - }, { timeoutMs: 10000, spanResourceMatch: new RegExp(`${name}$`) }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-focus.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - }) - }) -}) diff --git a/packages/dd-trace/src/config.js b/packages/dd-trace/src/config.js index ae890f688c5..2f2e432dce6 100644 --- a/packages/dd-trace/src/config.js +++ b/packages/dd-trace/src/config.js @@ -16,7 +16,7 @@ const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags') const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } = require('./git_properties') const { updateConfig } = require('./telemetry') const telemetryMetrics = require('./telemetry/metrics') -const { getIsGCPFunction, getIsAzureFunctionConsumptionPlan } = require('./serverless') +const { getIsGCPFunction, getIsAzureFunction } = require('./serverless') const { ORIGIN_KEY } = require('./constants') const tracerMetrics = telemetryMetrics.manager.namespace('tracers') @@ -339,7 +339,7 @@ class Config { // Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent this.memcachedCommandEnabled = isTrue(DD_TRACE_MEMCACHED_COMMAND_ENABLED) - this.isAzureFunctionConsumptionPlan = getIsAzureFunctionConsumptionPlan() + this.isAzureFunction = getIsAzureFunction() this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG) this.installSignature = { id: DD_INSTRUMENTATION_INSTALL_ID, @@ -417,8 +417,8 @@ class Config { _isInServerlessEnvironment () { const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined const isGCPFunction = getIsGCPFunction() - const isAzureFunctionConsumptionPlan = getIsAzureFunctionConsumptionPlan() - return inAWSLambda || isGCPFunction || isAzureFunctionConsumptionPlan + const isAzureFunction = getIsAzureFunction() + return inAWSLambda || isGCPFunction || isAzureFunction } // for _merge to work, every config value must have a default value @@ -877,7 +877,7 @@ class Config { return coalesce( this.options.stats, process.env.DD_TRACE_STATS_COMPUTATION_ENABLED, - getIsGCPFunction() || getIsAzureFunctionConsumptionPlan() + getIsGCPFunction() || getIsAzureFunction() ) } diff --git a/packages/dd-trace/src/encode/0.4.js b/packages/dd-trace/src/encode/0.4.js index 7cf1ee0b2ef..11cf8c4b6c7 100644 --- a/packages/dd-trace/src/encode/0.4.js +++ b/packages/dd-trace/src/encode/0.4.js @@ -83,13 +83,17 @@ class AgentEncoder { span = formatSpan(span) bytes.reserve(1) - if (span.type) { + if (span.type && span.meta_struct) { + bytes.buffer[bytes.length++] = 0x8d + } else if (span.type || span.meta_struct) { bytes.buffer[bytes.length++] = 0x8c + } else { + bytes.buffer[bytes.length++] = 0x8b + } + if (span.type) { this._encodeString(bytes, 'type') this._encodeString(bytes, span.type) - } else { - bytes.buffer[bytes.length++] = 0x8b } this._encodeString(bytes, 'trace_id') @@ -114,6 +118,10 @@ class AgentEncoder { this._encodeMap(bytes, span.meta) this._encodeString(bytes, 'metrics') this._encodeMap(bytes, span.metrics) + if (span.meta_struct) { + this._encodeString(bytes, 'meta_struct') + this._encodeObject(bytes, span.meta_struct) + } } } @@ -263,6 +271,45 @@ class AgentEncoder { } } + _encodeObject (bytes, value, circularReferencesDetector = new Set()) { + circularReferencesDetector.add(value) + if (Array.isArray(value)) { + return this._encodeObjectAsArray(bytes, value, circularReferencesDetector) + } else if (value !== null && typeof value === 'object') { + return this._encodeObjectAsMap(bytes, value, circularReferencesDetector) + } else if (typeof value === 'string' || typeof value === 'number') { + this._encodeValue(bytes, value) + } + } + + _encodeObjectAsMap (bytes, value, circularReferencesDetector) { + const keys = Object.keys(value) + const validKeys = keys.filter(key => + typeof value[key] === 'string' || + typeof value[key] === 'number' || + (value[key] !== null && typeof value[key] === 'object' && !circularReferencesDetector.has(value[key]))) + + this._encodeMapPrefix(bytes, validKeys.length) + + for (const key of validKeys) { + this._encodeString(bytes, key) + this._encodeObject(bytes, value[key], circularReferencesDetector) + } + } + + _encodeObjectAsArray (bytes, value, circularReferencesDetector) { + const validValue = value.filter(item => + typeof item === 'string' || + typeof item === 'number' || + (item !== null && typeof item === 'object' && !circularReferencesDetector.has(item))) + + this._encodeArrayPrefix(bytes, validValue) + + for (const item of validValue) { + this._encodeObject(bytes, item, circularReferencesDetector) + } + } + _cacheString (value) { if (!(value in this._stringMap)) { this._stringCount++ diff --git a/packages/dd-trace/src/plugins/index.js b/packages/dd-trace/src/plugins/index.js index 064b0475345..82217ada440 100644 --- a/packages/dd-trace/src/plugins/index.js +++ b/packages/dd-trace/src/plugins/index.js @@ -44,7 +44,6 @@ module.exports = { get 'jest-config' () { return require('../../../datadog-plugin-jest/src') }, get 'jest-environment-node' () { return require('../../../datadog-plugin-jest/src') }, get 'jest-environment-jsdom' () { return require('../../../datadog-plugin-jest/src') }, - get 'jest-jasmine2' () { return require('../../../datadog-plugin-jest/src') }, get 'jest-runtime' () { return require('../../../datadog-plugin-jest/src') }, get 'jest-worker' () { return require('../../../datadog-plugin-jest/src') }, get koa () { return require('../../../datadog-plugin-koa/src') }, diff --git a/packages/dd-trace/src/proxy.js b/packages/dd-trace/src/proxy.js index 98401a1769f..7d47b059142 100644 --- a/packages/dd-trace/src/proxy.js +++ b/packages/dd-trace/src/proxy.js @@ -92,7 +92,7 @@ class Tracer extends NoopProxy { }) } - if (config.isGCPFunction || config.isAzureFunctionConsumptionPlan) { + if (config.isGCPFunction || config.isAzureFunction) { require('./serverless').maybeStartServerlessMiniAgent(config) } diff --git a/packages/dd-trace/src/serverless.js b/packages/dd-trace/src/serverless.js index b863a56afed..d352cae899e 100644 --- a/packages/dd-trace/src/serverless.js +++ b/packages/dd-trace/src/serverless.js @@ -53,18 +53,16 @@ function getIsGCPFunction () { return isDeprecatedGCPFunction || isNewerGCPFunction } -function getIsAzureFunctionConsumptionPlan () { +function getIsAzureFunction () { const isAzureFunction = process.env.FUNCTIONS_EXTENSION_VERSION !== undefined && process.env.FUNCTIONS_WORKER_RUNTIME !== undefined - const azureWebsiteSKU = process.env.WEBSITE_SKU - const isConsumptionPlan = azureWebsiteSKU === undefined || azureWebsiteSKU === 'Dynamic' - return isAzureFunction && isConsumptionPlan + return isAzureFunction } module.exports = { maybeStartServerlessMiniAgent, getIsGCPFunction, - getIsAzureFunctionConsumptionPlan, + getIsAzureFunction, getRustBinaryPath } diff --git a/packages/dd-trace/test/encode/0.4.spec.js b/packages/dd-trace/test/encode/0.4.spec.js index 812053972bb..cd20a318e4d 100644 --- a/packages/dd-trace/test/encode/0.4.spec.js +++ b/packages/dd-trace/test/encode/0.4.spec.js @@ -235,4 +235,223 @@ describe('encode', () => { expect(trace[0].meta).to.deep.equal({ bar: 'baz', '_dd.span_links': encodedLink }) expect(trace[0].metrics).to.deep.equal({ example: 1 }) }) + + describe('meta_struct', () => { + it('should encode meta_struct with simple key value object', () => { + const metaStruct = { + foo: 'bar', + baz: 123 + } + data[0].meta_struct = metaStruct + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + expect(trace[0].meta_struct).to.deep.equal(metaStruct) + }) + + it('should encode meta_struct with simple array of simple values', () => { + const metaStruct = ['one', 2, 'three', 4, 5, 'six'] + data[0].meta_struct = metaStruct + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + expect(trace[0].meta_struct).to.deep.equal(metaStruct) + }) + + it('should encode meta_struct with array of objects', () => { + const metaStruct = [{ foo: 'bar' }, { baz: 123 }] + data[0].meta_struct = metaStruct + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + expect(trace[0].meta_struct).to.deep.equal(metaStruct) + }) + + it('should encode meta_struct with empty object and array', () => { + const metaStruct = { + foo: {}, + bar: [] + } + data[0].meta_struct = metaStruct + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + expect(trace[0].meta_struct).to.deep.equal(metaStruct) + }) + + it('should encode meta_struct with possible real use case', () => { + const metaStruct = { + '_dd.stack': { + exploit: [ + { + type: 'test', + language: 'nodejs', + id: 'someuuid', + message: 'Threat detected', + frames: [ + { + id: 0, + file: 'test.js', + line: 1, + column: 31, + function: 'test' + }, + { + id: 1, + file: 'test2.js', + line: 54, + column: 77, + function: 'test' + }, + { + id: 2, + file: 'test.js', + line: 1245, + column: 41, + function: 'test' + }, + { + id: 3, + file: 'test3.js', + line: 2024, + column: 32, + function: 'test' + } + ] + } + ] + } + } + data[0].meta_struct = metaStruct + + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + expect(trace[0].meta_struct).to.deep.equal(metaStruct) + }) + + it('should encode meta_struct ignoring circular references in objects', () => { + const circular = { + bar: 'baz', + deeper: { + foo: 'bar' + } + } + circular.deeper.circular = circular + const metaStruct = { + foo: circular + } + data[0].meta_struct = metaStruct + + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + + const expectedMetaStruct = { + foo: { + bar: 'baz', + deeper: { + foo: 'bar' + } + } + } + expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + }) + + it('should encode meta_struct ignoring circular references in arrays', () => { + const circular = [{ + bar: 'baz' + }] + circular.push(circular) + const metaStruct = { + foo: circular + } + data[0].meta_struct = metaStruct + + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + + const expectedMetaStruct = { + foo: [{ + bar: 'baz' + }] + } + expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + }) + + it('should encode meta_struct ignoring undefined properties', () => { + const metaStruct = { + foo: 'bar', + undefinedProperty: undefined + } + data[0].meta_struct = metaStruct + + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + + const expectedMetaStruct = { + foo: 'bar' + } + expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + }) + + it('should encode meta_struct ignoring null properties', () => { + const metaStruct = { + foo: 'bar', + nullProperty: null + } + data[0].meta_struct = metaStruct + + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + + const expectedMetaStruct = { + foo: 'bar' + } + expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + }) + + it('should not encode null meta_struct', () => { + data[0].meta_struct = null + + encoder.encode(data) + + const buffer = encoder.makePayload() + + const decoded = msgpack.decode(buffer, { codec }) + const trace = decoded[0] + + expect(trace[0].meta_struct).to.be.undefined + }) + }) }) diff --git a/packages/dd-trace/test/encode/0.5.spec.js b/packages/dd-trace/test/encode/0.5.spec.js index 94a8efbbb5c..2ef925c60c3 100644 --- a/packages/dd-trace/test/encode/0.5.spec.js +++ b/packages/dd-trace/test/encode/0.5.spec.js @@ -189,4 +189,31 @@ describe('encode 0.5', () => { expect(payload[5]).to.equal(1) expect(payload[11]).to.equal(0) }) + + it('should ignore meta_struct property', () => { + data[0].meta_struct = { foo: 'bar' } + + encoder.encode(data) + + const buffer = encoder.makePayload() + const decoded = msgpack.decode(buffer, { codec }) + const stringMap = decoded[0] + const trace = decoded[1][0] + + expect(trace).to.be.instanceof(Array) + expect(trace[0]).to.be.instanceof(Array) + expect(stringMap[trace[0][0]]).to.equal(data[0].service) + expect(stringMap[trace[0][1]]).to.equal(data[0].name) + expect(stringMap[trace[0][2]]).to.equal(data[0].resource) + expect(trace[0][3].toString(16)).to.equal(data[0].trace_id.toString()) + expect(trace[0][4].toString(16)).to.equal(data[0].span_id.toString()) + expect(trace[0][5].toString(16)).to.equal(data[0].parent_id.toString()) + expect(trace[0][6].toNumber()).to.equal(data[0].start) + expect(trace[0][7].toNumber()).to.equal(data[0].duration) + expect(trace[0][8]).to.equal(0) + expect(trace[0][9]).to.deep.equal({ [stringMap.indexOf('bar')]: stringMap.indexOf('baz') }) + expect(trace[0][10]).to.deep.equal({ [stringMap.indexOf('example')]: 1 }) + expect(stringMap[trace[0][11]]).to.equal('') // unset + expect(trace[0][12]).to.be.undefined // Everything works the same as without meta_struct, and nothing else is added + }) }) diff --git a/packages/dd-trace/test/serverless.spec.js b/packages/dd-trace/test/serverless.spec.js index 8ba4eb33273..021f45e89e8 100644 --- a/packages/dd-trace/test/serverless.spec.js +++ b/packages/dd-trace/test/serverless.spec.js @@ -54,7 +54,7 @@ describe('Serverless', () => { expect(spawnStub).to.have.been.calledOnce }) - it('should spawn mini agent when FUNCTIONS_WORKER_RUNTIME, FUNCTIONS_EXTENSION_VERSION env vars are defined', () => { + it('should spawn mini agent when Azure env vars are defined', () => { process.env.FUNCTIONS_WORKER_RUNTIME = 'node' process.env.FUNCTIONS_EXTENSION_VERSION = '4' @@ -63,26 +63,6 @@ describe('Serverless', () => { expect(spawnStub).to.have.been.calledOnce }) - it('should spawn mini agent when Azure Function env vars are defined and SKU is dynamic', () => { - process.env.FUNCTIONS_WORKER_RUNTIME = 'node' - process.env.FUNCTIONS_EXTENSION_VERSION = '4' - process.env.WEBSITE_SKU = 'Dynamic' - - proxy.init() - - expect(spawnStub).to.have.been.calledOnce - }) - - it('should NOT spawn mini agent when Azure Function env vars are defined but SKU is NOT dynamic', () => { - process.env.FUNCTIONS_WORKER_RUNTIME = 'node' - process.env.FUNCTIONS_EXTENSION_VERSION = '4' - process.env.WEBSITE_SKU = 'Basic' - - proxy.init() - - expect(spawnStub).to.not.have.been.called - }) - it('should log error if mini agent binary path is invalid', () => { process.env.K_SERVICE = 'test_function' process.env.FUNCTION_TARGET = 'function_target' diff --git a/yarn.lock b/yarn.lock index 09544b060cb..29daf95f359 100644 --- a/yarn.lock +++ b/yarn.lock @@ -442,10 +442,10 @@ node-addon-api "^6.1.0" node-gyp-build "^3.9.0" -"@datadog/pprof@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.2.0.tgz#a6c2779335b4f0fd51754e4de193e98591de387e" - integrity sha512-pSwLARpNLAIV1JttxXOBRKTn/NQYXDy1PJaV458YFDdAYxnBqpsYTat3/nX+8V5GoN4SfdHDci3zqXM+Ym66gQ== +"@datadog/pprof@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.3.0.tgz#c2f58d328ecced7f99887f1a559d7fe3aecb9219" + integrity sha512-53z2Q3K92T6Pf4vz4Ezh8kfkVEvLzbnVqacZGgcbkP//q0joFzO8q00Etw1S6NdnCX0XmX08ULaF4rUI5r14mw== dependencies: delay "^5.0.0" node-gyp-build "<4.0"