Files
mesh-catalog/modules/supabase/files/analytics-reconcile.exs
T

87 lines
3.9 KiB
Elixir

# Generated by the mesh. Do not edit: module supabase writes this file and replaces it at every push.
#
# logflare 1.4.0 copies two of its settings into its own database once and never reads them again:
# LOGFLARE_API_KEY becomes the default user's api_key when that user is created, and
# POSTGRES_BACKEND_URL becomes each source's backend when that source is created. A changed value
# reaches neither: a rotated key is refused, and a moved database is never written to (every
# source here pointed at db:5432, a name that resolves nowhere, so nothing was stored).
#
# Run by the start script before logflare starts, through logflare's own code (its Repo, its Users
# and Backends contexts and their changesets): it makes both copies say what the environment says.
# It changes nothing when they already do, says what it changed, never prints the key or a URL's
# password, and stops the start when it cannot finish.
fail = fn why ->
IO.puts(:stderr, "analytics: #{why}; not starting")
System.halt(1)
end
say = fn what -> IO.puts(:stderr, "analytics: #{what}") end
key = System.get_env("LOGFLARE_API_KEY")
url = System.get_env("POSTGRES_BACKEND_URL")
schema = System.get_env("POSTGRES_BACKEND_SCHEMA")
if key in [nil, ""], do: fail.("LOGFLARE_API_KEY is not set, so the key logflare must accept is unknown")
if url in [nil, ""], do: fail.("POSTGRES_BACKEND_URL is not set, so where logflare must store logs is unknown")
where = fn u ->
case URI.parse(to_string(u)) do
%URI{host: host, port: port, path: path} when is_binary(host) -> "#{host}:#{port}#{path}"
_ -> "an unreadable URL"
end
end
Application.ensure_all_started(:ssl)
outcome =
Ecto.Migrator.with_repo(Logflare.Repo, fn _repo ->
# Backends.update_source_backend_config restarts the source's pipeline after saving, and looks
# it up in this registry to do so; logflare is not running yet, so the registry is empty and
# there is nothing to restart.
{:ok, _} = Registry.start_link(keys: :unique, name: Logflare.Backends.SourceRegistry)
case Logflare.SingleTenant.get_default_user() do
nil ->
say.("no default user yet; logflare creates it from LOGFLARE_API_KEY and POSTGRES_BACKEND_URL as it starts")
:ok
user ->
if user.api_key == key and is_nil(user.old_api_key) do
say.("the default user's API key is LOGFLARE_API_KEY already")
else
# old_api_key is cleared too: logflare's UI can swap it back in, and the key replaced
# here may be one that leaked.
case Logflare.Users.update_user_all_fields(user, %{api_key: key, old_api_key: nil}) do
{:ok, _} -> say.("the default user's API key was replaced with LOGFLARE_API_KEY")
{:error, changeset} -> fail.("the default user's API key could not be replaced: #{inspect(changeset.errors)}")
end
end
results =
for source <- Logflare.Sources.list_sources_by_user(user),
backend <- Logflare.Backends.list_source_backends(source),
backend.type == :postgres do
if backend.config.url == url and backend.config.schema == schema do
:same
else
case Logflare.Backends.update_source_backend_config(backend, %{url: url, schema: schema}) do
{:ok, _} ->
say.("source #{source.name} stored logs at #{where.(backend.config.url)} schema #{inspect(backend.config.schema)}; now at #{where.(url)} schema #{inspect(schema)}")
{:error, changeset} ->
fail.("source #{source.name}'s backend could not be pointed at #{where.(url)}: #{inspect(changeset.errors)}")
end
end
end
say.("#{Enum.count(results, &(&1 == :same))} source backend(s) already at #{where.(url)}")
:ok
end
end)
case outcome do
{:ok, :ok, _} -> :ok
other -> fail.("logflare's database could not be reconciled: #{inspect(other)}")
end