@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
112 lines (101 loc) • 4.33 kB
text/typescript
// SPDX-License-Identifier: Apache-2.0
import {Flags as flags} from '../../commands/flags.js';
import {type ConfigManager} from '../config-manager.js';
import {type K8Factory} from '../../integration/kube/k8-factory.js';
import {type SoloLogger} from '../logging/solo-logger.js';
import {type Lock, type LockRenewalService} from './lock.js';
import {IntervalLock} from './interval-lock.js';
import {LockHolder} from './lock-holder.js';
import {inject, injectable} from 'tsyringe-neo';
import {patchInject} from '../dependency-injection/container-helper.js';
import {type NamespaceName} from '../../types/namespace/namespace-name.js';
import {InjectTokens} from '../dependency-injection/inject-tokens.js';
import {LockAcquisitionError} from './lock-acquisition-error.js';
import {type RemoteConfigRuntimeStateApi} from '../../business/runtime-state/api/remote-config-runtime-state-api.js';
/**
* Manages the acquisition and renewal of locks.
*/
export class LockManager {
/**
* Creates a new lock manager.
*
* @param _renewalService - the lock renewal service.
* @param _logger - the logger.
* @param k8Factory - the Kubernetes client.
* @param configManager - the configuration manager.
* @param remoteConfigRuntimeState
*/
public constructor(
private readonly _renewalService?: LockRenewalService,
private readonly _logger?: SoloLogger,
private readonly k8Factory?: K8Factory,
private readonly configManager?: ConfigManager,
private readonly remoteConfigRuntimeState?: RemoteConfigRuntimeStateApi,
) {
this._renewalService = patchInject(_renewalService, InjectTokens.LockRenewalService, this.constructor.name);
this._logger = patchInject(_logger, InjectTokens.SoloLogger, this.constructor.name);
this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
this.configManager = patchInject(configManager, InjectTokens.ConfigManager, this.constructor.name);
this.remoteConfigRuntimeState = patchInject(
remoteConfigRuntimeState,
InjectTokens.RemoteConfigRuntimeState,
this.constructor.name,
);
}
/**
* Creates a new lease. This lease is not acquired until the `acquire` method is called.
*
* @returns a new lease instance.
*/
public async create(): Promise<Lock> {
let namespace: NamespaceName;
try {
namespace = this.remoteConfigRuntimeState.getNamespace();
} catch {
// If the namespace is not set in the remote config, we will use the current namespace.
}
if (!namespace) {
namespace = await this.currentNamespace();
}
return new IntervalLock(this.k8Factory, this._renewalService, LockHolder.default(), namespace);
}
/**
* Retrieves the renewal service implementation.
*
* @returns the lease renewal service.
*/
public get renewalService(): LockRenewalService {
return this._renewalService;
}
/**
* Retrieves the logger instance.
*
* @returns the logger.
*/
public get logger(): SoloLogger {
return this._logger;
}
/**
* Retrieves the user or configuration supplied namespace to use for lease acquisition.
*
* @returns the namespace to use for lease acquisition or null if no namespace is specified.
* @throws LockAcquisitionError if the namespace does not exist and cannot be created.
*/
private async currentNamespace(): Promise<NamespaceName> {
const deploymentNamespace: NamespaceName = this.configManager.getFlag(flags.namespace);
const clusterSetupNamespace: NamespaceName = this.configManager.getFlag(flags.clusterSetupNamespace);
if (!deploymentNamespace && !clusterSetupNamespace) {
return null;
}
const namespace: NamespaceName = deploymentNamespace || clusterSetupNamespace;
if (!(await this.k8Factory.default().namespaces().has(namespace))) {
await this.k8Factory.default().namespaces().create(namespace);
if (!(await this.k8Factory.default().namespaces().has(namespace))) {
throw new LockAcquisitionError(`failed to create the '${namespace}' namespace`);
}
}
return namespace;
}
}