From 9f00a513f8738b50299f57463844bc9b4efffbd8 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Tue, 15 Sep 2026 12:23:59 -0400 Subject: [PATCH] feat: include jambonz and drachtio versions in X-Jambonz-Discover response (#152) - jambonzVersion is read from the schema_version table via the existing db-helpers pool (now exposed on srf.locals); a DB error yields null rather than failing discovery - drachtioVersion is captured from the drachtio connect handshake and stored on srf.locals - README example updated Co-authored-by: Claude Fable 5.1 --- README.md | 4 +++- app.js | 5 ++++- lib/server-control/index.js | 36 +++++++++++++++++++++++++++++------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d5cadc2..6ce3174 100644 --- a/README.md +++ b/README.md @@ -40,13 +40,15 @@ An OPTIONS request carrying the header `X-Jambonz-Discover: true` (from any IP, ```json { + "jambonzVersion": "v11.1.4", + "drachtioVersion": "v0.9.3", "featureServers": ["10.0.0.10:5060"], "sipServers": ["1.2.3.4"], "rtpServers": ["10.0.0.20"] } ``` -`featureServers`, `sipServers` and `rtpServers` are the IPs (feature servers include the port) of the active feature servers, SIP servers and RTP servers respectively. When `JAMBONES_SERVER_CONTROL` is not set, the discovery header is ignored and OPTIONS behaves as normal. +`jambonzVersion` is read from the `schema_version` table in the database and `drachtioVersion` is the version reported by the drachtio server on connect; either is `null` if it could not be determined. `featureServers`, `sipServers` and `rtpServers` are the IPs (feature servers include the port) of the active feature servers, SIP servers and RTP servers respectively. When `JAMBONES_SERVER_CONTROL` is not set, the discovery header is ignored and OPTIONS behaves as normal. ## CLI Management diff --git a/app.js b/app.js index d5cdbf2..ea3c25a 100644 --- a/app.js +++ b/app.js @@ -51,6 +51,7 @@ const Registrar = require('@jambonz/mw-registrar'); const digestChallenge = require('@jambonz/digest-utils'); const debug = require('debug')('jambonz:sbc-registrar'); const { + pool, lookupAuthHook, lookupAllVoipCarriers, lookupSipGatewaysByCarrier, @@ -111,6 +112,7 @@ srf.locals = { ...srf.locals, logger, stats, + pool, addToSet, removeFromSet, isMemberOfSet, retrieveSet, registrar: new Registrar(logger, client), dbHelpers: { @@ -155,7 +157,8 @@ srf.on('connect', (err, hp, version, localHostports) => { // drachtio-srf re-emits 'connect' on every reconnect; distinguish a reconnect from first connect const isReconnect = drachtioConnected; drachtioConnected = true; - logger.info(`connected to drachtio listening on ${hp}, local hostports: ${localHostports}`); + logger.info(`connected to drachtio ${version} listening on ${hp}, local hostports: ${localHostports}`); + srf.locals.drachtioVersion = version || null; if (localHostports) { const locals = localHostports.split(','); diff --git a/lib/server-control/index.js b/lib/server-control/index.js index a008f23..3b229ad 100644 --- a/lib/server-control/index.js +++ b/lib/server-control/index.js @@ -35,22 +35,44 @@ const isDiscoverRequest = (req) => { }; /** - * Read the current cluster topology from redis: the IPs of the feature - * servers, SIP servers and RTP servers. - * @param {object} srf - drachtio srf instance (uses srf.locals.retrieveSet) - * @returns {Promise<{featureServers: string[], sipServers: string[], rtpServers: string[]}>} + * Read the installed jambonz version from the schema_version table. + * A failure here must not fail discovery, so it resolves to null on any error. + * @param {object} pool - mysql connection pool (may be undefined in tests) + * @returns {Promise} + */ +const _lookupJambonzVersion = (pool) => new Promise((resolve) => { + if (!pool) return resolve(null); + pool.query('SELECT version FROM schema_version', (err, results) => { + if (err) { + debug({err}, 'failed reading schema_version'); + return resolve(null); + } + resolve(results && results.length ? results[0].version : null); + }); +}); + +/** + * Read the current cluster topology from redis (the IPs of the feature + * servers, SIP servers and RTP servers) plus the jambonz version + * (schema_version table) and the drachtio server version (captured on connect). + * @param {object} srf - drachtio srf instance (uses srf.locals.retrieveSet, srf.locals.pool) + * @returns {Promise<{jambonzVersion: string|null, drachtioVersion: string|null, + * featureServers: string[], sipServers: string[], rtpServers: string[]}>} */ const discoverServers = async(srf) => { - const { retrieveSet } = srf.locals; + const { retrieveSet, pool, drachtioVersion } = srf.locals; const { featureServers, sipServers, rtpServers } = _setNames(); - const [fs, sip, rtp] = await Promise.all([ + const [fs, sip, rtp, jambonzVersion] = await Promise.all([ retrieveSet(featureServers), retrieveSet(sipServers), - retrieveSet(rtpServers) + retrieveSet(rtpServers), + _lookupJambonzVersion(pool) ]); const topology = { + jambonzVersion, + drachtioVersion: drachtioVersion || null, featureServers: fs || [], sipServers: sip || [], rtpServers: rtp || []