Skip to content

Commit 9bc3549

Browse files
committed
feat(grpc): always-on server, remove the enable toggle
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).
1 parent a4c3d39 commit 9bc3549

4 files changed

Lines changed: 38 additions & 81 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ cluster. The contract lives at `proto/hyper/grpc/v0/hyper.proto`
6363
> **v0:** unstable — the contract may change without notice during early
6464
> development.
6565
66-
The server is opt-in and configured entirely from your own `config/`; it is
67-
stateless and may run on every node. See
66+
The server always runs on every node and is configured (port, TLS) entirely
67+
from your own `config/`; it is stateless. See
6868
[the gRPC guide](docs/grpc.md) for configuration, TLS, connecting from any
6969
language, and regenerating the bindings.
7070

config/config.exs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ config :hyper, Hyper.Img.Db.Repo,
4343
hostname: "localhost",
4444
pool_size: 10
4545

46-
# The public gRPC interface is opt-in and off by default. Operators enable and
47-
# configure it from their own config (port, TLS, adapter opts) -- see
48-
# `Hyper.Grpc.Config`. Everything but `:enabled` is passed through to
49-
# `GRPC.Server.Supervisor`.
50-
config :hyper, Hyper.Grpc, enabled: false
46+
# The public gRPC interface always runs (it is a core interface, not opt-in).
47+
# Tune the listener -- port, TLS, adapter opts -- from your own config; see
48+
# `Hyper.Grpc.Config`. With no config it serves plaintext on port 50051.

docs/grpc.md

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,20 @@ A machine is addressed by its **`vm_id`** — a URL-safe base64 string the serve
2424
mints at creation. The server is stateless and identical on every node;
2525
placement and routing are cluster-wide, so any node can serve any request.
2626

27-
### Optional fields
28-
29-
`CreateMachineRequest` uses proto3 `optional` (explicit field presence) instead
30-
of magic sentinel enum values. An **unset** field is absent on the wire (and
31-
`nil` in Elixir), and the server applies its default:
32-
33-
| Field | Type | Unset means |
34-
| --------------- | -------------- | ------------------------------------ |
35-
| `img_id` | `string` | required |
36-
| `instance_type` | `InstanceType` | `INSTANCE_TYPE_BASE` |
37-
| `arch` | `Architecture` | resolve to the scheduling node's arch|
38-
| `boot_args` | `string` | the standard serial-console cmdline |
39-
40-
There is no `*_UNSPECIFIED` member: leave the field out to get the default.
41-
4227
## Configuring the server
4328

