Current section

Files

Jump to
flame_ec2 lib flame_ec2 instance_metadata.ex
Raw

lib/flame_ec2/instance_metadata.ex

defmodule FlameEC2.InstanceMetadata do
@moduledoc false
require Logger
def get(metadata_url, metadata_token_url, root_keys \\ nil) do
metadata_url =
if String.ends_with?(metadata_url, "/") do
metadata_url
else
metadata_url <> "/"
end
case imds_v2(metadata_url, metadata_token_url, root_keys) do
{:ok, metadata} ->
{:ok, metadata}
{:error, _} ->
Logger.warning(
"Failed to fetch EC2 instance metadata via IMDSv2! IMDSv2 is a recommended setting for EC2 instances. For now, falling back to IMDSv1"
)
imds_v1(metadata_url, root_keys)
end
end
defp imds_v2(metadata_url, metadata_token_url, root_keys) do
with {:ok, token} <- fetch_token(metadata_token_url) do
fetch_metadata(metadata_url, [{"X-aws-ec2-metadata-token", token}], root_keys)
end
end
defp imds_v1(metadata_url, root_keys) do
fetch_metadata(metadata_url, [], root_keys)
end
defp fetch_token(metadata_token_url) do
case Req.put(metadata_token_url, headers: [{"X-aws-ec2-metadata-token-ttl-seconds", "21600"}]) do
{:ok, %{status: 200, body: token}} -> {:ok, token}
_ -> {:error, "Failed to fetch IMDSv2 token"}
end
end
defp fetch_metadata(url, headers, root_keys) do
case Req.get(url, headers: headers) do
{:ok, %{status: 200, body: body}} when is_binary(body) ->
if String.ends_with?(url, "/") do
result = do_recurse_metadata(url, headers, root_keys, body)
{:ok, result}
else
{:ok, body}
end
_ ->
{:error, "Failed to fetch metadata"}
end
end
defp do_recurse_metadata(url, headers, root_keys, body) do
items =
body
|> String.split("\n")
|> Enum.filter(&(byte_size(&1) > 0))
|> filter_root_keys(root_keys)
Enum.reduce(items, %{}, fn item, acc ->
key = String.trim_trailing(item, "/")
if String.ends_with?(item, "/") do
new_url = url <> item
case fetch_metadata(new_url, headers, nil) do
{:ok, value} -> Map.put(acc, key, value)
_ -> acc
end
else
case Req.get(url <> item, headers: headers) do
{:ok, %{body: value}} when is_binary(value) -> Map.put(acc, key, value)
_ -> acc
end
end
end)
end
defp filter_root_keys(items, nil) do
items
end
defp filter_root_keys(items, []) do
items
end
defp filter_root_keys(items, root_keys) do
Enum.filter(items, fn item ->
String.trim_trailing(item, "/") in root_keys
end)
end
end