diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 00000000..487193fb --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,291 @@ +# Development Guide + +This document covers development setup, configuration, and contributing to Hypeman. + +## Prerequisites + +**Go 1.25.4+**, **KVM**, **erofs-utils**, **dnsmasq** + +```bash +# Verify prerequisites +mkfs.erofs --version +dnsmasq --version +``` + +**Install on Debian/Ubuntu:** +```bash +sudo apt-get install erofs-utils dnsmasq +``` + +**KVM Access:** User must be in `kvm` group for VM access: +```bash +sudo usermod -aG kvm $USER +# Log out and back in, or use: newgrp kvm +``` + +**Network Capabilities:** + +Before running or testing Hypeman, ensure IPv4 forwarding is enabled: + +```bash +# Enable IPv4 forwarding (temporary - until reboot) +sudo sysctl -w net.ipv4.ip_forward=1 + +# Enable IPv4 forwarding (persistent across reboots) +echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf +sudo sysctl -p +``` + +**Why:** Required for routing traffic between VM network and external network. + +The hypeman binary needs network administration capabilities to create bridges and TAP devices: +```bash +# After building, grant network capabilities +sudo setcap 'cap_net_admin,cap_net_bind_service=+eip' /path/to/hypeman + +# For development builds +sudo setcap 'cap_net_admin,cap_net_bind_service=+eip' ./bin/hypeman + +# Verify capabilities +getcap ./bin/hypeman +``` + +**Note:** The `i` (inheritable) flag allows child processes spawned by hypeman (like `ip` and `iptables` commands) to inherit capabilities via the ambient capability set. + +**Note:** These capabilities must be reapplied after each rebuild. For production deployments, set capabilities on the installed binary. For local testing, this is handled automatically in `make test`. + +**File Descriptor Limits:** + +Caddy (used for ingress) requires a higher file descriptor limit than the default on some systems. If you see "Too many open files" errors, increase the limit: + +```bash +# Check current limit (also check with: sudo bash -c 'ulimit -n') +ulimit -n + +# Increase temporarily (current session) +ulimit -n 65536 + +# For persistent changes, add to /etc/security/limits.conf: +* soft nofile 65536 +* hard nofile 65536 +root soft nofile 65536 +root hard nofile 65536 +``` + +## Configuration + +### Environment variables + +Hypeman can be configured using the following environment variables: + +| Variable | Description | Default | +|----------|-------------|---------| +| `PORT` | HTTP server port | `8080` | +| `DATA_DIR` | Directory for storing VM images, volumes, and other data | `/var/lib/hypeman` | +| `BRIDGE_NAME` | Name of the network bridge for VM networking | `vmbr0` | +| `SUBNET_CIDR` | CIDR notation for the VM network subnet (gateway derived automatically) | `10.100.0.0/16` | +| `UPLINK_INTERFACE` | Host network interface to use for VM internet access | _(auto-detect)_ | +| `JWT_SECRET` | Secret key for JWT authentication (required for production) | _(empty)_ | +| `DNS_SERVER` | DNS server IP address for VMs | `1.1.1.1` | +| `MAX_CONCURRENT_BUILDS` | Maximum number of concurrent image builds | `1` | +| `MAX_OVERLAY_SIZE` | Maximum size for overlay filesystem | `100GB` | +| `ENV` | Deployment environment (filters telemetry, e.g. your name for dev) | `unset` | +| `OTEL_ENABLED` | Enable OpenTelemetry traces/metrics | `false` | +| `OTEL_ENDPOINT` | OTLP gRPC endpoint | `127.0.0.1:4317` | +| `OTEL_SERVICE_INSTANCE_ID` | Instance ID for telemetry (differentiates multiple servers) | hostname | +| `LOG_LEVEL` | Default log level (debug, info, warn, error) | `info` | +| `LOG_LEVEL_` | Per-subsystem log level (API, IMAGES, INSTANCES, NETWORK, VOLUMES, VMM, SYSTEM, EXEC, CADDY) | inherits default | +| `CADDY_LISTEN_ADDRESS` | Address for Caddy ingress listeners | `0.0.0.0` | +| `CADDY_ADMIN_ADDRESS` | Address for Caddy admin API | `127.0.0.1` | +| `CADDY_ADMIN_PORT` | Port for Caddy admin API | `2019` | +| `CADDY_STOP_ON_SHUTDOWN` | Stop Caddy when hypeman shuts down (set to `true` for dev) | `false` | +| `ACME_EMAIL` | Email for ACME certificate registration (required for TLS ingresses) | _(empty)_ | +| `ACME_DNS_PROVIDER` | DNS provider for ACME challenges: `cloudflare` | _(empty)_ | +| `ACME_CA` | ACME CA URL (empty = Let's Encrypt production) | _(empty)_ | +| `TLS_ALLOWED_DOMAINS` | Comma-separated allowed domains for TLS (e.g., `*.example.com,api.other.com`) | _(empty)_ | +| `DNS_PROPAGATION_TIMEOUT` | Max time to wait for DNS propagation (e.g., `2m`) | _(empty)_ | +| `DNS_RESOLVERS` | Comma-separated DNS resolvers for propagation checking | _(empty)_ | +| `CLOUDFLARE_API_TOKEN` | Cloudflare API token (when using `cloudflare` provider) | _(empty)_ | + +**Important: Subnet Configuration** + +The default subnet `10.100.0.0/16` is chosen to avoid common conflicts. Hypeman will detect conflicts with existing routes on startup and fail with guidance. + +If you need a different subnet, set `SUBNET_CIDR` in your environment. The gateway is automatically derived as the first IP in the subnet (e.g., `10.100.0.0/16` → `10.100.0.1`). + +**Alternative subnets if needed:** +- `172.30.0.0/16` - Private range between common Docker (172.17.x.x) and cloud provider (172.31.x.x) ranges +- `10.200.0.0/16` - Another private range option + +**Example:** +```bash +# In your .env file +SUBNET_CIDR=172.30.0.0/16 +``` + +**Finding the uplink interface (`UPLINK_INTERFACE`)** + +`UPLINK_INTERFACE` tells Hypeman which host interface to use for routing VM traffic to the outside world (for iptables MASQUERADE rules). On many hosts this is `eth0`, but laptops and more complex setups often use Wi‑Fi or other names. + +**Quick way to discover it:** +```bash +# Ask the kernel which interface is used to reach the internet +ip route get 1.1.1.1 +``` +Look for the `dev` field in the output, for example: +```text +1.1.1.1 via 192.168.12.1 dev wlp2s0 src 192.168.12.98 +``` +In this case, `wlp2s0` is the uplink interface, so you would set: +```bash +UPLINK_INTERFACE=wlp2s0 +``` + +You can also inspect all routes: +```bash +ip route show +``` +Pick the interface used by the default route (usually the line starting with `default`). Avoid using local bridges like `docker0`, `br-...`, `virbr0`, or `vmbr0` as the uplink; those are typically internal virtual networks, not your actual internet-facing interface. + +### TLS Ingress (HTTPS) + +Hypeman uses Caddy with automatic ACME certificates for TLS termination. Certificates are issued via DNS-01 challenges (Cloudflare). + +To enable TLS ingresses: + +1. Configure ACME credentials in your `.env`: +```bash +# Required for any TLS ingress +ACME_EMAIL=admin@example.com + +# For Cloudflare +ACME_DNS_PROVIDER=cloudflare +CLOUDFLARE_API_TOKEN=your-api-token +``` + +2. Create an ingress with TLS enabled: +```bash +curl -X POST http://localhost:8080/v1/ingresses \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-https-app", + "rules": [{ + "match": {"hostname": "app.example.com", "port": 443}, + "target": {"instance": "my-instance", "port": 8080}, + "tls": true, + "redirect_http": true + }] + }' +``` + +Certificates are stored in `$DATA_DIR/caddy/data/` and auto-renewed by Caddy. + +### Setup + +```bash +cp .env.example .env +# Edit .env and set JWT_SECRET and other configuration values +``` + +### Data directory + +Hypeman stores data in a configurable directory. Configure permissions for this directory. + +```bash +sudo mkdir /var/lib/hypeman +sudo chown $USER:$USER /var/lib/hypeman +``` + +### Dockerhub login + +Requires Docker Hub authentication to avoid rate limits when running the tests: +```bash +docker login +``` + +Docker itself isn't required to be installed. `~/.docker/config.json` is a standard used for handling registry authentication. + +## Build + +```bash +make build +``` + +## Running the Server + +1. Generate a JWT token for testing (optional): +```bash +make gen-jwt +``` + +2. Start the server with hot-reload for development: +```bash +make dev +``` +The server will start on port 8080 (configurable via `PORT` environment variable). + +### Local OpenTelemetry (optional) + +To collect traces and metrics locally, run the Grafana LGTM stack (Loki, Grafana, Tempo, Mimir): + +```bash +# Start Grafana LGTM (UI at http://localhost:3000, login: admin/admin) +# Note, if you are developing on a shared server, you can use the same LGTM stack as your peer(s) +# You will be able to sort your metrics, traces, and logs using the ENV configuration (see below) +docker run -d --name lgtm \ + -p 127.0.0.1:3000:3000 \ + -p 127.0.0.1:4317:4317 \ + -p 127.0.0.1:4318:4318 \ + -p 127.0.0.1:9090:9090 \ + -p 127.0.0.1:4040:4040 \ + grafana/otel-lgtm:latest + +# If developing on a remote server, forward the port to your local machine: +# ssh -L 3001:localhost:3000 your-server (then open http://localhost:3001) + +# Enable OTel in .env (set ENV to your name to filter your telemetry) +echo "OTEL_ENABLED=true" >> .env +echo "ENV=yourname" >> .env + +# Restart dev server +make dev +``` + +Open http://localhost:3000 to view traces (Tempo), metrics (Mimir), and logs (Loki) in Grafana. + +**Import the Hypeman dashboard:** +1. Go to Dashboards → New → Import +2. Upload `dashboards/hypeman.json` or paste its contents +3. Select the Prometheus datasource and click Import + +Use the Environment/Instance dropdowns to filter by `deployment.environment` or `service.instance.id`. + +## Testing + +Network tests require elevated permissions to create bridges and TAP devices. + +```bash +make test +``` + +The test command compiles test binaries, grants capabilities via `sudo setcap`, then runs tests as the current user (not root). You may be prompted for your sudo password during the capability grant step. + +## Code Generation + +After modifying `openapi.yaml`, regenerate the Go code: + +```bash +make oapi-generate +``` + +After modifying dependency injection in `cmd/api/wire.go` or `lib/providers/providers.go`, regenerate wire code: + +```bash +make generate-wire +``` + +Or generate everything at once: + +```bash +make generate-all +``` diff --git a/README.md b/README.md index 8ff31f22..6bab0dbf 100644 --- a/README.md +++ b/README.md @@ -1,294 +1,161 @@ -# Hypeman +

