# Copyright(c) 2015-2018 ACCESS CO., LTD. All rights reserved. use Croma defmodule Antikythera.Env do @moduledoc """ Module to provide helpers to access environment variables defined by antikythera. ## Environments and deployments Antikythera instances and gears may run in the following two modes: - As a mix project, when invoked by running `iex` or `mix` command-line tool. - As an OTP release, which is generated by e.g. `antikythera_core.generate_release` task. In general there are multiple deployments per antikythera instance. To distingish the target deployment from code, antikythera defines the following environment variables: - At compile time (for metaprogramming): `ANTIKYTHERA_COMPILE_ENV` must be appropriately set; the value can be retrieved by `compile_env/0`. - At runtime: `ANTIKYTHERA_RUNTIME_ENV` must be appropriately set; the value can be retrieved by `runtime_env/0`. It is the responsibility of antikythera instance administrators to correctly set these environment variables when compiling/running antikythera and gears. Possible values returned by `compile_env/0` and `runtime_env/0` are: - names of deployments given by `:deployments` application config - `:local` (running an OTP release at local machine for testing purpose) - `:undefined` As an example, if you set `:dev` and `:prod` in `:deployments`, then you get: | | Mix.env() at compile-time | Mix.env() at runtime | compile_env(), runtime_env() | |---------------------------+----------------------------------------+-------------------------+------------------------------| | $ iex -S mix | :dev (toplevel), :prod (dependencies) | :dev | :undefined | | $ mix test | :test (toplevel), :prod (dependencies) | :test | :undefined | | $ mix antikythera_local.* | :prod | (:mix is not available) | :local | | :dev deployment | :prod | (:mix is not available) | :dev | | :prod deployment | :prod | (:mix is not available) | :prod | You can use these values to distinguish the current context from your code. """ deployment_envs = Application.fetch_env!(:antikythera, :deployments) |> Keyword.keys() use Croma.SubtypeOfAtom, values: (deployment_envs ++ [:local, :undefined]) alias Antikythera.{GearName, GearNameStr, Url} alias AntikytheraCore.Handler.CowboyRouting, as: Routing defmodule Mapping do Enum.each(deployment_envs, fn env -> env_str = Atom.to_string(env) def env_var_to_atom(unquote(env_str)), do: unquote(env) end) def env_var_to_atom("local"), do: :local def env_var_to_atom(_ ), do: :undefined Enum.each(deployment_envs, fn env -> def cloud?(unquote(env)), do: true end) def cloud?(_), do: false end defun no_listen?() :: boolean do case System.get_env() do %{"NO_LISTEN" => "true"} -> true %{"TEST_MODE" => "blackbox_" <> _} -> true _otherwise -> false end end defun runtime_env() :: t , do: System.get_env("ANTIKYTHERA_RUNTIME_ENV") |> Mapping.env_var_to_atom() defun running_with_release?() :: boolean, do: runtime_env() != :undefined defun running_in_cloud?() :: boolean, do: Mapping.cloud?(runtime_env()) @compile_env System.get_env("ANTIKYTHERA_COMPILE_ENV") |> Mapping.env_var_to_atom() @compiling_for_release? @compile_env != :undefined @compiling_for_cloud? Mapping.cloud?(@compile_env) defun compile_env() :: t , do: @compile_env defun compiling_for_release?() :: boolean, do: @compiling_for_release? defun compiling_for_cloud?() :: boolean, do: @compiling_for_cloud? @antikythera_instance_name Application.fetch_env!(:antikythera, :antikythera_instance_name) defun antikythera_instance_name() :: atom, do: @antikythera_instance_name @gear_action_timeout_default 10_000 @gear_action_timeout String.to_integer(System.get_env("GEAR_ACTION_TIMEOUT") || "#{@gear_action_timeout_default}") @doc """ Timeout (in milli-seconds) for gear actions. This can be configurable by specifying `"GEAR_ACTION_TIMEOUT"` environment variable when compiling antikythera. Defaults to `#{@gear_action_timeout_default}`. """ defun gear_action_timeout() :: pos_integer, do: @gear_action_timeout @default_port 8080 @default_port_during_test 8081 # To see which port number to use, we have to get the current mix environment at runtime, # since `Mix.env()` always returns `:prod` during compilation of antikythera within a gear project. # However, when running with an OTP release, `Mix.env/0` is not available at runtime # (as `:mix` is not included in antikythera's runtime dependencies). # Therefore we need both compile-time- and runtime-branching. if @compiling_for_release? do defp default_port_at_runtime(), do: @default_port else defp default_port_at_runtime(), do: (if Mix.env() == :test, do: @default_port_during_test, else: @default_port) end @doc """ TCP port to listen to for incoming web requests. The port can be specified by `"PORT"` runtime environment variable. Defaults to `#{@default_port_during_test}` during `mix test`, and `#{@default_port}` otherwise (thus one can run both `iex -S mix` and `mix test` at the same time). """ defun port_to_listen() :: non_neg_integer do case System.get_env("PORT") do nil -> default_port_at_runtime() port_str -> String.to_integer(port_str) end end defun default_base_url(gear_name :: v[GearName.t | GearNameStr.t], env :: v[t] \\ @compile_env) :: Url.t do if Mapping.cloud?(env) do "https://" <> Routing.default_domain(gear_name, env) else "http://" <> Routing.default_domain(gear_name, env) <> ":#{port_to_listen()}" end end end