Skip to content

Commit c456749

Browse files
refactor(vm): extract Hyper.Vm.Id with firecracker-safe ids (#33)
1 parent 47dec42 commit c456749

15 files changed

Lines changed: 77 additions & 34 deletions

File tree

lib/hyper.ex

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ defmodule Hyper do
1818
@spec create_vm(Hyper.Vm.Spec.t()) :: {:ok, Hyper.Vm.t()} | {:error, term()}
1919
def create_vm(%Hyper.Vm.Spec{} = spec) do
2020
with {:ok, arch} <- resolve_arch(spec.arch) do
21-
vm_id = gen_vm_id()
21+
vm_id = Hyper.Vm.Id.generate()
2222
spec = %{spec | arch: arch}
2323
instance_spec = Hyper.Vm.Instance.spec(spec.type)
2424

@@ -34,17 +34,13 @@ defmodule Hyper do
3434
end
3535
end
3636

37-
@doc "Generate a fresh VM id (url-safe base64, dm-name compatible)."
38-
@spec gen_vm_id() :: Hyper.Vm.id()
39-
def gen_vm_id, do: Base.url_encode64(:crypto.strong_rand_bytes(9), padding: false)
40-
4137
@spec resolve_arch(Hyper.Vm.Instance.arch() | nil) ::
4238
{:ok, Hyper.Vm.Instance.arch()} | {:error, term()}
4339
defp resolve_arch(nil), do: Sys.Arch.current()
4440
defp resolve_arch(arch), do: {:ok, arch}
4541

4642
@doc "Cluster-wide: which node currently runs `vm_id`? `nil` if unknown."
47-
@spec whereis(Hyper.Vm.id()) :: node() | nil
43+
@spec whereis(Hyper.Vm.Id.t()) :: node() | nil
4844
def whereis(vm_id), do: Hyper.Cluster.Routing.whereis(vm_id)
4945

5046
@doc """
@@ -57,7 +53,7 @@ defmodule Hyper do
5753
died with its host, so "unknown" is the truthful answer. Only `:erpc`'s own
5854
transport failures are swallowed; a genuine fault in the lookup still raises.
5955
"""
60-
@spec id(Hyper.Vm.t()) :: Hyper.Vm.id() | nil
56+
@spec id(Hyper.Vm.t()) :: Hyper.Vm.Id.t() | nil
6157
def id(pid) when is_pid(pid) do
6258
:erpc.call(node(pid), Hyper.Cluster.Routing, :id_for, [pid])
6359
catch

lib/hyper/cluster/routing.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ defmodule Hyper.Cluster.Routing do
2929
def via(key), do: {:via, Horde.Registry, {@name, key}}
3030

3131
@doc "Which node currently runs `vm_id`? `nil` if unknown."
32-
@spec whereis(Hyper.Vm.id()) :: node() | nil
32+
@spec whereis(Hyper.Vm.Id.t()) :: node() | nil
3333
@decorate with_span("Hyper.Cluster.Routing.whereis", include: [:vm_id])
3434
def whereis(vm_id) do
3535
case Horde.Registry.lookup(@name, {vm_id, :supervisor}) do
@@ -43,7 +43,7 @@ defmodule Hyper.Cluster.Routing do
4343
replica via a registry match spec; intended to be called on the node that owns
4444
`pid` (see `Hyper.id/1`).
4545
"""
46-
@spec id_for(pid()) :: Hyper.Vm.id() | nil
46+
@spec id_for(pid()) :: Hyper.Vm.Id.t() | nil
4747
@decorate with_span("Hyper.Cluster.Routing.id_for")
4848
def id_for(pid) when is_pid(pid) do
4949
case Horde.Registry.select(@name, [
@@ -55,7 +55,7 @@ defmodule Hyper.Cluster.Routing do
5555
end
5656

5757
@doc "Every VM the cluster currently knows about, paired with the node it runs on."
58-
@spec all() :: [{Hyper.Vm.id(), node()}]
58+
@spec all() :: [{Hyper.Vm.Id.t(), node()}]
5959
@decorate with_span("Hyper.Cluster.Routing.all")
6060
def all do
6161
@name

lib/hyper/grpc/codec.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ defmodule Hyper.Grpc.Codec do
8585
end
8686

8787
@doc "Convert a domain result to an outbound response message, or an error to `GRPC.RPCError`."
88-
@spec to_grpc({:created, Hyper.Vm.id(), node()}) :: CreateVmResponse.t()
88+
@spec to_grpc({:created, Hyper.Vm.Id.t(), node()}) :: CreateVmResponse.t()
8989
def to_grpc({:created, vm_id, node}) when is_binary(vm_id),
9090
do: %CreateVmResponse{vm_id: vm_id, node: to_string(node)}
9191

92-
@spec to_grpc({:located, Hyper.Vm.id(), node()}) :: GetVmResponse.t()
92+
@spec to_grpc({:located, Hyper.Vm.Id.t(), node()}) :: GetVmResponse.t()
9393
def to_grpc({:located, vm_id, node}),
9494
do: %GetVmResponse{vm_id: vm_id, node: to_string(node)}
9595

96-
@spec to_grpc({:vms, [{Hyper.Vm.id(), node()}]}) :: ListVmsResponse.t()
96+
@spec to_grpc({:vms, [{Hyper.Vm.Id.t(), node()}]}) :: ListVmsResponse.t()
9797
def to_grpc({:vms, vms}),
9898
do: %ListVmsResponse{vms: Enum.map(vms, &vm/1)}
9999

@@ -117,7 +117,7 @@ defmodule Hyper.Grpc.Codec do
117117
def to_grpc({:exit, {:nodedown, _}}), do: rpc_error(:machine_unreachable)
118118
def to_grpc({:exit, reason}), do: rpc_error({:stop_failed, reason})
119119

120-
@spec vm({Hyper.Vm.id(), node()}) :: Vm.t()
120+
@spec vm({Hyper.Vm.Id.t(), node()}) :: Vm.t()
121121
defp vm({vm_id, node}), do: %Vm{vm_id: vm_id, node: to_string(node)}
122122

123123
@spec instance_type(instance_enum()) :: {:ok, Hyper.Vm.Instance.t()}

lib/hyper/img/db/lease.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ defmodule Hyper.Img.Db.Lease do
4949
Upserts on `(node_id, vm_id)` - the same call both takes a fresh lease and
5050
heartbeats a live one.
5151
"""
52-
@spec bump(Hyper.Img.id(), Hyper.Vm.id(), Unit.Time.t()) ::
52+
@spec bump(Hyper.Img.id(), Hyper.Vm.Id.t(), Unit.Time.t()) ::
5353
{:ok, %__MODULE__{}} | {:error, Ecto.Changeset.t()}
5454
@decorate with_span("Hyper.Img.Db.Lease.bump", include: [:image_id, :vm_id])
5555
def bump(image_id, vm_id, ttl) do
@@ -72,7 +72,7 @@ defmodule Hyper.Img.Db.Lease do
7272
Release the lease issued to the given node_id and the given vm_id. Since each VM only ever uses
7373
one image, it is not necessary to specify the image id.
7474
"""
75-
@spec release(Hyper.Vm.id()) :: :ok
75+
@spec release(Hyper.Vm.Id.t()) :: :ok
7676
@decorate with_span("Hyper.Img.Db.Lease.release", include: [:vm_id])
7777
def release(vm_id) do
7878
query = from(l in __MODULE__, where: l.node_id == ^to_string(node()) and l.vm_id == ^vm_id)

lib/hyper/node.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ defmodule Hyper.Node do
6363
layer, resolve the kernel, and start the VM supervisor. The uid is freed and
6464
the mutable layer torn down automatically when the VM supervisor dies.
6565
"""
66-
@spec start_image_vm(Hyper.Vm.id(), Hyper.Vm.Spec.t()) :: {:ok, pid()} | {:error, term()}
66+
@spec start_image_vm(Hyper.Vm.Id.t(), Hyper.Vm.Spec.t()) :: {:ok, pid()} | {:error, term()}
6767
@decorate with_span("Hyper.Node.start_image_vm", include: [:vm_id, :spec])
6868
def start_image_vm(vm_id, %Hyper.Vm.Spec{} = spec) do
6969
with {:ok, uid} <- Users.claim(),
@@ -89,7 +89,7 @@ defmodule Hyper.Node do
8989
end
9090

9191
@doc false
92-
@spec build_opts(Hyper.Vm.id(), Hyper.Vm.Spec.t(), Users.id(), pid(), Path.t()) ::
92+
@spec build_opts(Hyper.Vm.Id.t(), Hyper.Vm.Spec.t(), Users.id(), pid(), Path.t()) ::
9393
FireVMM.Opts.t()
9494
def build_opts(vm_id, %Hyper.Vm.Spec{} = spec, uid, mutable, kernel) do
9595
%FireVMM.Opts{

lib/hyper/node/fire_vmm.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ defmodule Hyper.Node.FireVMM do
3434
defstruct [:vm_id, :uid, :gid, :type, :arch, :mutable, :kernel, :boot_args]
3535

3636
@type t :: %__MODULE__{
37-
vm_id: Hyper.Vm.id(),
37+
vm_id: Hyper.Vm.Id.t(),
3838
uid: Hyper.Node.Users.id(),
3939
gid: Hyper.Node.Users.id(),
4040
type: Hyper.Vm.Instance.t(),

lib/hyper/node/fire_vmm/chroot_jail.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ defmodule Hyper.Node.FireVMM.ChrootJail do
2727
`uid:gid`), and return `cold` with its kernel + rootfs paths rewritten to their
2828
in-jail equivalents. Fails the boot if either artifact cannot be staged.
2929
"""
30-
@spec stage(Hyper.Vm.id(), non_neg_integer(), non_neg_integer(), Cold.t()) ::
30+
@spec stage(Hyper.Vm.Id.t(), non_neg_integer(), non_neg_integer(), Cold.t()) ::
3131
{:ok, Cold.t()} | {:error, term()}
3232
@decorate with_span("Hyper.Node.FireVMM.ChrootJail.stage", include: [:vm_id])
3333
def stage(vm_id, uid, gid, %Cold{} = cold) do

lib/hyper/node/fire_vmm/client.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ defmodule Hyper.Node.FireVMM.Client do
6666
GenServer.start_link(__MODULE__, opts, gen_opts(name))
6767
end
6868

69-
@spec via(Hyper.Vm.id()) :: GenServer.name()
69+
@spec via(Hyper.Vm.Id.t()) :: GenServer.name()
7070
def via(vm_id), do: Hyper.Cluster.Routing.via({vm_id, :client})
7171

7272
# Cap on a single Firecracker API call.

lib/hyper/node/fire_vmm/jailer.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ defmodule Hyper.Node.FireVMM.Jailer do
125125
end
126126

127127
@doc "Host path of the VM's per-VM jail dir (`<chroot_base>/<exec>/<id>`)."
128-
@spec chroot_dir(Hyper.Vm.id()) :: Path.t()
128+
@spec chroot_dir(Hyper.Vm.Id.t()) :: Path.t()
129129
def chroot_dir(id) do
130130
Path.join([Hyper.Cfg.Dirs.chroot_base(), exec_name(), id])
131131
end
132132

133133
@doc "Host path of the VM's chroot root (`<chroot_base>/<exec>/<id>/root`)."
134-
@spec chroot_root(Hyper.Vm.id()) :: Path.t()
134+
@spec chroot_root(Hyper.Vm.Id.t()) :: Path.t()
135135
def chroot_root(id) do
136136
Path.join(chroot_dir(id), "root")
137137
end
@@ -141,7 +141,7 @@ defmodule Hyper.Node.FireVMM.Jailer do
141141
cgroup the jailer creates for firecracker. Reconstructed (the jailer owns its
142142
placement) so a relaunch can clear the stale leaf left by a prior incarnation.
143143
"""
144-
@spec cgroup_dir(Hyper.Vm.id()) :: Path.t()
144+
@spec cgroup_dir(Hyper.Vm.Id.t()) :: Path.t()
145145
def cgroup_dir(id) do
146146
Path.join(["/sys/fs/cgroup", Hyper.Cfg.Jails.cgroup(), exec_name(), id])
147147
end
@@ -153,7 +153,7 @@ defmodule Hyper.Node.FireVMM.Jailer do
153153
derive it independently and are guaranteed to agree. We do not control where
154154
the jailer places the socket, so the path is reconstructed here.
155155
"""
156-
@spec host_socket(Hyper.Vm.id()) :: Path.t()
156+
@spec host_socket(Hyper.Vm.Id.t()) :: Path.t()
157157
def host_socket(id) do
158158
Path.join([
159159
Hyper.Cfg.Dirs.chroot_base(),

lib/hyper/node/fire_vmm/state.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ defmodule Hyper.Node.FireVMM.State do
5858
:gen_statem.start_link(via(id), __MODULE__, opts, [])
5959
end
6060

61-
@spec stop(Hyper.Vm.id()) :: :ok
61+
@spec stop(Hyper.Vm.Id.t()) :: :ok
6262
def stop(id) do
6363
:gen_statem.call(via(id), :stop)
6464
end
@@ -174,7 +174,7 @@ defmodule Hyper.Node.FireVMM.State do
174174

175175
# Cold boot, issued through the Client and aborting at the first error:
176176
# machine-config -> boot-source -> each drive -> each NIC -> InstanceStart.
177-
@spec apply_spec(Hyper.Vm.id(), BootSpec.Cold.t()) :: :ok | {:error, term()}
177+
@spec apply_spec(Hyper.Vm.Id.t(), BootSpec.Cold.t()) :: :ok | {:error, term()}
178178
@decorate with_span("Hyper.Node.FireVMM.State.Configuring.apply_spec", include: [:id])
179179
defp apply_spec(id, %BootSpec.Cold{} = cold) do
180180
via = Client.via(id)

0 commit comments

Comments
 (0)