UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

137 lines (136 loc) 7.14 kB
import { type ComponentUpgradeMigrationStep } from './component-upgrade-rules-types.js'; export { type ComponentUpgradeMigrationStep } from './component-upgrade-rules-types.js'; /** * Static utility for loading component migration rules and planning upgrade paths. */ export declare class ComponentUpgradeMigrationRules { /** * Module-level cache for the loaded migration config. Once loaded (from file or embedded * defaults), the config is stored here to avoid redundant file I/O on subsequent calls. * Reset via `resetUpgradeMigrationConfigCache()` in tests. */ private static cachedConfig; /** * Resets the cached migration config. Intended for use in tests only. * * This allows tests to: * - Switch between different config files between test cases. * - Force re-loading to verify file parsing behavior. * - Ensure test isolation (no state leaks between test cases). */ static resetCache(): void; /** * Plans the complete upgrade migration path for a component. * * This is the main entry point of the module. Given a component name, current version, and * target version, it returns an ordered list of migration steps that the caller should * execute sequentially to safely upgrade the component. * * ## Behavior by scenario * * ### Downgrade or same-version (current >= target) * Returns a single step using the component's default strategy. No boundary analysis is * performed since boundaries only apply to forward upgrades. * * ### Forward upgrade with no boundaries crossed * Returns a single step using the default strategy (typically `'in-place'`). * * ### Forward upgrade crossing one or more boundaries * Returns multiple steps, one per boundary group (after merging consecutive same-strategy * boundaries). The last boundary's step targets the final `targetVersion` (not the boundary * version itself). If there's remaining distance after all boundaries, a final default- * strategy step covers the gap. * * ## Example migration plans * * **Current config** — default `'in-place'`, `'recreate'` boundary at `0.28.1`: * * ``` * planUpgradeMigrationPath('block-node', '0.28.0', '0.28.1') * → [{ from: '0.28.0', to: '0.28.1', strategy: 'recreate' }] * // Boundary at 0.28.1 crossed; step targets the final version directly. * * planUpgradeMigrationPath('block-node', '0.28.0', '0.35.0') * → [{ from: '0.28.0', to: '0.35.0', strategy: 'recreate' }] * // Boundary at 0.28.1 crossed; last boundary so step jumps to target (0.35.0). * * planUpgradeMigrationPath('block-node', '0.28.1', '0.35.0') * → [{ from: '0.28.1', to: '0.35.0', strategy: 'in-place' }] * // Already at/past 0.28.1; no boundary crossed → default in-place. * ``` * * **Hypothetical addition** — adding a second `'recreate'` boundary at 0.29.0: * * ``` * // Config: boundaries: [{ version: '0.28.1', recreate }, { version: '0.29.0', recreate }] * * planUpgradeMigrationPath('block-node', '0.28.0', '0.29.0') * → [{ from: '0.28.0', to: '0.29.0', strategy: 'recreate' }] * // Both boundaries have 'recreate' → merged into 1 step; last boundary targets final version. * * planUpgradeMigrationPath('block-node', '0.28.1', '0.29.0') * → [{ from: '0.28.1', to: '0.29.0', strategy: 'recreate' }] * // Only the 0.29.0 boundary crossed → 1 recreate step. * ``` * * @param component - Component name (e.g., 'block-node'). Must match a key in the config. * @param currentVersion - The currently installed version (semver string). * @param targetVersion - The desired target version (semver string). * @returns Ordered array of migration steps to execute. */ static planUpgradeMigrationPath(component: string, currentVersion: string, targetVersion: string): ComponentUpgradeMigrationStep[]; /** * Loads the component upgrade migration configuration. * * Loading priority: * 1. Return cached config if already loaded (performance optimization). * 2. Try to read and parse the external JSON override file at `constants.UPGRADE_MIGRATIONS_FILE`. * This allows operators to customize migration rules without modifying source code. * 3. If the file doesn't exist or fails to parse, silently fall back to a safe empty config * (`{components: {}}`). Unknown components then receive an `'in-place'` default with no boundaries. * * The fallback behavior is intentional: we never want a missing or malformed config file to * block upgrades entirely. The resource-backed defaults always provide a safe baseline. * * Historical note: before this refactor, the default config lived in the deleted * `DEFAULT_COMPONENT_UPGRADE_MIGRATION_CONFIG` constant. The contents now live in * `resources/component-upgrade-migrations.json` and are loaded dynamically at runtime. */ private static loadConfig; /** * Retrieves the migration config for a specific component by name. * * If the component has no entry in the config (e.g., a newly added component that hasn't * had any migration rules defined yet), returns a safe default: `'in-place'` strategy with * no boundaries. This means unknown components always get a simple Helm upgrade. * * @param component - The component name (e.g., 'block-node'). * @returns The component's migration config, or a default no-op config. */ private static getComponentConfig; /** * Finds all boundary rules that are "crossed" during a forward upgrade from `current` to `target`. * * A boundary is "crossed" when: * current < boundary.version <= target * * This means: * - If you're already AT or ABOVE the boundary version, it's not crossed (you've already * passed it in a previous upgrade). * - If the target is BELOW the boundary version, it's not crossed (you haven't reached it yet). * * After finding crossed boundaries, they are: * 1. Sorted ascending by version (so the earliest boundary is processed first). * 2. **Reduced (merged)**: consecutive boundaries with the SAME strategy are collapsed into * one entry (keeping the later version). This optimization avoids unnecessary intermediate * steps. For example, if boundaries at 0.28.0 and 0.30.0 both require `'recreate'`, there's * no point recreating at 0.28.0 and then recreating again at 0.30.0 — we can skip straight * to 0.30.0 with a single recreate. * * @param componentConfig - The component's migration config containing boundary rules. * @param current - The current installed version (parsed SemanticVersion<string>). * @param target - The desired target version (parsed SemanticVersion<string>). * @returns Sorted and reduced array of crossed boundary rules. */ private static findCrossedBoundaries; }