feat(deployment): Implement Deployment and DeploymentManager classes with CRUD operations and service integration
This commit is contained in:
97
ts/manager.deployment/classes.deployment.ts
Normal file
97
ts/manager.deployment/classes.deployment.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
|
||||
export class Deployment extends plugins.smartdata.SmartDataDbDoc<
|
||||
Deployment,
|
||||
plugins.servezoneInterfaces.data.IDeployment
|
||||
> {
|
||||
@plugins.smartdata.unI()
|
||||
public id: string = plugins.smartunique.uniSimple('deployment');
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public serviceId: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public nodeId: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public containerId?: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public usedImageId: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public version: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public deployedAt: number;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public deploymentLog: string[] = [];
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public status: 'scheduled' | 'starting' | 'running' | 'stopping' | 'stopped' | 'failed';
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public healthStatus?: 'healthy' | 'unhealthy' | 'unknown';
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public resourceUsage?: {
|
||||
cpuUsagePercent: number;
|
||||
memoryUsedMB: number;
|
||||
lastUpdated: number;
|
||||
};
|
||||
|
||||
public static async createDeployment(
|
||||
deploymentData: Partial<plugins.servezoneInterfaces.data.IDeployment>
|
||||
): Promise<Deployment> {
|
||||
const deployment = new Deployment();
|
||||
if (deploymentData.serviceId) deployment.serviceId = deploymentData.serviceId;
|
||||
if (deploymentData.nodeId) deployment.nodeId = deploymentData.nodeId;
|
||||
if (deploymentData.containerId) deployment.containerId = deploymentData.containerId;
|
||||
if (deploymentData.usedImageId) deployment.usedImageId = deploymentData.usedImageId;
|
||||
if (deploymentData.version) deployment.version = deploymentData.version;
|
||||
if (deploymentData.deployedAt) deployment.deployedAt = deploymentData.deployedAt;
|
||||
if (deploymentData.deploymentLog) deployment.deploymentLog = deploymentData.deploymentLog;
|
||||
if (deploymentData.status) deployment.status = deploymentData.status;
|
||||
if (deploymentData.healthStatus) deployment.healthStatus = deploymentData.healthStatus;
|
||||
if (deploymentData.resourceUsage) deployment.resourceUsage = deploymentData.resourceUsage;
|
||||
|
||||
await deployment.save();
|
||||
return deployment;
|
||||
}
|
||||
|
||||
public async updateHealthStatus(healthStatus: 'healthy' | 'unhealthy' | 'unknown') {
|
||||
this.healthStatus = healthStatus;
|
||||
await this.save();
|
||||
}
|
||||
|
||||
public async updateResourceUsage(cpuUsagePercent: number, memoryUsedMB: number) {
|
||||
this.resourceUsage = {
|
||||
cpuUsagePercent,
|
||||
memoryUsedMB,
|
||||
lastUpdated: Date.now(),
|
||||
};
|
||||
await this.save();
|
||||
}
|
||||
|
||||
public async addLogEntry(entry: string) {
|
||||
this.deploymentLog.push(entry);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
public async createSavableObject(): Promise<plugins.servezoneInterfaces.data.IDeployment> {
|
||||
return {
|
||||
id: this.id,
|
||||
serviceId: this.serviceId,
|
||||
nodeId: this.nodeId,
|
||||
containerId: this.containerId,
|
||||
usedImageId: this.usedImageId,
|
||||
version: this.version,
|
||||
deployedAt: this.deployedAt,
|
||||
deploymentLog: this.deploymentLog,
|
||||
status: this.status,
|
||||
healthStatus: this.healthStatus,
|
||||
resourceUsage: this.resourceUsage,
|
||||
};
|
||||
}
|
||||
}
|
303
ts/manager.deployment/classes.deploymentmanager.ts
Normal file
303
ts/manager.deployment/classes.deploymentmanager.ts
Normal file
@@ -0,0 +1,303 @@
|
||||
import type { Cloudly } from '../classes.cloudly.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { Deployment } from './classes.deployment.js';
|
||||
|
||||
export class DeploymentManager {
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
public cloudlyRef: Cloudly;
|
||||
|
||||
get db() {
|
||||
return this.cloudlyRef.mongodbConnector.smartdataDb;
|
||||
}
|
||||
|
||||
public CDeployment = plugins.smartdata.setDefaultManagerForDoc(this, Deployment);
|
||||
|
||||
constructor(cloudlyRef: Cloudly) {
|
||||
this.cloudlyRef = cloudlyRef;
|
||||
|
||||
// Connect typedrouter to main router
|
||||
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||
|
||||
// Get all deployments
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_GetDeployments>(
|
||||
'getDeployments',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployments = await this.CDeployment.getInstances({});
|
||||
|
||||
return {
|
||||
deployments: await Promise.all(
|
||||
deployments.map((deployment) => deployment.createSavableObject())
|
||||
),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Get deployment by ID
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_GetDeploymentById>(
|
||||
'getDeploymentById',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployment = await this.CDeployment.getInstance({
|
||||
id: reqArg.deploymentId,
|
||||
});
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
|
||||
return {
|
||||
deployment: await deployment.createSavableObject(),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Get deployments by service
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_GetDeploymentsByService>(
|
||||
'getDeploymentsByService',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployments = await this.CDeployment.getInstances({
|
||||
serviceId: reqArg.serviceId,
|
||||
});
|
||||
|
||||
return {
|
||||
deployments: await Promise.all(
|
||||
deployments.map((deployment) => deployment.createSavableObject())
|
||||
),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Get deployments by node
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_GetDeploymentsByNode>(
|
||||
'getDeploymentsByNode',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployments = await this.CDeployment.getInstances({
|
||||
nodeId: reqArg.nodeId,
|
||||
});
|
||||
|
||||
return {
|
||||
deployments: await Promise.all(
|
||||
deployments.map((deployment) => deployment.createSavableObject())
|
||||
),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Create deployment
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_CreateDeployment>(
|
||||
'createDeployment',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployment = await Deployment.createDeployment(reqArg.deploymentData);
|
||||
|
||||
return {
|
||||
deployment: await deployment.createSavableObject(),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Update deployment
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_UpdateDeployment>(
|
||||
'updateDeployment',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployment = await this.CDeployment.getInstance({
|
||||
id: reqArg.deploymentId,
|
||||
});
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if (reqArg.deploymentData.status !== undefined) {
|
||||
deployment.status = reqArg.deploymentData.status;
|
||||
}
|
||||
if (reqArg.deploymentData.healthStatus !== undefined) {
|
||||
deployment.healthStatus = reqArg.deploymentData.healthStatus;
|
||||
}
|
||||
if (reqArg.deploymentData.containerId !== undefined) {
|
||||
deployment.containerId = reqArg.deploymentData.containerId;
|
||||
}
|
||||
if (reqArg.deploymentData.resourceUsage !== undefined) {
|
||||
deployment.resourceUsage = reqArg.deploymentData.resourceUsage;
|
||||
}
|
||||
|
||||
await deployment.save();
|
||||
|
||||
return {
|
||||
deployment: await deployment.createSavableObject(),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Delete deployment
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_DeleteDeploymentById>(
|
||||
'deleteDeploymentById',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployment = await this.CDeployment.getInstance({
|
||||
id: reqArg.deploymentId,
|
||||
});
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
|
||||
await deployment.delete();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Restart deployment
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_RestartDeployment>(
|
||||
'restartDeployment',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
const deployment = await this.CDeployment.getInstance({
|
||||
id: reqArg.deploymentId,
|
||||
});
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
|
||||
// TODO: Implement actual restart logic with Docker/container runtime
|
||||
deployment.status = 'starting';
|
||||
await deployment.save();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
deployment: await deployment.createSavableObject(),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Scale deployment
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.deployment.IReq_Any_Cloudly_ScaleDeployment>(
|
||||
'scaleDeployment',
|
||||
async (reqArg) => {
|
||||
await plugins.smartguard.passGuardsOrReject(reqArg, [
|
||||
this.cloudlyRef.authManager.validIdentityGuard,
|
||||
]);
|
||||
|
||||
// TODO: Implement scaling logic
|
||||
// This would create/delete deployment instances based on replicas count
|
||||
|
||||
const deployment = await this.CDeployment.getInstance({
|
||||
id: reqArg.deploymentId,
|
||||
});
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
deployment: await deployment.createSavableObject(),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all deployments
|
||||
*/
|
||||
public async getAllDeployments(): Promise<Deployment[]> {
|
||||
return await this.CDeployment.getInstances({});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deployments for a specific service
|
||||
*/
|
||||
public async getDeploymentsForService(serviceId: string): Promise<Deployment[]> {
|
||||
return await this.CDeployment.getInstances({
|
||||
serviceId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deployments for a specific node
|
||||
*/
|
||||
public async getDeploymentsForNode(nodeId: string): Promise<Deployment[]> {
|
||||
return await this.CDeployment.getInstances({
|
||||
nodeId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new deployment
|
||||
*/
|
||||
public async createDeployment(
|
||||
serviceId: string,
|
||||
nodeId: string,
|
||||
version: string = 'latest'
|
||||
): Promise<Deployment> {
|
||||
return await Deployment.createDeployment({
|
||||
serviceId,
|
||||
nodeId,
|
||||
version,
|
||||
status: 'scheduled',
|
||||
deployedAt: Date.now(),
|
||||
deploymentLog: [`Deployment created at ${new Date().toISOString()}`],
|
||||
});
|
||||
}
|
||||
|
||||
public async start() {
|
||||
// DeploymentManager is ready - handlers are already registered in constructor
|
||||
console.log('DeploymentManager started');
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
// Cleanup if needed
|
||||
console.log('DeploymentManager stopped');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user