1
0
Fork 0
mirror of https://github.com/benbusby/farside.git synced 2025-04-20 02:48:42 +00:00
farside/lib/farside/instances.ex
Ben Busby a6dabe8bf3
Make conn values and services path configurable at runtime
Connection values (such as redis server port and the port to run farside
on) as well as the services json file to use can now be set via
environment variables:

FARSIDE_PORT sets the port for Farside to run on
FARSIDE_REDIS_PORT sets the redis server port for Farside to use
FARSIDE_SERVICES_JSON sets the services json file for Farside to use

This partially addresses the move towards de-listing Cloudflare
instances by default by allowing different services json files to be
used with different redis servers.

See #43
2022-07-27 13:53:33 -06:00

115 lines
2.9 KiB
Elixir

defmodule Farside.Instances do
@fallback_suffix Application.fetch_env!(:farside, :fallback_suffix)
@update_file Application.fetch_env!(:farside, :update_file)
@service_prefix Application.fetch_env!(:farside, :service_prefix)
@headers Application.fetch_env!(:farside, :headers)
@queries Application.fetch_env!(:farside, :queries)
@debug_header "======== "
@debug_spacer " "
def sync() do
File.rename(@update_file, "#{@update_file}-prev")
update()
# Add UTC time of last update
Redix.command(:redix, [
"SET",
"last_updated",
Calendar.strftime(DateTime.utc_now(), "%c")
])
end
def request(url) do
cond do
System.get_env("FARSIDE_TEST") ->
:good
true ->
HTTPoison.get(url, @headers)
|> then(&elem(&1, 1))
|> Map.get(:status_code)
|> case do
n when n < 400 ->
IO.puts("#{@debug_spacer}✓ [#{n}]")
:good
n ->
IO.puts("#{@debug_spacer}x [#{(n && n) || "error"}]")
:bad
end
end
end
def update() do
services_json = Application.fetch_env!(:farside, :services_json)
{:ok, file} = File.read(services_json)
{:ok, json} = Jason.decode(file)
# Loop through all instances and check each for availability
for service_json <- json do
service_atom = for {key, val} <- service_json, into: %{} do
{String.to_existing_atom(key), val}
end
service = struct(%Service{}, service_atom)
IO.puts("#{@debug_header}#{service.type}")
result =
Enum.filter(service.instances, fn instance_url ->
request_url =
instance_url <>
EEx.eval_string(
service.test_url,
query: Enum.random(@queries)
)
IO.puts("#{@debug_spacer}#{request_url}")
request(request_url) == :good
end)
add_to_redis(service, result)
log_results(service.type, result)
end
end
def add_to_redis(service, instances) do
# Remove previous list of instances
Redix.command(:redix, [
"DEL",
"#{@service_prefix}#{service.type}"
])
# Update with new list of available instances
Redix.command(
:redix,
[
"LPUSH",
"#{@service_prefix}#{service.type}"
] ++ instances
)
# Set fallback to one of the available instances,
# or the default instance if all are "down"
if Enum.count(instances) > 0 do
Redix.command(:redix, [
"SET",
"#{service.type}#{@fallback_suffix}",
Enum.random(instances)
])
else
Redix.command(:redix, [
"SET",
"#{service.type}#{@fallback_suffix}",
service.fallback
])
end
end
def log_results(service_name, results) do
{:ok, file} = File.open(@update_file, [:append, {:delayed_write, 100, 20}])
IO.write(file, "#{service_name}: #{inspect(results)}\n")
File.close(file)
end
end