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 () => {
expect(testQenv.getEnvVarRequired('key1')).toEqual('original');
expect(testQenv.getEnvVarOnDemand('key1')).toEqual('original');
expect(testQenv.getEnvVarOnDemand('key1')).toEqual('original');
});
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');
});

View File

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

View File

@ -1,9 +1,5 @@
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 {
public requiredEnvVars: string[] = [];
public availableEnvVars: string[] = [];
@ -11,34 +7,54 @@ export class Qenv {
public keyValueObject: { [key: string]: any } = {};
public logger = new plugins.smartlog.ConsoleLog();
// filePaths
public qenvFilePathAbsolute: string;
public envFilePathAbsolute: string;
constructor(qenvFileBasePathArg = process.cwd(), envFileBasePathArg, failOnMissing = true) {
// lets make sure paths are absolute
this.qenvFilePathAbsolute = plugins.path.join(
plugins.path.resolve(qenvFileBasePathArg),
'qenv.yml'
);
this.envFilePathAbsolute = plugins.path.join(
plugins.path.resolve(envFileBasePathArg),
'env.json'
);
constructor(
qenvFileBasePathArg: string = process.cwd(),
envFileBasePathArg: string,
failOnMissing: boolean = true
) {
this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg);
this.loadRequiredEnvVars();
this.loadAvailableEnvVars();
this.checkForMissingEnvVars(failOnMissing);
}
this.getRequiredEnvVars();
this.getAvailableEnvVars();
private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg: string) {
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) {
console.info('Required Env Vars are:');
console.log(this.requiredEnvVars);
console.error('However some Env variables could not be resolved:');
console.log(this.missingEnvVars);
console.info('Required Env Vars are:', this.requiredEnvVars);
console.error('Missing Env Vars:', this.missingEnvVars);
if (failOnMissing) {
this.logger.log('error', 'Exiting!');
this.logger.log('error', 'Exiting due to missing env vars!');
process.exit(1);
} else {
this.logger.log('warn', 'qenv is not set to fail on missing environment variables');
@ -46,150 +62,73 @@ export class Qenv {
}
}
/**
* only gets an environment variable if it is required within a read qenv.yml file
* @param envVarName
*/
public getEnvVarRequired(envVarName): string {
return this.keyValueObject[envVarName];
public getEnvVarOnDemand(envVarName: string): string | undefined {
return (
this.getFromEnvironmentVariable(envVarName) ||
this.getFromEnvJsonFile(envVarName) ||
this.getFromDockerSecret(envVarName) ||
this.getFromDockerSecretJson(envVarName)
);
}
/**
* tries to get any env var even if it is not required
* @param wantedEnvVar
*/
public getEnvVarOnDemand(wantedEnvVar: string): string {
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];
public getEnvVarOnDemandAsObject(envVarName: string): any {
const rawValue = this.getEnvVarOnDemand(envVarName);
if (rawValue && rawValue.startsWith('base64Object:')) {
const base64Part = rawValue.split('base64Object:')[1];
return this.decodeBase64(base64Part);
}
return rawValue;
}
// env file check
// lets determine the actual env yml
let envJsonFileAsObject;
private getFromEnvironmentVariable(envVarName: string): string | undefined {
return process.env[envVarName];
}
private getFromEnvJsonFile(envVarName: string): string | undefined {
try {
envJsonFileAsObject = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute);
} catch (err) {
envJsonFileAsObject = {};
}
if (envJsonFileAsObject.hasOwnProperty(wantedEnvVar)) {
envVarFromEnvJsonFile = envJsonFileAsObject[wantedEnvVar];
const envJson = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute);
const value = envJson[envVarName];
if (typeof value === 'object') {
return 'base64Object:' + this.encodeBase64(value);
}
return value;
} catch (error) {
return undefined;
}
}
// docker secret check
if (
plugins.smartfile.fs.isDirectory('/run') &&
plugins.smartfile.fs.isDirectory('/run/secrets') &&
plugins.smartfile.fs.fileExistsSync(`/run/secrets/${wantedEnvVar}`)
) {
envVarFromDockerSecret = plugins.smartfile.fs.toStringSync(`/run/secrets/${wantedEnvVar}`);
private getFromDockerSecret(envVarName: string): string | undefined {
const secretPath = `/run/secrets/${envVarName}`;
if (plugins.smartfile.fs.fileExistsSync(secretPath)) {
return plugins.smartfile.fs.toStringSync(secretPath);
}
return undefined;
}
// docker secret.json
if (
plugins.smartfile.fs.isDirectory('/run') &&
plugins.smartfile.fs.isDirectory('/run/secrets')
) {
private getFromDockerSecretJson(envVarName: string): string | undefined {
if (plugins.smartfile.fs.isDirectory('/run/secrets')) {
const availableSecrets = plugins.smartfile.fs.listAllItemsSync('/run/secrets');
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}`);
envVarFromDockerSecret = secretObject[wantedEnvVar];
const value = secretObject[envVarName];
if (typeof value === 'object') {
return 'base64Object:' + this.encodeBase64(value);
}
return value;
}
}
}
// 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;
}
return undefined;
}
/**
* gets the required env values
*/
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 encodeBase64(data: any): string {
const jsonString = JSON.stringify(data);
return Buffer.from(jsonString).toString('base64');
}
/**
* gets the available env vars
*/
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;
};
private decodeBase64(encodedString: string): any {
const decodedString = Buffer.from(encodedString, 'base64').toString('utf-8');
return JSON.parse(decodedString);
}
}