defmodule ExecutionPlane.Process.Transport.Surface.Registry do @moduledoc """ Internal registry of built-in execution-surface adapters. """ alias ExecutionPlane.Contracts.AdapterSelectionPolicy.V1, as: AdapterSelectionPolicy @base_adapters %{ lower_simulation: ExecutionPlane.Process.Transport.LowerSimulation, local_subprocess: ExecutionPlane.Process.Transport.LocalSubprocess, ssh_exec: ExecutionPlane.Process.Transport.SSHExec, guest_bridge: ExecutionPlane.Process.Transport.GuestBridge } @optional_adapters %{ test_guest_local: ExecutionPlane.TestSupport.GuestLocalAdapter, test_restricted_spawn: ExecutionPlane.TestSupport.RestrictedSpawnAdapter } @type fetch_error :: {:unsupported_surface_kind, atom() | term()} @spec adapter_selection_policy() :: AdapterSelectionPolicy.t() def adapter_selection_policy do AdapterSelectionPolicy.new!(%{ selection_surface: "adapter_registry", owner_repo: "execution_plane", config_key: "execution_plane.process_transport.surface_registry", default_value_when_unset: "local_subprocess", fail_closed_action_when_misconfigured: "reject_surface_resolution" }) end @spec supported_surface_kinds() :: [atom(), ...] def supported_surface_kinds do adapters() |> Map.keys() |> Enum.sort() end @spec registered?(term()) :: boolean() def registered?(surface_kind) when is_atom(surface_kind), do: Map.has_key?(adapters(), surface_kind) def registered?(_other), do: false @spec fetch(term()) :: {:ok, module()} | {:error, fetch_error()} def fetch(surface_kind) when is_atom(surface_kind) do case Map.fetch(adapters(), surface_kind) do {:ok, adapter} -> {:ok, adapter} :error -> {:error, {:unsupported_surface_kind, surface_kind}} end end def fetch(surface_kind), do: {:error, {:unsupported_surface_kind, surface_kind}} defp adapters do Enum.reduce(@optional_adapters, @base_adapters, fn {surface_kind, adapter}, acc -> if Code.ensure_loaded?(adapter) do Map.put(acc, surface_kind, adapter) else acc end end) end end