diff --git a/.github/dependabot.yml b/.github/dependabot.yml index de123d0346f..6c9e1961e25 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -48,7 +48,15 @@ updates: - "eslint*" - package-ecosystem: "npm" - directory: "/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp" + directory: "/test/instrumentation/azure-functions/fixtures/azfunc3" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + reviewers: + - "elastic/apm-agent-node-js" + + - package-ecosystem: "npm" + directory: "/test/instrumentation/azure-functions/fixtures/azfunc4" schedule: interval: "weekly" open-pull-requests-limit: 5 diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 3eff56acc0e..fdbc45e701e 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -41,12 +41,15 @@ See the <> guide. [float] ===== Features +* Support instrumentation of Azure Functions using the https://learn.microsoft.com/en-ca/azure/azure-functions/functions-node-upgrade-v4[v4 Node.js programming model]. + ({pull}4426[#4426]) + [float] ===== Bug fixes * Fix instrumentation of `@aws-sdk/client-s3`, `@aws-sdk/client-sqs`, and - `@aws-sdk/client-sns` for versions 3.723.0 and later. (Internally the AWS SDK - clients updated to `@smithy/smithy-client@4`.) + `@aws-sdk/client-sns` for versions 3.723.0 and later. Internally the AWS SDK + clients updated to `@smithy/smithy-client@4`. ({pull}4398[#4398]) [float] ===== Chores diff --git a/docs/azure-functions.asciidoc b/docs/azure-functions.asciidoc index 08aa0884e24..cb91dfd6697 100644 --- a/docs/azure-functions.asciidoc +++ b/docs/azure-functions.asciidoc @@ -9,7 +9,7 @@ endif::[] === Monitoring Node.js Azure Functions -The Node.js APM Agent can trace function invocations in an https://learn.microsoft.com/en-us/azure/azure-functions/[Azure Functions] app. +The Node.js APM Agent can trace function invocations in an https://learn.microsoft.com/en-us/azure/azure-functions/[Azure Functions] app, using either v3 or https://learn.microsoft.com/en-us/azure/azure-functions/functions-node-upgrade-v4[v4 of the Node.js programming model]. [float] @@ -36,7 +36,7 @@ will result in unreasonably large deployments that will be very slow to publish and will run your Azure Function app VM out of disk space. ==== -You can also take a look at and use this https://github.com/elastic/apm-agent-nodejs/tree/main/examples/an-azure-function-app/[Azure Functions example app with Elastic APM already integrated]. +You can also take a look at and use this https://github.com/elastic/apm-agent-nodejs/tree/main/examples/azure-function-app/[Azure Functions example app with Elastic APM already integrated]. [float] [[azure-functions-setup]] @@ -69,17 +69,14 @@ require('elastic-apm-node').start({ ---- <1> Optional <> can be added here. -2. Add a "main" entry to your package.json pointing to the app init file. +2. Change the "main" entry in your "package.json" to point to the initapm.js file. + [source,json] ---- ... - "main": "initapm.js", + "main": "{initapm.js,src/functions/*.js}", ... ---- -+ -If your application already has a "main" init file, you can instead add the -`require('elastic-apm-node').start()` to top of that file. [float] @@ -103,7 +100,7 @@ For example: image::./images/azure-functions-configuration.png[Configuring the APM Agent in the Azure Portal] -For local testing via `func start` you can set these environment variables in +For local testing via `func start`, you can set these environment variables in your terminal, or in the "local.settings.json" file. See the <> for full details on supported configuration variables. @@ -127,13 +124,11 @@ of time, so allow a minute or so for data to appear. [[azure-functions-limitations]] ==== Limitations -This instrumentation does not send an APM transaction or error to APM server when -a handler has an `uncaughtException` or `unhandledRejection`. -The Azure Functions Node.js reference https://learn.microsoft.com/en-ca/azure/azure-functions/functions-reference-node#use-async-and-await[has a section] with best practices for avoiding these cases. +Distributed tracing for incoming HTTP requests to Azure Functions (using v4 of the programming model) does *not* work, because of a issue with Azure's handling of trace-context. See https://github.com/elastic/apm-agent-nodejs/pull/4426#issuecomment-2596922653[this] for details. + +Azure Functions instrumentation currently does _not_ collect system metrics in the background because of a concern with unintentionally increasing Azure Functions costs (for Consumption plans). -Azure Functions instrumentation currently does _not_ collect system metrics in -the background because of a concern with unintentionally increasing Azure -Functions costs (for Consumption plans). +Elastic APM's <> is not supported for Azure Functions. [float] diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 979465fb4c2..db517480352 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -63,7 +63,7 @@ These are the frameworks that we officially support: |======================================================================= | Framework | Version | Note | <> | N/A | -| <> | ~4 | See https://learn.microsoft.com/en-ca/azure/azure-functions/set-runtime-version[the guide on Azure Functions runtime versions]. +| <> | v3, v4 | https://learn.microsoft.com/en-us/azure/azure-functions/functions-node-upgrade-v4[Node.js programming model v3 and v4] | <> | ^4.0.0 | | <> | >=1.0.0 | See also https://www.fastify.io/docs/latest/Reference/LTS/[Fastify's own LTS documentation] | <> | >=17.9.0 <22.0.0 | diff --git a/eslint.config.js b/eslint.config.js index c05b5de444d..ecadd738cff 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -30,7 +30,7 @@ module.exports = [ 'examples/esbuild/dist/**', 'examples/typescript/dist/**', - 'examples/an-azure-function-app/**', + 'examples/azure-function-app/**', 'lib/opentelemetry-bridge/opentelemetry-core-mini/**', 'test/babel/out.js', 'test/lambda/fixtures/esbuild-bundled-handler/hello.js', diff --git a/examples/an-azure-function-app/Bye/index.js b/examples/an-azure-function-app/Bye/index.js deleted file mode 100644 index f0d0364ce0a..00000000000 --- a/examples/an-azure-function-app/Bye/index.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = async function (context, _req) { - const body = JSON.stringify({ good: 'bye' }) - context.res = { - status: 200, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(body) - }, - body - } -} diff --git a/examples/an-azure-function-app/Hi/function.json b/examples/an-azure-function-app/Hi/function.json deleted file mode 100644 index 91052aaf8a0..00000000000 --- a/examples/an-azure-function-app/Hi/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "Anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "req", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "res" - } - ] -} \ No newline at end of file diff --git a/examples/an-azure-function-app/Hi/index.js b/examples/an-azure-function-app/Hi/index.js deleted file mode 100644 index 2a50aa6ac08..00000000000 --- a/examples/an-azure-function-app/Hi/index.js +++ /dev/null @@ -1,28 +0,0 @@ -const http = require('http') -const https = require('https') - -module.exports = async function (context, req) { - return new Promise((resolve, reject) => { - // Call the 'Bye' Function in this same Function App... - const url = new URL(req.url) - url.pathname = '/api/Bye' - const proto = (url.protocol === 'https:' ? https : http) - proto.get(url, res => { - res.resume() - res.on('error', reject) - res.on('end', () => { - // ... then respond. - const body = JSON.stringify({ hi: 'there' }) - context.res = { - status: 200, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(body) - }, - body - } - resolve() - }) - }) - }) -} diff --git a/examples/an-azure-function-app/initapm.js b/examples/an-azure-function-app/initapm.js deleted file mode 100644 index 482b0b1be46..00000000000 --- a/examples/an-azure-function-app/initapm.js +++ /dev/null @@ -1,3 +0,0 @@ -require('elastic-apm-node').start({ - // ... -}) diff --git a/examples/an-azure-function-app/package.json b/examples/an-azure-function-app/package.json deleted file mode 100644 index 275269bd469..00000000000 --- a/examples/an-azure-function-app/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "an-azure-function-app", - "version": "1.0.0", - "description": "An example Azure Function app showing Elastic APM integration for tracing/monitoring", - "private": true, - "main": "initapm.js", - "scripts": { - "start": "func start" - }, - "dependencies": { - "elastic-apm-node": "^3.42.0" - } -} diff --git a/examples/an-azure-function-app/.gitignore b/examples/azure-function-app/.gitignore similarity index 100% rename from examples/an-azure-function-app/.gitignore rename to examples/azure-function-app/.gitignore diff --git a/examples/an-azure-function-app/.npmrc b/examples/azure-function-app/.npmrc similarity index 100% rename from examples/an-azure-function-app/.npmrc rename to examples/azure-function-app/.npmrc diff --git a/examples/azure-function-app/Makefile b/examples/azure-function-app/Makefile new file mode 100644 index 00000000000..cb7b5530b95 --- /dev/null +++ b/examples/azure-function-app/Makefile @@ -0,0 +1,15 @@ +APP_NAME=$(USER)-example-azure-function-app + +.PHONY: print-app-name +print-app-name: + @echo "APP_NAME: $(APP_NAME)" + +.PHONY: publish +publish: + func azure functionapp publish "$(APP_NAME)" + +# Note that the Azure Functions log stream is extremely flaky. Don't expect it +# to reliably be able to show logs from the deployed function app. +.PHONY: logstream +logstream: + func azure functionapp logstream "$(APP_NAME)" diff --git a/examples/an-azure-function-app/README.md b/examples/azure-function-app/README.md similarity index 63% rename from examples/an-azure-function-app/README.md rename to examples/azure-function-app/README.md index e34bd852a7b..f6dafbca622 100644 --- a/examples/an-azure-function-app/README.md +++ b/examples/azure-function-app/README.md @@ -1,10 +1,10 @@ -This directory holds a very small Azure Function App implemented in Node.js -and setup to be traced by the Elastic APM agent. The App has two "functions": - -1. `Hi` - an HTTP-triggered function that will call the `Bye` function, then - respond with `{"hi":"there"}`. -2. `Bye` - an HTTP-triggered function that will respond with `{"good":"bye"}`. +This directory holds a simple Azure Function (using v4 of the Node.js +programming model) implemented in Node.js and setup to be traced by the Elastic +APM agent. The App has a single function: +- `Hello`: an HTTP-triggered function that will call worldtimeapi.org to get + the current time in Vancouver and respond with + `{"hello": "world", "current time in Vancouver": "..."}` # Testing locally @@ -13,14 +13,7 @@ and setup to be traced by the Elastic APM agent. The App has two "functions": 2. Install the [Azure Functions Core Tools](https://github.com/Azure/azure-functions-core-tools), which provide a `func` CLI tool for running Azure Functions locally for - development, and for publishing an Function App to Azure. One way to - install is via: - - npm install -g azure-functions-core-tools@4 - - It is recommended that you **not** install it in the local `./node_modules` - folder, because its large install size will get in the way of publishing to - Azure. + development, and for publishing an Function App to Azure. 3. Set environment variable to configure the APM agent, for example: @@ -29,12 +22,12 @@ and setup to be traced by the Elastic APM agent. The App has two "functions": export ELASTIC_APM_SECRET_TOKEN=... ``` -4. `npm start` +4. `npm start` (This calls `func start` to run the Azure Function app locally.) 5. In a separate terminal, call the Azure Function via: ``` - curl -i http://localhost:7071/api/Hi + curl -i http://localhost:7071/api/Hello ``` @@ -52,7 +45,7 @@ and setup to be traced by the Elastic APM agent. The App has two "functions": 4. Call your functions: ``` - curl -i https://.azurewebsites.net/api/hi + curl -i https://.azurewebsites.net/api/hello ``` The result (after a minute for data to propagate) should be a `` service diff --git a/examples/azure-function-app/host.json b/examples/azure-function-app/host.json new file mode 100644 index 00000000000..06d01bdaa95 --- /dev/null +++ b/examples/azure-function-app/host.json @@ -0,0 +1,15 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[4.*, 5.0.0)" + } +} diff --git a/examples/azure-function-app/initapm.js b/examples/azure-function-app/initapm.js new file mode 100644 index 00000000000..00a368a32ea --- /dev/null +++ b/examples/azure-function-app/initapm.js @@ -0,0 +1 @@ +require('elastic-apm-node').start() diff --git a/examples/an-azure-function-app/local.settings.json b/examples/azure-function-app/local.settings.json similarity index 79% rename from examples/an-azure-function-app/local.settings.json rename to examples/azure-function-app/local.settings.json index 9dcd935ef0c..8638bc7ec71 100644 --- a/examples/an-azure-function-app/local.settings.json +++ b/examples/azure-function-app/local.settings.json @@ -5,7 +5,7 @@ "AzureWebJobsStorage": "", "REGION_NAME": "test-region-name", - "WEBSITE_SITE_NAME": "an-azure-function-app", + "WEBSITE_SITE_NAME": "example-azure-function-app", "WEBSITE_INSTANCE_ID": "test-website-instance-id" } } diff --git a/examples/azure-function-app/package.json b/examples/azure-function-app/package.json new file mode 100644 index 00000000000..bd09f8c86d6 --- /dev/null +++ b/examples/azure-function-app/package.json @@ -0,0 +1,18 @@ +{ + "name": "example-azure-function-app", + "version": "2.0.0", + "description": "An example Azure Function app showing Elastic APM integration for tracing/monitoring", + "private": true, + "main": "{initapm.js,src/functions/*.js}", + "engines": { + "node": ">=20" + }, + "scripts": { + "start": "func start", + "dev:sync-local-apm-agent-changes": "rsync -av ../../lib/ ./node_modules/elastic-apm-node/lib/" + }, + "dependencies": { + "@azure/functions": "^4.0.0", + "elastic-apm-node": "^4.11.0" + } +} diff --git a/examples/azure-function-app/src/functions/Hello.js b/examples/azure-function-app/src/functions/Hello.js new file mode 100644 index 00000000000..5d2bc432f4a --- /dev/null +++ b/examples/azure-function-app/src/functions/Hello.js @@ -0,0 +1,24 @@ +const { app } = require('@azure/functions'); + +app.http('Hello', { + methods: ['GET'], + authLevel: 'anonymous', + handler: async (_request, _context) => { + const url = new URL('http://worldtimeapi.org/api/timezone/America/Vancouver'); + const timeRes = await fetch(url, { signal: AbortSignal.timeout(5000) }); + const timeBody = await timeRes.json(); + + const body = JSON.stringify({ + hello: 'world', + 'current time in Vancouver': timeBody.datetime + }); + return { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(body) + }, + body + }; + }, +}); diff --git a/lib/instrumentation/azure-functions.js b/lib/instrumentation/azure-functions.js index ea1247bae06..d158a1b8b6e 100644 --- a/lib/instrumentation/azure-functions.js +++ b/lib/instrumentation/azure-functions.js @@ -363,30 +363,55 @@ function instrument(agent) { isFirstRun = false; } + // In programming model v3 the InvocationContext includes + // `bindingDefinitions` and `executionContext`. In v4 the structure is a + // little different. + let bindingDefinitions = context.bindingDefinitions; + if (!bindingDefinitions) { + bindingDefinitions = []; + // Input bindings + bindingDefinitions.push({ + name: context?.options?.trigger?.name, + type: context?.options?.trigger?.type, + direction: context?.options?.trigger?.direction, + }); + // Output bindings + bindingDefinitions.push(context?.options?.return); + } + let executionContext = context.executionContext; + if (!executionContext) { + executionContext = { + functionDirectory: '', + functionName: context.functionName, + }; + } + const funcInfo = (hookCtx.hookData.funcInfo = new FunctionInfo( - context.bindingDefinitions, - context.executionContext, + bindingDefinitions, + executionContext, log, )); const triggerType = funcInfo.triggerType; - // Handle trace-context. - // Note: We ignore the `context.traceContext`. By default it is W3C - // trace-context that continues the given traceparent in headers. However, - // we do not injest that span, so would get a broken distributed trace if - // we included it. + // `InvocationContext.traceContext` is broken: it results in sampled=false + // (i.e. traces are discarded) and/or broken traces because it creates an + // internal Span ID in the trace that cannot be ingested. + // See this for full explanation: + // https://github.com/elastic/apm-agent-nodejs/pull/4426#issuecomment-2596922653 let traceparent; let tracestate; - if (triggerType === TRIGGER_HTTP && context.req && context.req.headers) { - traceparent = - context.req.headers.traceparent || - context.req.headers['elastic-apm-traceparent']; + if (triggerType === TRIGGER_HTTP && context?.req?.headers?.traceparent) { + traceparent = context.req.headers.traceparent; tracestate = context.req.headers.tracestate; + log.trace( + { traceparent, tracestate }, + 'azure-functions: get trace-context from HTTP trigger request headers', + ); } const trans = (hookCtx.hookData.trans = ins.startTransaction( // This is the default name. Trigger-specific values are added below. - context.executionContext.functionName, + executionContext.functionName, TRANS_TYPE_FROM_TRIGGER_TYPE[triggerType], { childOf: traceparent, @@ -399,7 +424,7 @@ function instrument(agent) { const accountId = getAzureAccountId(); const resourceGroup = process.env.WEBSITE_RESOURCE_GROUP; const fnAppName = process.env.WEBSITE_SITE_NAME; - const fnName = context.executionContext.functionName; + const fnName = executionContext.functionName; const faasData = { trigger: { type: FAAS_TRIGGER_TYPE_FROM_TRIGGER_TYPE[triggerType], diff --git a/lib/instrumentation/transaction.js b/lib/instrumentation/transaction.js index 5280bee3220..feb9d469df3 100644 --- a/lib/instrumentation/transaction.js +++ b/lib/instrumentation/transaction.js @@ -91,6 +91,7 @@ function Transaction(agent, name, ...args) { sampled: this.sampled, name: this.name, type: this.type, + traceparent: this.traceparent, }); this._defaultName = name || ''; diff --git a/test/instrumentation/azure-functions/azfunctestutils.js b/test/instrumentation/azure-functions/azfunctestutils.js new file mode 100644 index 00000000000..910cf2c7a68 --- /dev/null +++ b/test/instrumentation/azure-functions/azfunctestutils.js @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict'; + +const http = require('http'); + +/** + * Wait for the test "func start" to be ready. + * + * This polls the admin endpoint until + * it gets a 200 response, assuming the server is ready by then. + * It times out after ~60s -- so long because startup on Windows CI has been + * found to take a long time (it is downloading 250MB+ in "ExtensionBundle"s). + * + * @param {Test} t - This is only used to `t.comment(...)` with progress. + * @param {Function} cb - Calls `cb(err)` if there was a timeout, `cb()` on + * success. + */ +function waitForServerReady(t, cb) { + let sentinel = 30; + const INTERVAL_MS = 2000; + + const pollForServerReady = () => { + const req = http.get( + 'http://127.0.0.1:7071/admin/functions', + { + agent: false, + timeout: 500, + }, + (res) => { + res.resume(); + res.on('end', () => { + if (res.statusCode !== 200) { + scheduleNextPoll(`statusCode=${res.statusCode}`); + } else { + cb(); + } + }); + }, + ); + req.on('error', (err) => { + scheduleNextPoll(err.message); + }); + }; + + const scheduleNextPoll = (msg) => { + t.comment( + `[sentinel=${sentinel} ${new Date().toISOString()}] wait another 2s for server ready: ${msg}`, + ); + sentinel--; + if (sentinel <= 0) { + cb(new Error('timed out')); + } else { + setTimeout(pollForServerReady, INTERVAL_MS); + } + }; + + pollForServerReady(); +} + +async function makeTestRequest(t, testReq) { + return new Promise((resolve, reject) => { + const reqOpts = testReq.reqOpts; + const url = `http://127.0.0.1:7071${reqOpts.path}`; + t.comment( + `makeTestRequest: "${testReq.testName}" (${reqOpts.method} ${url})`, + ); + const req = http.request( + url, + { + method: reqOpts.method, + }, + (res) => { + const chunks = []; + res.on('data', (chunk) => { + chunks.push(chunk); + }); + res.on('end', () => { + const body = Buffer.concat(chunks); + if (testReq.expectedRes.statusCode) { + t.equal( + res.statusCode, + testReq.expectedRes.statusCode, + `res.statusCode === ${testReq.expectedRes.statusCode}`, + ); + } + if (testReq.expectedRes.headers) { + for (const [k, v] of Object.entries(testReq.expectedRes.headers)) { + if (v instanceof RegExp) { + t.ok( + v.test(res.headers[k]), + `res.headers[${JSON.stringify(k)}] =~ ${v}`, + ); + } else { + t.equal( + res.headers[k], + v, + `res.headers[${JSON.stringify(k)}] === ${JSON.stringify(v)}`, + ); + } + } + } + if (testReq.expectedRes.body) { + if (testReq.expectedRes.body instanceof RegExp) { + t.ok( + testReq.expectedRes.body.test(body), + `body =~ ${testReq.expectedRes.body}`, + ); + } else if (typeof testReq.expectedRes.body === 'string') { + t.equal(body.toString(), testReq.expectedRes.body, 'body'); + } else { + t.fail( + `unsupported type for TEST_REQUESTS[].expectedRes.body: ${typeof testReq + .expectedRes.body}`, + ); + } + } + resolve(); + }); + }, + ); + req.on('error', reject); + req.end(); + }); +} + +function getEventField(e, fieldName) { + return (e.transaction || e.error || e.span)[fieldName]; +} + +module.exports = { + getEventField, + makeTestRequest, + waitForServerReady, +}; diff --git a/test/instrumentation/azure-functions/azure-functions.test.js b/test/instrumentation/azure-functions/azure-functions-v3.test.js similarity index 78% rename from test/instrumentation/azure-functions/azure-functions.test.js rename to test/instrumentation/azure-functions/azure-functions-v3.test.js index 758435bc2d9..9c368cade91 100644 --- a/test/instrumentation/azure-functions/azure-functions.test.js +++ b/test/instrumentation/azure-functions/azure-functions-v3.test.js @@ -6,10 +6,11 @@ 'use strict'; +// Test Azure Functions programming model v3. + const assert = require('assert'); const { exec, spawn } = require('child_process'); const fs = require('fs'); -const http = require('http'); const os = require('os'); const path = require('path'); @@ -19,13 +20,20 @@ const treekill = require('tree-kill'); const { MockAPMServer } = require('../../_mock_apm_server'); const { formatForTComment } = require('../../_utils'); - -if (!semver.satisfies(process.version, '>=14.1.0 <19')) { - // The "14.1.0" version is selected to skip testing on Node.js v14.0.0 - // because of the issue described here: - // https://github.com/elastic/apm-agent-nodejs/issues/3279#issuecomment-1532084620 +const { + getEventField, + makeTestRequest, + waitForServerReady, +} = require('./azfunctestutils'); + +// Azure Functions programming model v3 supports node 14.x-20.x: +// https://learn.microsoft.com/en-ca/azure/azure-functions/functions-reference-node?tabs=javascript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#supported-versions +// However, let's only test with node 18.x for now. The testing involves +// installing the ridiculously large "azure-functions-core-tools" dep, so it +// isn't worth testing all versions. +if (!semver.satisfies(process.version, '^18.0.1')) { console.log( - `# SKIP Azure Functions runtime ~4 does not support node ${process.version} (https://aka.ms/functions-node-versions)`, + '# SKIP Azure Functions v3 tests, only testing with Node.js v18.latest', ); process.exit(); } else if (os.platform() === 'win32') { @@ -35,130 +43,6 @@ if (!semver.satisfies(process.version, '>=14.1.0 <19')) { process.exit(); } -/** - * Wait for the test "func start" to be ready. - * - * This polls the admin endpoint until - * it gets a 200 response, assuming the server is ready by then. - * It times out after ~60s -- so long because startup on Windows CI has been - * found to take a long time (it is downloading 250MB+ in "ExtensionBundle"s). - * - * @param {Test} t - This is only used to `t.comment(...)` with progress. - * @param {Function} cb - Calls `cb(err)` if there was a timeout, `cb()` on - * success. - */ -function waitForServerReady(t, cb) { - let sentinel = 30; - const INTERVAL_MS = 2000; - - const pollForServerReady = () => { - const req = http.get( - 'http://127.0.0.1:7071/admin/functions', - { - agent: false, - timeout: 500, - }, - (res) => { - res.resume(); - res.on('end', () => { - if (res.statusCode !== 200) { - scheduleNextPoll(`statusCode=${res.statusCode}`); - } else { - cb(); - } - }); - }, - ); - req.on('error', (err) => { - scheduleNextPoll(err.message); - }); - }; - - const scheduleNextPoll = (msg) => { - t.comment( - `[sentinel=${sentinel} ${new Date().toISOString()}] wait another 2s for server ready: ${msg}`, - ); - sentinel--; - if (sentinel <= 0) { - cb(new Error('timed out')); - } else { - setTimeout(pollForServerReady, INTERVAL_MS); - } - }; - - pollForServerReady(); -} - -async function makeTestRequest(t, testReq) { - return new Promise((resolve, reject) => { - const reqOpts = testReq.reqOpts; - const url = `http://127.0.0.1:7071${reqOpts.path}`; - t.comment( - `makeTestRequest: "${testReq.testName}" (${reqOpts.method} ${url})`, - ); - const req = http.request( - url, - { - method: reqOpts.method, - }, - (res) => { - const chunks = []; - res.on('data', (chunk) => { - chunks.push(chunk); - }); - res.on('end', () => { - const body = Buffer.concat(chunks); - if (testReq.expectedRes.statusCode) { - t.equal( - res.statusCode, - testReq.expectedRes.statusCode, - `res.statusCode === ${testReq.expectedRes.statusCode}`, - ); - } - if (testReq.expectedRes.headers) { - for (const [k, v] of Object.entries(testReq.expectedRes.headers)) { - if (v instanceof RegExp) { - t.ok( - v.test(res.headers[k]), - `res.headers[${JSON.stringify(k)}] =~ ${v}`, - ); - } else { - t.equal( - res.headers[k], - v, - `res.headers[${JSON.stringify(k)}] === ${JSON.stringify(v)}`, - ); - } - } - } - if (testReq.expectedRes.body) { - if (testReq.expectedRes.body instanceof RegExp) { - t.ok( - testReq.expectedRes.body.test(body), - `body =~ ${testReq.expectedRes.body}`, - ); - } else if (typeof testReq.expectedRes.body === 'string') { - t.equal(body.toString(), testReq.expectedRes.body, 'body'); - } else { - t.fail( - `unsupported type for TEST_REQUESTS[].expectedRes.body: ${typeof testReq - .expectedRes.body}`, - ); - } - } - resolve(); - }); - }, - ); - req.on('error', reject); - req.end(); - }); -} - -function getEventField(e, fieldName) { - return (e.transaction || e.error || e.span)[fieldName]; -} - /** * Assert that the given `apmEvents` (events that the mock APM server received) * match all the expected APM events in `TEST_REQUESTS`. @@ -168,7 +52,7 @@ function checkExpectedApmEvents(t, apmEvents) { if (apmEvents.length > 0) { const metadata = apmEvents.shift().metadata; t.ok(metadata, 'metadata is first event'); - t.equal(metadata.service.name, 'AJsAzureFnApp', 'metadata.service.name'); + t.equal(metadata.service.name, 'azfunc3', 'metadata.service.name'); t.equal( metadata.service.framework.name, 'Azure Functions', @@ -196,7 +80,7 @@ function checkExpectedApmEvents(t, apmEvents) { ); t.equal( metadata.cloud.instance.name, - 'AJsAzureFnApp', + 'azfunc3', 'metadata.cloud.instance.name', ); t.equal( @@ -256,7 +140,7 @@ function checkExpectedApmEvents(t, apmEvents) { const UUID_RE = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i; -const fnAppDir = path.join(__dirname, 'fixtures', 'AJsAzureFnApp'); +const fnAppDir = path.join(__dirname, 'fixtures', 'azfunc3'); const funcExe = path.resolve(fnAppDir, 'node_modules/.bin/func') + (os.platform() === 'win32' ? '.cmd' : ''); @@ -277,14 +161,10 @@ var TEST_REQUESTS = [ t.equal(trans.type, 'request', 'transaction.type'); t.equal(trans.outcome, 'success', 'transaction.outcome'); t.equal(trans.result, 'HTTP 2xx', 'transaction.result'); - t.equal( - trans.faas.name, - 'AJsAzureFnApp/HttpFn1', - 'transaction.faas.name', - ); + t.equal(trans.faas.name, 'azfunc3/HttpFn1', 'transaction.faas.name'); t.equal( trans.faas.id, - '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/AJsAzureFnApp/functions/HttpFn1', + '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/azfunc3/functions/HttpFn1', 'transaction.faas.id', ); t.equal(trans.faas.trigger.type, 'http', 'transaction.faas.trigger.type'); @@ -332,11 +212,7 @@ var TEST_REQUESTS = [ t.equal(trans.name, 'GET /api/HttpFnError', 'transaction.name'); t.equal(trans.outcome, 'failure', 'transaction.outcome'); t.equal(trans.result, 'HTTP 5xx', 'transaction.result'); - t.equal( - trans.faas.name, - 'AJsAzureFnApp/HttpFnError', - 'transaction.faas.name', - ); + t.equal(trans.faas.name, 'azfunc3/HttpFnError', 'transaction.faas.name'); t.equal(trans.faas.coldstart, false, 'transaction.faas.coldstart'); t.equal( trans.context.request.method, @@ -538,14 +414,10 @@ var TEST_REQUESTS = [ const trans = apmEventsForReq[0].transaction; t.equal(trans.name, 'GET /api/HttpFn1', 'transaction.name'); t.equal(trans.result, 'HTTP 2xx', 'transaction.result'); - t.equal( - trans.faas.name, - 'AJsAzureFnApp/HttpFn1', - 'transaction.faas.name', - ); + t.equal(trans.faas.name, 'azfunc3/HttpFn1', 'transaction.faas.name'); t.equal( trans.faas.id, - '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/AJsAzureFnApp/functions/HttpFn1', + '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/azfunc3/functions/HttpFn1', 'transaction.faas.id', ); t.equal( @@ -574,12 +446,12 @@ var TEST_REQUESTS = [ t.equal(trans.result, 'HTTP 2xx', 'transaction.result'); t.equal( trans.faas.name, - 'AJsAzureFnApp/HttpFnRouteTemplate', + 'azfunc3/HttpFnRouteTemplate', 'transaction.faas.name', ); t.equal( trans.faas.id, - '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/AJsAzureFnApp/functions/HttpFnRouteTemplate', + '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/azfunc3/functions/HttpFnRouteTemplate', 'transaction.faas.id', ); t.equal( @@ -605,7 +477,7 @@ var TEST_REQUESTS = [ t.equal(apmEventsForReq.length, 4); const t1 = apmEventsForReq[0].transaction; t.equal(t1.name, 'GET /api/HttpFnDistTraceA', 't1.name'); - t.equal(t1.faas.name, 'AJsAzureFnApp/HttpFnDistTraceA', 't1.faas.name'); + t.equal(t1.faas.name, 'azfunc3/HttpFnDistTraceA', 't1.faas.name'); const s1 = apmEventsForReq[1].span; t.equal(s1.name, 'spanA', 's1.name'); t.equal(s1.parent_id, t1.id, 's1 is a child of t1'); @@ -615,7 +487,7 @@ var TEST_REQUESTS = [ t.equal(s2.parent_id, s1.id, 's2 is a child of s1'); const t2 = apmEventsForReq[3].transaction; t.equal(t2.name, 'GET /api/HttpFnDistTraceB', 't2.name'); - t.equal(t2.faas.name, 'AJsAzureFnApp/HttpFnDistTraceB', 't2.faas.name'); + t.equal(t2.faas.name, 'azfunc3/HttpFnDistTraceB', 't2.faas.name'); t.equal(t2.parent_id, s2.id, 't2 is a child of s2'); t.equal( t2.context.request.headers.traceparent, @@ -659,7 +531,7 @@ tape.test( }, ); -tape.test('azure functions', function (suite) { +tape.test('azure functions v3', function (suite) { let apmServer; let apmServerUrl; @@ -673,7 +545,7 @@ tape.test('azure functions', function (suite) { }); let fnAppProc; - suite.test('setup: "func start" for AJsAzureFnApp fixture', (t) => { + suite.test('setup: "func start" for azfunc3 fixture', (t) => { fnAppProc = spawn(funcExe, ['start'], { cwd: fnAppDir, env: Object.assign({}, process.env, { @@ -698,11 +570,11 @@ tape.test('azure functions', function (suite) { // binaries to "$fnAppDir/node_modules/azure-functions-core-tools/...", // which means a local test run on macOS followed by an attempted test run // in Docker will result in a crash: - // node_tests_1 | # ["func start" stderr] /app/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/node_modules/azure-functions-core-tools/bin/func: 1: Syntax error: "(" unexpected + // node_tests_1 | # ["func start" stderr] /app/test/instrumentation/azure-functions/fixtures/azfunc3/node_modules/azure-functions-core-tools/bin/func: 1: Syntax error: "(" unexpected // node_tests_1 | not ok 2 "func start" failed early: code=2 // For now the workaround is to manually clean that tree before running // tests on a separate OS: - // rm -rf test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/node_modules + // rm -rf test/instrumentation/azure-functions/fixtures/azfunc3/node_modules t.fail(`"func start" failed early: code=${code}`); fnAppProc = null; clearTimeout(earlyCloseTimer); diff --git a/test/instrumentation/azure-functions/azure-functions-v4.test.js b/test/instrumentation/azure-functions/azure-functions-v4.test.js new file mode 100644 index 00000000000..80977f25059 --- /dev/null +++ b/test/instrumentation/azure-functions/azure-functions-v4.test.js @@ -0,0 +1,341 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict'; + +// Test Azure Functions programming model v4. + +const assert = require('assert'); +const { exec, spawn } = require('child_process'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +const semver = require('semver'); +const tape = require('tape'); +const treekill = require('tree-kill'); + +const { MockAPMServer } = require('../../_mock_apm_server'); +const { formatForTComment } = require('../../_utils'); +const { + getEventField, + makeTestRequest, + waitForServerReady, +} = require('./azfunctestutils'); + +// Azure Functions programming model v4 supports node 18.x-20.x: +// https://learn.microsoft.com/en-ca/azure/azure-functions/functions-reference-node?tabs=javascript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#supported-versions +// However, let's only test with node 20.x for now. The testing involves +// installing the ridiculously large "azure-functions-core-tools" dep, so it +// isn't worth testing all versions. +if (!semver.satisfies(process.version, '^20.0.1')) { + console.log( + '# SKIP Azure Functions v4 tests, only testing with Node.js v20.latest', + ); + process.exit(); +} else if (os.platform() === 'win32') { + console.log( + '# SKIP Azure Functions tests on Windows because of flaky azure-functions-core-tools install (see https://github.com/elastic/apm-agent-nodejs/issues/3107)', + ); + process.exit(); +} + +/** + * Assert that the given `apmEvents` (events that the mock APM server received) + * match all the expected APM events in `TEST_REQUESTS`. + */ +function checkExpectedApmEvents(t, apmEvents) { + // metadata + if (apmEvents.length > 0) { + const metadata = apmEvents.shift().metadata; + t.ok(metadata, 'metadata is first event'); + t.equal(metadata.service.name, 'azfunc4', 'metadata.service.name'); + t.equal( + metadata.service.framework.name, + 'Azure Functions', + 'metadata.service.framework.name', + ); + t.equal( + metadata.service.framework.version, + '~4', + 'metadata.service.framework.version', + ); + t.equal( + metadata.service.runtime.name, + 'node', + 'metadata.service.runtime.name', + ); + t.equal( + metadata.service.node.configured_name, + 'test-website-instance-id', + 'metadata.service.node.configured_name', + ); + t.equal( + metadata.cloud.account.id, + '2491fc8e-f7c1-4020-b9c6-78509919fd16', + 'metadata.cloud.account.id', + ); + t.equal( + metadata.cloud.instance.name, + 'azfunc4', + 'metadata.cloud.instance.name', + ); + t.equal( + metadata.cloud.project.name, + 'my-resource-group', + 'metadata.cloud.project.name', + ); + t.equal(metadata.cloud.provider, 'azure', 'metadata.cloud.provider'); + t.equal(metadata.cloud.region, 'test-region-name', 'metadata.cloud.region'); + t.equal( + metadata.cloud.service.name, + 'functions', + 'metadata.cloud.service.name', + ); + } + + // Filter out any metadata from separate requests, and metricsets which we + // aren't testing. + apmEvents = apmEvents.filter((e) => !e.metadata).filter((e) => !e.metricset); + + // Sort all the remaining APM events and check expectations from TEST_REQUESTS. + apmEvents = apmEvents.sort((a, b) => { + return getEventField(a, 'timestamp') < getEventField(b, 'timestamp') + ? -1 + : 1; + }); + TEST_REQUESTS.forEach((testReq) => { + t.comment(`check APM events for "${testReq.testName}"`); + // Collect all events for this transaction's trace_id, and pass that to + // the `checkApmEvents` function for this request. + let apmEventsForReq = []; + if (apmEvents.length > 0) { + assert( + apmEvents[0].transaction, + `next APM event is a transaction: ${JSON.stringify(apmEvents[0])}`, + ); + const traceId = apmEvents[0].transaction.trace_id; + apmEventsForReq = apmEvents.filter( + (e) => getEventField(e, 'trace_id') === traceId, + ); + apmEvents = apmEvents.filter( + (e) => getEventField(e, 'trace_id') !== traceId, + ); + } + testReq.checkApmEvents(t, apmEventsForReq); + }); + + t.equal( + apmEvents.length, + 0, + 'no additional unexpected APM server events: ' + JSON.stringify(apmEvents), + ); +} + +// ---- tests + +const UUID_RE = + /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i; + +const fnAppDir = path.join(__dirname, 'fixtures', 'azfunc4'); +const funcExe = + path.resolve(fnAppDir, 'node_modules/.bin/func') + + (os.platform() === 'win32' ? '.cmd' : ''); + +var TEST_REQUESTS = [ + { + testName: 'HttpExample', + reqOpts: { method: 'GET', path: '/api/HttpExample' }, + expectedRes: { + statusCode: 200, // the Azure Functions default + headers: { myheadername: 'MyHeaderValue' }, + body: 'HttpExample body', + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1); + const trans = apmEventsForReq[0].transaction; + t.equal(trans.name, 'GET /api/HttpExample', 'transaction.name'); + t.equal(trans.type, 'request', 'transaction.type'); + t.equal(trans.outcome, 'success', 'transaction.outcome'); + t.equal(trans.result, 'HTTP 2xx', 'transaction.result'); + t.equal(trans.faas.name, 'azfunc4/HttpExample', 'transaction.faas.name'); + t.equal( + trans.faas.id, + '/subscriptions/2491fc8e-f7c1-4020-b9c6-78509919fd16/resourceGroups/my-resource-group/providers/Microsoft.Web/sites/azfunc4/functions/HttpExample', + 'transaction.faas.id', + ); + t.equal(trans.faas.trigger.type, 'http', 'transaction.faas.trigger.type'); + t.ok( + UUID_RE.test(trans.faas.execution), + 'transaction.faas.execution ' + trans.faas.execution, + ); + t.equal(trans.faas.coldstart, true, 'transaction.faas.coldstart'); + t.equal( + trans.context.request.method, + 'GET', + 'transaction.context.request.method', + ); + t.equal( + trans.context.request.url.full, + 'http://127.0.0.1:7071/api/HttpExample', + 'transaction.context.request.url.full', + ); + t.ok( + trans.context.request.headers, + 'transaction.context.request.headers', + ); + t.equal( + trans.context.response.status_code, + 200, + 'transaction.context.response.status_code', + ); + t.equal( + trans.context.response.headers.MyHeaderName, + 'MyHeaderValue', + 'transaction.context.response.headers.MyHeaderName', + ); + }, + }, + // TODO: test more of the cases that are covered in azure-functions-v3.test.js +]; + +// We need to `npm ci` for a first test run. +tape.test( + `setup: npm ci (in ${fnAppDir})`, + { skip: fs.existsSync(funcExe) }, + (t) => { + const startTime = Date.now(); + exec( + 'npm ci', + { + cwd: fnAppDir, + }, + function (err, stdout, stderr) { + t.error( + err, + `"npm ci" succeeded (took ${(Date.now() - startTime) / 1000}s)`, + ); + if (err) { + t.comment( + `$ npm ci\n-- stdout --\n${stdout}\n-- stderr --\n${stderr}\n--`, + ); + } + t.end(); + }, + ); + }, +); + +tape.test('azure functions v4', function (suite) { + let apmServer; + let apmServerUrl; + + suite.test('setup', function (t) { + apmServer = new MockAPMServer(); + apmServer.start(function (serverUrl) { + apmServerUrl = serverUrl; + t.comment('mock APM apmServerUrl: ' + apmServerUrl); + t.end(); + }); + }); + + let fnAppProc; + suite.test('setup: "func start" for azfunc4 fixture', (t) => { + fnAppProc = spawn(funcExe, ['start'], { + cwd: fnAppDir, + env: Object.assign({}, process.env, { + ELASTIC_APM_SERVER_URL: apmServerUrl, + ELASTIC_APM_API_REQUEST_TIME: '2s', + }), + }); + fnAppProc.on('error', (err) => { + t.error(err, 'no error from "func start"'); + }); + fnAppProc.stdout.on('data', (data) => { + t.comment(`["func start" stdout] ${formatForTComment(data)}`); + }); + fnAppProc.stderr.on('data', (data) => { + t.comment(`["func start" stderr] ${formatForTComment(data)}`); + }); + + // Allow some time for an early fail of `func start`, e.g. if there is + // already a user of port 7071... + const onEarlyClose = (code) => { + // Warning/Limitation: The 'npm ci' above installs platform-specific + // binaries to "$fnAppDir/node_modules/azure-functions-core-tools/...", + // which means a local test run on macOS followed by an attempted test run + // in Docker will result in a crash: + // node_tests_1 | # ["func start" stderr] /app/test/instrumentation/azure-functions/fixtures/azfunc3/node_modules/azure-functions-core-tools/bin/func: 1: Syntax error: "(" unexpected + // node_tests_1 | not ok 2 "func start" failed early: code=2 + // For now the workaround is to manually clean that tree before running + // tests on a separate OS: + // rm -rf test/instrumentation/azure-functions/fixtures/azfunc3/node_modules + t.fail(`"func start" failed early: code=${code}`); + fnAppProc = null; + clearTimeout(earlyCloseTimer); + t.end(); + }; + fnAppProc.on('close', onEarlyClose); + const earlyCloseTimer = setTimeout(() => { + fnAppProc.removeListener('close', onEarlyClose); + + // ... then wait for the server to be ready. + waitForServerReady(t, (waitErr) => { + if (waitErr) { + t.fail( + `error waiting for "func start" to be ready: ${waitErr.message}`, + ); + treekill(fnAppProc.pid, 'SIGKILL'); + fnAppProc = null; + } else { + t.comment('"func start" is ready'); + } + t.end(); + }); + }, 1000); + }); + + suite.test('make requests', async (t) => { + if (!fnAppProc) { + t.skip('there is no fnAppProc'); + t.end(); + return; + } + + apmServer.clear(); + for (let i = 0; i < TEST_REQUESTS.length; i++) { + await makeTestRequest(t, TEST_REQUESTS[i]); + } + + t.end(); + }); + + suite.test('check all APM events', (t) => { + if (!fnAppProc) { + t.skip('there is no fnAppProc'); + t.end(); + return; + } + + // To ensure we get all the trace data from the instrumented function app + // server, we wait 2x the `apiRequestTime` (set above) before stopping it. + fnAppProc.on('close', (_code) => { + checkExpectedApmEvents(t, apmServer.events); + t.end(); + }); + t.comment('wait 4s for trace data to be sent before closing "func start"'); + setTimeout(() => { + treekill(fnAppProc.pid, 'SIGKILL'); + }, 4000); // 2x ELASTIC_APM_API_REQUEST_TIME set above + }); + + suite.test('teardown', function (t) { + apmServer.close(); + t.end(); + }); + + suite.end(); +}); diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnError/function.json b/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnError/function.json deleted file mode 100644 index 91052aaf8a0..00000000000 --- a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnError/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "Anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "req", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "res" - } - ] -} \ No newline at end of file diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/README.md b/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/README.md deleted file mode 100644 index e9264594afb..00000000000 --- a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/README.md +++ /dev/null @@ -1,12 +0,0 @@ -A Node.js JavaScript Azure function app to be used for testing of -elastic-apm-node. - -# Notes on how this was created - -- `func init AJsAzureFnApp` -- Remove "azure-functions-core-tools" devDep and move to top-level to share - between possibly many fixtures. -- An HTTP-triggered function: `func new --name HttpFn1 --template "HTTP trigger" --authlevel "anonymous"` -- ... - - diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/.gitignore b/test/instrumentation/azure-functions/fixtures/azfunc3/.gitignore similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/.gitignore rename to test/instrumentation/azure-functions/fixtures/azfunc3/.gitignore diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFn1/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFn1/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFn1/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFn1/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFn1/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFn1/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFn1/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFn1/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnBindingsRes/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnBindingsRes/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnBindingsRes/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnBindingsRes/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnBindingsRes/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnBindingsRes/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnBindingsRes/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnBindingsRes/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnContextDone/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnContextDone/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnContextDone/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnContextDone/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnContextDone/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnContextDone/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnContextDone/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnContextDone/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceA/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceA/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceA/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceA/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceA/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceA/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceA/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceA/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceB/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceB/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceB/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceB/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceB/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceB/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnDistTraceB/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnDistTraceB/index.js diff --git a/examples/an-azure-function-app/Bye/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnError/function.json similarity index 100% rename from examples/an-azure-function-app/Bye/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnError/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnError/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnError/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnError/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnError/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnContext/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnContext/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnContext/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnContext/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnContext/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnContext/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnContext/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnContext/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnObject/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnObject/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnObject/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnObject/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnObject/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnObject/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnObject/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnObject/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnResponseData/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnResponseData/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnResponseData/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnResponseData/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnResponseData/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnResponseData/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnResponseData/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnResponseData/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnString/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnString/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnString/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnString/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnString/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnString/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnReturnString/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnReturnString/index.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnRouteTemplate/function.json b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnRouteTemplate/function.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnRouteTemplate/function.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnRouteTemplate/function.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnRouteTemplate/index.js b/test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnRouteTemplate/index.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/HttpFnRouteTemplate/index.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/HttpFnRouteTemplate/index.js diff --git a/test/instrumentation/azure-functions/fixtures/azfunc3/README.md b/test/instrumentation/azure-functions/fixtures/azfunc3/README.md new file mode 100644 index 00000000000..9e3ea2e8ed6 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc3/README.md @@ -0,0 +1,3 @@ +A Node.js Azure Functions app, using **version 3** of the Node.js +programming model. See: +https://learn.microsoft.com/en-ca/azure/azure-functions/functions-node-upgrade-v4 diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/host.json b/test/instrumentation/azure-functions/fixtures/azfunc3/host.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/host.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/host.json diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/initapm.js b/test/instrumentation/azure-functions/fixtures/azfunc3/initapm.js similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/initapm.js rename to test/instrumentation/azure-functions/fixtures/azfunc3/initapm.js diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/local.settings.json b/test/instrumentation/azure-functions/fixtures/azfunc3/local.settings.json similarity index 92% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/local.settings.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/local.settings.json index 51058f07a53..8910ca349e0 100644 --- a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/local.settings.json +++ b/test/instrumentation/azure-functions/fixtures/azfunc3/local.settings.json @@ -7,7 +7,7 @@ "WEBSITE_NODE_DEFAULT_VERSION": "~16", "FUNCTIONS_EXTENSION_VERSION": "~4", - "WEBSITE_SITE_NAME": "AJsAzureFnApp", + "WEBSITE_SITE_NAME": "azfunc3", "WEBSITE_OWNER_NAME": "2491fc8e-f7c1-4020-b9c6-78509919fd16+my-resource-group-ARegionShortNamewebspace", "WEBSITE_RESOURCE_GROUP": "my-resource-group", "WEBSITE_INSTANCE_ID": "test-website-instance-id", diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/package-lock.json b/test/instrumentation/azure-functions/fixtures/azfunc3/package-lock.json similarity index 99% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/package-lock.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/package-lock.json index c2b5968f996..786aa2cee4a 100644 --- a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/package-lock.json +++ b/test/instrumentation/azure-functions/fixtures/azfunc3/package-lock.json @@ -1,5 +1,5 @@ { - "name": "AJsAzureFnApp", + "name": "azfunc3", "version": "1.0.0", "lockfileVersion": 2, "requires": true, diff --git a/test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/package.json b/test/instrumentation/azure-functions/fixtures/azfunc3/package.json similarity index 100% rename from test/instrumentation/azure-functions/fixtures/AJsAzureFnApp/package.json rename to test/instrumentation/azure-functions/fixtures/azfunc3/package.json diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/.funcignore b/test/instrumentation/azure-functions/fixtures/azfunc4/.funcignore new file mode 100644 index 00000000000..17bd0f697c2 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/.funcignore @@ -0,0 +1,10 @@ +*.js.map +*.ts +.git* +.vscode +local.settings.json +test +getting_started.md +node_modules/@types/ +node_modules/azure-functions-core-tools/ +node_modules/typescript/ \ No newline at end of file diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/.npmrc b/test/instrumentation/azure-functions/fixtures/azfunc4/.npmrc new file mode 100644 index 00000000000..4eae7876ff1 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/.npmrc @@ -0,0 +1 @@ +lockfile-version=3 diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/README.md b/test/instrumentation/azure-functions/fixtures/azfunc4/README.md new file mode 100644 index 00000000000..1da6020a3af --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/README.md @@ -0,0 +1,3 @@ +A Node.js Azure Functions app, using **version 4** of the Node.js +programming model. See: +https://learn.microsoft.com/en-ca/azure/azure-functions/functions-node-upgrade-v4 diff --git a/examples/an-azure-function-app/host.json b/test/instrumentation/azure-functions/fixtures/azfunc4/host.json similarity index 89% rename from examples/an-azure-function-app/host.json rename to test/instrumentation/azure-functions/fixtures/azfunc4/host.json index fd4bee790b9..9df913614d9 100644 --- a/examples/an-azure-function-app/host.json +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/host.json @@ -10,6 +10,6 @@ }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" + "version": "[4.*, 5.0.0)" } } \ No newline at end of file diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/initapm.js b/test/instrumentation/azure-functions/fixtures/azfunc4/initapm.js new file mode 100644 index 00000000000..db2f85666c6 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/initapm.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +// For the normal use case an "initapm.js" would look like: +// module.exports = require('elastic-apm-node').start(/* { ... } */) + +module.exports = require('../../../../../').start(); diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/local.settings.json b/test/instrumentation/azure-functions/fixtures/azfunc4/local.settings.json new file mode 100644 index 00000000000..574eb480636 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/local.settings.json @@ -0,0 +1,14 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "node", + "AzureWebJobsStorage": "", + + "FUNCTIONS_EXTENSION_VERSION": "~4", + "WEBSITE_SITE_NAME": "azfunc4", + "WEBSITE_OWNER_NAME": "2491fc8e-f7c1-4020-b9c6-78509919fd16+my-resource-group-ARegionShortNamewebspace", + "WEBSITE_RESOURCE_GROUP": "my-resource-group", + "WEBSITE_INSTANCE_ID": "test-website-instance-id", + "REGION_NAME": "test-region-name" + } +} diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/package-lock.json b/test/instrumentation/azure-functions/fixtures/azfunc4/package-lock.json new file mode 100644 index 00000000000..e78727a8d2b --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/package-lock.json @@ -0,0 +1,426 @@ +{ + "name": "azfunc4", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "dependencies": { + "@azure/functions": "^4.0.0", + "azure-functions-core-tools": "^4.0.6821" + } + }, + "node_modules/@azure/functions": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-4.6.0.tgz", + "integrity": "sha512-vGq9jXlgrJ3KaI8bepgfpk26zVY8vFZsQukF85qjjKTAR90eFOOBNaa+mc/0ViDY2lcdrU2fL/o1pQyZUtTDsw==", + "dependencies": { + "cookie": "^0.7.0", + "long": "^4.0.0", + "undici": "^5.13.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/azure-functions-core-tools": { + "version": "4.0.6821", + "resolved": "https://registry.npmjs.org/azure-functions-core-tools/-/azure-functions-core-tools-4.0.6821.tgz", + "integrity": "sha512-D+VcQKPk+aVk9WRO56zlkq9xmeO+kxixDIMEIH2tG9OSSSxmioNOdjkpEnZuUEUdv6PPFn9vybcLhGCXL4Xftg==", + "hasInstallScript": true, + "hasShrinkwrap": true, + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "chalk": "3.0.0", + "extract-zip": "^2.0.1", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "rimraf": "4.4.1" + }, + "bin": { + "azfun": "lib/main.js", + "azurefunctions": "lib/main.js", + "func": "lib/main.js" + }, + "engines": { + "node": ">=6.9.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "node_modules/azure-functions-core-tools/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "optional": true + }, + "node_modules/azure-functions-core-tools/node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/azure-functions-core-tools/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/ansi-styles": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", + "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/azure-functions-core-tools/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/azure-functions-core-tools/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/azure-functions-core-tools/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/azure-functions-core-tools/node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/azure-functions-core-tools/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/azure-functions-core-tools/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/azure-functions-core-tools/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/azure-functions-core-tools/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/azure-functions-core-tools/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/azure-functions-core-tools/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/azure-functions-core-tools/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/azure-functions-core-tools/node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/azure-functions-core-tools/node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "node_modules/azure-functions-core-tools/node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/azure-functions-core-tools/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/azure-functions-core-tools/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "license": "ISC", + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/azure-functions-core-tools/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-functions-core-tools/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/azure-functions-core-tools/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + } + } +} diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/package.json b/test/instrumentation/azure-functions/fixtures/azfunc4/package.json new file mode 100644 index 00000000000..4f755c02b79 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/package.json @@ -0,0 +1,13 @@ +{ + "name": "", + "version": "1.0.0", + "description": "", + "main": "{initapm.js,src/functions/*.js}", + "scripts": { + "start": "func start" + }, + "dependencies": { + "@azure/functions": "^4.0.0", + "azure-functions-core-tools": "^4.0.6821" + } +} diff --git a/test/instrumentation/azure-functions/fixtures/azfunc4/src/functions/HttpExample.js b/test/instrumentation/azure-functions/fixtures/azfunc4/src/functions/HttpExample.js new file mode 100644 index 00000000000..4b85e0f3f37 --- /dev/null +++ b/test/instrumentation/azure-functions/fixtures/azfunc4/src/functions/HttpExample.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +const { app } = require('@azure/functions'); + +app.http('HttpExample', { + methods: ['GET', 'POST'], + authLevel: 'anonymous', + handler: async (_request, _context) => { + return { + body: 'HttpExample body', + headers: { + MyHeaderName: 'MyHeaderValue', + }, + }; + }, +});