UNPKG

kubricate

Version:

A TypeScript framework for building reusable, type-safe Kubernetes infrastructure — without the YAML mess.

295 lines (294 loc) 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SecretManager = void 0; var _utils = /*#__PURE__*/require("../internal/utils.js"); /** * SecretManager is a type-safe registry for managing secret providers and their secrets. * * - Register provider definitions (with type-safe config). * - Register named provider instances based on those definitions. * - Add secrets referencing the registered providers. */ class SecretManager { /** * Internal runtime storage for secret values (not type-safe). * Intended for use in actual secret resolution or access. */ _secrets = {}; _providers = {}; _connectors = {}; _defaultProvider; _defaultConnector; logger; constructor() {} /** * Registers a new provider instance using a valid provider name * * @param provider - The unique name of the provider (e.g., 'Kubernetes.Secret'). * @param instance - Configuration specific to the provider type. * @returns A SecretManager instance with the provider added. */ addProvider(provider, instance) { if (this._providers[provider]) { throw new Error(`Provider ${provider} already exists`); } // Set the name of the provider instance instance.name = provider; this._providers[provider] = instance; return this; } /** * Sets the default provider for the SecretManager. * This provider will be used if no specific provider is specified when adding a secret. * * Providers support multiple instances, so this is a way to set a default. * * @param provider - The unique name of the provider (e.g., 'Kubernetes.Secret'). * @returns A SecretManager instance with the provider added. */ setDefaultProvider(provider) { this._defaultProvider = provider; return this; } /** * Adds a new connector instance using a valid connector name * * @param connector - The unique name of the connector (e.g., 'EnvConnector'). * @param instance - Configuration specific to the connector type. * @returns A SecretManager instance with the connector added. */ addConnector(connector, instance) { if (this._connectors[connector]) { throw new Error(`Connector ${connector} already exists`); } this._connectors[connector] = instance; return this; } /** * Sets the default connector for the SecretManager. * This connector will be used if no specific connector is specified when adding a secret. * * Connectors support multiple instances, so this is a way to set a default. * * @param connector - The unique name of the connector (e.g., 'EnvConnector'). * @returns A SecretManager instance with the connector added. */ setDefaultConnector(connector) { this._defaultConnector = connector; return this; } /** * Adds a new secret to the registry and links it to an existing provider. * * @param optionsOrName - SecretOptions * @returns A new SecretManager instance with the secret added. */ addSecret(optionsOrName) { if (typeof optionsOrName === 'string') { if (this._secrets[optionsOrName]) { throw new Error(`Secret ${optionsOrName} already exists`); } this._secrets[optionsOrName] = { name: optionsOrName }; } else { if (this._secrets[optionsOrName.name]) { throw new Error(`Secret ${optionsOrName.name} already exists`); } this._secrets[optionsOrName.name] = optionsOrName; } return this; } /** * @interal Internal method to get the current secrets in the manager. * This is not intended for public use. * * @returns The current secrets in the registry. */ getSecrets() { // Ensure that all secrets have a provider and connector set // before returning the secrets. this.build(); return this._secrets; } /** * @internal Internal method to prepare secrets for use. * This is not intended for public use. * * When a secret is added, it may not have a provider or connector set. * This method ensures that all secrets have a provider and connector set. */ prepareSecrets() { for (const secret of Object.values(this._secrets)) { if (!secret.provider) { secret.provider = this._defaultProvider; } if (!secret.connector) { secret.connector = this._defaultConnector; } } } /** * @internal Internal method to get the current providers in the manager. * This is not intended for public use. * * @param key - The unique name of the connector (e.g., 'EnvConnector'). * @returns The connector instance associated with the given key. * @throws Error if the connector is not found. */ getConnector(key) { (0, _utils.validateString)(key); if (!this._connectors[key]) { throw new Error(`Connector ${key} not found`); } return this._connectors[key]; } /** * @internal Internal method to get the current providers in the manager. * This is not intended for public use. * * @param key - The unique name of the provider (e.g., 'Kubernetes.Secret'). * @returns The provider instance associated with the given key. * @throws Error if the provider is not found. */ getProvider(key) { (0, _utils.validateString)(key); if (!this._providers[key]) { throw new Error(`Provider ${key} not found`); } return this._providers[key]; } /** * @internal Internal method to build the SecretManager. * This is not intended for public use. * * Post processing step to ensure that all secrets is ready for use. */ build() { if (Object.keys(this._connectors).length === 0) { throw new Error('No connectors registered'); } if (Object.keys(this._providers).length === 0) { throw new Error('No providers registered'); } if (Object.keys(this._secrets).length === 0) { throw new Error('No secrets registered'); } if (!this._defaultProvider && Object.keys(this._providers).length > 1) { throw new Error('No default provider set, and multiple providers registered'); } if (!this._defaultConnector && Object.keys(this._connectors).length > 1) { throw new Error('No default connector set, and multiple connectors registered'); } if (!this._defaultProvider) { this._defaultProvider = Object.keys(this._providers)[0]; } if (!this._defaultConnector) { this._defaultConnector = Object.keys(this._connectors)[0]; } this.prepareSecrets(); this.logger?.debug('SecretManager[build] All secrets have a provider and connector set'); this.logger?.debug('SecretManager[build] All configurations are valid'); this.logger?.debug(`Default provider: ${String(this._defaultProvider)}`); this.logger?.debug(`Default connector: ${String(this._defaultConnector)}`); return this; } resolveProvider(provider) { return provider ? this.getProvider(provider) : this.getProvider(this._defaultProvider); } resolveConnector(connector) { return connector ? this.getConnector(connector) : this.getConnector(this._defaultConnector); } getConnectors() { return this._connectors; } getProviders() { return this._providers; } getDefaultProvider() { return this._defaultProvider; } getDefaultConnector() { return this._defaultConnector; } /** * @internal Internal method to get the current providers in the manager. * This is not intended for public use. * * Prepares secrets using registered connectors and providers. * This does not perform any side-effects like `kubectl`. * * @returns An array of SecretManagerEffect objects, each containing the name, value, and effects of the secret. * @throws Error if a connector or provider is not found for a secret. */ async prepare() { this.build(); const secrets = this.getSecrets(); const resolved = {}; const loadedKeys = new Set(); for (const secret of Object.values(secrets)) { const connector = this.resolveConnector(secret.connector); if (!loadedKeys.has(secret.name)) { await connector.load([secret.name]); resolved[secret.name] = connector.get(secret.name); loadedKeys.add(secret.name); } } return Object.values(secrets).map(secret => { const provider = this.resolveProvider(secret.provider); return { name: secret.name, // TODO: Consider to remove the value from provider, due to only `secret apply` is using it value: resolved[secret.name], effects: provider.prepare(secret.name, resolved[secret.name]) }; }); } /** * Resolves the registered provider instance for a given secret name. * This method is used during the secret injection planning phase (e.g., `useSecrets`) * and does not resolve or load secret values. * * @param secretName - The name of the secret to resolve. * @returns The BaseProvider associated with the secret. * @throws If the secret is not registered or has no provider. */ resolveProviderFor(secretName) { this.build(); const secret = this._secrets[secretName]; if (!secret) { throw new Error(`Secret "${secretName}" is not registered.`); } return { providerInstance: this.resolveProvider(secret.provider), providerId: String(secret.provider) }; } /** * Resolves the actual secret value and its associated provider for a given secret name. * This method is used at runtime when the secret is being applied (e.g., `secret apply`). * It loads the value from the appropriate connector and returns both the value and the provider. * * @param secretName - The name of the secret to resolve and load. * @returns An object containing the resolved provider and loaded secret value. * @throws If the secret is not registered or its connector/provider cannot be found. */ async resolveSecretValueForApply(secretName) { const secret = this._secrets[secretName]; if (!secret) { throw new Error(`Secret "${secretName}" is not registered.`); } const connector = this.resolveConnector(secret.connector); await connector.load([secret.name]); const value = connector.get(secret.name); const provider = this.resolveProvider(secret.provider); return { provider, value }; } } exports.SecretManager = SecretManager; //# sourceMappingURL=SecretManager.js.map