Skip to content

Commit

Permalink
Refactor jellyfish configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
mickel8 committed Sep 18, 2023
1 parent a9d88f5 commit 3b2008b
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 168 deletions.
6 changes: 3 additions & 3 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ config :jellyfish, server_api_token: "development", dev_routes: true

# For development, we disable any cache and enable
# debugging and code reloading.
# We set ip and port in the runtime.exs to allow
# for spawning more thane one Jellyfish instance.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with esbuild to bundle .js and .css sources.
config :jellyfish, JellyfishWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 5002],
check_origin: false,
code_reloader: true,
debug_errors: true,
secret_key_base: "eUQ+pDd8FmAwDrE4taJYgcYtaMqFMLygkVRBPfH8G98U1aveWB3Oa9TkOoehK61t",
watchers: []

# ## SSL Support
Expand Down
4 changes: 3 additions & 1 deletion config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import Config
config :logger, level: :info

# run the server automatically when using prod release
config :jellyfish, JellyfishWeb.Endpoint, server: true
config :jellyfish, JellyfishWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 8080],
server: true

# Runtime production configuration, including reading
# of environment variables, is done on config/runtime.exs.
199 changes: 36 additions & 163 deletions config/runtime.exs
Original file line number Diff line number Diff line change
@@ -1,74 +1,19 @@
import Config

alias Jellyfish.ConfigReader

# config/runtime.exs is executed for all environments, including
# during releases. It is executed after compilation and before the
# system starts, so it is typically used to load production configuration
# and secrets from environment variables or elsewhere. Do not define
# any compile-time configuration in here, as it won't be applied.
# The block below contains prod specific runtime configuration.
defmodule ConfigParser do
def parse_integrated_turn_port_range(range) do
with [str1, str2] <- String.split(range, "-"),
from when from in 0..65_535 <- String.to_integer(str1),
to when to in from..65_535 and from <= to <- String.to_integer(str2) do
{from, to}
else
_else ->
raise("""
Bad INTEGRATED_TURN_PORT_RANGE environment variable value. Expected "from-to", where `from` and `to` \
are numbers between 0 and 65535 and `from` is not bigger than `to`, got: \
#{inspect(range)}
""")
end
end

def parse_ip(var_value, var_name) do
var_value = var_value |> to_charlist()

case :inet.parse_address(var_value) do
{:ok, parsed_ip} ->
parsed_ip

_error ->
with {:ok, parsed_ip} <- :inet.getaddr(var_value, :inet) do
parsed_ip
else
_error ->
raise("""
Bad #{var_name} environment variable value. Expected valid ip address, got: #{inspect(var_value)}
""")
end
end
end

def parse_port_number(nil, _var_name), do: nil

def parse_port_number(var_value, var_name) do
with {port, _sufix} when port in 1..65535 <- Integer.parse(var_value) do
port
else
_var ->
raise(
"Bad #{var_name} environment variable value. Expected valid port number, got: #{inspect(var_value)}"
)
end
end

def get_env!(env_key) do
case System.get_env(env_key) do
nil -> raise("Environmental variable #{env_key} was not set properly")
env_val -> env_val
end
end
end
config :ex_dtls, impl: :nif
config :opentelemetry, traces_exporter: :none

hosts =
System.get_env("NODES", "")
|> String.split(" ")
|> Enum.reject(&(&1 == ""))
|> Enum.map(&String.to_atom(&1))
hosts = ConfigReader.read_nodes("NODES")

