/** * Cloud-Init Generator * Generates cloud-init configuration files */ import { yaml, path as pathUtil } from '../plugins.ts'; import { log } from '../logging.ts'; import type { ICloudInitConfig } from '../interfaces/iso-config.interface.ts'; export interface INetworkConfig { wifi?: { ssid: string; password: string; }; } export class CloudInitGenerator { /** * Generate user-data file content */ generateUserData(config: ICloudInitConfig): string { const userDataObj: Record = { '#cloud-config': null, }; // Hostname if (config.hostname) { userDataObj.hostname = config.hostname; } // Users if (config.users && config.users.length > 0) { userDataObj.users = config.users.map((user) => ({ name: user.name, ...(user.ssh_authorized_keys && { ssh_authorized_keys: user.ssh_authorized_keys }), ...(user.sudo && { sudo: user.sudo }), ...(user.shell && { shell: user.shell }), ...(user.groups && { groups: user.groups }), ...(user.lock_passwd !== undefined && { lock_passwd: user.lock_passwd }), ...(user.passwd && { passwd: user.passwd }), })); } // Packages if (config.packages && config.packages.length > 0) { userDataObj.packages = config.packages; } // Package update/upgrade if (config.package_update !== undefined) { userDataObj.package_update = config.package_update; } if (config.package_upgrade !== undefined) { userDataObj.package_upgrade = config.package_upgrade; } // Run commands if (config.runcmd && config.runcmd.length > 0) { userDataObj.runcmd = config.runcmd; } // Write files if (config.write_files && config.write_files.length > 0) { userDataObj.write_files = config.write_files; } // Timezone if (config.timezone) { userDataObj.timezone = config.timezone; } // Locale if (config.locale) { userDataObj.locale = config.locale; } // SSH settings if (config.ssh) { userDataObj.ssh = config.ssh; } // Add any additional custom directives for (const [key, value] of Object.entries(config)) { if (!['hostname', 'users', 'packages', 'package_update', 'package_upgrade', 'runcmd', 'write_files', 'timezone', 'locale', 'ssh'].includes(key)) { userDataObj[key] = value; } } // Remove the cloud-config comment from the object before YAML conversion delete userDataObj['#cloud-config']; // Convert to YAML and add the cloud-config header const yamlContent = yaml.stringify(userDataObj); return `#cloud-config\n${yamlContent}`; } /** * Generate network-config file content (for WiFi) */ generateNetworkConfig(networkConfig: INetworkConfig): string { if (!networkConfig.wifi) { return yaml.stringify({ version: 2 }); } const { ssid, password } = networkConfig.wifi; const netConfig = { version: 2, wifis: { wlan0: { dhcp4: true, optional: true, 'access-points': { [ssid]: { password: password, }, }, }, }, }; return yaml.stringify(netConfig); } /** * Generate meta-data file content */ generateMetaData(hostname?: string): string { const metaData = { 'instance-id': `iid-${Date.now()}`, 'local-hostname': hostname || 'ubuntu', }; return yaml.stringify(metaData); } /** * Write cloud-init files to a directory */ async writeCloudInitFiles( outputDir: string, cloudInitConfig: ICloudInitConfig, networkConfig?: INetworkConfig, ): Promise { log.info('Generating cloud-init configuration files...'); // Generate user-data const userData = this.generateUserData(cloudInitConfig); const userDataPath = pathUtil.join(outputDir, 'user-data'); await Deno.writeTextFile(userDataPath, userData); log.success('Generated user-data'); // Generate network-config if (networkConfig) { const netConfig = this.generateNetworkConfig(networkConfig); const netConfigPath = pathUtil.join(outputDir, 'network-config'); await Deno.writeTextFile(netConfigPath, netConfig); log.success('Generated network-config'); } // Generate meta-data const metaData = this.generateMetaData(cloudInitConfig.hostname); const metaDataPath = pathUtil.join(outputDir, 'meta-data'); await Deno.writeTextFile(metaDataPath, metaData); log.success('Generated meta-data'); } }