Skip to content

feat: public gRPC interface (Hyper.Grpc)#18

Merged
markovejnovic merged 28 commits into
mainfrom
feat/grpc
Jun 24, 2026
Merged

feat: public gRPC interface (Hyper.Grpc)#18
markovejnovic merged 28 commits into
mainfrom
feat/grpc

Conversation

@markovejnovic

@markovejnovic markovejnovic commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Adds Routing.id_for/1 (pid -> vm_id via match spec), Routing.all/0 (cluster-wide VM list), and Hyper.id/1 (erpc to owning node for lag-free resolution).
…erly

Finding 1: `connect/2` now defaults to `GRPC.Client.Adapters.Mint`
(`:gun` is an optional dep not fetched in this project). Caller-supplied
`adapter:` in opts still wins.

Finding 2: `grpc_child/1` delegates cert/key fetching to `fetch_tls_key!/3`,
which raises a clear `ArgumentError` (naming the env var to set) when either
value is nil or absent — instead of passing nil to SSL and blowing up later.
Add ## gRPC interface section to README.md covering serving env-vars,
BEAM connect example, and regeneration instructions. Add `proto.gen`
alias to mix.exs documenting how the vendored lib/hyper/grpc/v1/ files
were produced (not run in CI).
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 53 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/hyper/grpc/codec.ex 0.00% 23 Missing ⚠️
lib/hyper/grpc/server.ex 0.00% 14 Missing ⚠️
lib/hyper/grpc.ex 0.00% 8 Missing ⚠️
lib/hyper/cluster/routing.ex 0.00% 4 Missing ⚠️
lib/hyper.ex 0.00% 3 Missing ⚠️
lib/hyper/application.ex 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

markovejnovic and others added 20 commits June 23, 2026 17:56
- stop_machine: distinguish :noproc (NOT_FOUND), :nodedown (UNAVAILABLE),
  and any other exit (INTERNAL) via Codec.stop_exit_error/1, so a genuine
  controller crash is no longer masked as NOT_FOUND.
- Hyper.id/1: swallow :erpc transport failures (unreachable owning node) and
  return nil instead of crashing the caller; real lookup faults still raise.
- create_machine: reject an unresolvable id with UNAVAILABLE rather than
  returning an empty vm_id.
…degen

- Version the public API as v0 (hyper.grpc.v0 / Hyper.Grpc.V0): unstable,
  may break during early development.
- Drop *_UNSPECIFIED enum sentinels; instance_type/arch/boot_args are now
  proto3 'optional' (explicit presence -> nil in Elixir), with server-side
  defaults applied in the codec.
- Move the contract out of priv/ to proto/hyper/grpc/v0/hyper.proto and
  rename to hyper.proto.
- Stop committing generated code: lib/hyper/grpc/v0/hyper.pb.ex is gitignored
  and produced by a new :grpc_gen Mix compiler (protoc + protoc-gen-elixir),
  mirroring the :firecracker_gen pattern. CI installs protoc + the plugin.
- Codec public surface reduced to from_grpc/1 and to_grpc/1, both pattern
  matched; all wire/error mapping folded behind them.
- Replace env-var server config with config :hyper, Hyper.Grpc, read by a new
  Hyper.Grpc.Config module: only :enabled is special, everything else passes
  through to GRPC.Server.Supervisor so operators own port/TLS/adapter opts.
- Remove this PR's tests per request; verified via compile/format/credo/
  dialyzer + a manual codec round-trip.
- Add docs/grpc.md (configure, TLS, connect, regenerate) and trim the README
  section to point at it.
The gRPC interface is a core API, not an add-on, and an idle listener costs
next to nothing -- so it should not be disablable. Drop config :hyper,
Hyper.Grpc, :enabled and start the server unconditionally on every node.
Config now only tunes the listener (port default 50051, TLS, adapter opts).
Document the co-located-nodes caveat (each node binds :port).
- Replace the aggregate response() union on to_grpc/1 with one @SPEC per
  input pattern (overloaded contract), so each shape declares its own precise
  return type. Tighter than the union and dialyzer-clean.
- docs/grpc.md: link the .proto via a github.com/main URL instead of a
  repo-relative path, which hexdocs can't resolve.
Read config :hyper, Hyper.Grpc.Config (note the .Config key). enabled?/0
defaults to true; server_children/0 returns [] when enabled: false. :enabled
is stripped from the options passed through to GRPC.Server.Supervisor.
- instance_type/1 and arch/1: per-clause @specs over precise enum-atom types
  (instance_enum/arch_enum) instead of atom(); drop the {:error, :invalid_*}
  catch-all clauses and the now-orphaned rpc_error clauses that fed them.
- Remove blank_to_nil/1: proto3 presence already distinguishes unset (nil)
  from explicit empty string, so boot_args passes through unchanged.
- service hyper.grpc.v0.Machines -> hyper.grpc.v0.Hyper
- RPCs: CreateMachine/StopMachine/GetMachine/ListMachines ->
  CreateVm/StopVm/GetVm/ListVms (handlers create_vm/stop_vm/get_vm/list_vms)
- messages: *Machine{Request,Response} -> *Vm{Request,Response}; Machine -> Vm
  (ListVmsResponse.vms); aligns the wire contract with the BEAM Hyper.create_vm
  terminology and drops the Machine/stutter naming.
Regenerate is automatic (:grpc_gen). Updated server, codec, moduledocs, docs/grpc.md.
- Drop 'optional' from instance_type and arch in CreateVmRequest so the fields
  are non-nullable; remove the nil clauses from the codec. An unset field now
  decodes to its enum zero value (MICRO / X86_64) rather than nil.
- Add thorough doc comments throughout hyper.proto: service, every RPC (with
  gRPC error codes), enum values (with vCPU/memory per instance size), and
  every message/field.
Remove the empty StopVmResponse and ListVmsRequest messages; StopVm now
returns google.protobuf.Empty and ListVms takes it. Codec/server updated to
use Google.Protobuf.Empty. protoc resolves the well-known import with no extra
include path.
Populate the scaffolded blocks (client, CreateVm, ListVms, GetVm, StopVm) with
async grpcio (grpc.aio) examples; drop the redundant standalone Python section.
Flip Hyper.Grpc.Config.enabled? default from true to false; with no config the
gRPC server no longer starts. Set enabled: true to opt in.
Replace the keyword-lookup helpers with a typed defstruct
(enabled/port/cred/adapter_opts) plus load/0 and server_options/1.
struct!/2 now rejects unknown config keys (catches typos) instead of silently
passing them through; server_children/0 loads the struct and gates on
config.enabled.
Add the missing 'import grpc' to the client snippet; replace the TODO .proto
link with a github.com/main permalink.
@markovejnovic markovejnovic merged commit df1f5fc into main Jun 24, 2026
3 checks passed
@markovejnovic markovejnovic deleted the feat/grpc branch June 24, 2026 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant