Current section
Files
Jump to
Current section
Files
lib/etcd_client.ex
defmodule LibclusterEtcd.EtcdClient do
@keys "/v2/keys"
@default_http_opts [
timeout: 20_000,
connect_timeout: 20_000,
autoredirect: false,
relaxed: true
]
@default_ttl 30
def push(etcd_server, dir, value, ttl \\ @default_ttl, http_opts \\ [])
when is_binary(dir) and is_integer(ttl) do
req = make_request(etcd_server, dir, %{"value" => value, "ttl" => ttl})
with {:ok, {201, response}} <- exec_request(:post, req, http_opts),
{:ok, json} <- Jason.decode(response),
key <- json |> Map.fetch!("node") |> Map.fetch!("key") |> cleanup(dir) do
{:ok, :binary.copy(key)}
else
{:error, error} ->
{:error, error}
error ->
{:error, error}
end
end
def refresh_ttl(etcd_server, dir, key, ttl \\ @default_ttl, prev_exist \\ true, http_opts \\ [])
when is_binary(dir) and is_binary(key) and is_integer(ttl) do
req =
make_request(etcd_server, dir <> "/" <> key, %{
"ttl" => ttl,
"refresh" => true,
"prevExist" => prev_exist
})
with {:ok, {200, _response}} <- exec_request(:put, req, http_opts) do
{:ok, :refreshed}
else
{:error, error} ->
{:error, error}
{:ok, {404, _}} ->
{:error, :key_not_found}
error ->
{:error, error}
end
end
def list(etcd_server, dir, http_opts \\ []) when is_binary(dir) do
req = make_request(etcd_server, dir)
with {:ok, {200, response}} <- exec_request(:get, req, http_opts),
{:ok, json} <- Jason.decode(response) do
keys =
json
|> Map.get("node", %{"nodes" => []})
|> Map.get("nodes", [])
|> Enum.map(fn map ->
key = map |> Map.fetch!("key") |> cleanup(dir)
{:binary.copy(key), :binary.copy(map["value"])}
end)
{:ok, keys}
else
{:error, error} ->
{:error, error}
{:ok, {404, _}} ->
{:ok, []}
error ->
{:error, error}
end
end
def delete(etcd_server, dir, key, http_opts \\ []) when is_binary(dir) and is_binary(key) do
req = make_request(etcd_server, dir <> "/" <> "key")
with {:ok, {200, _response}} <- exec_request(:delete, req, http_opts) do
{:ok, :deleted}
else
{:error, error} ->
{:error, error}
error ->
{:error, error}
end
end
defp make_request(etcd_server, path) do
url = etcd_server |> make_url(path) |> String.to_charlist()
{url, []}
end
defp make_request(etcd_server, path, payload) do
url = etcd_server |> make_url(path) |> String.to_charlist()
{url, [], 'application/x-www-form-urlencoded', payload |> form_encode()}
end
defp exec_request(method, request, http_opts) do
req_http_opts = @default_http_opts |> Keyword.merge(http_opts)
opts = [body_format: :binary, full_result: false]
with {:ok, result} <- :httpc.request(method, request, req_http_opts, opts) do
{:ok, result}
end
end
defp make_url(base, path) do
if String.starts_with?(path, "/") do
base <> @keys <> path
else
base <> @keys <> "/" <> path
end
end
defp form_encode(payload) when is_map(payload),
do: payload |> Enum.map_join("&", fn {key, value} -> "#{key}=#{value}" end)
defp cleanup(key, dir) when is_binary(dir) and is_binary(key) do
key
|> String.slice((String.length(dir) + 2)..(String.length(key) - 1))
end
end