K8s changes (#24)

* support multiple CIDRs

* use outbound in k8s

* k8s: user service for rtpengine

* bugfix: drachtio connection was dropped after successful connect

* Dockerfile

* k8s pre-stop hook

* make hjook executable

* update deps
This commit is contained in:
Dave Horton
2022-01-06 12:37:04 -05:00
committed by GitHub
parent 607fb3d127
commit 9d228d4805
7 changed files with 1019 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
FROM node:16 FROM node:17.0.1-slim
WORKDIR /opt/app/ WORKDIR /opt/app/
COPY package.json ./ COPY package.json ./
RUN npm install RUN npm install

23
app.js
View File

@@ -11,7 +11,6 @@ assert.ok(process.env.JAMBONES_NETWORK_CIDR, 'missing JAMBONES_NETWORK_CIDR env
const Srf = require('drachtio-srf'); const Srf = require('drachtio-srf');
const srf = new Srf('sbc-outbound'); const srf = new Srf('sbc-outbound');
const CIDRMatcher = require('cidr-matcher'); const CIDRMatcher = require('cidr-matcher');
const matcher = new CIDRMatcher([process.env.JAMBONES_NETWORK_CIDR]);
const opts = Object.assign({ const opts = Object.assign({
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;} timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'}); }, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
@@ -51,6 +50,12 @@ const {createHash, retrieveHash, incrKey, decrKey, retrieveSet} = require('@jamb
port: process.env.JAMBONES_REDIS_PORT || 6379 port: process.env.JAMBONES_REDIS_PORT || 6379
}, logger); }, logger);
const cidrs = process.env.JAMBONES_NETWORK_CIDR
.split(',')
.map((s) => s.trim());
logger.info({cidrs}, 'internal network CIDRs');
const matcher = new CIDRMatcher(cidrs);
const activeCallIds = new Map(); const activeCallIds = new Map();
srf.locals = {...srf.locals, srf.locals = {...srf.locals,
@@ -86,7 +91,7 @@ const {getRtpEngine, setRtpEngines} = require('@jambonz/rtpengine-utils')([], lo
}); });
srf.locals.getRtpEngine = getRtpEngine; srf.locals.getRtpEngine = getRtpEngine;
if (process.env.DRACHTIO_HOST) { if (process.env.DRACHTIO_HOST && !process.env.K8S) {
srf.connect({host: process.env.DRACHTIO_HOST, port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET }); srf.connect({host: process.env.DRACHTIO_HOST, port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET });
srf.on('connect', (err, hp) => { srf.on('connect', (err, hp) => {
logger.info(`connected to drachtio listening on ${hp}`); logger.info(`connected to drachtio listening on ${hp}`);
@@ -107,6 +112,7 @@ if (process.env.DRACHTIO_HOST) {
}); });
} }
else { else {
logger.info(`listening in outbound mode on port ${process.env.DRACHTIO_PORT}`);
srf.listen({port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET}); srf.listen({port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET});
} }
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === 'test') {
@@ -121,6 +127,11 @@ srf.invite((req, res) => {
session.connect(); session.connect();
}); });
const PORT = process.env.HTTP_PORT || 3000;
const getCount = () => activeCallIds.size;
const healthCheck = require('@jambonz/http-health-check');
healthCheck({port: PORT, logger, path: '/', fn: getCount});
/* update call stats periodically */ /* update call stats periodically */
setInterval(() => { setInterval(() => {
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']); stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
@@ -137,11 +148,13 @@ const arrayCompare = (a, b) => {
return true; return true;
}; };
/* update rtpengines periodically */ const serviceName = process.env.JAMBONES_RTPENGINES || process.env.K8S_RTPENGINE_SERVICE_NAME;
if (process.env.JAMBONES_RTPENGINES) { if (serviceName) {
setRtpEngines([process.env.JAMBONES_RTPENGINES]); logger.info(`rtpengine(s) will be found at: ${serviceName}`);
setRtpEngines([serviceName]);
} }
else { else {
/* update rtpengines periodically */
const getActiveRtpServers = async() => { const getActiveRtpServers = async() => {
try { try {
const set = await retrieveSet(setNameRtp); const set = await retrieveSet(setNameRtp);

29
bin/k8s-pre-stop-hook.js Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env node
const bent = require('bent');
const getJSON = bent('json');
const PORT = process.env.HTTP_PORT || 3000;
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
(async function() {
try {
do {
const obj = await getJSON(`http://127.0.0.1:${PORT}/`);
const {calls} = obj;
if (calls === 0) {
console.log('no calls on the system, we can exit');
process.exit(0);
}
else {
console.log(`waiting for ${calls} to exit..`);
}
await sleep(10000);
} while (1);
} catch (err) {
console.error(err, 'Error querying health endpoint');
process.exit(-1);
}
})();

View File

@@ -208,7 +208,8 @@ class CallSession extends Emitter {
} catch (err) { } catch (err) {
debug(err); debug(err);
this.logger.error(err, 'Error performing lcr'); this.logger.error(err, 'Error performing lcr');
return this.res.send(488); this.res.send(488);
return this.srf.endSession(this.req);
} }
debug(`sending call to PSTN ${uris}`); debug(`sending call to PSTN ${uris}`);
} }
@@ -375,8 +376,9 @@ class CallSession extends Emitter {
} }
} }
} catch (err) { } catch (err) {
if ('abandonded' !== err.message) this.logger.error(err, `Error setting up outbonund call to: ${uris}`); if ('abandonded' !== err.message) this.logger.error(err, `Error setting up outbound call to: ${uris}`);
this.emit('failed'); this.emit('failed');
this.srf.endSession(this.req);
this.rtpEngineResource.destroy(); this.rtpEngineResource.destroy();
} }
} }
@@ -422,6 +424,7 @@ class CallSession extends Emitter {
duration: Math.floor((now - callStart) / 1000) duration: Math.floor((now - callStart) / 1000)
}).catch((err) => this.logger.error({err}, 'Error writing cdr for completed call')); }).catch((err) => this.logger.error({err}, 'Error writing cdr for completed call'));
} }
this.srf.endSession(this.req);
}); });
}); });
@@ -598,6 +601,7 @@ Duration=${payload.duration} `
this.rtpEngineResource.destroy(); this.rtpEngineResource.destroy();
this.activeCallIds.delete(this.req.get('Call-ID')); this.activeCallIds.delete(this.req.get('Call-ID'));
this.uas.other.destroy(); this.uas.other.destroy();
this.srf.endSession(this.req);
}); });
// modify rtpengine to stream to new feature server // modify rtpengine to stream to new feature server

View File

@@ -26,11 +26,12 @@ module.exports = (srf, logger, opts) => {
if (!req.locals.account_sid) { if (!req.locals.account_sid) {
logger.info('missing X-Account-Sid on outbound call'); logger.info('missing X-Account-Sid on outbound call');
return res.send(403, { res.send(403, {
headers: { headers: {
'X-Reason': 'missing X-Account-Sid' 'X-Reason': 'missing X-Account-Sid'
} }
}); });
return req.srf.endSession(req);
} }
stats.increment('sbc.invites', ['direction:outbound']); stats.increment('sbc.invites', ['direction:outbound']);
@@ -47,7 +48,8 @@ module.exports = (srf, logger, opts) => {
req.locals.account = await lookupAccountBySid(req.locals.account_sid); req.locals.account = await lookupAccountBySid(req.locals.account_sid);
} catch (err) { } catch (err) {
req.locals.logger.error({err}, `Error looking up account sid ${req.locals.account_sid}`); req.locals.logger.error({err}, `Error looking up account sid ${req.locals.account_sid}`);
return res.send(500); res.send(500);
return req.srf.endSession(req);
} }
next(); next();
}; };
@@ -106,6 +108,7 @@ module.exports = (srf, logger, opts) => {
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert')); }).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
res.send(503, 'Maximum Calls In Progress'); res.send(503, 'Maximum Calls In Progress');
return req.srf.endSession(req);
} }
next(); next();
} catch (err) { } catch (err) {
@@ -127,7 +130,7 @@ module.exports = (srf, logger, opts) => {
'X-Reason': 'invalid request-uri' 'X-Reason': 'invalid request-uri'
} }
}); });
return; return req.srf.endSession(req);
} }
const aor = `${uri.user}@${uri.host}`; const aor = `${uri.user}@${uri.host}`;
let reg; let reg;
@@ -166,7 +169,7 @@ module.exports = (srf, logger, opts) => {
if (account) { if (account) {
logger.info({host: uri.host, account}, `returning 404 to unregistered user in valid domain: ${req.uri}`); logger.info({host: uri.host, account}, `returning 404 to unregistered user in valid domain: ${req.uri}`);
res.send(404); res.send(404);
return; return req.srf.endSession(req);
} }
} }
} }
@@ -181,7 +184,8 @@ module.exports = (srf, logger, opts) => {
if (!/^\d+$/.test(req.calledNumber.slice(1))) { if (!/^\d+$/.test(req.calledNumber.slice(1))) {
debug(`unable to route call to ${aor}; no registered user found`); debug(`unable to route call to ${aor}; no registered user found`);
logger.info(`unable to route call to ${aor}; no registered user found`); logger.info(`unable to route call to ${aor}; no registered user found`);
return res.send(404); res.send(404);
return req.srf.endSession(req);
} }
debug('sending call to LCR'); debug('sending call to LCR');

965
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,8 @@
"jslint": "eslint app.js lib" "jslint": "eslint app.js lib"
}, },
"dependencies": { "dependencies": {
"@jambonz/db-helpers": "^0.6.14", "@jambonz/db-helpers": "^0.6.16",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/mw-registrar": "0.2.1", "@jambonz/mw-registrar": "0.2.1",
"@jambonz/realtimedb-helpers": "^0.4.9", "@jambonz/realtimedb-helpers": "^0.4.9",
"@jambonz/rtpengine-utils": "^0.1.21", "@jambonz/rtpengine-utils": "^0.1.21",