Skip to content

Commit ad3dcd7

Browse files
authored
Merge pull request #117 from fres-co/master
Allow custom cloud provider
2 parents 45042df + b5e23de commit ad3dcd7

16 files changed

Lines changed: 577 additions & 74 deletions

.eslintrc.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@ module.exports = {
22
root: true,
33
parser: '@typescript-eslint/parser',
44
env: {
5-
node: true,
5+
node: true,
66
},
77
parserOptions: {
8-
ecmaVersion: 2020,
9-
sourceType: "module",
8+
ecmaVersion: 2020,
9+
sourceType: 'module',
1010
},
11-
plugins: [
12-
'@typescript-eslint',
13-
],
11+
plugins: ['@typescript-eslint'],
1412
extends: [
15-
'eslint:recommended',
16-
'plugin:@typescript-eslint/recommended',
17-
"prettier/@typescript-eslint",
18-
"plugin:prettier/recommended",
13+
'eslint:recommended',
14+
'plugin:@typescript-eslint/recommended',
15+
'prettier/@typescript-eslint',
16+
'plugin:prettier/recommended',
1917
],
20-
};
18+
rules: {
19+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
20+
},
21+
};

digitalOcean.config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"jvb": {
3+
"size": "s-2vcpu-4gb",
4+
"image": "INSERT_HERE_YOUR_SNAPSHOT_ID",
5+
"ssh_keys": [],
6+
"backups": false,
7+
"ipv6": false,
8+
"tags": ["test", "jisti", "jvb"]
9+
}
10+
}

groups-custom.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"groupEntries": [
3+
{
4+
"name": "custom-bridgeGroup1",
5+
"type": "jvb",
6+
"region": "FRA1",
7+
"environment": "lonely",
8+
"compartmentId": "",
9+
"instanceConfigurationId": "",
10+
"enableAutoScale": true,
11+
"enableLaunch": true,
12+
"gracePeriodTTLSec": 480,
13+
"protectedTTLSec": 600,
14+
"scalingOptions": {
15+
"minDesired": 1,
16+
"maxDesired": 2,
17+
"desiredCount": 2,
18+
"scaleUpQuantity": 1,
19+
"scaleDownQuantity": 1,
20+
"scaleUpThreshold": 1,
21+
"scaleDownThreshold": 2,
22+
"scalePeriod": 60,
23+
"scaleUpPeriodsCount": 2,
24+
"scaleDownPeriodsCount": 4
25+
},
26+
"cloud": "custom"
27+
}
28+
]
29+
}

groups-digitalocean.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"groupEntries": [
3+
{
4+
"name": "custom-bridgeGroup1",
5+
"type": "jvb",
6+
"region": "FRA1",
7+
"environment": "lonely",
8+
"compartmentId": "",
9+
"instanceConfigurationId": "",
10+
"enableAutoScale": true,
11+
"enableLaunch": true,
12+
"gracePeriodTTLSec": 480,
13+
"protectedTTLSec": 600,
14+
"scalingOptions": {
15+
"minDesired": 1,
16+
"maxDesired": 2,
17+
"desiredCount": 2,
18+
"scaleUpQuantity": 1,
19+
"scaleDownQuantity": 1,
20+
"scaleUpThreshold": 1,
21+
"scaleDownThreshold": 2,
22+
"scalePeriod": 60,
23+
"scaleUpPeriodsCount": 2,
24+
"scaleDownPeriodsCount": 4
25+
},
26+
"cloud": "digitalocean"
27+
}
28+
]
29+
}

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@types/sshpk": "^1.10.4",
1717
"bee-queue": "^1.2.3",
1818
"dotenv": "^8.2.0",
19+
"dots-wrapper": "^3.5.7",
1920
"envalid": "^6.0.2",
2021
"express": "^4.17.1",
2122
"express-jwt": "^6.0.0",

scripts/custom-launch.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
3+
echo "Launching new instance"
4+
5+
# the script should end by outputing the new instance id
6+
date +%s

src/app.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,15 @@ const cloudManager = new CloudManager({
9898
isDryRun: config.DryRun,
9999
ociConfigurationFilePath: config.OciConfigurationFilePath,
100100
ociConfigurationProfile: config.OciConfigurationProfile,
101+
digitalOceanAPIToken: config.DigitalOceanAPIToken,
102+
digitalOceanConfigurationFilePath: config.DigitalOceanConfigurationFilePath,
103+
101104
instanceTracker: instanceTracker,
102105
audit: audit,
106+
cloudProviders: config.CloudProviders,
107+
108+
customConfigurationLaunchScriptPath: config.CustomConfigurationLaunchScriptPath,
109+
customConfigurationLaunchScriptTimeoutMs: config.CustomConfigurationLaunchScriptTimeoutMs,
103110
});
104111

