Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make metrics ip and port, and http endpoint port configurable #87

Merged
merged 9 commits into from
Sep 20, 2023
1 change: 0 additions & 1 deletion config/ci.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ config :jellyfish, server_api_token: "development"
# you can enable the server option below.
config :jellyfish, JellyfishWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4002],
secret_key_base: "DtVd7qfpae0tk5zRgAM75hOaCc+phk38gDFVvLPyqVN/vvVg0EPmksTSm5JcyjoJ",
server: false

# Print only warnings and errors during test
Expand Down
1 change: 0 additions & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ config :jellyfish, JellyfishWeb.Endpoint,
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: {0, 0, 0, 0, 0, 0, 0, 0}, port: 8080],
server: true

# Runtime production configuration, including reading
# of environment variables, is done on config/runtime.exs.
190 changes: 39 additions & 151 deletions config/runtime.exs
Original file line number Diff line number Diff line change
@@ -1,75 +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_ip(addr) do
addr = addr |> to_charlist()

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

_error ->
with {:ok, parsed_ip} <- :inet.getaddr(addr, :inet) do
parsed_ip
else
_error ->
raise("""
Bad integrated TURN address. Expected IPv4 or a valid hostname, got: \
#{inspect(addr)}
""")
end
end
end

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_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
sgfn marked this conversation as resolved.
Show resolved Hide resolved
config :libcluster,
topologies: [
epmd_cluster: [
Expand All @@ -82,109 +26,53 @@ 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"
Rados13 marked this conversation as resolved.
Show resolved Hide resolved
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_integrated_turn_ip(),
integrated_turn_listen_ip:
System.get_env("INTEGRATED_TURN_LISTEN_IP", "127.0.0.1")
|> ConfigParser.parse_integrated_turn_ip(),
webrtc_used: ConfigReader.read_boolean("WEBRTC_USED") || true,
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
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

if prod? do
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.
"""
config :jellyfish, JellyfishWeb.Endpoint,
secret_key_base:
System.get_env("SECRET_KEY_BASE") || Base.encode64(:crypto.strong_rand_bytes(48)),
http: [port: port]
Rados13 marked this conversation as resolved.
Show resolved Hide resolved

config :jellyfish, server_api_token: token
if check_origin = ConfigReader.read_boolean("CHECK_ORIGIN") do
config :jellyfish, JellyfishWeb.Endpoint, check_origin: check_origin
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
"""
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.
"""

check_origin? = System.get_env("CHECK_ORIGIN", "true") == "true"
nil ->
:ok
Rados13 marked this conversation as resolved.
Show resolved Hide resolved

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
token ->
config :jellyfish, server_api_token: token
end

# ## 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
1 change: 0 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ config :jellyfish,
# you can enable the server option below.
config :jellyfish, JellyfishWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4002],
secret_key_base: "DtVd7qfpae0tk5zRgAM75hOaCc+phk38gDFVvLPyqVN/vvVg0EPmksTSm5JcyjoJ",
server: false

# Print only warnings and errors during test
Expand Down
3 changes: 1 addition & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ x-jellyfish-template: &jellyfish-template
environment: &jellyfish-environment
ERLANG_COOKIE: "panuozzo-pollo-e-pancetta"
SERVER_API_TOKEN: "development"
SECRET_KEY_BASE: "super-secret-key"
VIRTUAL_HOST: "localhost"
HOST: "localhost"
NODES: "app@app1 app@app2"
networks:
- net1
Expand Down
66 changes: 66 additions & 0 deletions lib/jellyfish/config_reader.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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 ->
raise("""
Bad #{env} environment variable value. Expected valid ip address, got: #{value}"
""")
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..65_535 ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it 1 here and 0 in read_port_range?

port

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

def read_nodes(env) do
value = System.get_env(env)

if value not in ["", nil] do
value
|> String.split(" ", trim: true)
|> 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"]
end
end
end
12 changes: 9 additions & 3 deletions lib/jellyfish_web/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ defmodule JellyfishWeb.Telemetry do

@impl true
def init(_arg) do
children = [
MetricsAggregator,
{TelemetryMetricsPrometheus, metrics: metrics(&last_value/2)}
metrics_ip = Application.fetch_env!(:jellyfish, :metrics_ip)
metrics_port = Application.fetch_env!(:jellyfish, :metrics_port)

metrics_opts = [
metrics: metrics(&last_value/2),
port: metrics_port,
plug_cowboy_opts: [ip: metrics_ip]
]

children = [MetricsAggregator, {TelemetryMetricsPrometheus, metrics_opts}]

Supervisor.init(children, strategy: :one_for_one)
end

Expand Down
Loading