diff --git a/lib/new_relic/harvest/collector/connect.ex b/lib/new_relic/harvest/collector/connect.ex index a27fa1f7..2920e1fe 100644 --- a/lib/new_relic/harvest/collector/connect.ex +++ b/lib/new_relic/harvest/collector/connect.ex @@ -14,6 +14,7 @@ defmodule NewRelic.Harvest.Collector.Connect do %{label_type: key, label_value: value} end), utilization: NewRelic.Util.utilization(), + metadata: NewRelic.Util.metadata(), environment: NewRelic.Util.elixir_environment(), agent_version: NewRelic.Config.agent_version() } diff --git a/lib/new_relic/util.ex b/lib/new_relic/util.ex index 74d7c5a5..fcbafb34 100644 --- a/lib/new_relic/util.ex +++ b/lib/new_relic/util.ex @@ -4,7 +4,7 @@ defmodule NewRelic.Util do alias NewRelic.Util.Vendor def hostname do - Vendor.maybe_heroku_dyno_hostname() || get_hostname() + maybe_heroku_dyno_hostname() || get_hostname() end def pid, do: System.get_pid() |> String.to_integer() @@ -57,15 +57,59 @@ defmodule NewRelic.Util do ] end + @nr_metadata_prefix "NEW_RELIC_METADATA_" + def metadata() do + System.get_env() + |> Enum.filter(fn {key, _} -> String.starts_with?(key, @nr_metadata_prefix) end) + |> Enum.into(%{}) + end + def utilization() do %{ - metadata_version: 3, + metadata_version: 5, logical_processors: :erlang.system_info(:logical_processors), total_ram_mib: get_system_memory(), hostname: hostname() } - |> Vendor.maybe_add_linux_boot_id() - |> Vendor.maybe_add_cloud_vendors() + |> maybe_add_ip_addresses + |> maybe_add_fqdn + |> maybe_add_linux_boot_id() + |> Vendor.maybe_add_vendors() + end + + def maybe_heroku_dyno_hostname do + System.get_env("DYNO") + |> case do + nil -> nil + "scheduler." <> _ -> "scheduler.*" + "run." <> _ -> "run.*" + name -> name + end + end + + def maybe_add_linux_boot_id(util) do + case File.read("/proc/sys/kernel/random/boot_id") do + {:ok, boot_id} -> Map.put(util, "boot_id", boot_id) + _ -> util + end + end + + def maybe_add_ip_addresses(util) do + case :inet.getif() do + {:ok, addrs} -> + ip_address = Enum.map(addrs, fn {ip, _, _} -> to_string(:inet.ntoa(ip)) end) + Map.put(util, :ip_address, ip_address) + + _ -> + util + end + end + + def maybe_add_fqdn(util) do + case :net_adm.dns_hostname(:net_adm.localhost()) do + {:ok, fqdn} -> Map.put(util, :full_hostname, to_string(fqdn)) + _ -> util + end end @mb 1024 * 1024 diff --git a/lib/new_relic/util/vendor.ex b/lib/new_relic/util/vendor.ex index 750f828a..1306b38c 100644 --- a/lib/new_relic/util/vendor.ex +++ b/lib/new_relic/util/vendor.ex @@ -1,30 +1,31 @@ defmodule NewRelic.Util.Vendor do @moduledoc false - def maybe_add_linux_boot_id(util) do - case File.read("/proc/sys/kernel/random/boot_id") do - {:ok, boot_id} -> Map.put(util, "boot_id", boot_id) - _ -> util - end - end - - def maybe_heroku_dyno_hostname do - System.get_env("DYNO") + def maybe_add_vendors(util, options \\ []) do + %{} + |> maybe_add_aws(options) + |> maybe_add_kubernetes() |> case do - nil -> nil - "scheduler." <> _ -> "scheduler.*" - "run." <> _ -> "run.*" - name -> name + vendors when map_size(vendors) == 0 -> util + vendors -> Map.put(util, :vendors, vendors) end end @aws_url "http://169.254.169.254/2016-09-02/dynamic/instance-identity/document" - def maybe_add_cloud_vendors(util, options \\ []) do + def maybe_add_aws(vendors, options \\ []) do Keyword.get(options, :aws_url, @aws_url) |> aws_vendor_hash() |> case do - nil -> util - aws_hash -> Map.put(util, :vendors, %{aws: aws_hash}) + nil -> vendors + aws_hash -> Map.put(vendors, :aws, aws_hash) + end + end + + def maybe_add_kubernetes(vendors) do + System.get_env("KUBERNETES_SERVICE_HOST") + |> case do + nil -> vendors + value -> Map.put(vendors, :kubernetes, %{kubernetes_service_host: value}) end end diff --git a/test/integration/agent_run_test.exs b/test/integration/agent_run_test.exs index 11573209..3dd592ed 100644 --- a/test/integration/agent_run_test.exs +++ b/test/integration/agent_run_test.exs @@ -18,10 +18,14 @@ defmodule AgentRunIntegrationTest do :ok end - test "has util data in connect payload" do - payload = Collector.Connect.payload() - ram = get_in(payload, [Access.at(0), :utilization, :total_ram_mib]) - assert is_integer(ram) + test "Connect payload" do + [payload] = Collector.Connect.payload() + + assert get_in(payload, [:utilization, :total_ram_mib]) + |> is_integer + + assert get_in(payload, [:metadata]) + |> is_map end test "Stores needed connect data" do diff --git a/test/util_test.exs b/test/util_test.exs index 23031b56..d0f0e1f9 100644 --- a/test/util_test.exs +++ b/test/util_test.exs @@ -64,12 +64,15 @@ defmodule UtilTest do end test "minimal utilization check" do - assert %{metadata_version: 3} = NewRelic.Util.utilization() + assert %{metadata_version: 5} = util = NewRelic.Util.utilization() + + assert util[:ip_address] |> is_list + assert util[:full_hostname] |> is_binary end test "AWS utilization fast timeout" do assert %{} == - NewRelic.Util.Vendor.maybe_add_cloud_vendors(%{}, + NewRelic.Util.Vendor.maybe_add_vendors(%{}, aws_url: "http://httpbin.org/delay/10" ) end @@ -77,10 +80,27 @@ defmodule UtilTest do test "AWS utilization info" do {:ok, _} = Plug.Cowboy.http(FakeAwsPlug, [], port: 8883) - util = NewRelic.Util.Vendor.maybe_add_cloud_vendors(%{}, aws_url: "http://localhost:8883") + util = NewRelic.Util.Vendor.maybe_add_vendors(%{}, aws_url: "http://localhost:8883") assert get_in(util, [:vendors, :aws, "instanceId"]) == "test.id" end + test "Kubernetes utilization info" do + System.put_env("KUBERNETES_SERVICE_HOST", "k8s-host") + + util = NewRelic.Util.utilization() + assert get_in(util, [:vendors, :kubernetes, :kubernetes_service_host]) == "k8s-host" + + System.delete_env("KUBERNETES_SERVICE_HOST") + end + + test "New Relic metadata detection" do + System.put_env("NEW_RELIC_METADATA_TEST", "value") + + assert NewRelic.Util.metadata() == %{"NEW_RELIC_METADATA_TEST" => "value"} + + System.delete_env("NEW_RELIC_METADATA_TEST") + end + test "hostname detection" do System.put_env("DYNO", "foobar") assert NewRelic.Util.hostname() == "foobar"