defmodule GRPC.Server.Supervisor do @moduledoc """ A simple supervisor to start your servers. You can add it to your OTP tree as below. To start the server, you can pass `start_server: true` and an option defmodule Your.App do use Application def start(_type, _args) do children = [ {GRPC.Server.Supervisor, endpoint: Your.Endpoint, port: 50051, start_server: true, ...}] Supervisor.start_link(children, strategy: :one_for_one, name: __MODULE__) end end """ use Supervisor # TODO: remove this type after support Elixir 1.14 exclusively. @type sup_flags() :: %{ strategy: Supervisor.strategy(), intensity: non_neg_integer(), period: pos_integer() } @default_adapter GRPC.Server.Adapters.Cowboy require Logger def start_link(endpoint) do Supervisor.start_link(__MODULE__, endpoint) end @doc """ ## Options * `:endpoint` - defines the endpoint module that will be started. * `:port` - the HTTP port for the endpoint. * `:servers` - the list of servers that will be be started. Either `:endpoint` or `:servers` must be present, but not both. """ @spec init(tuple()) :: no_return @spec init(keyword()) :: {:ok, {sup_flags(), [Supervisor.child_spec()]}} | :ignore def init(opts) def init(opts) when is_tuple(opts) do raise ArgumentError, "passing a tuple as configuration for GRPC.Server.Supervisor is no longer supported. See the documentation for more information on how to configure." end def init(opts) when is_list(opts) do unless is_nil(Application.get_env(:grpc, :start_server)) do raise "the :start_server config key has been deprecated.\ The currently supported way is to configure it\ through the :start_server option for the GRPC.Server.Supervisor" end endpoint_or_servers = case {opts[:endpoint], opts[:servers]} do {endpoint, servers} when (not is_nil(endpoint) and not is_nil(servers)) or (is_nil(endpoint) and is_nil(servers)) -> raise ArgumentError, "either :endpoint or :servers must be passed, but not both." {endpoint, nil} -> endpoint {nil, servers} when not is_list(servers) -> raise ArgumentError, "either :servers must be a list of modules" {nil, servers} when is_list(servers) -> servers end children = if opts[:start_server] do [child_spec(endpoint_or_servers, opts[:port], opts)] else [] end Supervisor.init(children, strategy: :one_for_one) end @doc """ Return a child_spec to start server. ## Options * `:cred` - a credential created by functions of `GRPC.Credential`, an insecure server will be created without this option * `:start_server` - determines if the server will be started. If present, has more precedence then the `config :gprc, :start_server` config value (i.e. `start_server: false` will not start the server in any case). """ @spec child_spec(endpoint_or_servers :: atom() | [atom()], port :: integer, opts :: keyword()) :: Supervisor.Spec.spec() def child_spec(endpoint_or_servers, port, opts \\ []) def child_spec(endpoint, port, opts) when is_atom(endpoint) do {endpoint, servers} = try do {endpoint, endpoint.__meta__(:servers)} rescue FunctionClauseError -> Logger.warn( "deprecated: servers as argument of GRPC.Server.Supervisor, please use GRPC.Endpoint" ) {nil, endpoint} end adapter = Keyword.get(opts, :adapter) || @default_adapter servers = GRPC.Server.servers_to_map(servers) adapter.child_spec(endpoint, servers, port, opts) end def child_spec(servers, port, opts) when is_list(servers) do adapter = Keyword.get(opts, :adapter) || @default_adapter servers = GRPC.Server.servers_to_map(servers) adapter.child_spec(nil, servers, port, opts) end end