package cloudinit import ( "fmt" "log/slog" "os" "strings" ) // ApplyHostname sets the system hostname and updates /etc/hostname and /etc/hosts. func ApplyHostname(cfg *Config) error { hostname := cfg.Hostname if hostname == "" { slog.Info("no hostname in cloud-init, skipping") return nil } // Set the running hostname if err := os.WriteFile("/proc/sys/kernel/hostname", []byte(hostname), 0o644); err != nil { // Fallback: use the hostname command if err := run("hostname", hostname); err != nil { return fmt.Errorf("setting hostname: %w", err) } } // Write /etc/hostname if err := os.WriteFile("/etc/hostname", []byte(hostname+"\n"), 0o644); err != nil { return fmt.Errorf("writing /etc/hostname: %w", err) } // Ensure hostname is in /etc/hosts if err := ensureHostsEntry(hostname); err != nil { return fmt.Errorf("updating /etc/hosts: %w", err) } slog.Info("hostname set", "hostname", hostname) return nil } // SaveHostname writes the hostname to the persistent data partition so it // survives reboots (even without cloud-init on next boot). func SaveHostname(cfg *Config, destDir string) error { if cfg.Hostname == "" { return nil } if err := os.MkdirAll(destDir, 0o755); err != nil { return fmt.Errorf("creating hostname dir: %w", err) } dest := destDir + "/hostname" if err := os.WriteFile(dest, []byte(cfg.Hostname+"\n"), 0o644); err != nil { return fmt.Errorf("writing persistent hostname: %w", err) } slog.Info("hostname saved", "path", dest) return nil } func ensureHostsEntry(hostname string) error { data, err := os.ReadFile("/etc/hosts") if err != nil && !os.IsNotExist(err) { return err } content := string(data) entry := "127.0.0.1 " + hostname // Check if already present for _, line := range strings.Split(content, "\n") { if strings.Contains(line, hostname) { return nil } } // Append if !strings.HasSuffix(content, "\n") && content != "" { content += "\n" } content += entry + "\n" return os.WriteFile("/etc/hosts", []byte(content), 0o644) }