From 6e8152e3cba5a94d68b1858b88fd0f477b6f0c82 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Wed, 23 Feb 2022 08:38:01 -0500 Subject: [PATCH] LCC under Kubernetes must use service name for FS (#35) --- db/jambones-sql.sql | 2 +- lib/routes/api/accounts.js | 46 +++++++++++++++++++---------------- lib/routes/api/sms-inbound.js | 45 ++++++++++++++++++++++------------ package.json | 2 +- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/db/jambones-sql.sql b/db/jambones-sql.sql index 3614fa1..536885b 100644 --- a/db/jambones-sql.sql +++ b/db/jambones-sql.sql @@ -365,7 +365,7 @@ CREATE TABLE webhooks ( webhook_sid CHAR(36) NOT NULL UNIQUE , url VARCHAR(1024) NOT NULL, -method ENUM("GET","POST") NOT NULL DEFAULT 'POST', +method ENUM("GET","POST","WS") NOT NULL DEFAULT 'POST', username VARCHAR(255), password VARCHAR(255), PRIMARY KEY (webhook_sid) diff --git a/lib/routes/api/accounts.js b/lib/routes/api/accounts.js index 923cbd3..881aa7a 100644 --- a/lib/routes/api/accounts.js +++ b/lib/routes/api/accounts.js @@ -19,6 +19,23 @@ const translator = short(); let idx = 0; +const getFsUrl = async(logger, retrieveSet, setName) => { + if (process.env.K8S) return `http://${process.env.K8S_FEATURE_SERVER_SERVICE_NAME}:3000/v1/createCall`; + + try { + const fs = await retrieveSet(setName); + if (0 === fs.length) { + logger.info('No available feature servers to handle createCall API request'); + return ; + } + const ip = stripPort(fs[idx++ % fs.length]); + logger.info({fs}, `feature servers available for createCall API request, selecting ${ip}`); + return `http://${ip}:3000/v1/createCall`; + } catch (err) { + logger.error({err}, 'getFsUrl: error retreving feature servers from redis'); + } +}; + const stripPort = (hostport) => { const arr = /^(.*):(.*)$/.exec(hostport); if (arr) return arr[1]; @@ -567,18 +584,11 @@ router.post('/:sid/Calls', async(req, res) => { const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-fs`; const {retrieveSet, logger} = req.app.locals; + const serviceUrl = await getFsUrl(logger, retrieveSet, setName); + if (!serviceUrl) res.json({msg: 'no available feature servers at this time'}).status(480); try { - const fs = await retrieveSet(setName); - if (0 === fs.length) { - logger.info('No available feature servers to handle createCall API request'); - return res.json({msg: 'no available feature servers at this time'}).status(500); - } - const ip = stripPort(fs[idx++ % fs.length]); - logger.info({fs}, `feature servers available for createCall API request, selecting ${ip}`); - const serviceUrl = `http://${ip}:3000/v1/createCall`; await validateCreateCall(logger, sid, req); - logger.debug({payload: req.body}, `sending createCall API request to to ${ip}`); updateLastUsed(logger, sid, req).catch((err) => {}); request({ url: serviceUrl, @@ -587,11 +597,11 @@ router.post('/:sid/Calls', async(req, res) => { body: Object.assign(req.body, {account_sid: sid}) }, (err, response, body) => { if (err) { - logger.error(err, `Error sending createCall POST to ${ip}`); + logger.error(err, `Error sending createCall POST to ${serviceUrl}`); return res.sendStatus(500); } if (response.statusCode !== 201) { - logger.error({statusCode: response.statusCode}, `Non-success response returned by createCall ${ip}`); + logger.error({statusCode: response.statusCode}, `Non-success response returned by createCall ${serviceUrl}`); return res.sendStatus(500); } res.status(201).json(body); @@ -714,14 +724,8 @@ router.post('/:sid/Messages', async(req, res) => { const {retrieveSet, logger} = req.app.locals; try { - const fs = await retrieveSet(setName); - if (0 === fs.length) { - logger.info('No available feature servers to handle createMessage API request'); - return res.json({msg: 'no available feature servers at this time'}).status(500); - } - const ip = stripPort(fs[idx++ % fs.length]); - logger.info({fs}, `feature servers available for createMessage API request, selecting ${ip}`); - const serviceUrl = `http://${ip}:3000/v1/createMessage/${account_sid}`; + const serviceUrl = await getFsUrl(logger, retrieveSet, setName); + if (!serviceUrl) res.json({msg: 'no available feature servers at this time'}).status(480); await validateCreateMessage(logger, account_sid, req); const payload = { @@ -729,7 +733,7 @@ router.post('/:sid/Messages', async(req, res) => { account_sid, ...req.body }; - logger.debug({payload}, `sending createMessage API request to to ${ip}`); + logger.debug({payload}, `sending createMessage API request to to ${serviceUrl}`); updateLastUsed(logger, account_sid, req).catch(() => {}); request({ url: serviceUrl, @@ -738,7 +742,7 @@ router.post('/:sid/Messages', async(req, res) => { body: payload }, (err, response, body) => { if (err) { - logger.error(err, `Error sending createMessage POST to ${ip}`); + logger.error(err, `Error sending createMessage POST to ${serviceUrl}`); return res.sendStatus(500); } if (response.statusCode !== 200) { diff --git a/lib/routes/api/sms-inbound.js b/lib/routes/api/sms-inbound.js index a4b34d2..b42351c 100644 --- a/lib/routes/api/sms-inbound.js +++ b/lib/routes/api/sms-inbound.js @@ -5,15 +5,37 @@ const { v4: uuidv4 } = require('uuid'); const sysError = require('../error'); let idx = 0; +const getFsUrl = async(logger, retrieveSet, setName, provider) => { + if (process.env.K8S) return `http://${process.env.K8S_FEATURE_SERVER_SERVICE_NAME}:3000/v1/messaging/${provider}`; -async function doSendResponse(res, respondFn, body) { + try { + const fs = await retrieveSet(setName); + if (0 === fs.length) { + logger.info('No available feature servers to handle createCall API request'); + return ; + } + const ip = stripPort(fs[idx++ % fs.length]); + logger.info({fs}, `feature servers available for createCall API request, selecting ${ip}`); + return `http://${ip}:3000/v1/messaging/${provider}`; + } catch (err) { + logger.error({err}, 'getFsUrl: error retreving feature servers from redis'); + } +}; + +const stripPort = (hostport) => { + const arr = /^(.*):(.*)$/.exec(hostport); + if (arr) return arr[1]; + return hostport; +}; + +const doSendResponse = async(res, respondFn, body) => { if (typeof respondFn === 'number') res.sendStatus(respondFn); else if (typeof respondFn !== 'function') res.sendStatus(200); else { const payload = await respondFn(body); res.status(200).json(payload); } -} +}; router.post('/:provider', async(req, res) => { const provider = req.params.provider; @@ -67,17 +89,8 @@ router.post('/:provider', async(req, res) => { } try { - const fs = await retrieveSet(setName); - if (0 === fs.length) { - logger.info('No available feature servers to handle createCall API request'); - return res - .json({ - msg: 'no available feature servers at this time' - }) - .status(480); - } - const ip = fs[idx++ % fs.length]; - const serviceUrl = `http://${ip}:3000/v1/messaging/${provider}`; + const serviceUrl = await getFsUrl(logger, retrieveSet, setName, provider); + if (!serviceUrl) res.json({msg: 'no available feature servers at this time'}).status(480); const messageSid = uuidv4(); const payload = await Promise.resolve(filterFn({messageSid}, req.body)); @@ -113,7 +126,7 @@ router.post('/:provider', async(req, res) => { logger.debug({body: req.body, payload}, 'filtered incoming SMS'); - logger.info({payload, url: serviceUrl}, `sending incomingSms API request to FS at ${ip}`); + logger.info({payload, url: serviceUrl}, `sending incomingSms API request to FS at ${serviceUrl}`); request({ url: serviceUrl, @@ -123,7 +136,7 @@ router.post('/:provider', async(req, res) => { }, async(err, response, body) => { if (err) { - logger.error(err, `Error sending incomingSms POST to ${ip}`); + logger.error(err, `Error sending incomingSms POST to ${serviceUrl}`); return res.sendStatus(500); } if (200 === response.statusCode) { @@ -131,7 +144,7 @@ router.post('/:provider', async(req, res) => { logger.info({body}, 'sending response to provider for incomingSMS'); return doSendResponse(res, respondFn, body); } - logger.error({statusCode: response.statusCode}, `Non-success response returned by incomingSms ${ip}`); + logger.error({statusCode: response.statusCode}, `Non-success response returned by incomingSms ${serviceUrl}`); return res.sendStatus(500); }); } catch (err) { diff --git a/package.json b/package.json index 1a5583d..ef915c7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "app.js", "scripts": { "start": "node app.js", - "test": "NODE_ENV=test APPLY_JAMBONZ_DB_LIMITS=1 JWT_SECRET=foobarbazzle JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=info JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/ ", + "test": "NODE_ENV=test APPLY_JAMBONZ_DB_LIMITS=1 JWT_SECRET=foobarbazzle JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=error JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/ ", "integration-test": "NODE_ENV=test JAMBONES_TIME_SERIES_HOST=127.0.0.1 AWS_REGION='us-east-1' JAMBONES_CURRENCY=USD JWT_SECRET=foobarbazzle JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=debug JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/serve-integration.js", "upgrade-db": "node ./db/upgrade-jambonz-db.js", "coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",