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'); |             throw new Error('Deployment not found'); | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           const serviceId = deployment.serviceId; | ||||||
|           await deployment.delete(); |           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 { |           return { | ||||||
|             success: true, |             success: true, | ||||||
|           }; |           }; | ||||||
| @@ -281,7 +289,7 @@ export class DeploymentManager { | |||||||
|     nodeId: string, |     nodeId: string, | ||||||
|     version: string = 'latest' |     version: string = 'latest' | ||||||
|   ): Promise<Deployment> { |   ): Promise<Deployment> { | ||||||
|     return await Deployment.createDeployment({ |     const deployment = await Deployment.createDeployment({ | ||||||
|       serviceId, |       serviceId, | ||||||
|       nodeId, |       nodeId, | ||||||
|       version, |       version, | ||||||
| @@ -289,6 +297,19 @@ export class DeploymentManager { | |||||||
|       deployedAt: Date.now(), |       deployedAt: Date.now(), | ||||||
|       deploymentLog: [`Deployment created at ${new Date().toISOString()}`], |       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() { |   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 |    * Initialize the DNS manager | ||||||
|    */ |    */ | ||||||
|   | |||||||
| @@ -25,6 +25,12 @@ export class Service extends plugins.smartdata.SmartDataDbDoc< | |||||||
|     service.id = await Service.getNewId(); |     service.id = await Service.getNewId(); | ||||||
|     Object.assign(service, serviceDataArg); |     Object.assign(service, serviceDataArg); | ||||||
|     await service.save(); |     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; |     return service; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -54,4 +60,40 @@ export class Service extends plugins.smartdata.SmartDataDbDoc< | |||||||
|     } |     } | ||||||
|     return finalFlatObject; |     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({ |           const service = await Service.getInstance({ | ||||||
|             id: dataArg.serviceId, |             id: dataArg.serviceId, | ||||||
|           }); |           }); | ||||||
|  |            | ||||||
|  |           // Remove DNS entries before deleting the service | ||||||
|  |           await service.removeDnsEntries(); | ||||||
|  |            | ||||||
|           await service.delete(); |           await service.delete(); | ||||||
|           return { |           return { | ||||||
|             success: true, |             success: true, | ||||||
|   | |||||||
| @@ -77,5 +77,24 @@ export interface IDnsEntry { | |||||||
|      * Timestamp when the entry was last updated |      * Timestamp when the entry was last updated | ||||||
|      */ |      */ | ||||||
|     updatedAt?: number; |     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; |     resources?: IServiceRessources; | ||||||
|     domains: { |     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; |       name: string; | ||||||
|  |       /** | ||||||
|  |        * The port to expose (defaults to ports.web if not specified) | ||||||
|  |        */ | ||||||
|       port?: number; |       port?: number; | ||||||
|  |       /** | ||||||
|  |        * The protocol for this domain entry | ||||||
|  |        */ | ||||||
|       protocol?: 'http' | 'https' | 'ssh'; |       protocol?: 'http' | 'https' | 'ssh'; | ||||||
|     }[]; |     }[]; | ||||||
|     deploymentIds: string[]; |     deploymentIds: string[]; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user