-[![Test](https://github.com/onkernel/hypeman/actions/workflows/test.yml/badge.svg)](https://github.com/onkernel/hypeman/actions/workflows/test.yml) - -Run containerized workloads in VMs, powered by [Cloud Hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor). - -## Getting Started - -### Prerequisites - -**Go 1.25.4+**, **KVM**, **erofs-utils**, **dnsmasq** - -```bash -# Verify prerequisites -mkfs.erofs --version -dnsmasq --version ``` - -**Install on Debian/Ubuntu:** -```bash -sudo apt-get install erofs-utils dnsmasq -``` - -**KVM Access:** User must be in `kvm` group for VM access: -```bash -sudo usermod -aG kvm $USER -# Log out and back in, or use: newgrp kvm + ██╗ ██╗ ██╗ ██╗ ██████╗ ███████╗ ███╗ ███╗ █████╗ ███╗ ██╗ + ██║ ██║ ╚██╗ ██╔╝ ██╔══██╗ ██╔════╝ ████╗ ████║ ██╔══██╗ ████╗ ██║ + ███████║ ╚████╔╝ ██████╔╝ █████╗ ██╔████╔██║ ███████║ ██╔██╗ ██║ + ██╔══██║ ╚██╔╝ ██╔═══╝ ██╔══╝ ██║╚██╔╝██║ ██╔══██║ ██║╚██╗██║ + ██║ ██║ ██║ ██║ ███████╗ ██║ ╚═╝ ██║ ██║ ██║ ██║ ╚████║ + ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ``` -**Network Capabilities:** +

-Before running or testing Hypeman, ensure IPv4 forwarding is enabled: +

+ Run containerized workloads in VMs, powered by Cloud Hypervisor. +

-```bash -# Enable IPv4 forwarding (temporary - until reboot) -sudo sysctl -w net.ipv4.ip_forward=1 +--- -# Enable IPv4 forwarding (persistent across reboots) -echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf -sudo sysctl -p -``` +## Requirements -**Why:** Required for routing traffic between VM network and external network. +Hypeman server runs on **Linux** with **KVM** virtualization support. The CLI can run locally on the server or connect remotely from any machine. -The hypeman binary needs network administration capabilities to create bridges and TAP devices: -```bash -# After building, grant network capabilities -sudo setcap 'cap_net_admin,cap_net_bind_service=+eip' /path/to/hypeman +## Quick Start -# For development builds -sudo setcap 'cap_net_admin,cap_net_bind_service=+eip' ./bin/hypeman +Install Hypeman on your Linux server: -# Verify capabilities -getcap ./bin/hypeman +```bash +curl -fsSL https://get.hypeman.sh | bash ``` -**Note:** The `i` (inheritable) flag allows child processes spawned by hypeman (like `ip` and `iptables` commands) to inherit capabilities via the ambient capability set. +This installs both the Hypeman server and CLI. The installer handles all dependencies, KVM access, and network configuration automatically. -**Note:** These capabilities must be reapplied after each rebuild. For production deployments, set capabilities on the installed binary. For local testing, this is handled automatically in `make test`. +## CLI Installation (Remote Access) -**File Descriptor Limits:** - -Caddy (used for ingress) requires a higher file descriptor limit than the default on some systems. If you see "Too many open files" errors, increase the limit: +To connect to a Hypeman server from another machine, install just the CLI: +**Homebrew:** ```bash -# Check current limit (also check with: sudo bash -c 'ulimit -n') -ulimit -n - -# Increase temporarily (current session) -ulimit -n 65536 - -# For persistent changes, add to /etc/security/limits.conf: -* soft nofile 65536 -* hard nofile 65536 -root soft nofile 65536 -root hard nofile 65536 +brew tap onkernel/tap +brew install hypeman ``` -### Configuration - -#### Environment variables - -Hypeman can be configured using the following environment variables: - -| Variable | Description | Default | -|----------|-------------|---------| -| `PORT` | HTTP server port | `8080` | -| `DATA_DIR` | Directory for storing VM images, volumes, and other data | `/var/lib/hypeman` | -| `BRIDGE_NAME` | Name of the network bridge for VM networking | `vmbr0` | -| `SUBNET_CIDR` | CIDR notation for the VM network subnet (gateway derived automatically) | `10.100.0.0/16` | -| `UPLINK_INTERFACE` | Host network interface to use for VM internet access | _(auto-detect)_ | -| `JWT_SECRET` | Secret key for JWT authentication (required for production) | _(empty)_ | -| `DNS_SERVER` | DNS server IP address for VMs | `1.1.1.1` | -| `MAX_CONCURRENT_BUILDS` | Maximum number of concurrent image builds | `1` | -| `MAX_OVERLAY_SIZE` | Maximum size for overlay filesystem | `100GB` | -| `ENV` | Deployment environment (filters telemetry, e.g. your name for dev) | `unset` | -| `OTEL_ENABLED` | Enable OpenTelemetry traces/metrics | `false` | -| `OTEL_ENDPOINT` | OTLP gRPC endpoint | `127.0.0.1:4317` | -| `OTEL_SERVICE_INSTANCE_ID` | Instance ID for telemetry (differentiates multiple servers) | hostname | -| `LOG_LEVEL` | Default log level (debug, info, warn, error) | `info` | -| `LOG_LEVEL_` | Per-subsystem log level (API, IMAGES, INSTANCES, NETWORK, VOLUMES, VMM, SYSTEM, EXEC, CADDY) | inherits default | -| `CADDY_LISTEN_ADDRESS` | Address for Caddy ingress listeners | `0.0.0.0` | -| `CADDY_ADMIN_ADDRESS` | Address for Caddy admin API | `127.0.0.1` | -| `CADDY_ADMIN_PORT` | Port for Caddy admin API | `2019` | -| `CADDY_STOP_ON_SHUTDOWN` | Stop Caddy when hypeman shuts down (set to `true` for dev) | `false` | -| `ACME_EMAIL` | Email for ACME certificate registration (required for TLS ingresses) | _(empty)_ | -| `ACME_DNS_PROVIDER` | DNS provider for ACME challenges: `cloudflare` | _(empty)_ | -| `ACME_CA` | ACME CA URL (empty = Let's Encrypt production) | _(empty)_ | -| `TLS_ALLOWED_DOMAINS` | Comma-separated allowed domains for TLS (e.g., `*.example.com,api.other.com`) | _(empty)_ | -| `DNS_PROPAGATION_TIMEOUT` | Max time to wait for DNS propagation (e.g., `2m`) | _(empty)_ | -| `DNS_RESOLVERS` | Comma-separated DNS resolvers for propagation checking | _(empty)_ | -| `CLOUDFLARE_API_TOKEN` | Cloudflare API token (when using `cloudflare` provider) | _(empty)_ | - -**Important: Subnet Configuration** - -The default subnet `10.100.0.0/16` is chosen to avoid common conflicts. Hypeman will detect conflicts with existing routes on startup and fail with guidance. - -If you need a different subnet, set `SUBNET_CIDR` in your environment. The gateway is automatically derived as the first IP in the subnet (e.g., `10.100.0.0/16` → `10.100.0.1`). - -**Alternative subnets if needed:** -- `172.30.0.0/16` - Private range between common Docker (172.17.x.x) and cloud provider (172.31.x.x) ranges -- `10.200.0.0/16` - Another private range option - -**Example:** +**Go:** ```bash -# In your .env file -SUBNET_CIDR=172.30.0.0/16 +go install 'github.com/onkernel/hypeman-cli/cmd/hypeman@latest' ``` -**Finding the uplink interface (`UPLINK_INTERFACE`)** - -`UPLINK_INTERFACE` tells Hypeman which host interface to use for routing VM traffic to the outside world (for iptables MASQUERADE rules). On many hosts this is `eth0`, but laptops and more complex setups often use Wi‑Fi or other names. +**Configure remote access:** -**Quick way to discover it:** +1. On the server, generate an API token: ```bash -# Ask the kernel which interface is used to reach the internet -ip route get 1.1.1.1 -``` -Look for the `dev` field in the output, for example: -```text -1.1.1.1 via 192.168.12.1 dev wlp2s0 src 192.168.12.98 -``` -In this case, `wlp2s0` is the uplink interface, so you would set: -```bash -UPLINK_INTERFACE=wlp2s0 +hypeman-token ``` -You can also inspect all routes: +2. On your local machine, set the environment variables: ```bash -ip route show +export HYPEMAN_API_KEY="" +export HYPEMAN_BASE_URL="http://:8080" ``` -Pick the interface used by the default route (usually the line starting with `default`). Avoid using local bridges like `docker0`, `br-...`, `virbr0`, or `vmbr0` as the uplink; those are typically internal virtual networks, not your actual internet-facing interface. - -**TLS Ingress (HTTPS)** -Hypeman uses Caddy with automatic ACME certificates for TLS termination. Certificates are issued via DNS-01 challenges (Cloudflare). +## Usage -To enable TLS ingresses: - -1. Configure ACME credentials in your `.env`: ```bash -# Required for any TLS ingress -ACME_EMAIL=admin@example.com +# Pull an image +hypeman pull nginx:alpine -# For Cloudflare -ACME_DNS_PROVIDER=cloudflare -CLOUDFLARE_API_TOKEN=your-api-token -``` +# Boot a new VM (auto-pulls image if needed) +hypeman run --name my-app nginx:alpine -2. Create an ingress with TLS enabled: -```bash -curl -X POST http://localhost:8080/v1/ingresses \ - -H "Content-Type: application/json" \ - -d '{ - "name": "my-https-app", - "rules": [{ - "match": {"hostname": "app.example.com", "port": 443}, - "target": {"instance": "my-instance", "port": 8080}, - "tls": true, - "redirect_http": true - }] - }' -``` - -Certificates are stored in `$DATA_DIR/caddy/data/` and auto-renewed by Caddy. +# List running VMs +hypeman ps -**Setup:** +# Show all VMs +hypeman ps -a -```bash -cp .env.example .env -# Edit .env and set JWT_SECRET and other configuration values -``` +# View logs (supports VM name, ID, or partial ID) +hypeman logs my-app +hypeman logs -f my-app -#### Data directory +# Execute a command in a running VM +hypeman exec my-app whoami -Hypeman stores data in a configurable directory. Configure permissions for this directory. - -```bash -sudo mkdir /var/lib/hypeman -sudo chown $USER:$USER /var/lib/hypeman +# Shell into the VM +hypeman exec -it my-app /bin/sh ``` -#### Dockerhub login +### VM Lifecycle -Requires Docker Hub authentication to avoid rate limits when running the tests: ```bash -docker login -``` +# Stop the VM +hypeman stop my-app -Docker itself isn't required to be installed. `~/.docker/config.json` is a standard used for handling registry authentication. +# Start a stopped VM +hypeman start my-app -### Build +# Put the VM to sleep (paused) +hypeman standby my-app -```bash -make build -``` -### Running the Server - -1. Generate a JWT token for testing (optional): -```bash -make gen-jwt -``` +# Wake the VM (resumed) +hypeman restore my-app -2. Start the server with hot-reload for development: -```bash -make dev +# Delete all VMs +hypeman rm --force --all ``` -The server will start on port 8080 (configurable via `PORT` environment variable). -#### Local OpenTelemetry (optional) +### Ingress (Reverse Proxy) -To collect traces and metrics locally, run the Grafana LGTM stack (Loki, Grafana, Tempo, Mimir): +Create a reverse proxy from the host to your VM: ```bash -# Start Grafana LGTM (UI at http://localhost:3000, login: admin/admin) -# Note, if you are developing on a shared server, you can use the same LGTM stack as your peer(s) -# You will be able to sort your metrics, traces, and logs using the ENV configuration (see below) -docker run -d --name lgtm \ - -p 127.0.0.1:3000:3000 \ - -p 127.0.0.1:4317:4317 \ - -p 127.0.0.1:4318:4318 \ - -p 127.0.0.1:9090:9090 \ - -p 127.0.0.1:4040:4040 \ - grafana/otel-lgtm:latest - -# If developing on a remote server, forward the port to your local machine: -# ssh -L 3001:localhost:3000 your-server (then open http://localhost:3001) - -# Enable OTel in .env (set ENV to your name to filter your telemetry) -echo "OTEL_ENABLED=true" >> .env -echo "ENV=yourname" >> .env - -# Restart dev server -make dev -``` - -Open http://localhost:3000 to view traces (Tempo), metrics (Mimir), and logs (Loki) in Grafana. +# Create an ingress +hypeman ingress create --name my-ingress my-app --hostname my-nginx-app --port 80 --host-port 8081 -**Import the Hypeman dashboard:** -1. Go to Dashboards → New → Import -2. Upload `dashboards/hypeman.json` or paste its contents -3. Select the Prometheus datasource and click Import +# List ingresses +hypeman ingress list -Use the Environment/Instance dropdowns to filter by `deployment.environment` or `service.instance.id`. +# Test it +curl --header "Host: my-nginx-app" http://127.0.0.1:8081 -### Testing +# Delete an ingress +hypeman ingress delete my-ingress +``` -Network tests require elevated permissions to create bridges and TAP devices. +### TLS & Subdomain Routing ```bash -make test -``` - -The test command compiles test binaries, grants capabilities via `sudo setcap`, then runs tests as the current user (not root). You may be prompted for your sudo password during the capability grant step. +# TLS-terminating ingress (requires DNS credentials in server config) +hypeman ingress create --name my-tls-ingress my-app \ + --hostname hello.example.com -p 80 --host-port 7443 --tls -### Code Generation +# Test TLS +curl --resolve hello.example.com:7443:127.0.0.1 https://hello.example.com:7443 -After modifying `openapi.yaml`, regenerate the Go code: +# Subdomain-based routing +hypeman ingress create --name subdomain-ingress '{instance}' \ + --hostname '{instance}.example.com' -p 80 --host-port 8443 --tls -```bash -make oapi-generate +# Delete all ingresses +hypeman ingress delete --all ``` -After modifying dependency injection in `cmd/api/wire.go` or `lib/providers/providers.go`, regenerate wire code: +### Advanced Logging ```bash -make generate-wire +# View Cloud Hypervisor logs +hypeman logs --source vmm my-app + +# View Hypeman operational logs +hypeman logs --source hypeman my-app ``` -Or generate everything at once: +For all available commands, run `hypeman --help`. -```bash -make generate-all -``` +## Development + +See [DEVELOPMENT.md](DEVELOPMENT.md) for build instructions, configuration options, and contributing guidelines. + +## License + +See [LICENSE](LICENSE). diff --git a/scripts/install.sh b/scripts/install.sh index eac818cb..cb154dad 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -37,6 +37,38 @@ info() { echo -e "${GREEN}[INFO]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } +# Find the most recent release that has a specific artifact available +# Usage: find_release_with_artifact +# Returns: version tag (e.g., v0.5.0) or empty string if not found +find_release_with_artifact() { + local repo="$1" + local archive_prefix="$2" + local os="$3" + local arch="$4" + + # Fetch recent release tags (up to 10) + local tags + tags=$(curl -fsSL "https://api.github.com/repos/${repo}/releases?per_page=10" 2>/dev/null | grep '"tag_name"' | cut -d'"' -f4) + if [ -z "$tags" ]; then + return 1 + fi + + # Check each release for the artifact + for tag in $tags; do + local version_num="${tag#v}" + local artifact_name="${archive_prefix}_${version_num}_${os}_${arch}.tar.gz" + local artifact_url="https://github.com/${repo}/releases/download/${tag}/${artifact_name}" + + # Check if artifact exists (follow redirects, fail silently) + if curl -fsSL --head "$artifact_url" >/dev/null 2>&1; then + echo "$tag" + return 0 + fi + done + + return 1 +} + # ============================================================================= # Pre-flight checks - verify all requirements before doing anything # ============================================================================= @@ -96,6 +128,54 @@ esac info "Pre-flight checks passed" +# ============================================================================= +# System Configuration - KVM access and network capabilities +# ============================================================================= + +# Get the installing user (for adding to groups) +INSTALL_USER="${SUDO_USER:-$(whoami)}" + +# Ensure KVM access +if [ -e /dev/kvm ]; then + if getent group kvm &>/dev/null; then + if ! groups "$INSTALL_USER" 2>/dev/null | grep -qw kvm; then + info "Adding user ${INSTALL_USER} to kvm group..." + $SUDO usermod -aG kvm "$INSTALL_USER" + warn "You may need to log out and back in for kvm group membership to take effect" + fi + fi +else + warn "/dev/kvm not found - KVM may not be available on this system" +fi + +# Enable IPv4 forwarding (required for VM networking) +CURRENT_IP_FORWARD=$(sysctl -n net.ipv4.ip_forward 2>/dev/null || echo "0") +if [ "$CURRENT_IP_FORWARD" != "1" ]; then + info "Enabling IPv4 forwarding..." + $SUDO sysctl -w net.ipv4.ip_forward=1 > /dev/null + + # Make it persistent across reboots + if [ -d /etc/sysctl.d ]; then + echo 'net.ipv4.ip_forward=1' | $SUDO tee /etc/sysctl.d/99-hypeman.conf > /dev/null + elif ! grep -q '^net.ipv4.ip_forward=1' /etc/sysctl.conf 2>/dev/null; then + echo 'net.ipv4.ip_forward=1' | $SUDO tee -a /etc/sysctl.conf > /dev/null + fi +fi + +# Increase file descriptor limit for Caddy (ingress) +if [ -d /etc/security/limits.d ]; then + if [ ! -f /etc/security/limits.d/99-hypeman.conf ]; then + info "Configuring file descriptor limits for ingress..." + $SUDO tee /etc/security/limits.d/99-hypeman.conf > /dev/null << 'LIMITS' +# Hypeman: Increased file descriptor limits for Caddy ingress +* soft nofile 65536 +* hard nofile 65536 +root soft nofile 65536 +root hard nofile 65536 +LIMITS + fi +fi + # ============================================================================= # Create temp directory # ============================================================================= @@ -149,10 +229,10 @@ if [ -n "$BRANCH" ]; then else # Download release mode if [ -z "$VERSION" ]; then - info "Fetching latest version..." - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | cut -d'"' -f4) + info "Fetching latest version with available artifacts..." + VERSION=$(find_release_with_artifact "$REPO" "hypeman" "$OS" "$ARCH") if [ -z "$VERSION" ]; then - error "Failed to fetch latest version" + error "Failed to find a release with artifacts for ${OS}/${ARCH}" fi fi info "Installing version: $VERSION" @@ -326,11 +406,10 @@ $SUDO systemctl start "$SERVICE_NAME" CLI_REPO="onkernel/hypeman-cli" if [ -z "$CLI_VERSION" ]; then - info "Fetching latest CLI version..." - CLI_VERSION=$(curl -fsSL "https://api.github.com/repos/${CLI_REPO}/releases/latest" | grep '"tag_name"' | cut -d'"' -f4) + info "Fetching latest CLI version with available artifacts..." + CLI_VERSION=$(find_release_with_artifact "$CLI_REPO" "hypeman" "$OS" "$ARCH") if [ -z "$CLI_VERSION" ]; then - warn "Failed to fetch latest CLI version, skipping CLI installation" - CLI_VERSION="" + warn "Failed to find a CLI release with artifacts for ${OS}/${ARCH}, skipping CLI installation" fi fi