Skip to content

Commit 48d4158

Browse files
committed
chore: add additional jsdoc comments
Signed-off-by: Nathan Klick <[email protected]>
1 parent 9451ad3 commit 48d4158

File tree

4 files changed

+247
-40
lines changed

4 files changed

+247
-40
lines changed

src/core/lease/lease_holder.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,28 @@ import { MissingArgumentError } from '../errors.ts'
1919
import os from 'node:os'
2020
import process from 'node:process'
2121

22+
/**
23+
* A representation of a leaseholder who is identified by a username, hostname, and process id (PID). This implementation
24+
* is serializable to/from a JSON object and is comparable to other leaseholders.
25+
*/
2226
export class LeaseHolder {
27+
/** The user's identity which is typically the OS login username. */
2328
private readonly _username: string
29+
30+
/** The machine's identity which is typically the hostname. */
2431
private readonly _hostname: string
32+
33+
/** The process identifier which is typically the OS PID. */
2534
private readonly _processId: number
2635

36+
/**
37+
* Constructs a new leaseholder with the given username, hostname, and process id. This constructor is private and
38+
* should not be called directly. Use the static factory methods to create a new instance.
39+
*
40+
* @param username - the user's identity.
41+
* @param hostname - the machine's identity.
42+
* @param processId - the process identifier.
43+
*/
2744
private constructor (username: string, hostname: string, processId: number) {
2845
if (!username) throw new MissingArgumentError('username is required')
2946
if (!hostname) throw new MissingArgumentError('hostname is required')
@@ -34,26 +51,54 @@ export class LeaseHolder {
3451
this._processId = processId
3552
}
3653

54+
/**
55+
* Creates a new leaseholder with the given username. The hostname is set to the current machine's hostname and the
56+
* process id is set to the current process's PID.
57+
* @param username - the user's identity.
58+
* @returns a new leaseholder instance.
59+
*/
3760
public static of (username: string): LeaseHolder {
3861
return new LeaseHolder(username, os.hostname(), process.pid)
3962
}
4063

64+
/**
65+
* Creates a new leaseholder by retrieving the current user's identity, the current machine's hostname, and the
66+
* current process's PID.
67+
* @returns a new leaseholder instance.
68+
*/
4169
public static default (): LeaseHolder {
4270
return LeaseHolder.of(os.userInfo().username)
4371
}
4472

73+
/**
74+
* The user's identity which is typically the OS login username.
75+
* @returns the user's identity.
76+
*/
4577
public get username (): string {
4678
return this._username
4779
}
4880

81+
82+
/**
83+
* The machine's identity which is typically the hostname.
84+
* @returns the machine's identity.
85+
*/
4986
public get hostname (): string {
5087
return this._hostname
5188
}
5289

90+
/**
91+
* The process identifier which is typically the OS PID.
92+
* @returns the process identifier.
93+
*/
5394
public get processId (): number {
5495
return this._processId
5596
}
5697

98+
/**
99+
* Returns a plain object representation of this leaseholder. This object may be serialized to JSON.
100+
* @returns a plain object representation of this leaseholder.
101+
*/
57102
public toObject (): any {
58103
return {
59104
username: this._username,
@@ -62,14 +107,31 @@ export class LeaseHolder {
62107
}
63108
}
64109

110+
/**
111+
* Compares this leaseholder to another leaseholder to determine if they are equal. Two leaseholders are equal if
112+
* their username, hostname, and process id are the same.
113+
* @param other - the other leaseholder to compare.
114+
* @returns true if the leaseholders are equal; false otherwise.
115+
*/
65116
public equals (other: LeaseHolder): boolean {
66117
return this.username === other.username && this.hostname === other.hostname && this.processId === other.processId
67118
}
68119

120+
/**
121+
* Compares this leaseholder to another leaseholder to determine if they are the same machine. Two leaseholders are
122+
* the same machine if their username and hostname are the same.
123+
* @param other - the other leaseholder to compare.
124+
* @returns true if the leaseholders are the same machine; false otherwise.
125+
*/
69126
public isSameMachineIdentity (other: LeaseHolder): boolean {
70127
return this.username === other.username && this.hostname === other.hostname
71128
}
72129

130+
/**
131+
* Determines if the process associated with this leaseholder is still alive. This method will return false if the
132+
* process is not alive or an error occurs while checking the process status.
133+
* @returns true if the process is alive; false otherwise.
134+
*/
73135
public isProcessAlive (): boolean {
74136
try {
75137
return process.kill(this.processId, 0)
@@ -78,10 +140,19 @@ export class LeaseHolder {
78140
}
79141
}
80142

143+
/**
144+
* Serializes this leaseholder to a JSON string representation.
145+
* @returns a JSON string representation of this leaseholder.
146+
*/
81147
public toJson (): string {
82148
return JSON.stringify(this.toObject())
83149
}
84150

151+
/**
152+
* Deserializes a JSON string representation of a leaseholder into a new leaseholder instance.
153+
* @param json - the JSON string representation of a leaseholder.
154+
* @returns a new leaseholder instance.
155+
*/
85156
public static fromJson (json: string): LeaseHolder {
86157
const obj: ReturnType<LeaseHolder['toObject']> = JSON.parse(json)
87158
return new LeaseHolder(obj.username, obj.hostname, obj.pid)

src/core/lease/lease_manager.ts

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,55 +24,90 @@ import { Lease } from './lease.ts'
2424
import { LeaseHolder } from './lease_holder.ts'
2525
import { LeaseAcquisitionError } from './lease_errors.ts'
2626

27+
/**
28+
* Manages the acquisition and renewal of leases.
29+
*/
2730
export class LeaseManager {
2831

29-
private readonly _logger: SoloLogger
30-
private readonly _renewalService: LeaseRenewalService
31-
32-
constructor (
33-
private readonly k8: K8,
34-
private readonly configManager: ConfigManager,
35-
logger: SoloLogger,
36-
renewalService: LeaseRenewalService
37-
) {
38-
if (!k8) throw new MissingArgumentError('an instance of core/K8 is required')
39-
if (!logger) throw new MissingArgumentError('an instance of core/SoloLogger is required')
40-
if (!configManager) throw new MissingArgumentError('an instance of core/ConfigManager is required')
41-
if (!renewalService) throw new MissingArgumentError('an instance of core/LeaseRenewalService is required')
32+
/** The injected logger instance. */
33+
private readonly _logger: SoloLogger
4234

43-
this._logger = logger
44-
this._renewalService = renewalService
45-
}
35+
/** The injected lease renewal service instance. */
36+
private readonly _renewalService: LeaseRenewalService
4637

47-
public async create (): Promise<Lease> {
48-
return new Lease(this.k8, this._renewalService, LeaseHolder.default(), await this.currentNamespace())
49-
}
38+
/**
39+
* Creates a new lease manager.
40+
*
41+
* @param k8 - the Kubernetes client.
42+
* @param configManager - the configuration manager.
43+
* @param logger - the logger.
44+
* @param renewalService - the lease renewal service.
45+
*/
46+
constructor (
47+
private readonly k8: K8,
48+
private readonly configManager: ConfigManager,
49+
logger: SoloLogger,
50+
renewalService: LeaseRenewalService
51+
) {
52+
if (!k8) throw new MissingArgumentError('an instance of core/K8 is required')
53+
if (!logger) throw new MissingArgumentError('an instance of core/SoloLogger is required')
54+
if (!configManager) throw new MissingArgumentError('an instance of core/ConfigManager is required')
55+
if (!renewalService) throw new MissingArgumentError('an instance of core/LeaseRenewalService is required')
5056

51-
public get renewalService (): LeaseRenewalService {
52-
return this._renewalService
53-
}
57+
this._logger = logger
58+
this._renewalService = renewalService
59+
}
5460

55-
public get logger (): SoloLogger {
56-
return this._logger
57-
}
61+
/**
62+
* Creates a new lease. This lease is not acquired until the `acquire` method is called.
63+
*
64+
* @returns a new lease instance.
65+
*/
66+
public async create (): Promise<Lease> {
67+
return new Lease(this.k8, this._renewalService, LeaseHolder.default(), await this.currentNamespace())
68+
}
5869

59-
private async currentNamespace (): Promise<string> {
60-
const deploymentNamespace = this.configManager.getFlag<string>(flags.namespace)
61-
const clusterSetupNamespace = this.configManager.getFlag<string>(flags.clusterSetupNamespace)
70+
/**
71+
* Retrieves the renewal service implementation.
72+
*
73+
* @returns the lease renewal service.
74+
*/
75+
public get renewalService (): LeaseRenewalService {
76+
return this._renewalService
77+
}
6278

63-
if (!deploymentNamespace && !clusterSetupNamespace) {
64-
return null
79+
/**
80+
* Retrieves the logger instance.
81+
*
82+
* @returns the logger.
83+
*/
84+
public get logger (): SoloLogger {
85+
return this._logger
6586
}
66-
const namespace = deploymentNamespace ? deploymentNamespace : clusterSetupNamespace
6787

68-
if (!await this.k8.hasNamespace(namespace)) {
69-
await this.k8.createNamespace(namespace)
88+
/**
89+
* Retrieves the user or configuration supplied namespace to use for lease acquisition.
90+
*
91+
* @returns the namespace to use for lease acquisition or null if no namespace is specified.
92+
* @throws LeaseAcquisitionError if the namespace does not exist and cannot be created.
93+
*/
94+
private async currentNamespace (): Promise<string> {
95+
const deploymentNamespace = this.configManager.getFlag<string>(flags.namespace)
96+
const clusterSetupNamespace = this.configManager.getFlag<string>(flags.clusterSetupNamespace)
7097

71-
if (!await this.k8.hasNamespace(namespace)) {
72-
throw new LeaseAcquisitionError(`failed to create the '${namespace}' namespace`)
73-
}
74-
}
98+
if (!deploymentNamespace && !clusterSetupNamespace) {
99+
return null
100+
}
101+
const namespace = deploymentNamespace ? deploymentNamespace : clusterSetupNamespace
102+
103+
if (!await this.k8.hasNamespace(namespace)) {
104+
await this.k8.createNamespace(namespace)
75105

76-
return namespace
77-
}
106+
if (!await this.k8.hasNamespace(namespace)) {
107+
throw new LeaseAcquisitionError(`failed to create the '${namespace}' namespace`)
108+
}
109+
}
110+
111+
return namespace
112+
}
78113
}

src/core/lease/lease_renewal.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,79 @@
1717
import { type Lease } from './lease.ts'
1818
import { SECONDS } from '../constants.ts'
1919

20+
/**
21+
* A service for managing cancellable lease renewals.
22+
*/
2023
export interface LeaseRenewalService {
24+
/**
25+
* Determines if a lease renewal is scheduled.
26+
* @param scheduleId - the unique identifier of the scheduled lease renewal.
27+
* @returns true if the lease renewal is scheduled; false otherwise.
28+
*/
2129
isScheduled (scheduleId: number): Promise<boolean>
30+
31+
/**
32+
* Schedules a lease renewal.
33+
* @param lease - the lease to be renewed.
34+
* @returns the unique identifier of the scheduled lease renewal.
35+
*/
2236
schedule (lease: Lease): Promise<number>
37+
38+
/**
39+
* Cancels a scheduled lease renewal.
40+
* @param scheduleId - the unique identifier of the scheduled lease renewal.
41+
* @returns true if the lease renewal was successfully cancelled; false otherwise.
42+
*/
2343
cancel (scheduleId: number): Promise<boolean>
44+
45+
/**
46+
* Cancels all scheduled lease renewals.
47+
* @returns a map of the unique identifiers of the scheduled lease renewals and their cancellation status.
48+
*/
2449
cancelAll (): Promise<Map<number, boolean>>
50+
51+
/**
52+
* Calculates the delay before the next lease renewal.
53+
* @param lease - the lease to be renewed.
54+
* @returns the delay in milliseconds.
55+
*/
2556
calculateRenewalDelay (lease: Lease): number
2657
}
2758

59+
/**
60+
* Implements a lease renewal service which utilizes a setInterval() based approach to renew leases at regular intervals.
61+
* The renewal delay is calculated as half the duration of the lease in seconds.
62+
*/
2863
export class IntervalLeaseRenewalService implements LeaseRenewalService {
64+
65+
/** The internal registry used to track all non-cancelled lease renewals. */
2966
private readonly _scheduledLeases: Map<number, Lease>
3067

68+
/**
69+
* Constructs a new interval lease renewal service.
70+
*/
3171
constructor () {
3272
this._scheduledLeases = new Map<number, Lease>()
3373
}
3474

75+
/**
76+
* Determines if a lease renewal is scheduled.
77+
* This implementation uses the internal registry to track all non-cancelled lease renewals.
78+
*
79+
* @param scheduleId - the unique identifier of the scheduled lease renewal.
80+
* @returns true if the lease renewal is scheduled; false otherwise.
81+
*/
3582
public async isScheduled (scheduleId: number): Promise<boolean> {
3683
return this._scheduledLeases.has(scheduleId)
3784
}
3885

86+
/**
87+
* Schedules a lease renewal.
88+
* This implementation uses the setInterval() method to renew the lease at regular intervals.
89+
*
90+
* @param lease - the lease to be renewed.
91+
* @returns the unique identifier of the scheduled lease renewal. The unique identifier is the ID of the setInterval() timeout.
92+
*/
3993
public async schedule (lease: Lease): Promise<number> {
4094
const renewalDelay = this.calculateRenewalDelay(lease)
4195
const timeout = setInterval(() => lease.tryRenew(), renewalDelay)
@@ -45,6 +99,15 @@ export class IntervalLeaseRenewalService implements LeaseRenewalService {
4599
return scheduleId
46100
}
47101

102+
/**
103+
* Cancels a scheduled lease renewal.
104+
* This implementation uses the clearInterval() method to cancel the scheduled lease renewal.
105+
* Due to the nature of the setInterval()/clearInterval() methods, the scheduled event may still fire at least once
106+
* after the cancellation.
107+
*
108+
* @param scheduleId - the unique identifier of the scheduled lease renewal. The unique identifier is the ID of the setInterval() timeout.
109+
* @returns true if the lease renewal was previously scheduled; false otherwise.
110+
*/
48111
public async cancel (scheduleId: number): Promise<boolean> {
49112
if (!scheduleId) return false
50113

@@ -55,6 +118,11 @@ export class IntervalLeaseRenewalService implements LeaseRenewalService {
55118
return this._scheduledLeases.delete(scheduleId)
56119
}
57120

121+
/**
122+
* Cancels all scheduled lease renewals.
123+
* This implementation cancels all scheduled lease renewals by iterating over the internal registry and clearing each timeout.
124+
* @returns a map of the unique identifiers of the scheduled lease renewals and their cancellation status.
125+
*/
58126
public async cancelAll (): Promise<Map<number, boolean>> {
59127
const result = new Map<number, boolean>()
60128
const keys = Array.from(this._scheduledLeases.keys())
@@ -66,6 +134,13 @@ export class IntervalLeaseRenewalService implements LeaseRenewalService {
66134
return result
67135
}
68136

137+
/**
138+
* Calculates the delay before the next lease renewal.
139+
* This implementation calculates the renewal delay as half the duration of the lease.
140+
*
141+
* @param lease - the lease to be renewed.
142+
* @returns the delay in milliseconds.
143+
*/
69144
public calculateRenewalDelay (lease: Lease): number {
70145
return Math.round(lease.durationSeconds * 0.5) * SECONDS
71146
}

0 commit comments

Comments
 (0)