diff --git a/packages/config-loader/src/lib/env.ts b/packages/config-loader/src/lib/env.ts index e9087c4387..83f86d4212 100644 --- a/packages/config-loader/src/lib/env.ts +++ b/packages/config-loader/src/lib/env.ts @@ -21,6 +21,24 @@ const ENV_PREFIX = 'APP_CONFIG_'; // Update the same pattern in config package if this is changed const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; +/** + * Read runtime configuration from the environment. + * + * Only environment variables prefixed with APP_CONFIG_ will be considered. + * + * For each variable, the prefix will be removed, and rest of the key will + * be split by '_'. Each part will then be used as keys to build up a nested + * config object structure. The treatment of the entire environment variable + * is case-sensitive. + * + * The value of the variable should be JSON serialized, as it will be parsed + * and the type will be kept intact. For example "true" and true are treated + * differently, as well as "42" and 42. + * + * For example, to set the config app.title to "My Title", use the following: + * + * APP_CONFIG_app_title='"My Title"' + */ export function readEnv(env: { [name: string]: string | undefined; }): AppConfig[] { diff --git a/packages/config-loader/src/lib/reader.ts b/packages/config-loader/src/lib/reader.ts index 01a8561f1f..044bf68288 100644 --- a/packages/config-loader/src/lib/reader.ts +++ b/packages/config-loader/src/lib/reader.ts @@ -19,6 +19,10 @@ import { isObject } from './utils'; import { JsonValue, JsonObject } from '@backstage/config'; import { ReaderContext } from './types'; +/** + * Reads and parses, and validates, and transforms a single config file. + * The transformation rewrites any special values, like the $secret key. + */ export async function readConfigFile(filePath: string, ctx: ReaderContext) { const configYaml = await ctx.readFile(filePath); const config = yaml.parse(configYaml); diff --git a/packages/config-loader/src/lib/resolver.ts b/packages/config-loader/src/lib/resolver.ts index d7a1a7f764..4a06f3ebca 100644 --- a/packages/config-loader/src/lib/resolver.ts +++ b/packages/config-loader/src/lib/resolver.ts @@ -24,7 +24,7 @@ type ResolveOptions = { }; /** - * Resolves all configuration files that should be loaded. + * Resolves all configuration files that should be loaded in the given environment. */ export async function resolveStaticConfig( options: ResolveOptions, diff --git a/packages/config-loader/src/lib/secrets.ts b/packages/config-loader/src/lib/secrets.ts index d43eaa1af5..c4157120ae 100644 --- a/packages/config-loader/src/lib/secrets.ts +++ b/packages/config-loader/src/lib/secrets.ts @@ -21,21 +21,31 @@ import { JsonObject, JsonValue } from '@backstage/config'; import { isObject, isNever } from './utils'; import { ReaderContext } from './types'; +// Reads a file and forwards the contents as is, assuming ut8 encoding type FileSecret = { + // Path to the secret file, relative to the config file. file: string; }; +// Reads the secret from an environment variable. type EnvSecret = { + // The name of the environment file. env: string; }; +// Reads a secret from a json-like file and extracts a value at a path. +// The supported extensions are define in dataSecretParser below. type DataSecret = { + // Path to the data secret file, relative to the config file. data: string; + // The path to the value inside the data file. + // Either a '.' separated list, or an array of path segments. path: string | string[]; }; type Secret = FileSecret | EnvSecret | DataSecret; +// Schema for each type of secret description const secretLoaderSchemas = { file: yup.object({ file: yup.string().required(), @@ -54,6 +64,7 @@ const secretLoaderSchemas = { }), }; +// The top-level secret schema, which figures out what type of secret it is. const secretSchema = yup.lazy(value => { if (typeof value !== 'object' || value === null) { return yup.object().required().label('secret'); @@ -75,6 +86,7 @@ const secretSchema = yup.lazy(value => { ); }); +// Parsers for each type of data secret file. const dataSecretParser: { [ext in string]: (content: string) => Promise; } = { @@ -83,6 +95,9 @@ const dataSecretParser: { '.yml': async content => yaml.parse(content), }; +/** + * Transforms a secret description into the actual secret value. + */ export async function readSecret( data: JsonObject, ctx: ReaderContext, diff --git a/packages/config-loader/src/lib/types.ts b/packages/config-loader/src/lib/types.ts index ba552537d0..c0f8b99dcf 100644 --- a/packages/config-loader/src/lib/types.ts +++ b/packages/config-loader/src/lib/types.ts @@ -19,6 +19,9 @@ import { JsonObject } from '@backstage/config'; export type ReadFileFunc = (path: string) => Promise; export type ReadSecretFunc = (desc: JsonObject) => Promise; +/** + * Common context that provides all the necessary hooks for reading configuration files. + */ export type ReaderContext = { env: { [name in string]?: string }; readFile: ReadFileFunc;