3 Infrastructure
Virgil edited this page 2026-02-19 17:01:22 +00:00

Infrastructure

Container management (LinuxKit/QEMU/Hyperkit), Hetzner cloud provisioning, and CloudNS DNS management.

Container Management

The container package manages LinuxKit-based virtual machines using QEMU on Linux and Hyperkit on macOS.

Container

A Container represents a running VM instance.

type Container struct {
    ID      string            // Unique container ID
    Name    string            // Human-readable name
    Image   string            // LinuxKit image used
    Status  string            // running, stopped, error
    PID     int               // Process ID of the VM
    Ports   map[string]string // Port mappings (host:container)
    Memory  string            // Allocated memory (e.g. "4G")
    CPUs    int               // Allocated CPU cores
}

RunOptions

Configuration for starting a new VM.

type RunOptions struct {
    Name    string            // Container name
    Detach  bool              // Run in background
    Memory  string            // Memory allocation (e.g. "2G", "4G")
    CPUs    int               // CPU core count
    Ports   map[string]string // Port forwarding (host:container)
    Volumes []string          // Volume mounts (host:container)
    SSHPort int               // SSH port for access (default: auto-assigned)
    SSHKey  string            // Path to SSH key for authentication
}

Manager Interface

The Manager interface provides a unified API for VM lifecycle operations, regardless of the underlying hypervisor.

type Manager interface {
    Run(ctx context.Context, opts RunOptions) (*Container, error)
    Stop(ctx context.Context, id string) error
    Logs(ctx context.Context, id string) (string, error)
    Exec(ctx context.Context, id string, cmd []string) (string, error)
    Inspect(ctx context.Context, id string) (*Container, error)
}

Creating a Manager

The NewLinuxKitManager factory selects the appropriate hypervisor backend based on the host platform.

import "forge.lthn.ai/core/go-devops/container"

// QEMU backend (Linux, or explicit selection)
mgr := container.NewLinuxKitManager("qemu")

// Hyperkit backend (macOS)
mgr := container.NewLinuxKitManager("hyperkit")

Running a VM

ctr, err := mgr.Run(ctx, container.RunOptions{
    Name:   "dev-environment",
    Memory: "4G",
    CPUs:   4,
    Ports: map[string]string{
        "8080": "80",   // Host 8080 -> Container 80
        "8443": "443",  // Host 8443 -> Container 443
    },
    Volumes: []string{
        "/home/dev/project:/workspace",
    },
    SSHPort: 2222,
    SSHKey:  "~/.ssh/id_ed25519",
})

fmt.Printf("Container %s running (PID: %d)\n", ctr.Name, ctr.PID)

Executing Commands

output, err := mgr.Exec(ctx, ctr.ID, []string{"uname", "-a"})
fmt.Println(output) // Linux dev-environment 5.15.0 ...

Viewing Logs

logs, err := mgr.Logs(ctx, ctr.ID)
fmt.Println(logs)

DevOps Environment

The devops package provides higher-level environment management on top of the container layer.

DevOps and Config

type DevOps struct {
    Config Config
    Images *ImageManager
}

type Config struct {
    DataDir   string // Persistent data directory
    CacheDir  string // Image and artefact cache
    StateFile string // Environment state file
}

ImageManager

The ImageManager handles pulling, caching, and verifying LinuxKit images.

type ImageManager struct {
    // Internal: cache dir, registry config
}

// Pull an image (uses cache if available)
img, err := images.Pull(ctx, "linuxkit/kernel:5.15")

// Verify image integrity
ok, err := images.Verify(img, expectedChecksum)

// List cached images
cached, err := images.List()

Hetzner Cloud Provisioning

The infra package includes a provisioner for Hetzner Cloud servers.

type HetznerProvisioner struct {
    // Internal: API client, SSH keys, default config
}

Provisioning a Server

import "forge.lthn.ai/core/go-devops/infra"

provisioner := infra.NewHetznerProvisioner(apiToken)

server, err := provisioner.Create(ctx, infra.ServerConfig{
    Name:       "worker-eu-01",
    Type:       "cx31",         // 2 vCPU, 8GB RAM, 80GB
    Location:   "fsn1",         // Falkenstein, Germany
    Image:      "ubuntu-24.04",
    SSHKeys:    []string{"deploy-key"},
    Labels: map[string]string{
        "role":    "worker",
        "cluster": "production",
    },
})

fmt.Printf("Server %s created at %s\n", server.Name, server.PublicIP)

Server Lifecycle

// List all servers
servers, err := provisioner.List(ctx)

// Get server details
server, err := provisioner.Get(ctx, serverID)

// Delete a server
err := provisioner.Delete(ctx, serverID)

CloudNS DNS Management

The infra package also provides DNS record management via CloudNS.

type CloudNSManager struct {
    // Internal: API credentials, zone cache
}

Managing DNS Records

dns := infra.NewCloudNSManager(authID, password)

// Create an A record
err := dns.CreateRecord(ctx, infra.DNSRecord{
    Zone: "lthn.ai",
    Name: "worker-01",
    Type: "A",
    TTL:  300,
    Value: server.PublicIP,
})

// Create a CNAME record
err := dns.CreateRecord(ctx, infra.DNSRecord{
    Zone:  "lthn.ai",
    Name:  "api",
    Type:  "CNAME",
    TTL:   3600,
    Value: "worker-01.lthn.ai",
})

// List records for a zone
records, err := dns.ListRecords(ctx, "lthn.ai")

// Delete a record
err := dns.DeleteRecord(ctx, "lthn.ai", recordID)

Example: Full Provisioning Workflow

import (
    "forge.lthn.ai/core/go-devops/infra"
    "forge.lthn.ai/core/go-devops/ansible"
)

// 1. Provision a Hetzner server
provisioner := infra.NewHetznerProvisioner(hetznerToken)
server, err := provisioner.Create(ctx, infra.ServerConfig{
    Name:     "web-prod-01",
    Type:     "cx31",
    Location: "fsn1",
    Image:    "ubuntu-24.04",
    SSHKeys:  []string{"deploy-key"},
})

// 2. Create DNS record
dns := infra.NewCloudNSManager(authID, password)
dns.CreateRecord(ctx, infra.DNSRecord{
    Zone:  "lthn.ai",
    Name:  "web-prod-01",
    Type:  "A",
    TTL:   300,
    Value: server.PublicIP,
})

// 3. Configure with Ansible
executor := ansible.NewExecutor("./playbooks")
executor.SetInventory("./inventory/dynamic.yml")
executor.SetVar("target_host", server.PublicIP)
result, err := executor.Run(ctx, "configure.yml")

See Also