diff --git a/server/model/monitor.js b/server/model/monitor.js index 97cfcb181..65f2327d2 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -6,7 +6,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MI SQL_DATETIME_FORMAT } = require("../../src/util"); const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery, - redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal + redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal, zookeeperConnect } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); @@ -182,6 +182,8 @@ class Monitor extends BeanModel { tlsCert: this.tlsCert, tlsKey: this.tlsKey, kafkaProducerSaslOptions: JSON.parse(this.kafkaProducerSaslOptions), + zookeeperHost: this.zookeeperHost, + zookeeperTimeout: this.zookeeperTimeout, }; } @@ -884,6 +886,12 @@ class Monitor extends BeanModel { bean.status = UP; bean.ping = dayjs().valueOf() - startTime; + } else if (this.type === "zookeeper") { + let startTime = dayjs().valueOf(); + + bean.msg = await zookeeperConnect(this.zookeeperHost, this.zookeeperTimeout); + bean.status = UP; + bean.ping = dayjs().valueOf() - startTime; } else { throw new Error("Unknown Monitor Type"); } diff --git a/server/server.js b/server/server.js index f21288938..574fe84ae 100644 --- a/server/server.js +++ b/server/server.js @@ -827,6 +827,8 @@ let needSetup = false; bean.kafkaProducerAllowAutoTopicCreation = monitor.kafkaProducerAllowAutoTopicCreation; bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly; + bean.zookeeperHost = monitor.zookeeperHost; + bean.zookeeperTimeout = monitor.zookeeperTimeout; bean.validate(); diff --git a/server/util-server.js b/server/util-server.js index 97c5eb6ce..448e2132a 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -526,35 +526,6 @@ exports.mongodbPing = async function (connectionString) { } }; -exports.zookeeperConnect = function (zookeeperHost, timeoutMs = 5000) { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(Error("Zookeeper operation timed out")); - }, timeoutMs); - - const config = { - connect: zookeeperHost, - timeout: 1000, - debug_level: ZooKeeper.ZOO_LOG_LEVEL_WARN, - host_order_deterministic: false, - }; - - const client = new ZooKeeper(config); - - client.connect(config, (err, _) => { - if (err) { - clearTimeout(timer); - reject(err); - } else { - clearTimeout(timer); - resolve("Successfully connected"); - } - - client.close(); - }); - }); -}; - /** * Query radius server * @param {string} hostname Hostname of radius server @@ -1194,3 +1165,40 @@ module.exports.axiosAbortSignal = (timeoutMs) => { } } }; + +/** + * Attempt to connect to the given zookeeper host under the given timeout. + * @param {string} zookeeperHost - The connection string for a single host in the form 'host:port' + * @param {number} timeoutMs - Timeout in milliseconds under which connection must be established. + * @returns {Promise} The result of connection attempt + */ +exports.zookeeperConnect = function (zookeeperHost, timeoutMs = 5000) { + return new Promise((resolve, reject) => { + // Seems like zookeeper client behavior is to retry indefitiely on timeout. + // So, an explicit timer is needed to prevent the monitor from getting stuck. + const timer = setTimeout(() => { + reject(Error("Zookeeper operation timed out")); + }, timeoutMs); + + const config = { + connect: zookeeperHost, + timeout: timeoutMs, // Causes the client to retry and does not fail. + debug_level: ZooKeeper.ZOO_LOG_LEVEL_WARN, + host_order_deterministic: false, + }; + + const client = new ZooKeeper(config); + + client.connect(config, (err, _) => { + if (err) { + clearTimeout(timer); + reject(err); + } else { + clearTimeout(timer); + resolve("Successfully connected"); + } + + client.close(); + }); + }); +};