fix(core): update

This commit is contained in:
Philipp Kunz 2023-08-09 12:49:52 +02:00
parent 072ca59ab0
commit 9d33054f03
3 changed files with 94 additions and 155 deletions

View File

@ -18,12 +18,12 @@ tap.test('should create a new class', async () => {
}); });
tap.test('key1 should be not be overwritten since it is already present', async () => { tap.test('key1 should be not be overwritten since it is already present', async () => {
expect(testQenv.getEnvVarRequired('key1')).toEqual('original'); expect(testQenv.getEnvVarOnDemand('key1')).toEqual('original');
expect(testQenv.getEnvVarOnDemand('key1')).toEqual('original'); expect(testQenv.getEnvVarOnDemand('key1')).toEqual('original');
}); });
tap.test('key2 should be read from Yml', async () => { tap.test('key2 should be read from Yml', async () => {
expect(testQenv.getEnvVarRequired('key2')).toEqual('fromJson'); expect(testQenv.getEnvVarOnDemand('key2')).toEqual('fromJson');
expect(testQenv.getEnvVarOnDemand('key2')).toEqual('fromJson'); expect(testQenv.getEnvVarOnDemand('key2')).toEqual('fromJson');
}); });

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/qenv', name: '@push.rocks/qenv',
version: '5.0.4', version: '5.0.5',
description: 'easy promised environments' description: 'easy promised environments'
} }

View File

