diff --git a/package-lock.json b/package-lock.json index 9ff82c3..9b6f25b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@aws-sdk/client-secrets-manager": "^3.973.5", "@aws-sdk/client-servicediscovery": "^3.973.5", "@aws-sdk/client-ssm": "^3.986.0", + "@aws-sdk/client-xray": "^3.989.0", "@studion/prettier-config": "^0.1.0", "@types/node": "^22", "exponential-backoff": "^3.1.2", @@ -1196,44 +1197,44 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.985.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.985.0.tgz", - "integrity": "sha512-81J8iE8MuXhdbMfIz4sWFj64Pe41bFi/uqqmqOC5SlGv+kwoyLsyKS/rH2tW2t5buih4vTUxskRjxlqikTD4oQ==", + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.993.0.tgz", + "integrity": "sha512-VLUN+wIeNX24fg12SCbzTUBnBENlL014yMKZvRhPkcn4wHR6LKgNrjsG3fZ03Xs0XoKaGtNFi1VVrq666sGBoQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.7", + "@aws-sdk/core": "^3.973.11", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.985.0", + "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.5", + "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.22.1", + "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.13", - "@smithy/middleware-retry": "^4.4.30", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.9", + "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.29", - "@smithy/util-defaults-mode-node": "^4.2.32", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", @@ -1245,9 +1246,77 @@ } }, "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { - "version": "3.985.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.985.0.tgz", - "integrity": "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA==", + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.993.0.tgz", + "integrity": "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-xray": { + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-xray/-/client-xray-3.993.0.tgz", + "integrity": "sha512-Eeuty4YT5qwi9hw3LDSEtYg9ZZ42pDeZ1jNXns/Rffv+RyQjkBvirA4G+KzRI7IcwsBPcry5kTy6Zo1EfIcQcA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.11", + "@aws-sdk/credential-provider-node": "^3.972.10", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.11", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.993.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.9", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.2", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.5", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-xray/node_modules/@aws-sdk/util-endpoints": { + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.993.0.tgz", + "integrity": "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.1", @@ -1261,19 +1330,19 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.7.tgz", - "integrity": "sha512-wNZZQQNlJ+hzD49cKdo+PY6rsTDElO8yDImnrI69p2PLBa7QomeUKAJWYp9xnaR38nlHqWhMHZuYLCQ3oSX+xg==", + "version": "3.973.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.11.tgz", + "integrity": "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.1", - "@aws-sdk/xml-builder": "^3.972.4", - "@smithy/core": "^3.22.1", + "@aws-sdk/xml-builder": "^3.972.5", + "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", @@ -1299,12 +1368,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.5.tgz", - "integrity": "sha512-LxJ9PEO4gKPXzkufvIESUysykPIdrV7+Ocb9yAhbhJLE4TiAYqbCVUE+VuKP1leGR1bBfjWjYgSV5MxprlX3mQ==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.9.tgz", + "integrity": "sha512-ZptrOwQynfupubvcngLkbdIq/aXvl/czdpEG8XJ8mN8Nb19BR0jaK0bR+tfuMU36Ez9q4xv7GGkHFqEEP2hUUQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", + "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", @@ -1315,20 +1384,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.7.tgz", - "integrity": "sha512-L2uOGtvp2x3bTcxFTpSM+GkwFIPd8pHfGWO1764icMbo7e5xJh0nfhx1UwkXLnwvocTNEf8A7jISZLYjUSNaTg==", + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.11.tgz", + "integrity": "sha512-hECWoOoH386bGr89NQc9vA/abkGf5TJrMREt+lhNcnSNmoBS04fK7vc3LrJBSQAUGGVj0Tz3f4dHB3w5veovig==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", + "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.9", + "@smithy/node-http-handler": "^4.4.10", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.11", + "@smithy/util-stream": "^4.5.12", "tslib": "^2.6.2" }, "engines": { @@ -1336,19 +1405,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.5.tgz", - "integrity": "sha512-SdDTYE6jkARzOeL7+kudMIM4DaFnP5dZVeatzw849k4bSXDdErDS188bgeNzc/RA2WGrlEpsqHUKP6G7sVXhZg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.973.7", - "@aws-sdk/credential-provider-env": "^3.972.5", - "@aws-sdk/credential-provider-http": "^3.972.7", - "@aws-sdk/credential-provider-login": "^3.972.5", - "@aws-sdk/credential-provider-process": "^3.972.5", - "@aws-sdk/credential-provider-sso": "^3.972.5", - "@aws-sdk/credential-provider-web-identity": "^3.972.5", - "@aws-sdk/nested-clients": "3.985.0", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.9.tgz", + "integrity": "sha512-zr1csEu9n4eDiHMTYJabX1mDGuGLgjgUnNckIivvk43DocJC9/f6DefFrnUPZXE+GHtbW50YuXb+JIxKykU74A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.11", + "@aws-sdk/credential-provider-env": "^3.972.9", + "@aws-sdk/credential-provider-http": "^3.972.11", + "@aws-sdk/credential-provider-login": "^3.972.9", + "@aws-sdk/credential-provider-process": "^3.972.9", + "@aws-sdk/credential-provider-sso": "^3.972.9", + "@aws-sdk/credential-provider-web-identity": "^3.972.9", + "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", @@ -1361,13 +1430,13 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.5.tgz", - "integrity": "sha512-uYq1ILyTSI6ZDCMY5+vUsRM0SOCVI7kaW4wBrehVVkhAxC6y+e9rvGtnoZqCOWL1gKjTMouvsf4Ilhc5NCg1Aw==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.9.tgz", + "integrity": "sha512-m4RIpVgZChv0vWS/HKChg1xLgZPpx8Z+ly9Fv7FwA8SOfuC6I3htcSaBz2Ch4bneRIiBUhwP4ziUo0UZgtJStQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", - "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/core": "^3.973.11", + "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", @@ -1380,17 +1449,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.6.tgz", - "integrity": "sha512-DZ3CnAAtSVtVz+G+ogqecaErMLgzph4JH5nYbHoBMgBkwTUV+SUcjsjOJwdBJTHu3Dm6l5LBYekZoU2nDqQk2A==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.10.tgz", + "integrity": "sha512-70nCESlvnzjo4LjJ8By8MYIiBogkYPSXl3WmMZfH9RZcB/Nt9qVWbFpYj6Fk1vLa4Vk8qagFVeXgxdieMxG1QA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.5", - "@aws-sdk/credential-provider-http": "^3.972.7", - "@aws-sdk/credential-provider-ini": "^3.972.5", - "@aws-sdk/credential-provider-process": "^3.972.5", - "@aws-sdk/credential-provider-sso": "^3.972.5", - "@aws-sdk/credential-provider-web-identity": "^3.972.5", + "@aws-sdk/credential-provider-env": "^3.972.9", + "@aws-sdk/credential-provider-http": "^3.972.11", + "@aws-sdk/credential-provider-ini": "^3.972.9", + "@aws-sdk/credential-provider-process": "^3.972.9", + "@aws-sdk/credential-provider-sso": "^3.972.9", + "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", @@ -1403,12 +1472,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.5.tgz", - "integrity": "sha512-HDKF3mVbLnuqGg6dMnzBf1VUOywE12/N286msI9YaK9mEIzdsGCtLTvrDhe3Up0R9/hGFbB+9l21/TwF5L1C6g==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.9.tgz", + "integrity": "sha512-gOWl0Fe2gETj5Bk151+LYKpeGi2lBDLNu+NMNpHRlIrKHdBmVun8/AalwMK8ci4uRfG5a3/+zvZBMpuen1SZ0A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", + "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -1420,14 +1489,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.5.tgz", - "integrity": "sha512-8urj3AoeNeQisjMmMBhFeiY2gxt6/7wQQbEGun0YV/OaOOiXrIudTIEYF8ZfD+NQI6X1FY5AkRsx6O/CaGiybA==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.9.tgz", + "integrity": "sha512-ey7S686foGTArvFhi3ifQXmgptKYvLSGE2250BAQceMSXZddz7sUSNERGJT2S7u5KIe/kgugxrt01hntXVln6w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.985.0", - "@aws-sdk/core": "^3.973.7", - "@aws-sdk/token-providers": "3.985.0", + "@aws-sdk/client-sso": "3.993.0", + "@aws-sdk/core": "^3.973.11", + "@aws-sdk/token-providers": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -1439,13 +1508,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.5.tgz", - "integrity": "sha512-OK3cULuJl6c+RcDZfPpaK5o3deTOnKZbxm7pzhFNGA3fI2hF9yDih17fGRazJzGGWaDVlR9ejZrpDef4DJCEsw==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.9.tgz", + "integrity": "sha512-8LnfS76nHXoEc9aRRiMMpxZxJeDG0yusdyo3NvPhCgESmBUgpMa4luhGbClW5NoX/qRcGxxM6Z/esqANSNMTow==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", - "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/core": "^3.973.11", + "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -1673,15 +1742,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.7.tgz", - "integrity": "sha512-HUD+geASjXSCyL/DHPQc/Ua7JhldTcIglVAoCV8kiVm99IaFSlAbTvEnyhZwdE6bdFyTL+uIaWLaCFSRsglZBQ==", + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.11.tgz", + "integrity": "sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", + "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.985.0", - "@smithy/core": "^3.22.1", + "@aws-sdk/util-endpoints": "3.993.0", + "@smithy/core": "^3.23.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -1691,9 +1760,9 @@ } }, "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/util-endpoints": { - "version": "3.985.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.985.0.tgz", - "integrity": "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA==", + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.993.0.tgz", + "integrity": "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.1", @@ -1707,44 +1776,44 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.985.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.985.0.tgz", - "integrity": "sha512-TsWwKzb/2WHafAY0CE7uXgLj0FmnkBTgfioG9HO+7z/zCPcl1+YU+i7dW4o0y+aFxFgxTMG+ExBQpqT/k2ao8g==", + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.993.0.tgz", + "integrity": "sha512-iOq86f2H67924kQUIPOAvlmMaOAvOLoDOIb66I2YqSUpMYB6ufiuJW3RlREgskxv86S5qKzMnfy/X6CqMjK6XQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.7", + "@aws-sdk/core": "^3.973.11", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.985.0", + "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.5", + "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.22.1", + "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.13", - "@smithy/middleware-retry": "^4.4.30", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.9", + "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.29", - "@smithy/util-defaults-mode-node": "^4.2.32", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", @@ -1756,9 +1825,9 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { - "version": "3.985.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.985.0.tgz", - "integrity": "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA==", + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.993.0.tgz", + "integrity": "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.1", @@ -1806,13 +1875,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.985.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.985.0.tgz", - "integrity": "sha512-+hwpHZyEq8k+9JL2PkE60V93v2kNhUIv7STFt+EAez1UJsJOQDhc5LpzEX66pNjclI5OTwBROs/DhJjC/BtMjQ==", + "version": "3.993.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.993.0.tgz", + "integrity": "sha512-+35g4c+8r7sB9Sjp1KPdM8qxGn6B/shBjJtEUN4e+Edw9UEQlZKIzioOGu3UAbyE0a/s450LdLZr4wbJChtmww==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.7", - "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/core": "^3.973.11", + "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -1906,12 +1975,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.5.tgz", - "integrity": "sha512-GsUDF+rXyxDZkkJxUsDxnA67FG+kc5W1dnloCFLl6fWzceevsCYzJpASBzT+BPjwUgREE6FngfJYYYMQUY5fZQ==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.9.tgz", + "integrity": "sha512-JNswdsLdQemxqaSIBL2HRhsHPUBBziAgoi5RQv6/9avmE5g5RSdt1hWr3mHJ7OxqRYf+KeB11ExWbiqfrnoeaA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", @@ -1930,13 +1999,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", - "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.5.tgz", + "integrity": "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.12.0", - "fast-xml-parser": "5.3.4", + "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" }, "engines": { @@ -4384,9 +4453,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.22.1.tgz", - "integrity": "sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==", + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.2.tgz", + "integrity": "sha512-HaaH4VbGie4t0+9nY3tNBRSxVTr96wzIqexUa6C2qx3MPePAuz7lIxPxYtt1Wc//SPfJLNoZJzfdt0B6ksj2jA==", "license": "Apache-2.0", "dependencies": { "@smithy/middleware-serde": "^4.2.9", @@ -4395,7 +4464,7 @@ "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.11", + "@smithy/util-stream": "^4.5.12", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -4612,12 +4681,12 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.13.tgz", - "integrity": "sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==", + "version": "4.4.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.16.tgz", + "integrity": "sha512-L5GICFCSsNhbJ5JSKeWFGFy16Q2OhoBizb3X2DrxaJwXSEujVvjG9Jt386dpQn2t7jINglQl0b4K/Su69BdbMA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.22.1", + "@smithy/core": "^3.23.2", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -4631,15 +4700,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.30", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.30.tgz", - "integrity": "sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==", + "version": "4.4.33", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.33.tgz", + "integrity": "sha512-jLqZOdJhtIL4lnA9hXnAG6GgnJlo1sD3FqsTxm9wSfjviqgWesY/TMBVnT84yr4O0Vfe0jWoXlfFbzsBVph3WA==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", @@ -4693,9 +4762,9 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.9.tgz", - "integrity": "sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.10.tgz", + "integrity": "sha512-u4YeUwOWRZaHbWaebvrs3UhwQwj+2VNmcVCwXcYTvPIuVyM7Ex1ftAj+fdbG/P4AkBwLq/+SKn+ydOI4ZJE9PA==", "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.2.8", @@ -4806,17 +4875,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.2.tgz", - "integrity": "sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==", + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.5.tgz", + "integrity": "sha512-xixwBRqoeP2IUgcAl3U9dvJXc+qJum4lzo3maaJxifsZxKUYLfVfCXvhT4/jD01sRrHg5zjd1cw2Zmjr4/SuKQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.22.1", - "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/core": "^3.23.2", + "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.11", + "@smithy/util-stream": "^4.5.12", "tslib": "^2.6.2" }, "engines": { @@ -4913,13 +4982,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.29.tgz", - "integrity": "sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==", + "version": "4.3.32", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.32.tgz", + "integrity": "sha512-092sjYfFMQ/iaPH798LY/OJFBcYu0sSK34Oy9vdixhsU36zlZu8OcYjF3TD4e2ARupyK7xaxPXl+T0VIJTEkkg==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, @@ -4928,16 +4997,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.32", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.32.tgz", - "integrity": "sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==", + "version": "4.2.35", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.35.tgz", + "integrity": "sha512-miz/ggz87M8VuM29y7jJZMYkn7+IErM5p5UgKIf8OtqVs/h2bXr1Bt3uTsREsI/4nK8a0PQERbAPsVPVNIsG7Q==", "license": "Apache-2.0", "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.11.2", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, @@ -4999,13 +5068,13 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.11", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.11.tgz", - "integrity": "sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==", + "version": "4.5.12", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.12.tgz", + "integrity": "sha512-D8tgkrmhAX/UNeCZbqbEO3uqyghUnEmmoO9YEvRuwxjlkKKUE7FOgCJnqpTlQPe9MApdWPky58mNQQHbnCzoNg==", "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.9", + "@smithy/node-http-handler": "^4.4.10", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", diff --git a/package.json b/package.json index 16a0ade..4d1de89 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "@aws-sdk/client-secrets-manager": "^3.973.5", "@aws-sdk/client-servicediscovery": "^3.973.5", "@aws-sdk/client-ssm": "^3.986.0", + "@aws-sdk/client-xray": "^3.989.0", "@studion/prettier-config": "^0.1.0", "@types/node": "^22", "exponential-backoff": "^3.1.2", diff --git a/tests/otel/config.test.ts b/tests/otel/config.test.ts new file mode 100644 index 0000000..4980f94 --- /dev/null +++ b/tests/otel/config.test.ts @@ -0,0 +1,492 @@ +import { it } from 'node:test'; +import * as assert from 'node:assert'; +import { OtelCollectorConfigBuilder } from '../../src/otel/config'; + +const awsRegion = 'us-west-2'; +const prometheusNamespace = 'test-namespace'; +const prometheusEndpoint = + 'https://aps-workspaces.us-west-2.amazonaws.com/workspaces/ws-12345/api/v1/remote_write'; +const logGroupName = 'cw-test-lg'; +const logStreamName = 'cw-test-ls'; +const logRetention = 7; + +const defaultMemoryLimiterConfig = { + check_interval: '1s', + limit_percentage: 80, + spike_limit_percentage: 15, +}; +const defaultBatchConfig = { + send_batch_size: 8192, + send_batch_max_size: 10000, + timeout: '5s', +}; + +export function testOtelConfigBuilder() { + it('should generate minimal configuration with OTLP receiver and debug exporter', () => { + const result = new OtelCollectorConfigBuilder() + .withOTLPReceiver(['http']) + .withDebug() + .build(); + + const expected = { + receivers: { + otlp: { + protocols: { + http: { endpoint: '0.0.0.0:4318' }, + }, + }, + }, + processors: {}, + exporters: { + debug: { verbosity: 'detailed' }, + }, + extensions: {}, + service: { pipelines: {} }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure batch processor', () => { + const result = new OtelCollectorConfigBuilder() + .withBatchProcessor() + .build(); + + const expected = { + receivers: {}, + processors: { + batch: defaultBatchConfig, + }, + exporters: {}, + extensions: {}, + service: { pipelines: {} }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure memory limiter processor', () => { + const result = new OtelCollectorConfigBuilder() + .withMemoryLimiterProcessor() + .build(); + + const expected = { + receivers: {}, + processors: { + memory_limiter: defaultMemoryLimiterConfig, + }, + exporters: {}, + extensions: {}, + service: { pipelines: {} }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('withBatchProcessor should use provided parameters', () => { + const result = new OtelCollectorConfigBuilder() + .withBatchProcessor('batch', 5000, 8000, '10s') + .build(); + + assert.deepStrictEqual(result.processors.batch, { + send_batch_size: 5000, + send_batch_max_size: 8000, + timeout: '10s', + }); + }); + + it('withMemoryLimiterProcessor should use provided parameters', () => { + const result = new OtelCollectorConfigBuilder() + .withMemoryLimiterProcessor('3s', 70, 15) + .build(); + + assert.deepStrictEqual(result.processors.memory_limiter, { + check_interval: '3s', + limit_percentage: 70, + spike_limit_percentage: 15, + }); + }); + + it('should configure Amazon Prometheus Service (APS) exporter', () => { + const result = new OtelCollectorConfigBuilder() + .withAPS(prometheusNamespace, prometheusEndpoint, awsRegion) + .build(); + + const expected = { + receivers: {}, + processors: {}, + exporters: { + prometheusremotewrite: { + namespace: prometheusNamespace, + endpoint: prometheusEndpoint, + auth: { authenticator: 'sigv4auth' }, + }, + }, + extensions: { + sigv4auth: { + region: awsRegion, + service: 'aps', + }, + }, + service: { + extensions: ['sigv4auth'], + pipelines: {}, + }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure AWS X-Ray exporter', () => { + const result = new OtelCollectorConfigBuilder() + .withAWSXRayExporter(awsRegion) + .build(); + + const expected = { + receivers: {}, + processors: {}, + exporters: { + awsxray: { region: awsRegion }, + }, + extensions: {}, + service: { pipelines: {} }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure CloudWatch logs exporter', () => { + const result = new OtelCollectorConfigBuilder() + .withCloudWatchLogsExporter( + awsRegion, + logGroupName, + logStreamName, + logRetention, + ) + .build(); + + const expected = { + receivers: {}, + processors: {}, + exporters: { + awscloudwatchlogs: { + region: awsRegion, + log_group_name: logGroupName, + log_stream_name: logStreamName, + log_retention: logRetention, + }, + }, + extensions: {}, + service: { pipelines: {} }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure health check extension', () => { + const result = new OtelCollectorConfigBuilder() + .withHealthCheckExtension() + .build(); + + const expected = { + receivers: {}, + processors: {}, + exporters: {}, + extensions: { + health_check: { endpoint: '0.0.0.0:13133' }, + }, + service: { + extensions: ['health_check'], + pipelines: {}, + }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure pprof extension', () => { + const result = new OtelCollectorConfigBuilder() + .withPprofExtension() + .build(); + + const expected = { + receivers: {}, + processors: {}, + exporters: {}, + extensions: { + pprof: { endpoint: '0.0.0.0:1777' }, + }, + service: { + extensions: ['pprof'], + pipelines: {}, + }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should configure pipelines', () => { + const result = new OtelCollectorConfigBuilder() + .withOTLPReceiver(['http']) + .withBatchProcessor() + .withMemoryLimiterProcessor() + .withAWSXRayExporter(awsRegion) + .withDebug() + .withCloudWatchLogsExporter( + awsRegion, + logGroupName, + logStreamName, + logRetention, + ) + .withMetricsPipeline(['otlp'], ['memory_limiter', 'batch'], ['debug']) + .withTracesPipeline( + ['otlp'], + ['memory_limiter', 'batch'], + ['awsxray', 'debug'], + ) + .withLogsPipeline( + ['otlp'], + ['memory_limiter', 'batch'], + ['awscloudwatchlogs'], + ) + .build(); + + const expected = { + receivers: { + otlp: { + protocols: { + http: { endpoint: '0.0.0.0:4318' }, + }, + }, + }, + processors: { + batch: defaultBatchConfig, + memory_limiter: defaultMemoryLimiterConfig, + }, + exporters: { + awsxray: { region: awsRegion }, + debug: { verbosity: 'detailed' }, + awscloudwatchlogs: { + region: awsRegion, + log_group_name: logGroupName, + log_stream_name: logStreamName, + log_retention: logRetention, + }, + }, + extensions: {}, + service: { + pipelines: { + metrics: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch'], + exporters: ['debug'], + }, + traces: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch'], + exporters: ['awsxray', 'debug'], + }, + logs: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch'], + exporters: ['awscloudwatchlogs'], + }, + }, + }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('withDebug should use provided verbosity', () => { + const verbosity = 'basic'; + + const result = new OtelCollectorConfigBuilder() + .withDebug(verbosity) + .build(); + + assert.strictEqual(result.exporters.debug?.verbosity, verbosity); + }); + + it('should generate default configuration', () => { + const result = new OtelCollectorConfigBuilder() + .withDefault({ + prometheusNamespace, + prometheusEndpoint, + region: awsRegion, + logGroupName, + logStreamName, + logRetention, + }) + .build(); + + const expected = { + receivers: { + otlp: { + protocols: { + http: { endpoint: '0.0.0.0:4318' }, + }, + }, + }, + processors: { + 'batch/metrics': defaultBatchConfig, + 'batch/traces': { + send_batch_max_size: 5000, + send_batch_size: 2000, + timeout: '2s', + }, + 'batch/logs': { + send_batch_max_size: 5000, + send_batch_size: 1024, + timeout: '2s', + }, + memory_limiter: defaultMemoryLimiterConfig, + }, + exporters: { + prometheusremotewrite: { + namespace: prometheusNamespace, + endpoint: prometheusEndpoint, + auth: { authenticator: 'sigv4auth' }, + }, + awsxray: { region: awsRegion }, + awscloudwatchlogs: { + region: awsRegion, + log_group_name: logGroupName, + log_stream_name: logStreamName, + log_retention: logRetention, + }, + }, + extensions: { + sigv4auth: { + region: awsRegion, + service: 'aps', + }, + health_check: { endpoint: '0.0.0.0:13133' }, + }, + service: { + extensions: ['sigv4auth', 'health_check'], + pipelines: { + metrics: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch/metrics'], + exporters: ['prometheusremotewrite'], + }, + traces: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch/traces'], + exporters: ['awsxray'], + }, + logs: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch/logs'], + exporters: ['awscloudwatchlogs'], + }, + }, + telemetry: { + logs: { level: 'error' }, + metrics: { level: 'basic' }, + }, + }, + }; + + assert.deepStrictEqual(result, expected); + }); + + it('should generate complete configuration', () => { + const result = new OtelCollectorConfigBuilder() + .withOTLPReceiver(['http']) + .withBatchProcessor() + .withMemoryLimiterProcessor() + .withAPS(prometheusNamespace, prometheusEndpoint, awsRegion) + .withAWSXRayExporter(awsRegion) + .withCloudWatchLogsExporter( + awsRegion, + logGroupName, + logStreamName, + logRetention, + ) + .withDebug() + .withTelemetry() + .withHealthCheckExtension() + .withPprofExtension() + .withMetricsPipeline( + ['otlp'], + ['memory_limiter', 'batch'], + ['prometheusremotewrite', 'debug'], + ) + .withTracesPipeline( + ['otlp'], + ['memory_limiter', 'batch'], + ['awsxray', 'debug'], + ) + .withLogsPipeline( + ['otlp'], + ['memory_limiter', 'batch'], + ['awscloudwatchlogs', 'debug'], + ) + .build(); + + const expected = { + receivers: { + otlp: { + protocols: { + http: { endpoint: '0.0.0.0:4318' }, + }, + }, + }, + processors: { + batch: defaultBatchConfig, + memory_limiter: defaultMemoryLimiterConfig, + }, + exporters: { + prometheusremotewrite: { + namespace: prometheusNamespace, + endpoint: prometheusEndpoint, + auth: { authenticator: 'sigv4auth' }, + }, + awsxray: { region: awsRegion }, + awscloudwatchlogs: { + region: awsRegion, + log_group_name: logGroupName, + log_stream_name: logStreamName, + log_retention: logRetention, + }, + debug: { verbosity: 'detailed' }, + }, + extensions: { + sigv4auth: { + region: awsRegion, + service: 'aps', + }, + health_check: { endpoint: '0.0.0.0:13133' }, + pprof: { endpoint: '0.0.0.0:1777' }, + }, + service: { + extensions: ['sigv4auth', 'health_check', 'pprof'], + pipelines: { + metrics: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch'], + exporters: ['prometheusremotewrite', 'debug'], + }, + traces: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch'], + exporters: ['awsxray', 'debug'], + }, + logs: { + receivers: ['otlp'], + processors: ['memory_limiter', 'batch'], + exporters: ['awscloudwatchlogs', 'debug'], + }, + }, + telemetry: { + logs: { level: 'error' }, + metrics: { level: 'basic' }, + }, + }, + }; + + assert.deepStrictEqual(result, expected); + }); +} diff --git a/tests/otel/index.test.ts b/tests/otel/index.test.ts index 4e6ca70..b8aba42 100644 --- a/tests/otel/index.test.ts +++ b/tests/otel/index.test.ts @@ -1,495 +1,45 @@ -import { describe, it } from 'node:test'; -import * as assert from 'node:assert'; -import { OtelCollectorConfigBuilder } from '../../src/otel/config'; +import { before, describe, after } from 'node:test'; +import { InlineProgramArgs, OutputMap } from '@pulumi/pulumi/automation'; +import { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs'; +import { XRayClient } from '@aws-sdk/client-xray'; +import * as automation from '../automation'; +import { requireEnv, unwrapOutputs } from '../util'; +import { testOtelConfigBuilder } from './config.test'; import { testOtelCollectorConfigBuilderValidation } from './validation.test'; - -const awsRegion = 'us-west-2'; -const prometheusNamespace = 'test-namespace'; -const prometheusEndpoint = - 'https://aps-workspaces.us-west-2.amazonaws.com/workspaces/ws-12345/api/v1/remote_write'; -const logGroupName = 'cw-test-lg'; -const logStreamName = 'cw-test-ls'; -const logRetention = 7; - -const defaultMemoryLimiterConfig = { - check_interval: '1s', - limit_percentage: 80, - spike_limit_percentage: 15, +import { testOtelIntegration } from './integration.test'; +import * as infraConfig from './infrastructure/config'; +import { OtelTestContext, ProgramOutput } from './test-context'; + +const programArgs: InlineProgramArgs = { + stackName: 'dev', + projectName: 'icb-test-otel', + program: () => import('./infrastructure'), }; -const defaultBatchConfig = { - send_batch_size: 8192, - send_batch_max_size: 10000, - timeout: '5s', -}; - -describe('OtelCollectorConfigBuilder', () => { - it('should generate minimal configuration with OTLP receiver and debug exporter', () => { - const result = new OtelCollectorConfigBuilder() - .withOTLPReceiver(['http']) - .withDebug() - .build(); - - const expected = { - receivers: { - otlp: { - protocols: { - http: { endpoint: '0.0.0.0:4318' }, - }, - }, - }, - processors: {}, - exporters: { - debug: { verbosity: 'detailed' }, - }, - extensions: {}, - service: { pipelines: {} }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('should configure batch processor', () => { - const result = new OtelCollectorConfigBuilder() - .withBatchProcessor() - .build(); - - const expected = { - receivers: {}, - processors: { - batch: defaultBatchConfig, - }, - exporters: {}, - extensions: {}, - service: { pipelines: {} }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('should configure memory limiter processor', () => { - const result = new OtelCollectorConfigBuilder() - .withMemoryLimiterProcessor() - .build(); - - const expected = { - receivers: {}, - processors: { - memory_limiter: defaultMemoryLimiterConfig, - }, - exporters: {}, - extensions: {}, - service: { pipelines: {} }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('withBatchProcessor should use provided parameters', () => { - const result = new OtelCollectorConfigBuilder() - .withBatchProcessor('batch', 5000, 8000, '10s') - .build(); - - assert.deepStrictEqual(result.processors.batch, { - send_batch_size: 5000, - send_batch_max_size: 8000, - timeout: '10s', - }); - }); - - it('withMemoryLimiterProcessor should use provided parameters', () => { - const result = new OtelCollectorConfigBuilder() - .withMemoryLimiterProcessor('3s', 70, 15) - .build(); - - assert.deepStrictEqual(result.processors.memory_limiter, { - check_interval: '3s', - limit_percentage: 70, - spike_limit_percentage: 15, - }); - }); - - it('should configure Amazon Prometheus Service (APS) exporter', () => { - const result = new OtelCollectorConfigBuilder() - .withAPS(prometheusNamespace, prometheusEndpoint, awsRegion) - .build(); - - const expected = { - receivers: {}, - processors: {}, - exporters: { - prometheusremotewrite: { - namespace: prometheusNamespace, - endpoint: prometheusEndpoint, - auth: { authenticator: 'sigv4auth' }, - }, - }, - extensions: { - sigv4auth: { - region: awsRegion, - service: 'aps', - }, - }, - service: { - extensions: ['sigv4auth'], - pipelines: {}, - }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('should configure AWS X-Ray exporter', () => { - const result = new OtelCollectorConfigBuilder() - .withAWSXRayExporter(awsRegion) - .build(); - - const expected = { - receivers: {}, - processors: {}, - exporters: { - awsxray: { region: awsRegion }, - }, - extensions: {}, - service: { pipelines: {} }, - }; - assert.deepStrictEqual(result, expected); - }); - - it('should configure CloudWatch logs exporter', () => { - const result = new OtelCollectorConfigBuilder() - .withCloudWatchLogsExporter( - awsRegion, - logGroupName, - logStreamName, - logRetention, - ) - .build(); - - const expected = { - receivers: {}, - processors: {}, - exporters: { - awscloudwatchlogs: { - region: awsRegion, - log_group_name: logGroupName, - log_stream_name: logStreamName, - log_retention: logRetention, - }, - }, - extensions: {}, - service: { pipelines: {} }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('should configure health check extension', () => { - const result = new OtelCollectorConfigBuilder() - .withHealthCheckExtension() - .build(); - - const expected = { - receivers: {}, - processors: {}, - exporters: {}, - extensions: { - health_check: { endpoint: '0.0.0.0:13133' }, - }, - service: { - extensions: ['health_check'], - pipelines: {}, - }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('should configure pprof extension', () => { - const result = new OtelCollectorConfigBuilder() - .withPprofExtension() - .build(); - - const expected = { - receivers: {}, - processors: {}, - exporters: {}, - extensions: { - pprof: { endpoint: '0.0.0.0:1777' }, - }, - service: { - extensions: ['pprof'], - pipelines: {}, - }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('should configure pipelines', () => { - const result = new OtelCollectorConfigBuilder() - .withOTLPReceiver(['http']) - .withBatchProcessor() - .withMemoryLimiterProcessor() - .withAWSXRayExporter(awsRegion) - .withDebug() - .withCloudWatchLogsExporter( - awsRegion, - logGroupName, - logStreamName, - logRetention, - ) - .withMetricsPipeline(['otlp'], ['memory_limiter', 'batch'], ['debug']) - .withTracesPipeline( - ['otlp'], - ['memory_limiter', 'batch'], - ['awsxray', 'debug'], - ) - .withLogsPipeline( - ['otlp'], - ['memory_limiter', 'batch'], - ['awscloudwatchlogs'], - ) - .build(); - - const expected = { - receivers: { - otlp: { - protocols: { - http: { endpoint: '0.0.0.0:4318' }, - }, - }, - }, - processors: { - batch: defaultBatchConfig, - memory_limiter: defaultMemoryLimiterConfig, - }, - exporters: { - awsxray: { region: awsRegion }, - debug: { verbosity: 'detailed' }, - awscloudwatchlogs: { - region: awsRegion, - log_group_name: logGroupName, - log_stream_name: logStreamName, - log_retention: logRetention, - }, - }, - extensions: {}, - service: { - pipelines: { - metrics: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch'], - exporters: ['debug'], - }, - traces: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch'], - exporters: ['awsxray', 'debug'], - }, - logs: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch'], - exporters: ['awscloudwatchlogs'], - }, - }, - }, - }; - - assert.deepStrictEqual(result, expected); - }); - - it('withDebug should use provided verbosity', () => { - const verbosity = 'basic'; - - const result = new OtelCollectorConfigBuilder() - .withDebug(verbosity) - .build(); - - assert.strictEqual(result.exporters.debug?.verbosity, verbosity); - }); - - it('should generate default configuration', () => { - const result = new OtelCollectorConfigBuilder() - .withDefault({ - prometheusNamespace, - prometheusEndpoint, - region: awsRegion, - logGroupName, - logStreamName, - logRetention, - }) - .build(); - - const expected = { - receivers: { - otlp: { - protocols: { - http: { endpoint: '0.0.0.0:4318' }, - }, - }, - }, - processors: { - 'batch/metrics': defaultBatchConfig, - 'batch/traces': { - send_batch_max_size: 5000, - send_batch_size: 2000, - timeout: '2s', - }, - 'batch/logs': { - send_batch_max_size: 5000, - send_batch_size: 1024, - timeout: '2s', - }, - memory_limiter: defaultMemoryLimiterConfig, - }, - exporters: { - prometheusremotewrite: { - namespace: prometheusNamespace, - endpoint: prometheusEndpoint, - auth: { authenticator: 'sigv4auth' }, - }, - awsxray: { region: awsRegion }, - awscloudwatchlogs: { - region: awsRegion, - log_group_name: logGroupName, - log_stream_name: logStreamName, - log_retention: logRetention, - }, - }, - extensions: { - sigv4auth: { - region: awsRegion, - service: 'aps', - }, - health_check: { endpoint: '0.0.0.0:13133' }, - }, - service: { - extensions: ['sigv4auth', 'health_check'], - pipelines: { - metrics: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch/metrics'], - exporters: ['prometheusremotewrite'], - }, - traces: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch/traces'], - exporters: ['awsxray'], - }, - logs: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch/logs'], - exporters: ['awscloudwatchlogs'], - }, - }, - telemetry: { - logs: { level: 'error' }, - metrics: { level: 'basic' }, - }, - }, - }; +const region = requireEnv('AWS_REGION'); +const ctx: OtelTestContext = { + config: { + region, + usersPath: infraConfig.usersPath, + errorPath: infraConfig.errorPath, + appName: infraConfig.appName, + prometheusNamespace: infraConfig.prometheusNamespace, + }, + clients: { + cloudwatchLogs: new CloudWatchLogsClient({ region }), + xray: new XRayClient({ region }), + }, +}; - assert.deepStrictEqual(result, expected); +describe('OpenTelemetry component deployment', () => { + before(async () => { + const outputs: OutputMap = await automation.deploy(programArgs); + ctx.outputs = unwrapOutputs(outputs); }); - it('should generate complete configuration', () => { - const result = new OtelCollectorConfigBuilder() - .withOTLPReceiver(['http']) - .withBatchProcessor() - .withMemoryLimiterProcessor() - .withAPS(prometheusNamespace, prometheusEndpoint, awsRegion) - .withAWSXRayExporter(awsRegion) - .withCloudWatchLogsExporter( - awsRegion, - logGroupName, - logStreamName, - logRetention, - ) - .withDebug() - .withTelemetry() - .withHealthCheckExtension() - .withPprofExtension() - .withMetricsPipeline( - ['otlp'], - ['memory_limiter', 'batch'], - ['prometheusremotewrite', 'debug'], - ) - .withTracesPipeline( - ['otlp'], - ['memory_limiter', 'batch'], - ['awsxray', 'debug'], - ) - .withLogsPipeline( - ['otlp'], - ['memory_limiter', 'batch'], - ['awscloudwatchlogs', 'debug'], - ) - .build(); - - const expected = { - receivers: { - otlp: { - protocols: { - http: { endpoint: '0.0.0.0:4318' }, - }, - }, - }, - processors: { - batch: defaultBatchConfig, - memory_limiter: defaultMemoryLimiterConfig, - }, - exporters: { - prometheusremotewrite: { - namespace: prometheusNamespace, - endpoint: prometheusEndpoint, - auth: { authenticator: 'sigv4auth' }, - }, - awsxray: { region: awsRegion }, - awscloudwatchlogs: { - region: awsRegion, - log_group_name: logGroupName, - log_stream_name: logStreamName, - log_retention: logRetention, - }, - debug: { verbosity: 'detailed' }, - }, - extensions: { - sigv4auth: { - region: awsRegion, - service: 'aps', - }, - health_check: { endpoint: '0.0.0.0:13133' }, - pprof: { endpoint: '0.0.0.0:1777' }, - }, - service: { - extensions: ['sigv4auth', 'health_check', 'pprof'], - pipelines: { - metrics: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch'], - exporters: ['prometheusremotewrite', 'debug'], - }, - traces: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch'], - exporters: ['awsxray', 'debug'], - }, - logs: { - receivers: ['otlp'], - processors: ['memory_limiter', 'batch'], - exporters: ['awscloudwatchlogs', 'debug'], - }, - }, - telemetry: { - logs: { level: 'error' }, - metrics: { level: 'basic' }, - }, - }, - }; - - assert.deepStrictEqual(result, expected); - }); + after(() => automation.destroy(programArgs)); - describe('validation', testOtelCollectorConfigBuilderValidation); + describe('Config builder', testOtelConfigBuilder); + describe('Config validation', testOtelCollectorConfigBuilderValidation); + describe('Integration', () => testOtelIntegration(ctx)); }); diff --git a/tests/otel/infrastructure/config.ts b/tests/otel/infrastructure/config.ts new file mode 100644 index 0000000..940dbea --- /dev/null +++ b/tests/otel/infrastructure/config.ts @@ -0,0 +1,9 @@ +export const appName = 'otel-test'; + +export const appImage = 'studiondev/observability-sample-app'; +export const appPort = 3000; + +export const usersPath = '/users'; +export const errorPath = '/error'; + +export const prometheusNamespace = 'icb_otel_integration'; diff --git a/tests/otel/infrastructure/index.ts b/tests/otel/infrastructure/index.ts new file mode 100644 index 0000000..5485968 --- /dev/null +++ b/tests/otel/infrastructure/index.ts @@ -0,0 +1,76 @@ +import * as aws from '@pulumi/aws'; +import * as pulumi from '@pulumi/pulumi'; +import * as studion from '@studion/infra-code-blocks'; +import { getCommonVpc } from '../../util'; +import { appImage, appPort, appName, prometheusNamespace } from './config'; + +const stackName = pulumi.getStack(); +const parent = new pulumi.ComponentResource( + 'studion:otel:TestGroup', + `${appName}-root`, +); +const tags = { + Env: stackName, + Project: appName, +}; + +const vpc = getCommonVpc(); +const cluster = new aws.ecs.Cluster(`${appName}-cluster`, { tags }, { parent }); + +const prometheusWorkspace = new aws.amp.Workspace( + `${appName}-workspace`, + { tags }, + { parent }, +); + +const cloudWatchLogGroupName = `/otel/test/${appName}-${stackName}`; +const cloudWatchLogStreamName = `${appName}-stream`; +const cloudWatchLogGroup = new aws.cloudwatch.LogGroup( + `${appName}-log-group`, + { + name: cloudWatchLogGroupName, + tags, + }, + { parent }, +); + +const otelCollector = new studion.openTelemetry.OtelCollectorBuilder( + appName, + stackName, +) + .withDefault({ + prometheusNamespace, + prometheusWorkspace, + region: aws.config.requireRegion(), + logGroup: cloudWatchLogGroup, + logStreamName: cloudWatchLogStreamName, + }) + .build(); + +const ecs = { + cluster, + desiredCount: 1, + size: 'small' as const, + autoscaling: { enabled: false }, +}; + +const webServer = new studion.WebServerBuilder(appName) + .configureWebServer(appImage, appPort, { + environment: [ + { name: 'OTEL_SERVICE_NAME', value: appName }, + { name: 'OTEL_EXPORTER_OTLP_ENDPOINT', value: 'http://127.0.0.1:4318' }, + { name: 'OTEL_EXPORTER_OTLP_PROTOCOL', value: 'http/json' }, + ], + }) + .configureEcs(ecs) + .withVpc(vpc.vpc) + .withOtelCollector(otelCollector) + .build({ parent }); + +export { + cloudWatchLogGroup, + cloudWatchLogStreamName, + webServer, + appName, + prometheusWorkspace, +}; diff --git a/tests/otel/integration.test.ts b/tests/otel/integration.test.ts new file mode 100644 index 0000000..fb8e37c --- /dev/null +++ b/tests/otel/integration.test.ts @@ -0,0 +1,155 @@ +import { it } from 'node:test'; +import * as assert from 'node:assert'; +import { Unwrap } from '@pulumi/pulumi'; +import { request } from 'undici'; +import { FilterLogEventsCommand } from '@aws-sdk/client-cloudwatch-logs'; +import { GetTraceSummariesCommand } from '@aws-sdk/client-xray'; +import { defaultProvider } from '@aws-sdk/credential-provider-node'; +import { Sha256 } from '@aws-crypto/sha256-js'; +import { HttpRequest } from '@smithy/protocol-http'; +import { SignatureV4 } from '@smithy/signature-v4'; +import { backOff } from '../util'; +import { OtelTestContext, ProgramOutput } from './test-context'; + +const backOffConfig = { numOfAttempts: 10 }; + +export function testOtelIntegration(ctx: OtelTestContext) { + it('should export logs to CloudWatch Logs', async () => { + const startTimeMs = Date.now(); + await requestEndpointWithExpectedStatus(ctx, ctx.config.usersPath, 200); + + const logGroup = ctx.outputs!.cloudWatchLogGroup; + const logStreamName = ctx.outputs!.cloudWatchLogStreamName; + const logGroupName = logGroup.name as unknown as Unwrap< + typeof logGroup.name + >; + + await backOff(async () => { + const response = await ctx.clients.cloudwatchLogs.send( + new FilterLogEventsCommand({ + logGroupName: logGroupName, + logStreamNames: [logStreamName], + startTime: startTimeMs, + }), + ); + + assert.ok( + (response.events?.length ?? 0) > 0, + 'Expected telemetry logs in CloudWatch log group', + ); + }, backOffConfig); + }); + + it('should export traces to AWS X-Ray', async () => { + await requestEndpointWithExpectedStatus(ctx, ctx.config.errorPath, 500); + + await backOff(async () => { + const endTime = new Date(); + const startTime = new Date(endTime.getTime() - 10 * 60_000); + const response = await ctx.clients.xray.send( + new GetTraceSummariesCommand({ + StartTime: startTime, + EndTime: endTime, + Sampling: false, + }), + ); + const summaries = response.TraceSummaries ?? []; + assert.ok( + summaries.length > 0, + 'Expected at least one X-Ray trace summary', + ); + assert.ok( + summaries.some(summary => summary.HasFault), + 'Expected at least one X-Ray trace with error', + ); + }, backOffConfig); + }); + + it('should export metrics to Prometheus (AMP)', async () => { + await requestEndpointWithExpectedStatus(ctx, ctx.config.usersPath, 200); + + const workspace = ctx.outputs!.prometheusWorkspace; + + await backOff(async () => { + const response = await queryPrometheusSeries( + ctx, + workspace, + `${ctx.config.prometheusNamespace}_.*`, + ); + assert.strictEqual(response.status, 'success'); + assert.ok( + response.data.length > 0, + `Expected at least one Prometheus series in namespace '${ctx.config.prometheusNamespace}'`, + ); + }, backOffConfig); + }); +} + +async function requestEndpointWithExpectedStatus( + ctx: OtelTestContext, + path: string, + expectedStatus: number, +): Promise { + await backOff(async () => { + const webServer = ctx.outputs!.webServer; + const dnsName = webServer.lb.lb.dnsName as unknown as Unwrap< + typeof webServer.lb.lb.dnsName + >; + const endpoint = `http://${dnsName}${path}`; + const response = await request(endpoint); + assert.strictEqual( + response.statusCode, + expectedStatus, + `Endpoint ${endpoint} should return ${expectedStatus}`, + ); + }, backOffConfig); +} + +type PrometheusSeriesResponse = { + status: 'success' | 'error'; + data: Array>; +}; + +async function queryPrometheusSeries( + ctx: OtelTestContext, + workspace: ProgramOutput['prometheusWorkspace'], + namespaceRegex: string, +): Promise { + const signer = new SignatureV4({ + service: 'aps', + region: ctx.config.region, + credentials: defaultProvider(), + sha256: Sha256, + }); + + const prometheusEndpoint = workspace.prometheusEndpoint as unknown as Unwrap< + typeof workspace.prometheusEndpoint + >; + const endpoint = `${prometheusEndpoint.replace(/\/$/, '')}/api/v1/series`; + const seriesUrl = new URL(endpoint); + seriesUrl.searchParams.append('match[]', `{__name__=~"${namespaceRegex}"}`); + const end = new Date(); + const start = new Date(end.getTime() - 60_000); + seriesUrl.searchParams.set('start', start.toISOString()); + seriesUrl.searchParams.set('end', end.toISOString()); + + const signedRequest = await signer.sign( + new HttpRequest({ + protocol: seriesUrl.protocol, + hostname: seriesUrl.hostname, + method: 'GET', + path: seriesUrl.pathname, + query: Object.fromEntries(seriesUrl.searchParams.entries()), + headers: { + host: seriesUrl.host, + }, + }), + ); + + const { body } = await request(seriesUrl.toString(), { + method: 'GET', + headers: signedRequest.headers, + }); + + return (await body.json()) as PrometheusSeriesResponse; +} diff --git a/tests/otel/test-context.ts b/tests/otel/test-context.ts new file mode 100644 index 0000000..294ca2c --- /dev/null +++ b/tests/otel/test-context.ts @@ -0,0 +1,31 @@ +import * as aws from '@pulumi/aws'; +import * as studion from '@studion/infra-code-blocks'; +import { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs'; +import { XRayClient } from '@aws-sdk/client-xray'; +import { AwsContext, ConfigContext, PulumiProgramContext } from '../types'; + +interface OtelTestConfig { + region: string; + usersPath: string; + errorPath: string; + appName: string; + prometheusNamespace: string; +} + +interface AwsClients { + cloudwatchLogs: CloudWatchLogsClient; + xray: XRayClient; +} + +export interface ProgramOutput { + webServer: studion.WebServer; + appName: string; + prometheusWorkspace: aws.amp.Workspace; + cloudWatchLogGroup: aws.cloudwatch.LogGroup; + cloudWatchLogStreamName: string; +} + +export interface OtelTestContext + extends ConfigContext, + PulumiProgramContext, + AwsContext {}