Skip to content

Commit 610a8a0

Browse files
authored
Merge pull request #175 from AbsaOSS/fix/logger
Fix issue setting up JSON formatted logging
2 parents d9199b9 + 55f7813 commit 610a8a0

File tree

15 files changed

+309
-178
lines changed

15 files changed

+309
-178
lines changed

vcxagency-node/src/api/api-health.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
/**
2+
* Copyright 2020 ABSA Group Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
'use strict'
18+
119
const { asyncHandler } = require('./middleware')
220

321
module.exports = function (app) {

vcxagency-node/src/configuration/app-config-loader.js

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,58 +18,27 @@
1818

1919
const path = require('path')
2020
const dotenv = require('dotenv')
21+
const fs = require('fs')
22+
const assert = require('assert')
2123

2224
const appConfigName = process.env.APP_CONFIG
2325
if (appConfigName) {
2426
const pathToConfig = path.resolve(__dirname, `../../config/${appConfigName}.env`)
27+
assert(fs.existsSync(pathToConfig), `File ${pathToConfig} not found.`)
2528
console.log(`App configuration will be loaded from ${pathToConfig}.`)
2629
dotenv.config({ path: pathToConfig })
2730
} else {
2831
console.log('App configuration will be loaded from supplied environment variables.')
2932
}
3033

31-
function buildAppConfigFromEnvVariables () {
32-
const appConfig = {
33-
LOG_LEVEL: process.env.LOG_LEVEL,
34-
LOG_ENABLE_INDYSDK: process.env.LOG_ENABLE_INDYSDK,
35-
LOG_JSON_TO_CONSOLE: process.env.LOG_JSON_TO_CONSOLE,
36-
LOG_HEALTH_REQUESTS: process.env.LOG_HEALTH_REQUESTS,
37-
DEV_MODE: process.env.DEV_MODE,
38-
39-
SERVER_PORT: process.env.SERVER_PORT,
40-
SERVER_HOSTNAME: process.env.SERVER_HOSTNAME,
41-
SERVER_MAX_REQUEST_SIZE_KB: process.env.SERVER_MAX_REQUEST_SIZE_KB,
42-
SERVER_ENABLE_TLS: process.env.SERVER_ENABLE_TLS,
43-
CERTIFICATE_PATH: process.env.CERTIFICATE_PATH,
44-
CERTIFICATE_KEY_PATH: process.env.CERTIFICATE_KEY_PATH,
45-
46-
AGENCY_WALLET_NAME: process.env.AGENCY_WALLET_NAME,
47-
AGENCY_DID: process.env.AGENCY_DID,
48-
AGENCY_SEED_SECRET: process.env.AGENCY_SEED_SECRET,
49-
AGENCY_WALLET_KEY_SECRET: process.env.AGENCY_WALLET_KEY_SECRET,
50-
51-
REDIS_URL: process.env.REDIS_URL,
52-
AGENCY_TYPE: process.env.AGENCY_TYPE,
53-
54-
MYSQL_HOST: process.env.MYSQL_HOST,
55-
MYSQL_PORT: process.env.MYSQL_PORT,
56-
MYSQL_ACCOUNT: process.env.MYSQL_ACCOUNT,
57-
MYSQL_PASSWORD_SECRET: process.env.MYSQL_PASSWORD_SECRET,
58-
MYSQL_DATABASE_APPLICATION: process.env.MYSQL_DATABASE_APPLICATION,
59-
MYSQL_DATABASE_WALLET: process.env.MYSQL_DATABASE_WALLET,
60-
MYSQL_DATABASE_WALLET_CONNECTION_LIMIT: process.env.MYSQL_DATABASE_WALLET_CONNECTION_LIMIT,
61-
62-
AWS_S3_PATH_CERT: process.env.AWS_S3_PATH_CERT,
63-
AWS_S3_BUCKET_CERT: process.env.AWS_S3_BUCKET_CERT,
64-
AWS_S3_PATH_CERT_KEY: process.env.AWS_S3_PATH_CERT_KEY,
65-
66-
ECS_CONTAINER_METADATA_URI_V4: process.env.ECS_CONTAINER_METADATA_URI_V4,
67-
WEBHOOK_RESPONSE_TIMEOUT_MS: process.env.WEBHOOK_RESPONSE_TIMEOUT_MS,
68-
EXPLAIN_QUERIES: process.env.EXPLAIN_QUERIES
34+
function loadEnvVariables (envVariables) {
35+
const inputConfig = {}
36+
for (const envName of envVariables) {
37+
inputConfig[envName] = process.env[envName]
6938
}
70-
appConfig.SERVER_MAX_REQUEST_SIZE_KB = parseInt(appConfig.SERVER_MAX_REQUEST_SIZE_KB)
71-
appConfig.MYSQL_DATABASE_WALLET_CONNECTION_LIMIT = parseInt(appConfig.MYSQL_DATABASE_WALLET_CONNECTION_LIMIT)
72-
return appConfig
39+
return inputConfig
7340
}
7441

75-
module.exports = { buildAppConfigFromEnvVariables }
42+
module.exports = {
43+
loadEnvVariables
44+
}

vcxagency-node/src/configuration/app-config.js

Lines changed: 106 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
'use strict'
1818

1919
const Joi = require('joi')
20+
const { loadEnvVariables } = require('./app-config-loader')
2021

2122
function stringifyAndHideSensitive (appConfig) {
2223
function hideSecrets (key, value) {
@@ -37,72 +38,127 @@ function stringifyAndHideSensitive (appConfig) {
3738

3839
const MB_AS_KB = 1024
3940

40-
const configValidation = Joi.object().keys({
41-
LOG_LEVEL: Joi.string().valid('silly', 'debug', 'info', 'warn', 'error'),
42-
LOG_ENABLE_INDYSDK: Joi.boolean().default(false),
43-
LOG_JSON_TO_CONSOLE: Joi.boolean().default(true),
44-
LOG_HEALTH_REQUESTS: Joi.boolean().default(false),
45-
DEV_MODE: Joi.boolean().default(false),
46-
47-
SERVER_HOSTNAME: Joi.string().default('0.0.0.0'),
48-
SERVER_PORT: Joi.number().integer().min(1025).max(65535).required(),
49-
SERVER_ENABLE_TLS: Joi.boolean().default(true),
50-
SERVER_MAX_REQUEST_SIZE_KB: Joi.number().integer().min(1).max(MB_AS_KB * 10).default(512),
51-
CERTIFICATE_PATH: Joi.string(),
52-
CERTIFICATE_KEY_PATH: Joi.string(),
53-
54-
AGENCY_WALLET_NAME: Joi.string().required(),
55-
AGENCY_WALLET_KEY_SECRET: Joi.string().min(20).required(),
56-
AGENCY_SEED_SECRET: Joi.string().min(20).required(),
57-
AGENCY_DID: Joi.string().required(),
58-
59-
REDIS_URL: Joi.string(),
60-
AGENCY_TYPE: Joi.string().valid('enterprise', 'client').required(),
61-
62-
MYSQL_HOST: Joi.string().required(),
63-
MYSQL_PORT: Joi.number().integer().min(1025).max(65535).default(3306).required(),
64-
MYSQL_ACCOUNT: Joi.string().required(),
65-
MYSQL_PASSWORD_SECRET: Joi.string().required(),
66-
MYSQL_DATABASE_APPLICATION: Joi.string().required(),
67-
MYSQL_DATABASE_WALLET: Joi.string().required(),
68-
MYSQL_DATABASE_WALLET_CONNECTION_LIMIT: Joi.number().integer().min(1).max(100).default(50),
69-
70-
AWS_S3_PATH_CERT: Joi.string(),
71-
AWS_S3_BUCKET_CERT: Joi.string(),
72-
AWS_S3_PATH_CERT_KEY: Joi.string(),
73-
74-
ECS_CONTAINER_METADATA_URI_V4: Joi.string().uri(),
75-
WEBHOOK_RESPONSE_TIMEOUT_MS: Joi.number().default(1000),
76-
EXPLAIN_QUERIES: Joi.boolean().default(false)
77-
})
78-
79-
function validateFinalConfig (appConfig) {
41+
function _getLoggingValidationRules () {
42+
return {
43+
LOG_LEVEL: Joi.string().valid('trace', 'debug', 'info', 'warn', 'error').default('info'),
44+
LOG_JSON_TO_CONSOLE: Joi.boolean().default(true),
45+
LOG_ENABLE_INDYSDK: Joi.boolean().default(false),
46+
DISABLE_COLOR_LOGS: Joi.boolean().default(false),
47+
LOG_HEALTH_REQUESTS: Joi.boolean().default(false),
48+
EXPLAIN_QUERIES: Joi.boolean().default(false),
49+
DANGEROUS_HTTP_DETAILS: Joi.boolean().default(false),
50+
ECS_CONTAINER_METADATA_URI_V4: Joi.string().uri()
51+
}
52+
}
53+
54+
function _mysqlValidationRules () {
55+
return {
56+
MYSQL_HOST: Joi.string().required(),
57+
MYSQL_PORT: Joi.number().integer().min(1025).max(65535).default(3306).required(),
58+
MYSQL_ACCOUNT: Joi.string().required(),
59+
MYSQL_PASSWORD_SECRET: Joi.string().required(),
60+
MYSQL_DATABASE_APPLICATION: Joi.string().required(),
61+
MYSQL_DATABASE_WALLET: Joi.string().required(),
62+
MYSQL_DATABASE_WALLET_CONNECTION_LIMIT: Joi.number().integer().min(1).max(100).default(50)
63+
}
64+
}
65+
66+
function _getTlsValidationRules () {
67+
return {
68+
SERVER_ENABLE_TLS: Joi.boolean().default(true),
69+
AWS_S3_BUCKET_CERT: Joi.string(),
70+
CERTIFICATE_PATH: Joi.string(),
71+
CERTIFICATE_KEY_PATH: Joi.string(),
72+
AWS_S3_PATH_CERT: Joi.string(),
73+
AWS_S3_PATH_CERT_KEY: Joi.string()
74+
}
75+
}
76+
77+
function _getServerValidationRules () {
78+
return {
79+
SERVER_PORT: Joi.number().integer().min(1025).max(65535).required(),
80+
SERVER_HOSTNAME: Joi.string().default('0.0.0.0'),
81+
SERVER_MAX_REQUEST_SIZE_KB: Joi.number().integer().min(1).max(MB_AS_KB * 10).default(512)
82+
}
83+
}
84+
85+
function _setupWalletValidationRules () {
86+
return {
87+
AGENCY_SEED_SECRET: Joi.string().min(20).required()
88+
}
89+
}
90+
91+
function _walletValidationRules () {
92+
return {
93+
AGENCY_WALLET_NAME: Joi.string().required(),
94+
AGENCY_WALLET_KEY_SECRET: Joi.string().min(20).required()
95+
}
96+
}
97+
98+
function _applicationValidationRules () {
99+
return {
100+
REDIS_URL: Joi.string().uri(),
101+
AGENCY_TYPE: Joi.string().valid('enterprise', 'client').required(),
102+
WEBHOOK_RESPONSE_TIMEOUT_MS: Joi.number().default(1000)
103+
}
104+
}
105+
106+
function _extraValidationTls (appConfig) {
107+
if (appConfig.SERVER_ENABLE_TLS) {
108+
if (!appConfig.CERTIFICATE_PATH || !appConfig.CERTIFICATE_KEY_PATH) {
109+
throw new Error('Valid certificate and key paths must be specified when TLS enabled!')
110+
}
111+
}
112+
}
113+
114+
function _extraValidationByAgencyType (appConfig) {
80115
if (appConfig.AGENCY_TYPE === 'client') {
81116
if (!appConfig.REDIS_URL) {
82117
throw new Error('Configuration for agency of type \'client\' must have REDIS_URL specified.')
83118
}
84119
}
85-
function validateTls () {
86-
if (appConfig.SERVER_ENABLE_TLS === true) {
87-
if (!appConfig.CERTIFICATE_PATH || !appConfig.CERTIFICATE_KEY_PATH) {
88-
throw new Error('Valid certificate and key paths must be specified when TLS enabled!')
89-
}
120+
}
121+
122+
const OP_MODES = {
123+
RUN_SERVER: {
124+
name: 'run-server',
125+
postValidations: [_extraValidationTls, _extraValidationByAgencyType],
126+
joiValidationBody: {
127+
..._getLoggingValidationRules(),
128+
..._mysqlValidationRules(),
129+
..._getTlsValidationRules(),
130+
..._getServerValidationRules(),
131+
..._setupWalletValidationRules(),
132+
..._walletValidationRules(),
133+
..._applicationValidationRules(),
134+
AGENCY_DID: Joi.string().required(),
135+
DEV_MODE: Joi.boolean().default(false),
136+
ECS_CONTAINER_METADATA_URI_V4: Joi.string().uri()
90137
}
91138
}
92-
validateTls()
93139
}
94140

95141
async function validateAppConfig (appConfig) {
96-
const { value: effectiveConfig, error } = configValidation.validate(appConfig)
142+
const operationModeInfo = OP_MODES.RUN_SERVER
143+
const { postValidations } = operationModeInfo
144+
const { value: effectiveConfig, error } = Joi.object().keys(operationModeInfo.joiValidationBody).validate(appConfig)
97145
if (error) {
98146
throw new Error(`Application configuration is not valid. Details ${stringifyAndHideSensitive(error)}`)
99147
}
100-
validateFinalConfig(effectiveConfig)
148+
for (const postValidation of postValidations) {
149+
postValidation(effectiveConfig)
150+
}
101151
return effectiveConfig
102152
}
103153

154+
async function loadConfiguration () {
155+
const envVariables = Object.keys(OP_MODES.RUN_SERVER.joiValidationBody)
156+
return loadEnvVariables(envVariables)
157+
}
158+
104159
module.exports = {
105160
validateAppConfig,
106-
validateFinalConfig,
107-
stringifyAndHideSensitive
161+
stringifyAndHideSensitive,
162+
loadConfiguration,
163+
OP_MODES
108164
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright 2020 ABSA Group Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
'use strict'
18+
19+
const express = require('express')
20+
const logger = require('./../logging/logger-builder')(__filename)
21+
const { fetchServerCertsFromS3 } = require('../scripts/download-certs')
22+
const { buildApplication } = require('./app')
23+
const { createWebServer, setupExpressApp } = require('./server')
24+
25+
async function startServer (appConfig) {
26+
try {
27+
process.on('SIGTERM', signal => {
28+
logger.warn(`Process ${process.pid} [startServer execution mode] received a SIGTERM signal.`)
29+
process.exit(0)
30+
})
31+
32+
process.on('SIGINT', signal => {
33+
logger.warn(`Process ${process.pid} [startServer execution mode] has been interrupted.`)
34+
process.exit(0)
35+
})
36+
37+
if (appConfig.SERVER_ENABLE_TLS) {
38+
logger.info('Fetching certificates/keys to serve.')
39+
await fetchServerCertsFromS3(appConfig)
40+
}
41+
42+
logger.debug('Going to build application internals.')
43+
const application = await buildApplication(appConfig)
44+
45+
const expressApp = express()
46+
expressApp.set('app-name', 'agency')
47+
48+
logger.debug('Going to build http/https server.')
49+
const httpServer = createWebServer(expressApp, appConfig.SERVER_ENABLE_TLS, appConfig.CERTIFICATE_PATH, appConfig.CERTIFICATE_KEY_PATH, logger)
50+
await setupExpressApp(expressApp, application, appConfig)
51+
52+
logger.debug(`Going to listen on port ${appConfig.SERVER_PORT}`)
53+
httpServer.listen(appConfig.SERVER_PORT, () => logger.info(`------------ Listening on port ${appConfig.SERVER_PORT} ------------`))
54+
} catch (err) {
55+
logger.error(`Unhandled error. Application will be terminated. ${err.stack}`)
56+
process.exit(255)
57+
}
58+
}
59+
60+
module.exports = {
61+
startServer
62+
}
Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,6 @@ function createWebServer (expressApp, enableTls, tlsCertPath, tlsKeyPath, logger
4747
}
4848
}
4949

50-
function reloadTrustedCerts (logger) {
51-
const TRUSTED_CA_CERTS_PATH = '/etc/ssl/certs/ca-certificates.crt'
52-
if (fs.existsSync(TRUSTED_CA_CERTS_PATH)) {
53-
https.globalAgent.options.ca = fs.readFileSync(TRUSTED_CA_CERTS_PATH)
54-
logger.warn(`Loaded additional trusted CA certificates from ${TRUSTED_CA_CERTS_PATH}`)
55-
} else {
56-
logger.warn('No additional trusted CA certificates were loaded.')
57-
}
58-
}
59-
6050
async function setupExpressApp (expressApp, application, appConfig) {
6151
const { entityForwardAgent, serviceNewMessages } = application
6252
logger.info('Setting up express endpoints and middleware.')
@@ -106,6 +96,5 @@ async function setupExpressApp (expressApp, application, appConfig) {
10696

10797
module.exports = {
10898
setupExpressApp,
109-
createWebServer,
110-
reloadTrustedCerts
99+
createWebServer
111100
}

0 commit comments

Comments
 (0)