unless Enum.empty?(hosts) do
if hosts do
config :libcluster,
topologies: [
epmd_cluster: [
Expand All @@ -81,122 +26,50 @@ end
prod? = config_env() == :prod

host =
case System.get_env("VIRTUAL_HOST") do
nil when prod? -> raise "Unset VIRTUAL_HOST environment variable"
case System.get_env("HOST") do
nil when prod? -> raise "Unset HOST environment variable"
nil -> "localhost"
other -> other
end

port =
case System.get_env("PORT") do
nil when prod? -> raise "Unset PORT environment variable"
nil -> 5002
other -> String.to_integer(other)
end

jellyfish_address = System.get_env("JELLYFISH_ADDRESS") || "#{host}:#{port}"

config :ex_dtls, impl: :nif
ConfigReader.read_port("PORT") ||
Application.get_env(:jellyfish, JellyfishWeb.Endpoint)[:http][:port]

config :jellyfish,
webrtc_used: String.downcase(System.get_env("WEBRTC_USED", "true")) not in ["false", "f", "0"],
integrated_turn_ip:
System.get_env("INTEGRATED_TURN_IP", "127.0.0.1")
|> ConfigParser.parse_ip("INTEGRATED_TURN_IP"),
integrated_turn_listen_ip:
System.get_env("INTEGRATED_TURN_LISTEN_IP", "127.0.0.1")
|> ConfigParser.parse_ip("INTEGRATED_TURN_LISTEN_IP"),
webrtc_used: ConfigReader.read_boolean("WEBRTC_USED"),
integrated_turn_ip: ConfigReader.read_ip("INTEGRATED_TURN_IP") || {127, 0, 0, 1},
integrated_turn_listen_ip: ConfigReader.read_ip("INTEGRATED_TURN_LISTEN_IP") || {127, 0, 0, 1},
integrated_turn_port_range:
System.get_env("INTEGRATED_TURN_PORT_RANGE", "50000-59999")
|> ConfigParser.parse_integrated_turn_port_range(),
integrated_turn_tcp_port:
System.get_env("INTEGRATED_TURN_TCP_PORT")
|> ConfigParser.parse_port_number("INTEGRATED_TURN_TCP_PORT"),
ConfigReader.read_port_range("INTEGRATED_TURN_PORT_RANGE") || {50_000, 59_999},
integrated_turn_tcp_port: ConfigReader.read_port("INTEGRATED_TURN_TCP_PORT"),
jwt_max_age: 24 * 3600,
output_base_path: System.get_env("OUTPUT_BASE_PATH", "jellyfish_output") |> Path.expand(),
address: jellyfish_address,
metrics_ip: System.get_env("METRICS_IP", "127.0.0.1") |> ConfigParser.parse_ip("METRICS_IP"),
metrics_port:
System.get_env("METRICS_PORT", "9568") |> ConfigParser.parse_port_number("METRICS_PORT")
address: System.get_env("JELLYFISH_ADDRESS") || "#{host}:#{port}",
metrics_ip: ConfigReader.read_ip("METRICS_IP") || {127, 0, 0, 1},
metrics_port: ConfigReader.read_port("METRICS_PORT") || 9568

config :opentelemetry, traces_exporter: :none
config :jellyfish, JellyfishWeb.Endpoint,
secret_key_base:
System.get_env("SECRET_KEY_BASE") || Base.encode64(:crypto.strong_rand_bytes(48)),
http: [port: port]

case config_env() do
env when env != :prod ->
# we set ip and port here to allow for
# running multiple Jellyfishes in development
config :jellyfish, JellyfishWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: port]
if check_origin = ConfigReader.read_boolean("CHECK_ORIGIN") do
config :jellyfish, JellyfishWeb.Endpoint, check_origin: check_origin
end

:prod ->
token =
System.fetch_env!("SERVER_API_TOKEN") ||
raise """
environment variable SERVER_API_TOKEN is missing.
SERVER_API_TOKEN is used for HTTP requests and
server WebSocket authorization.
"""
case System.get_env("SERVER_API_TOKEN") do
nil when prod? == true ->
raise """
environment variable SERVER_API_TOKEN is missing.
SERVER_API_TOKEN is used for HTTP requests and
server WebSocket authorization.
"""

token ->
config :jellyfish, server_api_token: token
end

# The secret key base is used to sign/encrypt cookies and other secrets.
# A default value is used in config/dev.exs and config/test.exs but you
# want to use a different value for prod and you most likely don't want
# to check this value into version control, so we use an environment
# variable instead.
secret_key_base =
System.get_env("SECRET_KEY_BASE") ||
raise """
environment variable SECRET_KEY_BASE is missing.
You can generate one by calling: mix phx.gen.secret
"""

check_origin? = System.get_env("CHECK_ORIGIN", "true") == "true"

config :jellyfish, JellyfishWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"],
check_origin: check_origin?,
http: [
# Enable IPv6 and bind on all interfaces.
# Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
# See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
# for details about using IPv6 vs IPv4 and loopback vs public addresses.
ip: {0, 0, 0, 0, 0, 0, 0, 0},
port: port
],
secret_key_base: secret_key_base

# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
# to your endpoint configuration:
#
# config :jellyfish, JellyfishWeb.Endpoint,
# https: [
# ...,
# port: 443,
# cipher_suite: :strong,
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
# ]
#
# The `cipher_suite` is set to `:strong` to support only the
# latest and more secure SSL ciphers. This means old browsers
# and clients may not be supported. You can set it to
# `:compatible` for wider support.
#
# `:keyfile` and `:certfile` expect an absolute path to the key
# and cert in disk or a relative path inside priv, for example
# "priv/ssl/server.key". For all supported SSL configuration
# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
#
# We also recommend setting `force_ssl` in your endpoint, ensuring
# no data is ever sent via http, always redirecting to https:
#
# config :jellyfish, JellyfishWeb.Endpoint,
# force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.
if prod? do
config :jellyfish, JellyfishWeb.Endpoint, url: [host: host, port: 443, scheme: "https"]
end
2 changes: 1 addition & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ config :jellyfish,
# We don't run a server during test. If one is required,
# you can enable the server option below.
config :jellyfish, JellyfishWeb.Endpoint,
secret_key_base: "DtVd7qfpae0tk5zRgAM75hOaCc+phk38gDFVvLPyqVN/vvVg0EPmksTSm5JcyjoJ",
http: [ip: {127, 0, 0, 1}, port: 4002],
server: false

# Print only warnings and errors during test
Expand Down
71 changes: 71 additions & 0 deletions lib/jellyfish/config_reader.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
defmodule Jellyfish.ConfigReader do
@moduledoc false

def read_port_range(env) do
if value = System.get_env(env) do
with [str1, str2] <- String.split(value, "-"),
from when from in 0..65_535 <- String.to_integer(str1),
to when to in from..65_535 and from <= to <- String.to_integer(str2) do
{from, to}
else
_else ->
raise("""
Bad #{env} environment variable value. Expected "from-to", where `from` and `to` \
are numbers between 0 and 65535 and `from` is not bigger than `to`, got: \
#{value}
""")
end
end
end

def read_ip(env) do
if value = System.get_env(env) do
value = value |> to_charlist()

case :inet.parse_address(value) do
{:ok, parsed_ip} ->
parsed_ip

_error ->
case :inet.getaddr(value, :inet) do
{:ok, parsed_ip} ->
parsed_ip

_error ->
raise("""
Bad #{env} environment variable value. Expected valid ip address, got: #{value}"
""")
end
end
end
end

def read_port(env) do
if value = System.get_env(env) do
case Integer.parse(value) do
{port, _sufix} when port in 1..65535 ->
port

_other ->
raise("""
Bad #{env} environment variable value. Expected valid port number, got: #{value}
""")
end
end
end

def read_nodes(env) do
if value = System.get_env(env) do
value
|> String.split(" ")
|> Enum.reject(&(&1 == ""))
|> Enum.map(&String.to_atom(&1))
end
end

def read_boolean(env) do
if value = System.get_env(env) do
String.downcase(value) not in ["false", "f", "0", nil]
end
end
end

0 comments on commit 3b2008b

Please sign in to comment.