105112
const lockManager: LockManager = new LockManager(logger, {

src/cloud_instance_manager.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { InstanceGroup } from './instance_group';
2+
import { Context } from './context';
3+
import { CloudRetryStrategy } from './cloud_manager';
4+
5+
export interface CloudInstance {
6+
instanceId: string;
7+
displayName: string;
8+
cloudStatus: string;
9+
}
10+
11+
export interface CloudInstanceManager {
12+
launchInstances(
13+
ctx: Context,
14+
group: InstanceGroup,
15+
groupCurrentCount: number,
16+
quantity: number,
17+
): Promise<Array<string | boolean>>;
18+
19+
getInstances(ctx: Context, group: InstanceGroup, cloudRetryStrategy: CloudRetryStrategy): Promise<CloudInstance[]>;
20+
}
21+
22+
export abstract class AbstractCloudInstanceManager implements CloudInstanceManager {
23+
static makeRandomString(length: number): string {
24+
let result = '';
25+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
26+
const charactersLength = characters.length;
27+
for (let i = 0; i < length; i++) {
28+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
29+
}
30+
return result;
31+
}
32+
33+
async launchInstances(
34+
ctx: Context,
35+
group: InstanceGroup,
36+
groupCurrentCount: number,
37+
quantity: number,
38+
): Promise<Array<string | boolean>> {
39+
ctx.logger.info(`[CloudInstanceManager] Launching a batch of ${quantity} instances in group ${group.name}`);
40+
41+
const indexes: Array<number> = [];
42+
for (let i = 0; i < quantity; i++) {
43+
indexes.push(i);
44+
}
45+
46+
const result = await Promise.all(
47+
indexes.map(async (index) => {
48+
return this.launchInstance(ctx, index, group);
49+
}),
50+
);
51+
ctx.logger.info(`Finished launching all the instances in group ${group.name}`);
52+
53+
return result;
54+
}
55+
56+
abstract launchInstance(ctx: Context, index: number, group: InstanceGroup): Promise<string | boolean>;
57+
58+
async getInstances(
59+
_ctx: Context,
60+
_group: InstanceGroup,
61+
_cloudRetryStrategy: CloudRetryStrategy,
62+
): Promise<CloudInstance[]> {
63+
return [];
64+
}
65+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import OracleInstanceManager from './oracle_instance_manager';
2+
import CustomInstanceManager from './custom_instance_manager';
3+
import DigitalOceanInstanceManager from './digital_ocean_instance_manager';
4+
import { CloudInstanceManager } from './cloud_instance_manager';
5+
6+
export interface CloudInstanceManagerSelectorOptions {
7+
cloudProviders: string[];
8+
isDryRun: boolean;
9+
ociConfigurationFilePath: string;
10+
ociConfigurationProfile: string;
11+
12+
digitalOceanAPIToken: string;
13+
digitalOceanConfigurationFilePath: string;
14+
15+
customConfigurationLaunchScriptPath: string;
16+
customConfigurationLaunchScriptTimeoutMs: number;
17+
}
18+
19+
export class CloudInstanceManagerSelector {
20+
private oracleInstanceManager: OracleInstanceManager;
21+
private digitalOceanInstanceManager: DigitalOceanInstanceManager;
22+
private customInstanceManager: CustomInstanceManager;
23+
24+
constructor(options: CloudInstanceManagerSelectorOptions) {
25+
if (options.cloudProviders.includes('oracle')) {
26+
this.oracleInstanceManager = new OracleInstanceManager({
27+
isDryRun: options.isDryRun,
28+
ociConfigurationFilePath: options.ociConfigurationFilePath,
29+
ociConfigurationProfile: options.ociConfigurationProfile,
30+
});
31+
}
32+
33+
if (options.cloudProviders.includes('custom')) {
34+
this.customInstanceManager = new CustomInstanceManager({
35+
isDryRun: options.isDryRun,
36+
customConfigurationLaunchScriptPath: options.customConfigurationLaunchScriptPath,
37+
customConfigurationLaunchScriptTimeoutMs: options.customConfigurationLaunchScriptTimeoutMs,
38+
});
39+
}
40+
if (options.cloudProviders.includes('digitalocean')) {
41+
this.digitalOceanInstanceManager = new DigitalOceanInstanceManager({
42+
isDryRun: options.isDryRun,
43+
digitalOceanAPIToken: options.digitalOceanAPIToken,
44+
digitalOceanConfigurationFilePath: options.digitalOceanConfigurationFilePath,
45+
});
46+
}
47+
}
48+
49+
selectInstanceManager(cloud: string): CloudInstanceManager {
50+
switch (cloud) {
51+
case 'oracle':
52+
return this.oracleInstanceManager;
53+
case 'digitalocean':
54+
return this.digitalOceanInstanceManager;
55+
case 'custom':
56+
return this.customInstanceManager;
57+
default:
58+
return null;
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)