'use strict'; const fs = require('fs'); const assert = require('assert'); const path = require('path'); const debug = require('debug')('xsenv'); const DEFAULT_SECRETS_PATH = '/etc/secrets/sapcp/'; const NodeCache = require('node-cache'); const k8sSecretsCache = new NodeCache({ stdTTL: 9 * 60 }); exports.readK8SServices = readK8SServices; const isFile = filePath => fs.statSync(filePath).isFile(); const isDirectory = dirPath => fs.statSync(dirPath).isDirectory(); function isJsonObject(str) { // Also consider the case in which the file is written by hand: // allow new lines and trailing whitespaces. return /^{(.|\r?\n?)*}(\s)*$/.test(str); } function readFileContent(filePath) { let content = fs.readFileSync(filePath, 'utf8'); const parseAllJson = process.env.XSENV_PARSE_ALL_JSON === 'true' || false; if (!parseAllJson) { if (!isJsonObject(content)) { return content; } } try { content = JSON.parse(content); } catch (err) { debug('Content of file %s is not valid JSON object', filePath); } return content; } function readFiles(dirPath) { const result = {}; for (let file of fs.readdirSync(dirPath)) { const filePath = path.join(dirPath, file); if (isFile(filePath)) { result[file] = readFileContent(filePath); } } return result; } function parseTags(tags, serviceName) { if (!tags) { return [serviceName]; } if (process.env.XSENV_PARSE_ALL_JSON) { return [...tags]; } return JSON.parse(tags); } function readInstance(serviceName, instanceName, instancePath) { const credentials = readFiles(instancePath); const tags = parseTags(credentials.tags, serviceName); // previously the library used `serviceName` as derived from the // file path as the only tag. To keep as close as possible to this // behaviour we need to make sure this `serviceName` is in `tags` if (!tags.includes(serviceName)) { tags.push(serviceName); } return { credentials, name: instanceName, label: credentials.label || serviceName, tags }; } function readServiceInstances(serviceName, servicePath) { const result = {}; for (let instanceName of fs.readdirSync(servicePath)) { const instancePath = path.join(servicePath, instanceName); if (isDirectory(instancePath)) { result[instanceName] = readInstance(serviceName, instanceName, instancePath); } } return result; } function readSecrets(secretsPath) { assert(isDirectory(secretsPath), 'secrets path must be a directory'); const result = {}; for (let serviceName of fs.readdirSync(secretsPath)) { const servicePath = path.join(secretsPath, serviceName); if (isDirectory(servicePath)) { Object.assign(result, readServiceInstances(serviceName, servicePath)); } } return result; } function readK8SServices(secretsPath, disableCache) { secretsPath = secretsPath || DEFAULT_SECRETS_PATH; assert(typeof secretsPath === 'string', 'secrets directory path must be string'); let result = undefined; let cachedSecrets = k8sSecretsCache.get(`${secretsPath}-services`); if (cachedSecrets && !disableCache) { debug(`Cached Secrets found: ${secretsPath}-services`); debug(cachedSecrets); return cachedSecrets; } if (fs.existsSync(secretsPath)) { result = readSecrets(secretsPath); debug(`Caching Secret: ${secretsPath}-services`); debug(result); k8sSecretsCache.set(`${secretsPath}-services`, result); } return result; }