Subiendo api v2
This commit is contained in:
0
deps/dns_cluster/.fetch
vendored
Normal file
0
deps/dns_cluster/.fetch
vendored
Normal file
4
deps/dns_cluster/.formatter.exs
vendored
Normal file
4
deps/dns_cluster/.formatter.exs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
BIN
deps/dns_cluster/.hex
vendored
Normal file
BIN
deps/dns_cluster/.hex
vendored
Normal file
Binary file not shown.
13
deps/dns_cluster/CHANGELOG.md
vendored
Normal file
13
deps/dns_cluster/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.3 (2024-02-02)
|
||||
* Support OTP 24
|
||||
|
||||
## 0.1.2 (2024-01-08)
|
||||
* Use `:inet_res.getbyname/2` to resolve the given hostname to support search list for host-name lookup, such as in k8s and similar setups
|
||||
|
||||
## 0.1.1 (2023-09-27)
|
||||
* Fix bug where an empty clauses would raise an argument error
|
||||
|
||||
## 0.1.0 (2023-07-11)
|
||||
* Initial release
|
||||
22
deps/dns_cluster/LICENSE.md
vendored
Normal file
22
deps/dns_cluster/LICENSE.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (c) 2023 Chris McCord
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
45
deps/dns_cluster/README.md
vendored
Normal file
45
deps/dns_cluster/README.md
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
# DNSCluster
|
||||
|
||||
Simple DNS clustering for distributed Elixir nodes.
|
||||
|
||||
## Installation
|
||||
|
||||
The package can be installed by adding `dns_cluster` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:dns_cluster, "~> 0.1.1"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Next, you can configure and start the cluster by adding it to your supervision
|
||||
tree in your `application.ex`:
|
||||
|
||||
```elixir
|
||||
children = [
|
||||
{Phoenix.PubSub, ...},
|
||||
{DNSCluster, query: Application.get_env(:my_app, :dns_cluster_query) || :ignore},
|
||||
MyAppWeb.Endpoint
|
||||
]
|
||||
```
|
||||
|
||||
If you are deploying with Elixir releases, the release must be set to support longnames and
|
||||
the node must be named. These can be set in your `rel/env.sh.eex` file:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
export RELEASE_DISTRIBUTION=name
|
||||
export RELEASE_NODE="myapp@fully-qualified-host-or-ip"
|
||||
```
|
||||
|
||||
By default, nodes from the same release will have the same cookie. If you want different
|
||||
applications or releases to connect to each other, then you must set the `RELEASE_COOKIE`,
|
||||
either in your deployment platform or inside `rel/env.sh.eex`:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
...
|
||||
export RELEASE_COOKIE="my-app-cookie"
|
||||
```
|
||||
13
deps/dns_cluster/hex_metadata.config
vendored
Normal file
13
deps/dns_cluster/hex_metadata.config
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{<<"links">>,
|
||||
[{<<"GitHub">>,<<"https://github.com/phoenixframework/dns_cluster">>}]}.
|
||||
{<<"name">>,<<"dns_cluster">>}.
|
||||
{<<"version">>,<<"0.1.3">>}.
|
||||
{<<"description">>,<<"Simple DNS clustering for distributed Elixir nodes">>}.
|
||||
{<<"elixir">>,<<"~> 1.11">>}.
|
||||
{<<"app">>,<<"dns_cluster">>}.
|
||||
{<<"licenses">>,[<<"MIT">>]}.
|
||||
{<<"files">>,
|
||||
[<<"lib">>,<<"lib/dns_cluster.ex">>,<<"CHANGELOG.md">>,<<"LICENSE.md">>,
|
||||
<<"mix.exs">>,<<"README.md">>,<<".formatter.exs">>]}.
|
||||
{<<"requirements">>,[]}.
|
||||
{<<"build_tools">>,[<<"mix">>]}.
|
||||
202
deps/dns_cluster/lib/dns_cluster.ex
vendored
Normal file
202
deps/dns_cluster/lib/dns_cluster.ex
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
defmodule DNSCluster do
|
||||
@moduledoc """
|
||||
Simple DNS based cluster discovery.
|
||||
|
||||
A DNS query is made every `:interval` milliseconds to discover new ips.
|
||||
Nodes will only be joined if their node basename matches the basename of the
|
||||
current node. For example if `node()` is `myapp-123@fdaa:1:36c9:a7b:198:c4b1:73c6:1`,
|
||||
a `Node.connect/1` attempt will be made against every IP returned by the DNS query,
|
||||
but will only be successful if there is a node running on the remote host with the same
|
||||
basename, for example `myapp-123@fdaa:1:36c9:a7b:198:c4b1:73c6:2`. Nodes running on
|
||||
remote hosts, but with different basenames will fail to connect and will be ignored.
|
||||
|
||||
## Examples
|
||||
|
||||
To start in your supervision tree, add the child:
|
||||
|
||||
children = [
|
||||
...,
|
||||
{DNSCluster, query: "myapp.internal"}
|
||||
]
|
||||
|
||||
See the `start_link/1` docs for all available options.
|
||||
|
||||
If you require more advanced clustering options and strategies, see the
|
||||
[libcluster](https://hexdocs.pm/libcluster) library.
|
||||
"""
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
defmodule Resolver do
|
||||
@moduledoc false
|
||||
|
||||
require Record
|
||||
Record.defrecord(:hostent, Record.extract(:hostent, from_lib: "kernel/include/inet.hrl"))
|
||||
|
||||
def basename(node_name) when is_atom(node_name) do
|
||||
[basename, _] = String.split(to_string(node_name), "@")
|
||||
basename
|
||||
end
|
||||
|
||||
def connect_node(node_name) when is_atom(node_name), do: Node.connect(node_name)
|
||||
|
||||
def list_nodes, do: Node.list(:visible)
|
||||
|
||||
def lookup(query, type) when is_binary(query) and type in [:a, :aaaa] do
|
||||
case :inet_res.getbyname(~c"#{query}", type) do
|
||||
{:ok, hostent(h_addr_list: addr_list)} -> addr_list
|
||||
{:error, _} -> []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc ~S"""
|
||||
Starts DNS based cluster discovery.
|
||||
|
||||
## Options
|
||||
|
||||
* `:name` - the name of the cluster. Defaults to `DNSCluster`.
|
||||
* `:query` - the required DNS query for node discovery, for example: `"myapp.internal"`.
|
||||
The value `:ignore` can be used to ignore starting the DNSCluster.
|
||||
* `:interval` - the millisec interval between DNS queries. Defaults to `5000`.
|
||||
* `:connect_timeout` - the millisec timeout to allow discovered nodes to connect.
|
||||
Defaults to `10_000`.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> DNSCluster.start_link(query: "myapp.internal")
|
||||
{:ok, pid}
|
||||
|
||||
iex> DNSCluster.start_link(query: :ignore)
|
||||
:ignore
|
||||
"""
|
||||
def start_link(opts) do
|
||||
GenServer.start_link(__MODULE__, opts, name: Keyword.get(opts, :name, __MODULE__))
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(opts) do
|
||||
case Keyword.fetch(opts, :query) do
|
||||
{:ok, :ignore} ->
|
||||
:ignore
|
||||
|
||||
{:ok, query} when is_binary(query) ->
|
||||
warn_on_invalid_dist()
|
||||
resolver = Keyword.get(opts, :resolver, Resolver)
|
||||
|
||||
state = %{
|
||||
interval: Keyword.get(opts, :interval, 5_000),
|
||||
basename: resolver.basename(node()),
|
||||
query: query,
|
||||
log: Keyword.get(opts, :log, false),
|
||||
poll_timer: nil,
|
||||
connect_timeout: Keyword.get(opts, :connect_timeout, 10_000),
|
||||
resolver: resolver
|
||||
}
|
||||
|
||||
{:ok, state, {:continue, :discover_ips}}
|
||||
|
||||
{:ok, other} ->
|
||||
raise ArgumentError, "expected :query to be a string, got: #{inspect(other)}"
|
||||
|
||||
:error ->
|
||||
raise ArgumentError, "missing required :query option in #{inspect(opts)}"
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue(:discover_ips, state) do
|
||||
{:noreply, do_discovery(state)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:discover_ips, state) do
|
||||
{:noreply, do_discovery(state)}
|
||||
end
|
||||
|
||||
defp do_discovery(state) do
|
||||
state
|
||||
|> connect_new_nodes()
|
||||
|> schedule_next_poll()
|
||||
end
|
||||
|
||||
defp connect_new_nodes(%{resolver: resolver, connect_timeout: timeout} = state) do
|
||||
node_names = for name <- resolver.list_nodes(), into: MapSet.new(), do: to_string(name)
|
||||
|
||||
ips = discover_ips(state)
|
||||
|
||||
_results =
|
||||
ips
|
||||
|> Enum.map(fn ip -> "#{state.basename}@#{ip}" end)
|
||||
|> Enum.filter(fn node_name -> !Enum.member?(node_names, node_name) end)
|
||||
|> Task.async_stream(
|
||||
fn new_name ->
|
||||
if resolver.connect_node(:"#{new_name}") do
|
||||
log(state, "#{node()} connected to #{new_name}")
|
||||
end
|
||||
end,
|
||||
max_concurrency: max(1, length(ips)),
|
||||
timeout: timeout
|
||||
)
|
||||
|> Enum.to_list()
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
defp log(state, msg) do
|
||||
if level = state.log, do: Logger.log(level, msg)
|
||||
end
|
||||
|
||||
defp schedule_next_poll(state) do
|
||||
%{state | poll_timer: Process.send_after(self(), :discover_ips, state.interval)}
|
||||
end
|
||||
|
||||
defp discover_ips(%{resolver: resolver, query: query}) do
|
||||
[:a, :aaaa]
|
||||
|> Enum.flat_map(&resolver.lookup(query, &1))
|
||||
|> Enum.uniq()
|
||||
|> Enum.map(&to_string(:inet.ntoa(&1)))
|
||||
end
|
||||
|
||||
defp warn_on_invalid_dist do
|
||||
release? = is_binary(System.get_env("RELEASE_NAME"))
|
||||
net_state = if function_exported?(:net_kernel, :get_state, 0), do: :net_kernel.get_state()
|
||||
|
||||
cond do
|
||||
!net_state ->
|
||||
:ok
|
||||
|
||||
net_state.started == :no and release? ->
|
||||
Logger.warning("""
|
||||
node not running in distributed mode. Ensure the following exports are set in your rel/env.sh.eex file:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
export RELEASE_DISTRIBUTION=name
|
||||
export RELEASE_NODE="myapp@fully-qualified-host-or-ip"
|
||||
""")
|
||||
|
||||
net_state.started == :no or
|
||||
(!release? and net_state.started != :no and net_state[:name_domain] != :longnames) ->
|
||||
Logger.warning("""
|
||||
node not running in distributed mode. When running outside of a release, you must start net_kernel manually with
|
||||
longnames.
|
||||
https://www.erlang.org/doc/man/net_kernel.html#start-2
|
||||
""")
|
||||
|
||||
net_state[:name_domain] != :longnames and release? ->
|
||||
Logger.warning("""
|
||||
node not running with longnames which are required for DNS discovery.
|
||||
Ensure the following exports are set in your rel/env.sh.eex file:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
export RELEASE_DISTRIBUTION=name
|
||||
export RELEASE_NODE="myapp@fully-qualified-host-or-ip"
|
||||
""")
|
||||
|
||||
true ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
39
deps/dns_cluster/mix.exs
vendored
Normal file
39
deps/dns_cluster/mix.exs
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
defmodule DNSCluster.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@version "0.1.3"
|
||||
@scm_url "https://github.com/phoenixframework/dns_cluster"
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :dns_cluster,
|
||||
package: package(),
|
||||
version: @version,
|
||||
elixir: "~> 1.11",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
source_url: @scm_url,
|
||||
homepage_url: @scm_url,
|
||||
description: "Simple DNS clustering for distributed Elixir nodes"
|
||||
]
|
||||
end
|
||||
|
||||
defp package do
|
||||
[
|
||||
maintainers: ["Chris McCord"],
|
||||
licenses: ["MIT"],
|
||||
links: %{"GitHub" => @scm_url},
|
||||
files: ~w(lib CHANGELOG.md LICENSE.md mix.exs README.md .formatter.exs)
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[{:ex_doc, ">= 0.0.0", only: :docs}]
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user