feat: add platform desired state manager
This commit is contained in:
@@ -30,6 +30,7 @@ import { DomainManager } from './manager.domain/classes.domainmanager.js';
|
||||
import { logger } from './logger.js';
|
||||
import { CloudlyAuthManager } from './manager.auth/classes.authmanager.js';
|
||||
import { CloudlySettingsManager } from './manager.settings/classes.settingsmanager.js';
|
||||
import { CloudlyPlatformManager } from './manager.platform/classes.platformmanager.js';
|
||||
|
||||
/**
|
||||
* Cloudly class can be used to instantiate a cloudly server.
|
||||
@@ -59,6 +60,7 @@ export class Cloudly {
|
||||
public authManager: CloudlyAuthManager;
|
||||
public secretManager: CloudlySecretManager;
|
||||
public settingsManager: CloudlySettingsManager;
|
||||
public platformManager: CloudlyPlatformManager;
|
||||
public clusterManager: ClusterManager;
|
||||
public coreflowManager: CloudlyCoreflowManager;
|
||||
public externalApiManager: ExternalApiManager;
|
||||
@@ -92,6 +94,7 @@ export class Cloudly {
|
||||
// managers
|
||||
this.authManager = new CloudlyAuthManager(this);
|
||||
this.settingsManager = new CloudlySettingsManager(this);
|
||||
this.platformManager = new CloudlyPlatformManager(this);
|
||||
this.clusterManager = new ClusterManager(this);
|
||||
this.coreflowManager = new CloudlyCoreflowManager(this);
|
||||
this.externalApiManager = new ExternalApiManager(this);
|
||||
@@ -127,6 +130,7 @@ export class Cloudly {
|
||||
await this.nodeManager.start();
|
||||
await this.baremetalManager.start();
|
||||
await this.serviceManager.start();
|
||||
await this.platformManager.start();
|
||||
await this.deploymentManager.start();
|
||||
await this.taskManager.init();
|
||||
|
||||
@@ -150,6 +154,7 @@ export class Cloudly {
|
||||
await this.mongodbConnector.stop();
|
||||
await this.secretManager.stop();
|
||||
await this.serviceManager.stop();
|
||||
await this.platformManager.stop();
|
||||
await this.deploymentManager.stop();
|
||||
await this.taskManager.stop();
|
||||
await this.externalRegistryManager.stop();
|
||||
|
||||
@@ -72,10 +72,14 @@ export class CloudlyCoreflowManager {
|
||||
console.log('trying to get clusterConfigSet');
|
||||
console.log(dataArg);
|
||||
const cluster = await this.cloudlyRef.clusterManager.getClusterBy_Identity(identity);
|
||||
const services = await this.cloudlyRef.serviceManager.CService.getInstances({});
|
||||
const platformDesiredState = await this.cloudlyRef.platformManager.getPlatformDesiredState();
|
||||
console.log('got cluster config and sending it back to coreflow');
|
||||
return {
|
||||
configData: await cluster.createSavableObject(),
|
||||
services: [],
|
||||
services: await Promise.all(services.map((service) => service.createSavableObject())),
|
||||
platformProviderConfigs: platformDesiredState.providerConfigs,
|
||||
platformBindings: platformDesiredState.bindings,
|
||||
};
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import type { CloudlyPlatformManager } from './classes.platformmanager.js';
|
||||
|
||||
@plugins.smartdata.managed()
|
||||
export class PlatformBinding extends plugins.smartdata.SmartDataDbDoc<
|
||||
PlatformBinding,
|
||||
plugins.servezoneInterfaces.platform.IPlatformBinding,
|
||||
CloudlyPlatformManager
|
||||
> {
|
||||
public static async upsertBinding(
|
||||
bindingArg: plugins.servezoneInterfaces.platform.IPlatformBinding,
|
||||
) {
|
||||
const existingBinding =
|
||||
bindingArg.id &&
|
||||
(await this.getInstance({
|
||||
id: bindingArg.id,
|
||||
}));
|
||||
const binding = existingBinding || new PlatformBinding();
|
||||
const timestamp = Date.now();
|
||||
|
||||
Object.assign(binding, {
|
||||
...bindingArg,
|
||||
id: bindingArg.id || (await this.getNewId()),
|
||||
status: bindingArg.status || 'requested',
|
||||
desiredState: bindingArg.desiredState || 'enabled',
|
||||
createdAt: bindingArg.createdAt || existingBinding?.createdAt || timestamp,
|
||||
updatedAt: timestamp,
|
||||
});
|
||||
await binding.save();
|
||||
return binding;
|
||||
}
|
||||
|
||||
@plugins.smartdata.unI()
|
||||
public id!: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public serviceId!: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public capability!: plugins.servezoneInterfaces.platform.TPlatformCapability;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public desiredState!: plugins.servezoneInterfaces.platform.TPlatformDesiredState;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public status!: plugins.servezoneInterfaces.platform.TPlatformBindingStatus;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public providerConfigId?: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public config?: { [key: string]: plugins.servezoneInterfaces.platform.TPlatformConfigValue };
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public endpoints?: plugins.servezoneInterfaces.platform.IPlatformServiceEndpoint[];
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public credentials?: plugins.servezoneInterfaces.platform.IPlatformCredentialRef[];
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public createdAt?: number;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public updatedAt?: number;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public errorText?: string;
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
import type { Cloudly } from '../classes.cloudly.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { PlatformBinding } from './classes.platformbinding.js';
|
||||
import { PlatformProviderConfig } from './classes.platformproviderconfig.js';
|
||||
|
||||
export class CloudlyPlatformManager {
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
public cloudlyRef: Cloudly;
|
||||
|
||||
public capabilities: plugins.servezoneInterfaces.platform.IPlatformCapability[] = [
|
||||
{ id: 'email', title: 'Email', accessMode: 'rpc', defaultProviderType: 'cloudly' },
|
||||
{ id: 'sms', title: 'SMS', accessMode: 'rpc', defaultProviderType: 'cloudly' },
|
||||
{ id: 'pushnotification', title: 'Push Notifications', accessMode: 'rpc', defaultProviderType: 'cloudly' },
|
||||
{ id: 'letter', title: 'Letters', accessMode: 'rpc', defaultProviderType: 'cloudly' },
|
||||
{ id: 'ai', title: 'AI', accessMode: 'rpc', defaultProviderType: 'cloudly' },
|
||||
{ id: 'database', title: 'Database', accessMode: 'binding', defaultProviderType: 'docker' },
|
||||
{ id: 'objectstorage', title: 'Object Storage', accessMode: 'binding', defaultProviderType: 's3' },
|
||||
{ id: 'logging', title: 'Logging', accessMode: 'sidecar', defaultProviderType: 'corelog' },
|
||||
{ id: 'backup', title: 'Backup', accessMode: 'internal', defaultProviderType: 'corebackup' },
|
||||
{ id: 'sip', title: 'SIP', accessMode: 'rpc', defaultProviderType: 'cloudly' },
|
||||
];
|
||||
|
||||
get db() {
|
||||
return this.cloudlyRef.mongodbConnector.smartdataDb;
|
||||
}
|
||||
|
||||
public CPlatformProviderConfig = plugins.smartdata.setDefaultManagerForDoc(
|
||||
this,
|
||||
PlatformProviderConfig,
|
||||
);
|
||||
public CPlatformBinding = plugins.smartdata.setDefaultManagerForDoc(this, PlatformBinding);
|
||||
|
||||
constructor(cloudlyRefArg: Cloudly) {
|
||||
this.cloudlyRef = cloudlyRefArg;
|
||||
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_GetPlatformDesiredState>(
|
||||
'getPlatformDesiredState',
|
||||
async (requestData) => {
|
||||
await this.passValidIdentity(requestData);
|
||||
return await this.getPlatformDesiredState();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_GetPlatformCapabilities>(
|
||||
'getPlatformCapabilities',
|
||||
async (requestData) => {
|
||||
await this.passValidIdentity(requestData);
|
||||
return {
|
||||
capabilities: this.capabilities,
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_GetPlatformProviderConfigs>(
|
||||
'getPlatformProviderConfigs',
|
||||
async (requestData) => {
|
||||
await this.passValidIdentity(requestData);
|
||||
const query = requestData.capability ? { capability: requestData.capability } : {};
|
||||
return {
|
||||
providerConfigs: await this.getProviderConfigs(query),
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_UpsertPlatformProviderConfig>(
|
||||
'upsertPlatformProviderConfig',
|
||||
async (requestData) => {
|
||||
await this.passAdminIdentity(requestData);
|
||||
const providerConfig = await PlatformProviderConfig.upsertProviderConfig(
|
||||
requestData.providerConfig,
|
||||
);
|
||||
return {
|
||||
providerConfig: await providerConfig.createSavableObject(),
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_DeletePlatformProviderConfigById>(
|
||||
'deletePlatformProviderConfigById',
|
||||
async (requestData) => {
|
||||
await this.passAdminIdentity(requestData);
|
||||
const providerConfig = await PlatformProviderConfig.getInstance({
|
||||
id: requestData.providerConfigId,
|
||||
});
|
||||
if (providerConfig) {
|
||||
await providerConfig.delete();
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_GetPlatformBindings>(
|
||||
'getPlatformBindings',
|
||||
async (requestData) => {
|
||||
await this.passValidIdentity(requestData);
|
||||
return {
|
||||
bindings: await this.getBindings({
|
||||
...(requestData.serviceId ? { serviceId: requestData.serviceId } : {}),
|
||||
...(requestData.capability ? { capability: requestData.capability } : {}),
|
||||
}),
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_UpsertPlatformBinding>(
|
||||
'upsertPlatformBinding',
|
||||
async (requestData) => {
|
||||
await this.passAdminIdentity(requestData);
|
||||
const binding = await PlatformBinding.upsertBinding(requestData.binding);
|
||||
return {
|
||||
binding: await binding.createSavableObject(),
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_UpdatePlatformBindingStatus>(
|
||||
'updatePlatformBindingStatus',
|
||||
async (requestData) => {
|
||||
await this.passAdminOrClusterIdentity(requestData);
|
||||
const binding = await PlatformBinding.getInstance({
|
||||
id: requestData.bindingId,
|
||||
});
|
||||
if (!binding) {
|
||||
throw new plugins.typedrequest.TypedResponseError(
|
||||
`Platform binding ${requestData.bindingId} not found`,
|
||||
);
|
||||
}
|
||||
binding.status = requestData.status;
|
||||
binding.updatedAt = Date.now();
|
||||
if (requestData.endpoints) {
|
||||
binding.endpoints = requestData.endpoints;
|
||||
}
|
||||
if (requestData.credentials) {
|
||||
binding.credentials = requestData.credentials;
|
||||
}
|
||||
if (requestData.errorText !== undefined) {
|
||||
binding.errorText = requestData.errorText;
|
||||
}
|
||||
await binding.save();
|
||||
return {
|
||||
binding: await binding.createSavableObject(),
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.platform.IReq_Any_Cloudly_DeletePlatformBindingById>(
|
||||
'deletePlatformBindingById',
|
||||
async (requestData) => {
|
||||
await this.passAdminIdentity(requestData);
|
||||
const binding = await PlatformBinding.getInstance({
|
||||
id: requestData.bindingId,
|
||||
});
|
||||
if (binding) {
|
||||
await binding.delete();
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public async start() {}
|
||||
|
||||
public async stop() {}
|
||||
|
||||
public async getPlatformDesiredState() {
|
||||
return {
|
||||
capabilities: this.capabilities,
|
||||
providerConfigs: await this.getProviderConfigs(),
|
||||
bindings: await this.getBindings(),
|
||||
};
|
||||
}
|
||||
|
||||
public async getProviderConfigs(queryArg: Record<string, unknown> = {}) {
|
||||
const providerConfigs = await this.CPlatformProviderConfig.getInstances(queryArg);
|
||||
return await Promise.all(
|
||||
providerConfigs.map((providerConfig) => providerConfig.createSavableObject()),
|
||||
);
|
||||
}
|
||||
|
||||
public async getBindings(queryArg: Record<string, unknown> = {}) {
|
||||
const bindings = await this.CPlatformBinding.getInstances(queryArg);
|
||||
return await Promise.all(bindings.map((binding) => binding.createSavableObject()));
|
||||
}
|
||||
|
||||
private async passValidIdentity(requestData: { identity: plugins.servezoneInterfaces.data.IIdentity }) {
|
||||
await plugins.smartguard.passGuardsOrReject(requestData, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
}
|
||||
|
||||
private async passAdminIdentity(requestData: { identity: plugins.servezoneInterfaces.data.IIdentity }) {
|
||||
await plugins.smartguard.passGuardsOrReject(requestData, [
|
||||
this.cloudlyRef.authManager.adminIdentityGuard,
|
||||
]);
|
||||
}
|
||||
|
||||
private async passAdminOrClusterIdentity(requestData: {
|
||||
identity: plugins.servezoneInterfaces.data.IIdentity;
|
||||
}) {
|
||||
await this.passValidIdentity(requestData);
|
||||
if (requestData.identity.role !== 'admin' && requestData.identity.role !== 'cluster') {
|
||||
throw new plugins.typedrequest.TypedResponseError('identity must be admin or cluster');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import type { CloudlyPlatformManager } from './classes.platformmanager.js';
|
||||
|
||||
@plugins.smartdata.managed()
|
||||
export class PlatformProviderConfig extends plugins.smartdata.SmartDataDbDoc<
|
||||
PlatformProviderConfig,
|
||||
plugins.servezoneInterfaces.platform.IPlatformProviderConfig,
|
||||
CloudlyPlatformManager
|
||||
> {
|
||||
public static async upsertProviderConfig(
|
||||
providerConfigArg: plugins.servezoneInterfaces.platform.IPlatformProviderConfig,
|
||||
) {
|
||||
const providerConfig =
|
||||
(providerConfigArg.id &&
|
||||
(await this.getInstance({
|
||||
id: providerConfigArg.id,
|
||||
}))) || new PlatformProviderConfig();
|
||||
|
||||
Object.assign(providerConfig, {
|
||||
...providerConfigArg,
|
||||
id: providerConfigArg.id || (await this.getNewId()),
|
||||
});
|
||||
await providerConfig.save();
|
||||
return providerConfig;
|
||||
}
|
||||
|
||||
@plugins.smartdata.unI()
|
||||
public id!: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public capability!: plugins.servezoneInterfaces.platform.TPlatformCapability;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public providerType!: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public name!: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public enabled!: boolean;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public config?: { [key: string]: plugins.servezoneInterfaces.platform.TPlatformConfigValue };
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public secretBundleId?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user