Skip to content

Multi-provider host auto-provisioning (HostProvider abstraction) #11

Description

@mathaix

Goal

One-click "Provision Host" from the UI on any cloud (GCP, AWS, Hetzner, …), using the same on-host bootstrap as bare-metal enrollment. Today auto-provisioning is GCP-only and takes a different (GCE-metadata) code path than enrollment.

Background / current state

There are two onboarding paths today:

Path Providers Mechanism
Enrollment (universal) GCP, AWS, Hetzner, bare-metal provision-host.sh + curl .../api/agent/install | bash -s -- <token>/api/agent/register
Auto-provision from UI (GCP-only) GCP backend/internal/provisioner/provisioner.go → GCE Instances.Insert + metadata injection

The enrollment path is already provider-agnostic and is documented as the unified path (see docs/getting-started.md). The GCP auto-provisioner is a separate, GCP-specific code path (injects agent-token/tunnel-token via GCE metadata; the agent self-configures from the metadata server). No AWS/Hetzner/user-data/cloud-init provisioning code exists.

Proposed design

Make cloud auto-provision a thin wrapper around the same bootstrap the enrollment path runs:

  1. Control plane mints an enrollment token.
  2. Renders user-data = provision-host.sh (install deps) + curl .../api/agent/install | bash -s -- <token>.
  3. Creates the VM on the chosen provider with that user-data (GCE startup-script / EC2 user-data / Hetzner cloud-init).
  4. The host registers via /api/agent/register exactly like a bare host — tunnel, host record, agent token, and GCS creds all flow through the existing register endpoint. No provider-specific code runs on the host.

Provider-specific code reduces to a thin interface:

type HostProvider interface {
    CreateInstance(ctx context.Context, spec ProvisionSpec, userData string) (instanceID string, err error)
    DeleteInstance(ctx context.Context, instanceID string) error
}
  • GCP impl first (wraps the existing compute Insert, minus the metadata/tunnel logic, plus startup-script).
  • AWS impl = EC2 RunInstances with user-data.
  • Hetzner impl = hcloud server create with user_data (cloud-init).

Scope / tasks

  • Define HostProvider interface + ProvisionSpec (machine type/size, region/zone, image, nested-virt).
  • Render the unified bootstrap user-data (reuse scripts/provision-host.sh + enrollment install).
  • Refactor provisioner.go to: mint token → render user-data → provider.CreateInstance(...); drop the GCP-metadata path so GCP goes through /api/agent/register like every other host.
  • GCP HostProvider impl (stock image + startup-script, or keep FC_SNAPSHOT_NAME fast path).
  • AWS HostProvider impl (EC2, nested-virt-capable instance types e.g. *.metal).
  • Hetzner HostProvider impl (hcloud).
  • UI: provider/region/size selection in the "Provision Host" flow.
  • Reconciler: provider-aware liveness (GCP has an instance API; others fall back to heartbeat-only — see heartbeat_checker.go).
  • Cross-provider artifact distribution: confirm /register GCS-creds hand-off works for AWS/Hetzner pulling the agent + kernel, or add an HTTP artifact source.

Related

  • Unified path documented in docs/getting-started.md (option A).
  • Follow-up: GitHub Action to build + push ocm-agent + Firecracker kernel artifacts to the artifact bucket.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions