UNPKG

@hashgraph/solo

Version:

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

141 lines (122 loc) 5.02 kB
// SPDX-License-Identifier: Apache-2.0 import { type CoordinationV1Api, V1Lease, V1LeaseSpec, V1MicroTime, V1ObjectMeta, type V1Status, } from '@kubernetes/client-node'; import {type Leases} from '../../../resources/lease/leases.js'; import {type NamespaceName} from '../../../../../types/namespace/namespace-name.js'; import {type SoloLogger} from '../../../../../core/logging/solo-logger.js'; import {container} from 'tsyringe-neo'; import {InjectTokens} from '../../../../../core/dependency-injection/inject-tokens.js'; import {K8ClientLease} from './k8-client-lease.js'; import {type Lease} from '../../../resources/lease/lease.js'; import {ResourceReadError} from '../../../errors/resource-operation-errors.js'; import {ResourceType} from '../../../resources/resource-type.js'; import {sleep} from '../../../../../core/helpers.js'; import {Duration} from '../../../../../core/time/duration.js'; import {getReasonPhrase, StatusCodes} from 'http-status-codes'; import {KubeApiResponse} from '../../../kube-api-response.js'; import {ResourceOperation} from '../../../resources/resource-operation.js'; export class K8ClientLeases implements Leases { private readonly logger: SoloLogger; public constructor(private readonly coordinationApiClient: CoordinationV1Api) { this.logger = container.resolve(InjectTokens.SoloLogger); } public async create( namespace: NamespaceName, leaseName: string, holderName: string, durationSeconds: number, ): Promise<Lease> { const lease: V1Lease = new V1Lease(); const metadata: V1ObjectMeta = new V1ObjectMeta(); metadata.name = leaseName; metadata.namespace = namespace.name; lease.metadata = metadata; const spec: V1LeaseSpec = new V1LeaseSpec(); spec.holderIdentity = holderName; spec.leaseDurationSeconds = durationSeconds; spec.acquireTime = new V1MicroTime(); lease.spec = spec; let result: V1Lease; try { result = await this.coordinationApiClient.createNamespacedLease({namespace: namespace.name, body: lease}); } catch (error) { KubeApiResponse.throwError(error, ResourceOperation.CREATE, ResourceType.LEASE, namespace, leaseName); } return K8ClientLease.fromV1Lease(result); } public async delete(namespace: NamespaceName, name: string): Promise<V1Status> { let result: V1Lease; try { result = await this.coordinationApiClient.deleteNamespacedLease({name, namespace: namespace.name}); } catch (error) { KubeApiResponse.throwError(error, ResourceOperation.DELETE, ResourceType.LEASE, namespace, name); } return result; } public async read(namespace: NamespaceName, leaseName: string, timesCalled: number = 0): Promise<Lease> { let result: V1Lease; try { result = await this.coordinationApiClient.readNamespacedLease({name: leaseName, namespace: namespace.name}); } catch (error) { if (error.code === StatusCodes.INTERNAL_SERVER_ERROR && timesCalled < 4) { // could be k8s control plane has no resources available this.logger.debug( `Retrying readNamespacedLease(${leaseName}, ${namespace}) in 5 seconds because of ${getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR)}`, ); await sleep(Duration.ofSeconds(5)); try { return await this.read(namespace, leaseName, timesCalled + 1); } catch (error) { throw new ResourceReadError(ResourceType.LEASE, namespace, leaseName, error); } } else { throw new ResourceReadError(ResourceType.LEASE, namespace, leaseName, error); } } return K8ClientLease.fromV1Lease(result); } public async renew(namespace: NamespaceName, leaseName: string, lease: Lease): Promise<Lease> { const v1Lease: V1Lease = K8ClientLease.toV1Lease(lease); v1Lease.spec.renewTime = new V1MicroTime(); let result: V1Lease; try { result = await this.coordinationApiClient.replaceNamespacedLease({ name: leaseName, namespace: namespace.name, body: v1Lease, }); } catch (error) { KubeApiResponse.throwError(error, ResourceOperation.REPLACE, ResourceType.LEASE, namespace, leaseName); } return K8ClientLease.fromV1Lease(result); } public async transfer(lease: Lease, newHolderName: string): Promise<Lease> { const v1Lease: V1Lease = K8ClientLease.toV1Lease(lease); v1Lease.spec.leaseTransitions++; v1Lease.spec.renewTime = new V1MicroTime(); v1Lease.spec.holderIdentity = newHolderName; let result: V1Lease; try { result = await this.coordinationApiClient.replaceNamespacedLease({ name: v1Lease.metadata.name, namespace: v1Lease.metadata.namespace, body: v1Lease, }); } catch (error) { KubeApiResponse.throwError( error, ResourceOperation.REPLACE, ResourceType.LEASE, lease.namespace, v1Lease.metadata.name, ); } return K8ClientLease.fromV1Lease(result); } }