feat(dns): Enhance DNS management with auto-generated entries and service activation
This commit is contained in:
		| @@ -181,8 +181,16 @@ export class DeploymentManager { | ||||
|             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, | ||||
|           }; | ||||
| @@ -281,7 +289,7 @@ export class DeploymentManager { | ||||
|     nodeId: string, | ||||
|     version: string = 'latest' | ||||
|   ): Promise<Deployment> { | ||||
|     return await Deployment.createDeployment({ | ||||
|     const deployment = await Deployment.createDeployment({ | ||||
|       serviceId, | ||||
|       nodeId, | ||||
|       version, | ||||
| @@ -289,6 +297,19 @@ export class DeploymentManager { | ||||
|       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() { | ||||
|   | ||||
| @@ -156,6 +156,95 @@ export class DnsManager { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Create a DNS entry for a service | ||||
|    * @param dnsEntryData The DNS entry data | ||||
|    */ | ||||
|   public async createServiceDnsEntry(dnsEntryData: plugins.servezoneInterfaces.data.IDnsEntry['data']) { | ||||
|     // If domainId is provided, get the domain and set the zone | ||||
|     if (dnsEntryData.domainId) { | ||||
|       const domain = await this.cloudlyRef.domainManager.CDomain.getInstance({ | ||||
|         id: dnsEntryData.domainId, | ||||
|       }); | ||||
|       if (domain) { | ||||
|         dnsEntryData.zone = domain.data.name; | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     // Create the DNS entry | ||||
|     const dnsEntry = await this.CDnsEntry.createDnsEntry(dnsEntryData); | ||||
|     return dnsEntry; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Activate DNS entries for a service when it's deployed | ||||
|    * @param serviceId The service ID | ||||
|    */ | ||||
|   public async activateServiceDnsEntries(serviceId: string) { | ||||
|     const dnsEntries = await this.CDnsEntry.getInstances({ | ||||
|       'data.sourceServiceId': serviceId, | ||||
|       'data.sourceType': 'service', | ||||
|     }); | ||||
|      | ||||
|     for (const entry of dnsEntries) { | ||||
|       entry.data.active = true; | ||||
|       entry.data.updatedAt = Date.now(); | ||||
|       await entry.save(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Deactivate DNS entries for a service when it's undeployed | ||||
|    * @param serviceId The service ID | ||||
|    */ | ||||
|   public async deactivateServiceDnsEntries(serviceId: string) { | ||||
|     const dnsEntries = await this.CDnsEntry.getInstances({ | ||||
|       'data.sourceServiceId': serviceId, | ||||
|       'data.sourceType': 'service', | ||||
|     }); | ||||
|      | ||||
|     for (const entry of dnsEntries) { | ||||
|       entry.data.active = false; | ||||
|       entry.data.updatedAt = Date.now(); | ||||
|       await entry.save(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Remove all DNS entries for a service | ||||
|    * @param serviceId The service ID | ||||
|    */ | ||||
|   public async removeServiceDnsEntries(serviceId: string) { | ||||
|     const dnsEntries = await this.CDnsEntry.getInstances({ | ||||
|       'data.sourceServiceId': serviceId, | ||||
|       'data.sourceType': 'service', | ||||
|     }); | ||||
|      | ||||
|     for (const entry of dnsEntries) { | ||||
|       await entry.delete(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Update DNS entry values when deployment happens | ||||
|    * @param serviceId The service ID | ||||
|    * @param ipAddress The IP address to set for the DNS entries | ||||
|    */ | ||||
|   public async updateServiceDnsEntriesIp(serviceId: string, ipAddress: string) { | ||||
|     const dnsEntries = await this.CDnsEntry.getInstances({ | ||||
|       'data.sourceServiceId': serviceId, | ||||
|       'data.sourceType': 'service', | ||||
|     }); | ||||
|      | ||||
|     for (const entry of dnsEntries) { | ||||
|       if (entry.data.type === 'A' || entry.data.type === 'AAAA') { | ||||
|         entry.data.value = ipAddress; | ||||
|         entry.data.updatedAt = Date.now(); | ||||
|         await entry.save(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Initialize the DNS manager | ||||
|    */ | ||||
|   | ||||
| @@ -25,6 +25,12 @@ export class Service extends plugins.smartdata.SmartDataDbDoc< | ||||
|     service.id = await Service.getNewId(); | ||||
|     Object.assign(service, serviceDataArg); | ||||
|     await service.save(); | ||||
|      | ||||
|     // Create DNS entries if service has web port and domains configured | ||||
|     if (service.data.ports?.web && service.data.domains?.length > 0) { | ||||
|       await service.createDnsEntries(); | ||||
|     } | ||||
|      | ||||
|     return service; | ||||
|   } | ||||
|  | ||||
| @@ -54,4 +60,40 @@ export class Service extends plugins.smartdata.SmartDataDbDoc< | ||||
|     } | ||||
|     return finalFlatObject; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Creates DNS entries for this service (in inactive state) | ||||
|    * These will be activated when the service is deployed | ||||
|    */ | ||||
|   public async createDnsEntries() { | ||||
|     const dnsManager = this.manager.cloudlyRef.dnsManager; | ||||
|      | ||||
|     for (const domain of this.data.domains) { | ||||
|       const dnsEntryData: plugins.servezoneInterfaces.data.IDnsEntry['data'] = { | ||||
|         type: 'A', // Default to A record, could be made configurable | ||||
|         name: domain.name, | ||||
|         value: '0.0.0.0', // Placeholder, will be updated on deployment | ||||
|         ttl: 3600, | ||||
|         zone: '', // Will be set based on domainId | ||||
|         domainId: domain.domainId, | ||||
|         active: false, // Created as inactive | ||||
|         description: `Auto-generated DNS entry for service ${this.data.name}`, | ||||
|         createdAt: Date.now(), | ||||
|         isAutoGenerated: true, | ||||
|         sourceServiceId: this.id, | ||||
|         sourceType: 'service', | ||||
|       }; | ||||
|        | ||||
|       // Create the DNS entry | ||||
|       await dnsManager.createServiceDnsEntry(dnsEntryData); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Removes DNS entries for this service | ||||
|    */ | ||||
|   public async removeDnsEntries() { | ||||
|     const dnsManager = this.manager.cloudlyRef.dnsManager; | ||||
|     await dnsManager.removeServiceDnsEntries(this.id); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -91,6 +91,10 @@ export class ServiceManager { | ||||
|           const service = await Service.getInstance({ | ||||
|             id: dataArg.serviceId, | ||||
|           }); | ||||
|            | ||||
|           // Remove DNS entries before deleting the service | ||||
|           await service.removeDnsEntries(); | ||||
|            | ||||
|           await service.delete(); | ||||
|           return { | ||||
|             success: true, | ||||
|   | ||||
| @@ -77,5 +77,24 @@ export interface IDnsEntry { | ||||
|      * Timestamp when the entry was last updated | ||||
|      */ | ||||
|     updatedAt?: number; | ||||
|      | ||||
|     /** | ||||
|      * Whether this DNS entry was auto-generated | ||||
|      */ | ||||
|     isAutoGenerated?: boolean; | ||||
|      | ||||
|     /** | ||||
|      * The service ID that created this DNS entry (for auto-generated entries) | ||||
|      */ | ||||
|     sourceServiceId?: string; | ||||
|      | ||||
|     /** | ||||
|      * The source type of this DNS entry | ||||
|      * - manual: Created by user through UI/API | ||||
|      * - service: Auto-generated from service configuration | ||||
|      * - system: Created by system processes | ||||
|      * - external: Synced from external DNS providers | ||||
|      */ | ||||
|     sourceType?: 'manual' | 'service' | 'system' | 'external'; | ||||
|   }; | ||||
| } | ||||
| @@ -54,8 +54,22 @@ export interface IService { | ||||
|     }; | ||||
|     resources?: IServiceRessources; | ||||
|     domains: { | ||||
|       /** | ||||
|        * Optional domain ID to specify which domain to use | ||||
|        * If not specified, will use the default domain or require manual configuration | ||||
|        */ | ||||
|       domainId?: string; | ||||
|       /** | ||||
|        * The subdomain name (e.g., 'api', 'www', '@' for root) | ||||
|        */ | ||||
|       name: string; | ||||
|       /** | ||||
|        * The port to expose (defaults to ports.web if not specified) | ||||
|        */ | ||||
|       port?: number; | ||||
|       /** | ||||
|        * The protocol for this domain entry | ||||
|        */ | ||||
|       protocol?: 'http' | 'https' | 'ssh'; | ||||
|     }[]; | ||||
|     deploymentIds: string[]; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user