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( '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( '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( '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( '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( '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( '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( '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'); } const serviceId = deployment.serviceId; await deployment.delete(); // Check if this was the last deployment for the service const remainingDeployments = await this.getDeploymentsForService(serviceId); if (remainingDeployments.length === 0) { // Deactivate DNS entries if no more deployments exist await this.cloudlyRef.dnsManager.deactivateServiceDnsEntries(serviceId); } return { success: true, }; } ) ); // Restart deployment this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( '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( '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 { return await this.CDeployment.getInstances({}); } /** * Get deployments for a specific service */ public async getDeploymentsForService(serviceId: string): Promise { return await this.CDeployment.getInstances({ serviceId, }); } /** * Get deployments for a specific node */ public async getDeploymentsForNode(nodeId: string): Promise { return await this.CDeployment.getInstances({ nodeId, }); } /** * Create a new deployment */ public async createDeployment( serviceId: string, nodeId: string, version: string = 'latest' ): Promise { const deployment = await Deployment.createDeployment({ serviceId, nodeId, version, status: 'scheduled', deployedAt: Date.now(), deploymentLog: [`Deployment created at ${new Date().toISOString()}`], }); // Activate DNS entries for the service await this.cloudlyRef.dnsManager.activateServiceDnsEntries(serviceId); // Get the node's IP address and update DNS entries const node = await this.cloudlyRef.nodeManager.CClusterNode.getInstance({ id: nodeId, }); if (node && node.data.publicIp) { await this.cloudlyRef.dnsManager.updateServiceDnsEntriesIp(serviceId, node.data.publicIp); } return deployment; } 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'); } }