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