defmodule Service do defstruct [ type: nil, test_url: nil, fallback: nil, instances: [] ] end defmodule Instances do @fallback_str Application.fetch_env!(:privacy_revolver, :fallback_str) @update_file Application.fetch_env!(:privacy_revolver, :update_file) @services_json Application.fetch_env!(:privacy_revolver, :services_json) def init() do File.rename(@update_file, "#{@update_file}-prev") update(@services_json) end def request(url) do case HTTPoison.get(url) do {:ok, %HTTPoison.Response{status_code: 200}} -> # TODO: Add validation of results, not just status code :good _ -> :bad end end def update(filename) do {:ok, file} = File.read(filename) {:ok, json} = Poison.decode(file, as: [%Service{}]) # Loop through all instances and check each for availability for service <- json do result = Enum.filter(service.instances, fn(instance_url) -> request(instance_url <> service.test_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.type ]) # Update with new list of available instances Redix.command(:redix, [ "LPUSH", 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_str}", Enum.random(instances) ]) else Redix.command(:redix, [ "SET", "#{service.type}#{@fallback_str}", 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 Instances.init()