@ -1,9 +1,5 @@
import * as plugins from './qenv.plugins.js'; import * as plugins from './qenv.plugins.js';
/**
* class Qenv
* allows to make assertions about the environments while being more flexibel in how to meet them
*/
export class Qenv { export class Qenv {
public requiredEnvVars: string[] = []; public requiredEnvVars: string[] = [];
public availableEnvVars: string[] = []; public availableEnvVars: string[] = [];
@ -11,34 +7,54 @@ export class Qenv {
public keyValueObject: { [key: string]: any } = {}; public keyValueObject: { [key: string]: any } = {};
public logger = new plugins.smartlog.ConsoleLog(); public logger = new plugins.smartlog.ConsoleLog();
// filePaths
public qenvFilePathAbsolute: string; public qenvFilePathAbsolute: string;
public envFilePathAbsolute: string; public envFilePathAbsolute: string;
constructor(qenvFileBasePathArg = process.cwd(), envFileBasePathArg, failOnMissing = true) { constructor(
// lets make sure paths are absolute qenvFileBasePathArg: string = process.cwd(),
this.qenvFilePathAbsolute = plugins.path.join( envFileBasePathArg: string,
plugins.path.resolve(qenvFileBasePathArg), failOnMissing: boolean = true
'qenv.yml' ) {
); this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg);
this.envFilePathAbsolute = plugins.path.join( this.loadRequiredEnvVars();
plugins.path.resolve(envFileBasePathArg), this.loadAvailableEnvVars();
'env.json' this.checkForMissingEnvVars(failOnMissing);
); }
this.getRequiredEnvVars(); private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg: string) {
this.getAvailableEnvVars(); this.qenvFilePathAbsolute = plugins.path.join(plugins.path.resolve(qenvFileBasePathArg), 'qenv.yml');
this.envFilePathAbsolute = plugins.path.join(plugins.path.resolve(envFileBasePathArg), 'env.json');
}
this.missingEnvVars = this.getMissingEnvVars(); private loadRequiredEnvVars() {
if (plugins.smartfile.fs.fileExistsSync(this.qenvFilePathAbsolute)) {
const qenvFile = plugins.smartfile.fs.toObjectSync(this.qenvFilePathAbsolute);
if (qenvFile?.required && Array.isArray(qenvFile.required)) {
this.requiredEnvVars.push(...qenvFile.required);
} else {
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
}
}
}
private loadAvailableEnvVars() {
for (const envVar of this.requiredEnvVars) {
const value = this.getEnvVarOnDemand(envVar);
if (value) {
this.availableEnvVars.push(envVar);
this.keyValueObject[envVar] = value;
}
}
}
private checkForMissingEnvVars(failOnMissing: boolean) {
this.missingEnvVars = this.requiredEnvVars.filter((envVar) => !this.availableEnvVars.includes(envVar));
// handle missing variables
if (this.missingEnvVars.length > 0) { if (this.missingEnvVars.length > 0) {
console.info('Required Env Vars are:'); console.info('Required Env Vars are:', this.requiredEnvVars);
console.log(this.requiredEnvVars); console.error('Missing Env Vars:', this.missingEnvVars);
console.error('However some Env variables could not be resolved:');
console.log(this.missingEnvVars);
if (failOnMissing) { if (failOnMissing) {
this.logger.log('error', 'Exiting!'); this.logger.log('error', 'Exiting due to missing env vars!');
process.exit(1); process.exit(1);
} else { } else {
this.logger.log('warn', 'qenv is not set to fail on missing environment variables'); this.logger.log('warn', 'qenv is not set to fail on missing environment variables');
@ -46,150 +62,73 @@ export class Qenv {
} }
} }
/** public getEnvVarOnDemand(envVarName: string): string | undefined {
* only gets an environment variable if it is required within a read qenv.yml file return (
* @param envVarName this.getFromEnvironmentVariable(envVarName) ||
*/ this.getFromEnvJsonFile(envVarName) ||
public getEnvVarRequired(envVarName): string { this.getFromDockerSecret(envVarName) ||
return this.keyValueObject[envVarName]; this.getFromDockerSecretJson(envVarName)
);
} }
/** public getEnvVarOnDemandAsObject(envVarName: string): any {
* tries to get any env var even if it is not required const rawValue = this.getEnvVarOnDemand(envVarName);
* @param wantedEnvVar if (rawValue && rawValue.startsWith('base64Object:')) {
*/ const base64Part = rawValue.split('base64Object:')[1];
public getEnvVarOnDemand(wantedEnvVar: string): string { return this.decodeBase64(base64Part);
let envVarFromEnvironmentVariable: string;
let envVarFromEnvJsonFile: string;
let envVarFromDockerSecret: string;
let dockerSecretJson: string;
// env var check
if (process.env[wantedEnvVar]) {
this.availableEnvVars.push(wantedEnvVar);
envVarFromEnvironmentVariable = process.env[wantedEnvVar];
} }
return rawValue;
}
// env file check private getFromEnvironmentVariable(envVarName: string): string | undefined {
// lets determine the actual env yml return process.env[envVarName];
let envJsonFileAsObject; }
private getFromEnvJsonFile(envVarName: string): string | undefined {
try { try {
envJsonFileAsObject = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute); const envJson = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute);
} catch (err) { const value = envJson[envVarName];
envJsonFileAsObject = {}; if (typeof value === 'object') {
} return 'base64Object:' + this.encodeBase64(value);
if (envJsonFileAsObject.hasOwnProperty(wantedEnvVar)) { }
envVarFromEnvJsonFile = envJsonFileAsObject[wantedEnvVar]; return value;
} catch (error) {
return undefined;
} }
}
// docker secret check private getFromDockerSecret(envVarName: string): string | undefined {
if ( const secretPath = `/run/secrets/${envVarName}`;
plugins.smartfile.fs.isDirectory('/run') && if (plugins.smartfile.fs.fileExistsSync(secretPath)) {
plugins.smartfile.fs.isDirectory('/run/secrets') && return plugins.smartfile.fs.toStringSync(secretPath);
plugins.smartfile.fs.fileExistsSync(`/run/secrets/${wantedEnvVar}`)
) {
envVarFromDockerSecret = plugins.smartfile.fs.toStringSync(`/run/secrets/${wantedEnvVar}`);
} }
return undefined;
}
// docker secret.json private getFromDockerSecretJson(envVarName: string): string | undefined {
if ( if (plugins.smartfile.fs.isDirectory('/run/secrets')) {
plugins.smartfile.fs.isDirectory('/run') &&
plugins.smartfile.fs.isDirectory('/run/secrets')
) {
const availableSecrets = plugins.smartfile.fs.listAllItemsSync('/run/secrets'); const availableSecrets = plugins.smartfile.fs.listAllItemsSync('/run/secrets');
for (const secret of availableSecrets) { for (const secret of availableSecrets) {
if (secret.includes('secret.json') && !envVarFromDockerSecret) { if (secret.includes('secret.json')) {
const secretObject = plugins.smartfile.fs.toObjectSync(`/run/secrets/${secret}`); const secretObject = plugins.smartfile.fs.toObjectSync(`/run/secrets/${secret}`);
envVarFromDockerSecret = secretObject[wantedEnvVar]; const value = secretObject[envVarName];
if (typeof value === 'object') {
return 'base64Object:' + this.encodeBase64(value);
}
return value;
} }
} }
} }
return undefined;
// warn if there is more than one candidate
const availableCcandidates: any[] = [];
[
envVarFromEnvironmentVariable,
envVarFromEnvJsonFile,
envVarFromDockerSecret,
dockerSecretJson,
].forEach((candidate) => {
if (candidate) {
availableCcandidates.push(candidate);
}
});
if (availableCcandidates.length > 1) {
this.logger.log(
'warn',
`found multiple candidates for ${wantedEnvVar} Choosing in the order of envVar, envFileVar, dockerSecret, dockerSecretJson`
);
console.log(availableCcandidates);
}
switch (true) {
case !!envVarFromEnvironmentVariable:
this.logger.log('ok', `found ${wantedEnvVar} as environment variable`);
return envVarFromEnvironmentVariable;
case !!envVarFromEnvJsonFile:
this.logger.log('ok', `found ${wantedEnvVar} as env.json variable`);
return envVarFromEnvJsonFile;
case !!envVarFromDockerSecret:
this.logger.log('ok', `found ${wantedEnvVar} as docker secret`);
return envVarFromDockerSecret;
case !!dockerSecretJson:
this.logger.log('ok', `found ${wantedEnvVar} as docker secret.json`);
return dockerSecretJson;
default:
this.logger.log(
'warn',
`could not find the wanted environment variable ${wantedEnvVar} anywhere`
);
return;
}
} }
/** private encodeBase64(data: any): string {
* gets the required env values const jsonString = JSON.stringify(data);
*/ return Buffer.from(jsonString).toString('base64');
private getRequiredEnvVars = () => { }
let qenvFile: any = {};
if (plugins.smartfile.fs.fileExistsSync(this.qenvFilePathAbsolute)) {
qenvFile = plugins.smartfile.fs.toObjectSync(this.qenvFilePathAbsolute);
}
if (!qenvFile || !qenvFile.required || !Array.isArray(qenvFile.required)) {
this.logger.log(
'warn',
`qenv (promised environment): ./qenv.yml File does not contain a 'required' Array! This might be ok though.`
);
} else {
for (const keyArg of Object.keys(qenvFile.required)) {
this.requiredEnvVars.push(qenvFile.required[keyArg]);
}
}
};
/** private decodeBase64(encodedString: string): any {
* gets the available env vars const decodedString = Buffer.from(encodedString, 'base64').toString('utf-8');
*/ return JSON.parse(decodedString);
private getAvailableEnvVars = () => { }
for (const requiredEnvVar of this.requiredEnvVars) {
const chosenVar = this.getEnvVarOnDemand(requiredEnvVar);
if (chosenVar) {
this.availableEnvVars.push(requiredEnvVar);
this.keyValueObject[requiredEnvVar] = chosenVar;
}
}
};
/**
* gets missing env vars
*/
private getMissingEnvVars = (): string[] => {
const missingEnvVars: string[] = [];
for (const envVar of this.requiredEnvVars) {
if (!this.availableEnvVars.includes(envVar)) {
missingEnvVars.push(envVar);
}
}
return missingEnvVars;
};
} }