-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2b325b8
commit 7a00df0
Showing
3 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import core = require('oci-core'); | ||
import common = require('oci-common'); | ||
import { InstanceGroup } from './instance_group'; | ||
import { Context } from './context'; | ||
import { CloudRetryStrategy } from './cloud_manager'; | ||
import { CloudInstanceManager, CloudInstance } from './cloud_instance_manager'; | ||
import { workrequests } from 'oci-sdk'; | ||
|
||
const maxTimeInSeconds = 60 * 60; // The duration for waiter configuration before failing. Currently set to 1 hour. | ||
const maxDelayInSeconds = 30; // The max delay for the waiter configuration. Currently set to 30 seconds | ||
|
||
const waiterConfiguration: common.WaiterConfiguration = { | ||
terminationStrategy: new common.MaxTimeTerminationStrategy(maxTimeInSeconds), | ||
delayStrategy: new common.ExponentialBackoffDelayStrategy(maxDelayInSeconds), | ||
}; | ||
|
||
export interface OracleInstancePoolManagerOptions { | ||
isDryRun: boolean; | ||
ociConfigurationFilePath: string; | ||
ociConfigurationProfile: string; | ||
} | ||
|
||
export default class OracleInstancePoolManager implements CloudInstanceManager { | ||
private isDryRun: boolean; | ||
private provider: common.ConfigFileAuthenticationDetailsProvider; | ||
private computeManagementClient: core.ComputeManagementClient; | ||
private workRequestClient: workrequests.WorkRequestClient; | ||
|
||
constructor(options: OracleInstancePoolManagerOptions) { | ||
this.isDryRun = options.isDryRun; | ||
this.provider = new common.ConfigFileAuthenticationDetailsProvider( | ||
options.ociConfigurationFilePath, | ||
options.ociConfigurationProfile, | ||
); | ||
this.computeManagementClient = new core.ComputeManagementClient({ | ||
authenticationDetailsProvider: this.provider, | ||
}); | ||
this.workRequestClient = new workrequests.WorkRequestClient({ | ||
authenticationDetailsProvider: this.provider, | ||
}); | ||
|
||
this.launchInstances = this.launchInstances.bind(this); | ||
} | ||
|
||
async detachInstance(ctx: Context, group: InstanceGroup, instance: string): Promise<void> { | ||
ctx.logger.info(`[oraclepool] Detaching instance ${instance}`); | ||
this.computeManagementClient.regionId = group.region; | ||
|
||
const cwaiter = this.computeManagementClient.createWaiters(this.workRequestClient, waiterConfiguration); | ||
const response = await cwaiter.forDetachInstancePoolInstance({ | ||
instancePoolId: group.instanceConfigurationId, | ||
detachInstancePoolInstanceDetails: { instanceId: instance }, | ||
}); | ||
ctx.logger.info(`[oraclepool] Finished detaching instance ${instance}`, { response }); | ||
} | ||
|
||
async launchInstances( | ||
ctx: Context, | ||
group: InstanceGroup, | ||
groupCurrentCount: number, | ||
quantity: number, | ||
): Promise<Array<string | boolean>> { | ||
ctx.logger.info(`[oraclepool] Launching a batch of ${quantity} instances in group ${group.name}`); | ||
|
||
this.computeManagementClient.regionId = group.region; | ||
const poolDetails = await this.computeManagementClient.getInstancePool({ | ||
instancePoolId: group.instanceConfigurationId, | ||
}); | ||
|
||
const poolInstances = await this.computeManagementClient.listInstancePoolInstances({ | ||
compartmentId: group.compartmentId, | ||
instancePoolId: group.instanceConfigurationId, | ||
}); | ||
|
||
const existingInstanceIds = poolInstances.items.map((instance) => { | ||
return instance.id; | ||
}); | ||
|
||
ctx.logger.debug(`[oraclepool] Instance pool ${group.name} instances`, { instances: poolInstances.items }); | ||
|
||
const newSize = quantity + groupCurrentCount; | ||
if (groupCurrentCount == poolDetails.instancePool.size) { | ||
ctx.logger.debug(`[oraclepool] Instance pool ${group.name} size matches current count`, { | ||
current: groupCurrentCount, | ||
size: poolDetails.instancePool.size, | ||
newSize, | ||
}); | ||
} else { | ||
ctx.logger.error(`[oraclepool] Instance pool ${group.name} size DOES NOT matches current count`, { | ||
current: groupCurrentCount, | ||
size: poolDetails.instancePool.size, | ||
newSize, | ||
}); | ||
} | ||
const updateResult = await this.computeManagementClient.updateInstancePool({ | ||
instancePoolId: group.instanceConfigurationId, | ||
updateInstancePoolDetails: { | ||
size: newSize, | ||
}, | ||
}); | ||
|
||
ctx.logger.info(`[oraclepool] Updated instance pool size for group ${group.name}`, { updateResult }); | ||
|
||
this.workRequestClient.regionId = group.region; | ||
const cwaiter = this.computeManagementClient.createWaiters(this.workRequestClient, waiterConfiguration); | ||
const runningPool = await cwaiter.forInstancePool( | ||
{ | ||
instancePoolId: group.instanceConfigurationId, | ||
}, | ||
core.models.InstancePool.LifecycleState.Running, | ||
); | ||
|
||
ctx.logger.info(`[oraclepool] Instance pool for ${group.name} back in running state`, { runningPool }); | ||
|
||
if (runningPool.instancePool.size == newSize) { | ||
ctx.logger.debug(`[oraclepool] Instance pool ${group.name} size matches new size`, { | ||
newSize, | ||
}); | ||
} else { | ||
ctx.logger.error(`[oraclepool] Instance pool ${group.name} size DOES NOT matches new size`, { | ||
newSize, | ||
}); | ||
} | ||
|
||
const newPoolInstances = await this.computeManagementClient.listInstancePoolInstances({ | ||
compartmentId: group.compartmentId, | ||
instancePoolId: group.instanceConfigurationId, | ||
}); | ||
|
||
const result = newPoolInstances.items | ||
.map((instance) => { | ||
return instance.id; | ||
}) | ||
.filter((instanceId) => { | ||
return !existingInstanceIds.includes(instanceId); | ||
}); | ||
|
||
ctx.logger.info(`[oraclepool] Finished launching all the instances in group ${group.name}`, { result }); | ||
|
||
return result; | ||
} | ||
|
||
async getInstances( | ||
ctx: Context, | ||
group: InstanceGroup, | ||
cloudRetryStrategy: CloudRetryStrategy, | ||
): Promise<Array<CloudInstance>> { | ||
const computeManagementClient = new core.ComputeManagementClient( | ||
{ | ||
authenticationDetailsProvider: this.provider, | ||
}, | ||
{ | ||
retryConfiguration: { | ||
terminationStrategy: new common.MaxTimeTerminationStrategy(cloudRetryStrategy.maxTimeInSeconds), | ||
delayStrategy: new common.ExponentialBackoffDelayStrategy(cloudRetryStrategy.maxDelayInSeconds), | ||
retryCondition: (response) => { | ||
return ( | ||
cloudRetryStrategy.retryableStatusCodes.filter((retryableStatusCode) => { | ||
return response.statusCode === retryableStatusCode; | ||
}).length > 0 | ||
); | ||
}, | ||
}, | ||
}, | ||
); | ||
computeManagementClient.regionId = group.region; | ||
|
||
const poolInstances = await computeManagementClient.listInstancePoolInstances({ | ||
compartmentId: group.compartmentId, | ||
instancePoolId: group.instanceConfigurationId, | ||
}); | ||
|
||
return poolInstances.items.map((instance) => { | ||
ctx.logger.debug('Found instance in oracle pool', { instance }); | ||
return { | ||
instanceId: instance.id, | ||
displayName: instance.displayName, | ||
cloudStatus: instance.state, | ||
}; | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
// @ts-nocheck | ||
|
||
import assert from 'node:assert'; | ||
import test, { afterEach, describe, mock } from 'node:test'; | ||
|
||
import OracleInstancePoolManager from '../oracle_instance_pool_manager'; | ||
|
||
function log(level, message, data) { | ||
console.log(`${Date.now()} ${level}: ${message}`); | ||
console.log(data); | ||
} | ||
|
||
describe('InstancePoolManager', () => { | ||
const manager = new OracleInstancePoolManager({ | ||
isDryRun: true, | ||
ociConfigurationFilePath: process.env.OCI_CONFIGURATION_FILE_PATH, | ||
ociConfigurationProfile: process.env.OCI_CONFIGURATION_PROFILE, | ||
}); | ||
const context = { | ||
logger: { | ||
debug: mock.fn(log.bind('debug')), | ||
info: mock.fn(log.bind('info')), | ||
error: mock.fn(log.bind('error')), | ||
}, | ||
}; | ||
|
||
afterEach(() => { | ||
mock.restoreAll(); | ||
}); | ||
|
||
describe('getInstances', () => { | ||
// This is a test for the getInstances method | ||
test('will call the correct endpoint', async () => { | ||
console.log('Starting getInstances test'); | ||
const instances = await manager.getInstances( | ||
context, | ||
{ | ||
name: 'group', | ||
region: process.env.REGION, | ||
compartmentId: process.env.COMPARTMENT_OCID, | ||
instanceConfigurationId: process.env.INSTANCE_POOL_ID, | ||
}, | ||
{ maxAttempts: 1, maxTimeInSeconds: 60, maxDelayInSeconds: 30, retryableStatusCodes: [404, 429] }, | ||
); | ||
console.log('ended getInstances test'); | ||
assert.ok(instances); | ||
console.log(instances); | ||
}); | ||
}); | ||
}); |