Subiendo api v2

This commit is contained in:
2025-04-16 10:03:13 -03:00
commit 226933fda7
7537 changed files with 576844 additions and 0 deletions

0
deps/dns_cluster/.fetch vendored Normal file
View File

4
deps/dns_cluster/.formatter.exs vendored Normal file
View 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

Binary file not shown.

13
deps/dns_cluster/CHANGELOG.md vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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