From 3bc15eb87ba4f37b2b1d1163dcec273ba1aa129b Mon Sep 17 00:00:00 2001 From: Brian Underwood Date: Tue, 27 Jun 2023 09:51:10 +0200 Subject: [PATCH] Allow specifying inet_address_family to support inet/inet6/local --- lib/telemetry_metrics_statsd.ex | 30 ++++++++++++++++++------- lib/telemetry_metrics_statsd/options.ex | 5 +++++ lib/telemetry_metrics_statsd/udp.ex | 17 +++++++------- test/telemetry_metrics_statsd_test.exs | 20 +++++++++++++++-- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/telemetry_metrics_statsd.ex b/lib/telemetry_metrics_statsd.ex index 0ac2a99..93a7862 100644 --- a/lib/telemetry_metrics_statsd.ex +++ b/lib/telemetry_metrics_statsd.ex @@ -527,20 +527,34 @@ defmodule TelemetryMetricsStatsd do %{state | udp_config: %{udp_config | host: new_address}} end - defp configure_host_resolution(%{host: host, port: port}) when is_tuple(host) do - %{host: host, port: port} + defp configure_host_resolution(%{ + host: host, + port: port, + inet_address_family: inet_address_family + }) + when is_tuple(host) do + %{host: host, port: port, inet_address_family: inet_address_family} end - defp configure_host_resolution(%{host: host, port: port, host_resolution_interval: interval}) + defp configure_host_resolution(%{ + host: host, + port: port, + inet_address_family: inet_address_family, + host_resolution_interval: interval + }) when is_integer(interval) do - {:ok, hostent(h_addr_list: [ip | _ips])} = :inet.gethostbyname(host) + {:ok, hostent(h_addr_list: [ip | _ips])} = :inet.gethostbyname(host, inet_address_family) Process.send_after(self(), :resolve_host, interval) - %{host: ip, port: port} + %{host: ip, port: port, inet_address_family: inet_address_family} end - defp configure_host_resolution(%{host: host, port: port}) do - {:ok, hostent(h_addr_list: [ip | _ips])} = :inet.gethostbyname(host) - %{host: ip, port: port} + defp configure_host_resolution(%{ + host: host, + port: port, + inet_address_family: inet_address_family + }) do + {:ok, hostent(h_addr_list: [ip | _ips])} = :inet.gethostbyname(host, inet_address_family) + %{host: ip, port: port, inet_address_family: inet_address_family} end defp update_pool(pool_id, new_host, new_port) do diff --git a/lib/telemetry_metrics_statsd/options.ex b/lib/telemetry_metrics_statsd/options.ex index c547b70..5039132 100644 --- a/lib/telemetry_metrics_statsd/options.ex +++ b/lib/telemetry_metrics_statsd/options.ex @@ -21,6 +21,11 @@ defmodule TelemetryMetricsStatsd.Options do default: 8125, doc: "Port of the StatsD server." ], + inet_address_family: [ + type: {:in, [:inet, :inet6, :local]}, + default: :inet, + doc: "The inet address family, as specified by the Erlang `:inet.address_family type()`." + ], socket_path: [ type: {:custom, __MODULE__, :socket_path, []}, doc: "Path to the Unix Domain Socket used for publishing instead of the hostname and port." diff --git a/lib/telemetry_metrics_statsd/udp.ex b/lib/telemetry_metrics_statsd/udp.ex index bd7ac7b..31198f8 100644 --- a/lib/telemetry_metrics_statsd/udp.ex +++ b/lib/telemetry_metrics_statsd/udp.ex @@ -11,22 +11,21 @@ defmodule TelemetryMetricsStatsd.UDP do @type config :: %{ :host => :inet.hostname() | :inet.ip_address() | :inet.local_address(), - optional(:port) => :inet.port_number() + optional(:port) => :inet.port_number(), + optional(:inet_address_family) => boolean() } @spec open(config()) :: {:ok, t()} | {:error, reason :: term()} def open(config) do - opts = [active: false] + opts = [{:active, false}] opts = - case config.host do - {:local, _} -> - [:local | opts] - - _ -> - opts - end + Enum.reduce(config, opts, fn + {:host, {:local, _}}, opts -> [:local | opts] + {:inet_address_family, value}, opts -> [value | opts] + {_key, _value}, opts -> opts + end) case :gen_udp.open(0, opts) do {:ok, socket} -> diff --git a/test/telemetry_metrics_statsd_test.exs b/test/telemetry_metrics_statsd_test.exs index 38ce6b2..f22e4af 100644 --- a/test/telemetry_metrics_statsd_test.exs +++ b/test/telemetry_metrics_statsd_test.exs @@ -625,6 +625,22 @@ defmodule TelemetryMetricsStatsdTest do assert udp.host == {127, 0, 0, 1} end + test "Supports IPv6" do + counter = given_counter("http.request.count") + + reporter = + start_reporter( + host: "::1", + inet_address_family: :inet6, + metrics: [counter] + ) + + pool_id = TelemetryMetricsStatsd.get_pool_id(reporter) + + {:ok, udp} = TelemetryMetricsStatsd.get_udp(pool_id) + assert udp.host == {0, 0, 0, 0, 0, 0, 0, 1} + end + test "is not periodically repeated by default" do counter = given_counter("http.request.count") @@ -678,8 +694,8 @@ defmodule TelemetryMetricsStatsdTest do end end - defp given_udp_port_opened() do - {:ok, socket} = :gen_udp.open(0, [:binary, active: false]) + defp given_udp_port_opened(inet_address_family \\ :inet) do + {:ok, socket} = :gen_udp.open(0, [:binary, inet_address_family, active: false]) {:ok, port} = :inet.port(socket) {socket, port} end