Compare commits

...
17 Commits
Author SHA1 Message Date
Hoan Luu Huu e00379d119 update media session to callee (#78) 2023-04-13 07:43:29 -04:00
EgleH 5174e140a2 Update base image to node:18.15-alpine3.16 (#77) 2023-04-12 13:18:19 -04:00
Dave Horton 7923ececaf push to docker 2023-04-10 09:43:25 -04:00
Anton Voylenko 66974c467d JAMBONES_MYSQL_PORT env added (#76) 2023-04-08 12:16:46 -04:00
Hoan Luu Huu 95bd061e2d feat: update stat collector version (#75) 2023-04-05 12:04:00 -04:00
Anton Voylenko 2f53c0bb4f Update README and add validation (#74)
* update README

* add JAMBONES_TIME_SERIES_HOST validation
2023-04-01 17:51:32 -04:00
Dave Horton f03308b11d bump version 2023-03-28 14:16:20 -04:00
Hoan Luu HuuandQuan HL 7c91ac9d68 feat: add instance_id tag to active call metric (#71)
Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-03-21 07:55:22 -04:00
Dave Horton fcffa1041c refactor of speech-utils 2023-03-14 09:54:46 -04:00
Dave Horton a6d5b25e40 bump version 2023-02-24 10:06:57 -05:00
Snyk bot 5cba07ee44 fix: Dockerfile to reduce vulnerabilities (#69)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326666
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326668
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326685
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326686
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326688
2023-02-23 07:23:07 -05:00
EgleH 579e21b5bf Upgrade node to node:18.14.0-alpine3.16 (#68) 2023-02-21 07:55:27 -05:00
Dave Horton 8864ab1430 update siprec client with fix for stop recording 2023-02-17 12:11:51 -05:00
Dave Horton 2187b03ca6 update siprec-client with fix for smart tap xml 2023-02-14 14:32:48 -05:00
Dave Horton 0808eeeda4 update to latest siprec client 2023-02-13 16:50:28 -05:00
Dave Horton f841e88bef bump version 2023-02-13 09:15:26 -05:00
Dave Horton b7873654b8 #63: dont select a carrier from a different account in LCR (#64)
* #63: dont select a carrier from a different account in LCR

* handle SIGTERM in K8S

* handle SIGTERM in K8S
2023-02-08 14:30:54 -05:00
9 changed files with 565 additions and 3428 deletions
+3 -3
View File
@@ -6,10 +6,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14.x
node-version: lts/*
- run: npm ci
- run: npm run jslint
- run: npm test
+31 -30
View File
@@ -2,16 +2,8 @@ name: Docker
on:
push:
# Publish `main` as Docker `latest` image.
branches:
- main
# Publish `v1.2.3` tags as releases.
tags:
- v*
env:
IMAGE_NAME: sbc-outbound
- '*'
jobs:
push:
@@ -20,32 +12,41 @@ jobs:
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v2
- name: Checkout code
uses: actions/checkout@v3
- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push image
- name: prepare tag
id: prepare_tag
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
IMAGE_ID=$GITHUB_REPOSITORY
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "main" ] && VERSION=latest
# Use Docker `latest` tag convention
[ "$VERSION" == "main" ] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
echo "image_id=$IMAGE_ID" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.prepare_tag.outputs.image_id }}:${{ steps.prepare_tag.outputs.version }}
build-args: |
GITHUB_REPOSITORY=$GITHUB_REPOSITORY
GITHUB_REF=$GITHUB_REF
+2 -2
View File
@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 node:18.12.1-alpine3.16 as base
FROM --platform=linux/amd64 node:18.15-alpine3.16 as base
RUN apk --update --no-cache add --virtual .builds-deps build-base python3
@@ -20,4 +20,4 @@ ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
CMD [ "node", "app.js" ]
CMD [ "node", "app.js" ]
+14 -14
View File
@@ -1,6 +1,6 @@
# sbc-outbound ![Build Status](https://github.com/jambonz/sbc-outbound/workflows/CI/badge.svg)
This application provides a part of the SBC (Session Border Controller) functionality of jambonz. It handles outbound INVITE requests from the cpaas application server that is going to carrier sip trunks or registered sip users/devices, including webrtc applications.
This application provides a part of the SBC (Session Border Controller) functionality of jambonz platfrom. It handles outbound INVITE requests from the cpaas application server that is going to carrier sip trunks or registered sip users/devices, including webrtc applications.
## Configuration
@@ -11,22 +11,26 @@ Configuration is provided via environment variables:
|DRACHTIO_HOST| ip address of drachtio server (typically '127.0.0.1')|yes|
|DRACHTIO_PORT| listening port of drachtio server for control connections (typically 9022)|yes|
|DRACHTIO_SECRET| shared secret|yes|
|ENABLE_METRICS| if 1, metrics will be generated|no|
|HTTP_PORT| tcp port listen port |no|
|JAMBONES_LOGLEVEL| log level for application, 'info' or 'debug'|no|
|JAMBONES_MYSQL_HOST| mysql host|yes|
|JAMBONES_MYSQL_PORT| mysql port |no|
|JAMBONES_MYSQL_USER| mysql username|yes|
|JAMBONES_MYSQL_PASSWORD| mysql password|yes|
|JAMBONES_MYSQL_DATABASE| mysql data|yes|
|JAMBONES_MYSQL_CONNECTION_LIMIT| mysql connection limit |no|
|DTMF_LISTEN_PORT| DTMF listening port |no|
|JAMBONES_NG_PROTOCOL| rtpengine NG protocol |no|
|RTPENGINE_PORT| rtpengine port |no|
|JAMBONES_CLUSTER_ID| cluster id |no|
|JAMBONES_NETWORK_CIDR| CIDR of private network that feature server is running in (e.g. '172.31.0.0/16')|yes|
|JAMBONES_REDIS_HOST| redis host|yes|
|JAMBONES_REDIS_PORT|redis port|yes|
|JAMBONES_REDIS_PORT|redis port|no|
|JAMBONES_RTPENGINES| commans-separated list of ip:ng-port for rtpengines (e.g. '172.31.32.10:22222')|yes|
|JAMBONES_SBCS| list of IP addresses (on the internal network) of SBCs, comma-separated|yes|
|STATS_HOST| ip address of metrics host (usually '127.0.0.1' since telegraf is installed locally|no|
|STATS_PORT| listening port for metrics host|no|
|STATS_PROTOCOL| 'tcp' or 'udp'|no|
|STATS_TELEGRAF| if 1, metrics will be generated in telegraf format|no|
|JAMBONES_TIME_SERIES_HOST| influxdb host |yes|
|JAMBONES_RECORD_ALL_CALLS| enable auto record calls |no|
|K8S| service running as kubernetes service |no|
|K8S_RTPENGINE_SERVICE_NAME| rtpengine service name(required for K8S) |no|
### running under pm2
Typically, this application runs under [pm2](https://pm2.io) using an [ecosystem.config.js](https://pm2.keymetrics.io/docs/usage/application-declaration/) file similar to this:
@@ -58,17 +62,13 @@ module.exports = {
JAMBONES_MYSQL_CONNECTION_LIMIT: 10,
JAMBONES_REDIS_HOST: 'jambonz.zzzzzzz.0001.usw1.cache.amazonaws.com',
JAMBONES_REDIS_PORT: 6379,
ENABLE_METRICS: 1,
STATS_HOST: '127.0.0.1',
STATS_PORT: 8125,
STATS_PROTOCOL: 'tcp',
STATS_TELEGRAF: 1,
JAMBONES_TIME_SERIES_HOST: '172.31.32.11',
JAMBONES_NETWORK_CIDR: '172.31.0.0/16'
}
}]
};
```
#### Running the test suite
To run the included test suite, you will need to have docker installed on your laptop.
```
+25 -3
View File
@@ -7,6 +7,7 @@ assert.ok(process.env.JAMBONES_REDIS_HOST, 'missing JAMBONES_REDIS_HOST env var'
assert.ok(process.env.DRACHTIO_PORT || process.env.DRACHTIO_HOST, 'missing DRACHTIO_PORT env var');
assert.ok(process.env.DRACHTIO_SECRET, 'missing DRACHTIO_SECRET env var');
assert.ok(process.env.JAMBONES_NETWORK_CIDR || process.env.K8S, 'missing JAMBONES_NETWORK_CIDR env var');
assert.ok(process.env.JAMBONES_TIME_SERIES_HOST, 'missing JAMBONES_TIME_SERIES_HOST env var');
const Srf = require('drachtio-srf');
const srf = new Srf('sbc-outbound');
@@ -46,6 +47,7 @@ const {
queryCallLimits
} = require('@jambonz/db-helpers')({
host: process.env.JAMBONES_MYSQL_HOST,
port: process.env.JAMBONES_MYSQL_PORT || 3306,
user: process.env.JAMBONES_MYSQL_USER,
password: process.env.JAMBONES_MYSQL_PASSWORD,
database: process.env.JAMBONES_MYSQL_DATABASE,
@@ -60,11 +62,13 @@ const {
retrieveSet,
isMemberOfSet
} = require('@jambonz/realtimedb-helpers')({
host: process.env.JAMBONES_REDIS_HOST || 'localhost',
host: process.env.JAMBONES_REDIS_HOST,
port: process.env.JAMBONES_REDIS_PORT || 6379
}, logger);
const activeCallIds = new Map();
const Emitter = require('events');
const idleEmitter = new Emitter();
srf.locals = {...srf.locals,
stats,
@@ -76,6 +80,7 @@ srf.locals = {...srf.locals,
AlertType,
queryCdrs,
activeCallIds,
idleEmitter,
dbHelpers: {
ping,
performLcr,
@@ -179,8 +184,9 @@ if (process.env.K8S || process.env.HTTP_PORT) {
if ('test' !== process.env.NODE_ENV) {
/* update call stats periodically */
setInterval(() => {
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
}, 5000);
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound',
`instance_id:${process.env.INSTANCE_ID || 0}`]);
}, 20000);
}
const lookupRtpServiceEndpoints = (lookup, serviceName) => {
@@ -238,4 +244,20 @@ else {
pingMsTeamsGateways(logger, srf);
process.on('SIGUSR2', handle.bind(null));
process.on('SIGTERM', handle.bind(null));
function handle(signal) {
logger.info(`got signal ${signal}`);
if (process.env.K8S) {
if (0 === activeCallIds.size) {
logger.info('exiting immediately since we have no calls in progress');
process.exit(0);
}
else {
idleEmitter.once('idle', () => process.exit(0));
}
}
}
module.exports = {srf};
+9 -2
View File
@@ -77,6 +77,7 @@ class CallSession extends Emitter {
this.logger = logger.child({callId: req.get('Call-ID')});
this.useWss = req.locals.registration && req.locals.registration.protocol === 'wss';
this.stats = this.srf.locals.stats;
this.idleEmitter = this.srf.locals.idleEmitter;
this.activeCallIds = this.srf.locals.activeCallIds;
this.writeCdrs = this.srf.locals.writeCdrs;
@@ -106,6 +107,11 @@ class CallSession extends Emitter {
return this._mediaReleased;
}
get calleeIsUsingSrtp() {
const tp = this.rtpEngineOpts?.uac?.mediaOpts['transport-protocol'];
return tp && -1 !== tp.indexOf('SAVP');
}
subscribeForDTMF(dlg) {
if (!this._subscribedForDTMF) {
this._subscribedForDTMF = true;
@@ -525,7 +531,7 @@ class CallSession extends Emitter {
}
this.logger.info(`call ended with normal termination, there are ${this.activeCallIds.size} active`);
if (this.activeCallIds.size === 0) this.idleEmitter.emit('idle');
this.srf.endSession(this.req);
});
});
@@ -626,7 +632,7 @@ Duration=${payload.duration} `
/* if this is a re-invite from the FS to change media anchoring, avoid sending the reinvite out */
let sdp;
if (isReleasingMedia) {
if (isReleasingMedia && !this.calleeIsUsingSrtp) {
this.logger.info(`got a reinvite from FS to ${reason}`);
sdp = dlg.other.remote.sdp;
if (!answerMedia.flags.includes('port latching')) answerMedia.flags.push('port latching');
@@ -853,6 +859,7 @@ Duration=${payload.duration} `
this.logger.info('call ended with normal termination');
this.rtpEngineResource.destroy();
this.activeCallIds.delete(this.req.get('Call-ID'));
if (this.activeCallIds.size === 0) this.idleEmitter.emit('idle');
this.uas.other.destroy();
this.srf.endSession(this.req);
});
+475 -3368
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "sbc-outbound",
"version": "v0.7.9",
"version": "v0.8.2",
"main": "app.js",
"engines": {
"node": ">= 12.0.0"
@@ -27,13 +27,13 @@
"jslint": "eslint app.js lib"
},
"dependencies": {
"@jambonz/db-helpers": "^0.7.3",
"@jambonz/realtimedb-helpers": "^0.5.7",
"@jambonz/db-helpers": "^0.7.4",
"@jambonz/realtimedb-helpers": "^0.7.0",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/mw-registrar": "0.2.2",
"@jambonz/rtpengine-utils": "^0.4.3",
"@jambonz/siprec-client-utils": "^0.2.0",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/siprec-client-utils": "^0.2.4",
"@jambonz/stats-collector": "^0.1.8",
"@jambonz/time-series": "^0.2.5",
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
+1 -1
View File
@@ -84,7 +84,7 @@ test('sbc-outbound tests', async(t) => {
await sippUac('uac-pcap-carrier-fail-limits.xml');
t.pass('fails when max calls in progress');
await waitFor(18);
await waitFor(25);
const res = await queryCdrs({account_sid: 'ed649e33-e771-403a-8c99-1780eabbc803'});
console.log(`cdrs: ${JSON.stringify(res)}`);