From 3c958523b522f08afa5c1a0821066a8b0c68cad8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 12 Jun 2020 16:09:06 +0200 Subject: [PATCH] packages/config-loader: add support for env secrets --- packages/config-loader/src/loader.ts | 38 ++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 23bb8799cf..26e258a588 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -83,6 +83,7 @@ type ReadFileFunc = (path: string) => Promise; type ReaderContext = { shouldReadSecrets: boolean; readFile: ReadFileFunc; + env: { [name in string]?: string }; }; function isObject(obj: JsonValue | undefined): obj is JsonObject { @@ -98,12 +99,19 @@ type FileSecret = { file: string; }; -type Secret = FileSecret; +type EnvSecret = { + env: string; +}; + +type Secret = FileSecret | EnvSecret; const secretLoaderSchemas = { file: yup.object({ file: yup.string().required(), }), + env: yup.object({ + env: yup.string().required(), + }), }; const secretSchema = yup.lazy(value => { @@ -127,21 +135,34 @@ const secretSchema = yup.lazy(value => { ); }); +// A thing to make sure we've narrowed the type down to never +function isNever() { + return void 0 as T; +} + export async function readSecret( data: JsonObject, - readFile: ReadFileFunc, + ctx: ReaderContext, ): Promise { + if (!ctx.shouldReadSecrets) { + return undefined; + } + const secret = secretSchema.validateSync(data) as Secret; if ('file' in secret) { - return readFile(secret.file); + return ctx.readFile(secret.file); + } + if ('env' in secret) { + return ctx.env[secret.env]; } + isNever(); throw new Error('Secret was left unhandled'); } -export async function readConfigFile(filePath: string, context: ReaderContext) { - const configYaml = await context.readFile(filePath); +export async function readConfigFile(filePath: string, ctx: ReaderContext) { + const configYaml = await ctx.readFile(filePath); const config = yaml.parse(configYaml); async function transform( @@ -170,12 +191,8 @@ export async function readConfigFile(filePath: string, context: ReaderContext) { throw TypeError(`Expected object at secret ${path}.$secret`); } - if (!context.shouldReadSecrets) { - return undefined; - } - try { - return await readSecret(obj.$secret, context.readFile); + return await readSecret(obj.$secret, ctx); } catch (error) { throw new Error(`Invalid secret at ${path}: ${error.message}`); } @@ -218,6 +235,7 @@ export async function loadStaticConfig( try { const rootPath = dirname(configPath); const config = await readConfigFile(configPath, { + env: process.env, shouldReadSecrets, readFile: (path: string) => { return fs.readFile(resolvePath(rootPath, path), 'utf8');