apminsight
Version:
monitor nodejs applications
314 lines (296 loc) • 10.1 kB
JavaScript
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
};