UNPKG

apminsight

Version:

monitor nodejs applications

314 lines (296 loc) 10.1 kB
var http = require("http"); var utils = require("./../util/utils"); var logger = require("./../util/logger"); var hostName = "169.254.169.254"; /* e.g. result's standard response format { detected: true, provider: 'AWS', instanceId: 'i-1234567890abcdef0', isAutoScaleEnabled: true, details: { //any provider-specific info } } */ function doCloudCheck(callback) { const cloudChecks = [ checkForKubernetes, checkForAws, checkForAzure, checkForGCP, checkForAwsFargate ]; let currentIndex = 0; function runNextCheck() { if (currentIndex >= cloudChecks.length) { // No provider detected after all checks callback(); return; } const currentCheck = cloudChecks[currentIndex]; currentCheck((result) => { if (result.detected) { callback(result.instanceId, result.provider, result.isAutoScaleEnabled); logger.info(`Application hosted in ${result.provider === "DOCKER" && result.isAutoScaleEnabled ? "Kubernetes" : result.provider}`); return; // Early exit if a provider is detected } currentIndex++; runNextCheck(); }); } runNextCheck(); } function checkForKubernetes(responseHandler) { const result = { detected: false, provider: 'DOCKER', instanceId: null, isAutoScaleEnabled: false, details: {} } if (process.env.KUBERNETES_SERVICE_HOST) { var kubernetesInsId = process.env.HOSTNAME; if (!utils.isEmpty(kubernetesInsId)) { result.detected = true; result.instanceId = String(kubernetesInsId); result.isAutoScaleEnabled = true; // Assuming auto-scale is enabled in Kubernetes responseHandler(result); return; } } responseHandler(result); logger.info("Application not hosted in Kubernetes."); } function awsAutoScaleEnvDetect(token, responseHandler) { const options = { host: hostName, path: "/latest/meta-data/autoscaling/target-lifecycle-state", }; if (!utils.isEmpty(token)) { options.headers = { "X-aws-ec2-metadata-token": token }; } sendReq(options, function (responseData, responseCode) { if (responseCode == 200) { responseHandler(true); // Auto-scale enabled } else { responseHandler(false); // Auto-scale not enabled } }); } function checkForAws(responseHandler) { const result = { detected: false, provider: 'AWS', instanceId: null, isAutoScaleEnabled: false, details: {} } //IMDSv1 check var options = { host: hostName, path: "/latest/meta-data/instance-id" }; sendReq(options, function (instanceId) { if (!utils.isEmpty(instanceId)) { awsAutoScaleEnvDetect("", function (isAutoScaleEnabled) { result.detected = true; result.instanceId = String(instanceId); result.isAutoScaleEnabled = isAutoScaleEnabled; responseHandler(result); }); } else { //IMDSv2 check var options = { host: hostName, path: "/latest/api/token", method: "PUT", headers: { "X-aws-ec2-metadata-token-ttl-seconds": 21600 } }; sendReq(options, function (token) { if (token) { var conOptions = { host: hostName, path: "/latest/meta-data/instance-id", headers: { "X-aws-ec2-metadata-token": token } }; sendReq(conOptions, function (instanceId, responseCode) { if (responseCode == 200) { if (!utils.isEmpty(instanceId)) { //auto-scale detection awsAutoScaleEnvDetect(token, function (isAutoScaleEnabled) { result.detected = true; result.instanceId = String(instanceId); result.isAutoScaleEnabled = isAutoScaleEnabled; responseHandler(result); }); return; } else { responseHandler(result); logger.info("Application not hosted in AWS."); } } }); } else { responseHandler(result); logger.info("Application not hosted in AWS."); } }); } }); } function checkForAzure(responseHandler) { const result = { detected: false, provider: 'AZURE', instanceId: null, isAutoScaleEnabled: false, details: {} } const options = { host: hostName, path: "/metadata/v1/maintenance", headers: { Metadata: true } }; sendReq(options, function (responseData, statusCode) { if (statusCode == 200) { result.detected = true; //instanceId detection var instanceOptions = { host: hostName, path: "/metadata/v1/InstanceInfo", headers: { Metadata: true } }; sendReq(instanceOptions, function (responseData) { var azureInsId; try { if (responseData) { var responseJson = JSON.parse(responseData); azureInsId = responseJson.ID; } } catch (err) { logger.error(`azure response parser error ${err.message}`, err); } if (!utils.isEmpty(azureInsId) && typeof azureInsId === "string") { result.instanceId = String(azureInsId); //autoscale detection const autoscaleOptions = { host: hostName, path: "/metadata/instance/compute/vmScaleSetName?api-version=2023-11-15&format=text", headers: { Metadata: true } }; sendReq(autoscaleOptions, function (computeData, statusCode) { result.isAutoScaleEnabled = statusCode == 200 && !utils.isEmpty(computeData); // true indicates auto-scale enabled responseHandler(result); }); } else { responseHandler(result); } }); }else { logger.info("Application not hosted in Azure"); responseHandler(result); } }); } function checkForGCP(responseHandler) { const result = { detected: false, provider: 'GCP', instanceId: null, isAutoScaleEnabled: false, details: {} } var options = { host: "metadata.google.internal", path: "/computeMetadata/v1/instance/id", headers: { "Metadata-Flavor": "Google" } }; sendReq(options, function (responseData, responseCode) { if (responseCode == 200) { result.detected = true; result.instanceId = ""; result.isAutoScaleEnabled = false; // false indicates no auto-scale responseHandler(result); } else { responseHandler(result); logger.info("Application not hosted in GCP"); } }); } function checkForAwsFargate(responseHandler) { const result = { detected: false, provider: 'AWS', instanceId: null, isAutoScaleEnabled: false, details: {} } var uriFromEnv = process.env.ECS_CONTAINER_METADATA_URI; var options = { host: "169.254.170.2", path: "/v2/metadata" }; var uri = uriFromEnv ? uriFromEnv : options; sendReq(uri, function (responseData, responseCode) { if (responseCode == 200) { result.detected = true; result.instanceId = ""; result.isAutoScaleEnabled = true; // true indicates auto-scale enabled responseHandler(result); } else { responseHandler(result); logger.info("Application not hosted in AWS_Fargate"); } }); } function sendReq(options, responseHandler) { try { var req = http.request(options, function (response) { if (response.statusCode < 200 || response.statusCode > 299) { responseHandler(); return; } response.setEncoding("utf8"); var rawData = ""; response.on("data", function (chunk) { rawData += chunk; }); response.on("end", function () { responseHandler(rawData, response.statusCode); }); }); req.on("socket", function (socket) { socket.setTimeout(500); socket.on("timeout", function () { req.abort(); }); }); req.on("error", function (error) { logger.error("HTTP request error: " + error.message); responseHandler(); }); req.end(); } catch (error) { logger.error("Error making HTTP request: " + error.message, error); responseHandler(); } } module.exports = { doCloudCheck: doCloudCheck };