44-
The server is **off by default**. Enable and configure it from your own
45-
application config under `config :hyper, Hyper.Grpc`. Only `:enabled` is special
46-
— every other key is passed straight through to
29+
The server **always runs** — it is a core interface, not an opt-in add-on, and
30+
an idle listener costs next to nothing. With no config it serves plaintext on
31+
port `50051`. Tune the listener from your own application config under
32+
`config :hyper, Hyper.Grpc`; every key is passed straight through to
4733
[`GRPC.Server.Supervisor`](https://hexdocs.pm/grpc_server/GRPC.Server.Supervisor.html),
48-
so you control the listener entirely (`Hyper.Grpc.Config` documents this).
34+
so you control it entirely (`Hyper.Grpc.Config` documents this).
4935

5036
### Plaintext (development)
5137

5238
```elixir
5339
# config/dev.exs
54-
config :hyper, Hyper.Grpc,
55-
enabled: true,
56-
port: 50_051
40+
config :hyper, Hyper.Grpc, port: 50_051
5741
```
5842

5943
`:port` defaults to `50051` if omitted.
@@ -67,7 +51,6 @@ paths are read at boot, not compile time:
6751
# config/runtime.exs
6852
if config_env() == :prod do
6953
config :hyper, Hyper.Grpc,
70-
enabled: true,
7154
port: 50_051,
7255
cred:
7356
GRPC.Credential.new(
@@ -85,9 +68,10 @@ supervisor option works too (`adapter_opts:`, `max_body_size:`, …).
8568

8669
### How it starts
8770

88-
`Hyper.Application` splices `Hyper.Grpc.server_children/0` into its supervision
89-
tree. That returns `[]` when `enabled: false`, so a default build starts no
90-
server. Run the server on as many nodes as you like.
71+
`Hyper.Application` unconditionally splices `Hyper.Grpc.server_children/0` into
72+
its supervision tree on every node. Because each node binds `:port`, running
73+
several nodes on one host (e.g. a local cluster) means giving each its own port
74+
in config.
9175

9276
## Connecting
9377

@@ -124,21 +108,3 @@ adapter):
124108
%Hyper.Grpc.V0.StopMachineRequest{vm_id: vm_id}
125109
)
126110
```
127-
128-
## Regenerating the bindings
129-
130-
The Elixir bindings (`lib/hyper/grpc/v0/hyper.pb.ex`) are **not committed**
131-
they are generated from the `.proto` by the `:grpc_gen` Mix compiler before the
132-
Elixir compiler runs, the same way the Firecracker bindings are. Editing the
133-
`.proto` and recompiling is enough; to force a regen run `mix grpc.gen`.
134-
135-
This requires `protoc` and the `protoc-gen-elixir` plugin in any environment
136-
that compiles Hyper from a fresh tree:
137-
138-
```sh
139-
sudo apt-get install -y protobuf-compiler # or: brew install protobuf
140-
mix escript.install hex protobuf 0.17.0 # provides protoc-gen-elixir
141-
```
142-
143-
The plugin escript installs to `~/.mix/escripts`, which the compiler prepends to
144-
`PATH` automatically.

lib/hyper/grpc.ex

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ defmodule Hyper.Grpc do
1515
1616
## Serving
1717
18-
The server is started by `Hyper.Application` when enabled in config. It is
19-
stateless and may run on every node; placement and routing are cluster-wide.
20-
See `Hyper.Grpc.Config` for configuration — operators own the listener (port,
21-
TLS, adapter options) entirely from their own `config/`.
18+
The server is always started by `Hyper.Application` — it is a core interface,
19+
not an add-on, and an idle listener costs next to nothing. It is stateless and
20+
runs on every node; placement and routing are cluster-wide. See
21+
`Hyper.Grpc.Config` for configuration — operators own the listener (port, TLS,
22+
adapter options) entirely from their own `config/`.
2223
2324
## Connecting from the BEAM
2425
@@ -35,58 +36,50 @@ defmodule Hyper.Grpc do
3536
gRPC server configuration, read from application env:
3637
3738
config :hyper, Hyper.Grpc,
38-
enabled: true,
3939
port: 50_051,
4040
# any other GRPC.Server.Supervisor option, e.g. TLS:
4141
cred: GRPC.Credential.new(ssl: [certfile: "/path/cert.pem", keyfile: "/path/key.pem"])
4242
43-
`:enabled` (default `false`) gates whether the server starts. Every other
44-
key is passed straight through to `GRPC.Server.Supervisor`, so operators
45-
control the listener — port, TLS credentials, adapter options, body limits —
46-
entirely from their own config. Hyper prescribes nothing beyond defaulting
47-
`:port` and pointing the supervisor at `Hyper.Grpc.Endpoint`.
43+
The server always runs (it is a core interface, not opt-in); this config
44+
only tunes the listener. Every key is passed straight through to
45+
`GRPC.Server.Supervisor`, so operators control port, TLS credentials,
46+
adapter options, and body limits entirely from their own config. Hyper
47+
prescribes nothing beyond defaulting `:port` and pointing the supervisor at
48+
`Hyper.Grpc.Endpoint`.
4849
4950
Load secrets however you like: put cert/key paths in `config/runtime.exs` and
5051
build the credential there, or read them from a vault — Hyper never touches
5152
the filesystem on your behalf.
53+
54+
> #### Co-located nodes {: .info}
55+
>
56+
> Every node binds `:port`. Running multiple nodes on one host (e.g. a local
57+
> cluster) requires giving each a distinct port via its own config.
5258
"""
5359

5460
@default_port 50_051
5561

56-
@doc "Whether the gRPC server should start."
57-
@spec enabled?() :: boolean()
58-
def enabled?, do: Keyword.get(all(), :enabled, false)
59-
6062
@doc """
6163
The options spliced into the `GRPC.Server.Supervisor` child: the operator's
62-
config minus `:enabled`, with the endpoint, `start_server`, and a default
63-
port filled in if absent.
64+
config, with the endpoint, `start_server`, and a default port filled in if
65+
absent.
6466
"""
6567
@spec server_options() :: keyword()
6668
def server_options do
67-
all()
68-
|> Keyword.delete(:enabled)
69+
Application.get_env(:hyper, Hyper.Grpc, [])
6970
|> Keyword.put_new(:endpoint, Hyper.Grpc.Endpoint)
7071
|> Keyword.put_new(:start_server, true)
7172
|> Keyword.put_new(:port, @default_port)
7273
end
73-
74-
@spec all() :: keyword()
75-
defp all, do: Application.get_env(:hyper, Hyper.Grpc, [])
7674
end
7775

7876
@doc """
79-
The supervisor children for the gRPC server: empty unless the server is
80-
enabled (see `Hyper.Grpc.Config`). Spliced into the app supervision tree by
81-
`Hyper.Application`.
77+
The gRPC server's supervisor child. Always present — the server is a core
78+
interface, started unconditionally by `Hyper.Application`.
8279
"""
83-
@spec server_children() :: [Supervisor.child_spec() | {module(), term()}]
80+
@spec server_children() :: [{module(), keyword()}]
8481
def server_children do
85-
if Config.enabled?() do
86-
[{GRPC.Server.Supervisor, Config.server_options()}]
87-
else
88-
[]
89-
end
82+
[{GRPC.Server.Supervisor, Config.server_options()}]
9083
end
9184

9285
@doc """

0 commit comments

Comments
 (0)