packages/config-loader: added data secret loader

This commit is contained in:
Patrik Oldsberg
2020-06-12 16:26:15 +02:00
parent 3c958523b5
commit c2b59e20cb
+48 -2
View File
@@ -17,7 +17,7 @@
import fs from 'fs-extra';
import * as yup from 'yup';
import yaml from 'yaml';
import { resolve as resolvePath, dirname } from 'path';
import { resolve as resolvePath, dirname, extname } from 'path';
import { AppConfig, JsonObject, JsonValue } from '@backstage/config';
import { findRootPath } from './paths';
import { LoadConfigOptions } from './types';
@@ -103,7 +103,12 @@ type EnvSecret = {
env: string;
};
type Secret = FileSecret | EnvSecret;
type DataSecret = {
data: string;
path: string | string[];
};
type Secret = FileSecret | EnvSecret | DataSecret;
const secretLoaderSchemas = {
file: yup.object({
@@ -112,6 +117,15 @@ const secretLoaderSchemas = {
env: yup.object({
env: yup.string().required(),
}),
data: yup.object({
data: yup.string().required(),
path: yup.lazy(value => {
if (typeof value === 'string') {
return yup.string().required();
}
return yup.array().of(yup.string().required()).required();
}),
}),
};
const secretSchema = yup.lazy<object>(value => {
@@ -135,6 +149,14 @@ const secretSchema = yup.lazy<object>(value => {
);
});
const dataSecretParser: {
[ext in string]: (content: string) => Promise<JsonObject>;
} = {
'.json': async content => JSON.parse(content),
'.yaml': async content => yaml.parse(content),
'.yml': async content => yaml.parse(content),
};
// A thing to make sure we've narrowed the type down to never
function isNever<T extends never>() {
return void 0 as T;
@@ -156,6 +178,30 @@ export async function readSecret(
if ('env' in secret) {
return ctx.env[secret.env];
}
if ('data' in secret) {
const ext = extname(secret.data);
const parser = dataSecretParser[ext];
if (!parser) {
throw new Error(`No data secret parser available for extension ${ext}`);
}
const content = await ctx.readFile(secret.data);
const { path } = secret;
const parts = typeof path === 'string' ? path.split('.') : path;
let value: JsonValue = await parser(content);
for (const [index, part] of parts.entries()) {
if (!isObject(value)) {
const errPath = parts.slice(0, index).join('.');
throw new Error(
`Value is not an object at ${errPath} in ${secret.data}`,
);
}
value = value[part];
}
return String(value);
}
isNever<typeof secret>();
throw new Error('Secret was left unhandled');