@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
137 lines (136 loc) • 7.14 kB
TypeScript
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;
}