169 lines
4.5 KiB
TypeScript
169 lines
4.5 KiB
TypeScript
|
|
/**
|
||
|
|
* 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<string, unknown> = {
|
||
|
|
'#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<void> {
|
||
|
|
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');
|
||
|
|
}
|
||
|
|
}
|