Files
herolib/lib/virt/herorun/hetzner_backend.v
Mahmoud-Emad 8d03eb822d feat: add HeroRun remote container management library
- Introduce Executor for remote container orchestration
- Add Container lifecycle management with tmux
- Support Alpine and Alpine Python base images
- Auto-install core dependencies on remote node
- Include full usage examples and updated README
2025-09-03 18:32:47 +03:00

119 lines
3.3 KiB
V
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module herorun
import os
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.osal.tmux
// HetznerBackend implements NodeBackend for Hetzner cloud servers
pub struct HetznerBackend {
pub mut:
node Node
}
// Create new Hetzner backend
pub fn new_hetzner_backend(args NewNodeArgs) !HetznerBackend {
console.print_header('🖥 Creating Hetzner Backend')
console.print_stdout('IP: ${args.node_ip}')
node := Node{
settings: NodeSettings{
node_ip: args.node_ip
user: args.user
}
}
return HetznerBackend{
node: node
}
}
// Implement NodeBackend interface
pub fn (mut h HetznerBackend) connect(args NodeConnectArgs) ! {
console.print_header('🔌 Connecting to Hetzner node')
console.print_item('Node IP: ${h.node.settings.node_ip}')
console.print_item('User: ${h.node.settings.user}')
// Basic SSH connection test
cmd := 'ssh ${h.node.settings.user}@${h.node.settings.node_ip} -o StrictHostKeyChecking=no exit'
stream_command(cmd)!
console.print_green('Connection successful')
// Ensure required packages are installed
console.print_header('📦 Ensuring required packages')
install_cmd := 'ssh ${h.node.settings.user}@${h.node.settings.node_ip} "apt-get update && apt-get install -y runc tmux curl xz-utils"'
stream_command(install_cmd)!
console.print_green('Dependencies installed')
}
pub fn (h HetznerBackend) send_command(args SendCommandArgs) ! {
console.print_header('💻 Running remote command on Hetzner')
console.print_item('Command: ${args.cmd}')
remote_cmd := 'ssh ${h.node.settings.user}@${h.node.settings.node_ip} "${args.cmd}"'
stream_command(remote_cmd)!
}
pub fn (mut h HetznerBackend) get_or_create_container(args NewContainerArgs) !Container {
console.print_header('📦 Get or create container: ${args.name}')
// Check if container exists
check_cmd := 'ssh ${h.node.settings.user}@${h.node.settings.node_ip} "runc list | grep ${args.name}"'
code := os.system(check_cmd)
if code != 0 {
console.print_stdout('Container not found, creating...')
h.create_container(args.name)!
} else {
console.print_stdout('Container already exists.')
}
// Create tmux session wrapper
mut t := tmux.new(sessionid: args.name)!
return Container{
name: args.name
node: h.node
tmux: t
}
}
pub fn (h HetznerBackend) get_info() !NodeInfo {
return NodeInfo{
ip: h.node.settings.node_ip
user: h.node.settings.user
provider: 'hetzner'
status: 'connected'
}
}
// Internal helper to create container
fn (h HetznerBackend) create_container(name string) ! {
ver := '3.20.3'
file := 'alpine-minirootfs-${ver}-x86_64.tar.gz'
url := 'https://dl-cdn.alpinelinux.org/alpine/v${ver[..4]}/releases/x86_64/${file}'
setup_cmd := '
mkdir -p /containers/${name}/rootfs &&
cd /containers/${name}/rootfs &&
echo "📥 Downloading Alpine rootfs: ${url}" &&
curl -fSL ${url} | tar -xzf - &&
cd /containers/${name} &&
rm -f config.json &&
runc spec
'
remote_cmd := 'ssh ${h.node.settings.user}@${h.node.settings.node_ip} "${setup_cmd}"'
stream_command(remote_cmd)!
console.print_green('Container ${name} rootfs prepared')
}
// Helper function for streaming commands
fn stream_command(cmd string) ! {
console.print_debug_title('Executing', cmd)
code := os.system(cmd)
if code != 0 {
console.print_stderr('Command failed: ${cmd} (exit ${code})')
return error('command failed: ${cmd} (exit ${code})')
}
}