Skip to content

Commit

Permalink
fix: update tenant admin reload endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
w3b6x9 committed Jun 15, 2022
1 parent 63911f7 commit 89e6fbe
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 42 deletions.
13 changes: 4 additions & 9 deletions lib/extensions/postgres/postgres.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,14 @@ defmodule Extensions.Postgres do
end
end

def stop(scope) do
@spec stop(String.t()) :: :ok
def stop(scope, timeout \\ :infinity) do
case :global.whereis_name({:tenant_db, :supervisor, scope}) do
:undefined ->
nil
Logger.warning("Database supervisor not found for tenant #{scope}")

pid ->
poller_pid = :global.whereis_name({:tenant_db, :replication, :poller, scope})
manager_pid = :global.whereis_name({:tenant_db, :replication, :manager, scope})

is_pid(poller_pid) && GenServer.stop(poller_pid, :normal)
is_pid(manager_pid) && GenServer.stop(manager_pid, :normal)

DynamicSupervisor.stop(pid, :shutdown)
DynamicSupervisor.stop(pid, :shutdown, timeout)
end
end

Expand Down
37 changes: 36 additions & 1 deletion lib/realtime_web/controllers/tenant_controller.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
defmodule RealtimeWeb.TenantController do
use RealtimeWeb, :controller
use PhoenixSwagger

require Logger

alias Extensions.Postgres
alias Realtime.Api
alias Realtime.Api.Tenant
alias PhoenixSwagger.{Path, Schema}

@stop_timeout 10_000

action_fallback(RealtimeWeb.FallbackController)

swagger_path :index do
Expand Down Expand Up @@ -59,7 +65,7 @@ defmodule RealtimeWeb.TenantController do
nil ->
conn
|> put_status(404)
|> render("no_found.json", tenant: nil)
|> render("not_found.json", tenant: nil)
end
end

Expand Down Expand Up @@ -111,6 +117,35 @@ defmodule RealtimeWeb.TenantController do
send_resp(conn, 204, "")
end

swagger_path :reload do
Path.post("/api/tenants/{external_id}/reload")
tag("Tenants")
description("Reload tenant database supervisor")

parameter(:tenant_id, :path, :string, "Tenant ID",
required: true,
example: "123e4567-e89b-12d3-a456-426655440000"
)

response(204, "")
response(404, "not found")
end

def reload(conn, %{"tenant_id" => tenant_id}) do
case Api.get_tenant_by_external_id(tenant_id) do
%Tenant{} ->
Postgres.stop(tenant_id, @stop_timeout)
send_resp(conn, 204, "")

nil ->
Logger.error("Atttempted to reload non-existant tenant #{tenant_id}")

conn
|> put_status(404)
|> render("not_found.json", tenant: nil)
end
end

def swagger_definitions do
%{
Tenant:
Expand Down
22 changes: 0 additions & 22 deletions lib/realtime_web/controllers/tenant_manager.ex

This file was deleted.

6 changes: 4 additions & 2 deletions lib/realtime_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ defmodule RealtimeWeb.Router do

scope "/api", RealtimeWeb do
pipe_through :api
resources "/tenants", TenantController
get "/manager/:tenant/reload", TenantManagerController, :reload

resources "/tenants", TenantController do
post "/reload", TenantController, :reload, as: :reload
end
end

scope "/api/swagger" do
Expand Down
4 changes: 2 additions & 2 deletions lib/realtime_web/views/tenant_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ defmodule RealtimeWeb.TenantView do
%{data: render_one(tenant, TenantView, "tenant.json")}
end

def render("no_found.json", %{tenant: nil}) do
%{error: "no found"}
def render("not_found.json", %{tenant: nil}) do
%{error: "not found"}
end

def render("tenant.json", %{tenant: tenant}) do
Expand Down
28 changes: 28 additions & 0 deletions priv/static/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,34 @@
"Tenants"
]
}
},
"/api/tenants/{external_id}/reload": {
"post": {
"description": "Reload tenant database supervisor",
"operationId": "RealtimeWeb.TenantController.reload",
"parameters": [
{
"description": "Tenant ID",
"in": "path",
"name": "tenant_id",
"required": true,
"type": "string",
"x-example": "123e4567-e89b-12d3-a456-426655440000"
}
],
"responses": {
"204": {
"description": ""
},
"404": {
"description": "not found"
}
},
"summary": "",
"tags": [
"Tenants"
]
}
}
},
"produces": [
Expand Down
53 changes: 49 additions & 4 deletions test/realtime/extensions/postgres/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,21 @@ defmodule Realtime.Extensions.PostgresTest do
end

describe "Postgres extensions" do
test "Check supervisor crash and respawn", %{} do
sup = :global.whereis_name({:tenant_db, :supervisor, @external_id})
test "Check supervisor crash and respawn" do
sup =
Enum.reduce_while(1..10, nil, fn x, acc ->
{:tenant_db, :supervisor, @external_id}
|> :global.whereis_name()
|> case do
:undefined ->
Process.sleep(500)
{:cont, acc}

pid ->
{:halt, pid}
end
end)

assert Process.alive?(sup)
DynamicSupervisor.terminate_child(Postgres.DynamicSupervisor, sup)
Process.sleep(5_000)
Expand All @@ -58,14 +71,14 @@ defmodule Realtime.Extensions.PostgresTest do
assert(sup != sup2)
end

test "Subscription manager updates oids", %{} do
test "Subscription manager updates oids" do
subscriber_manager_pid =
Enum.reduce_while(1..10, nil, fn x, acc ->
{:tenant_db, :replication, :poller, @external_id}
|> :global.whereis_name()
|> case do
:undefined ->
Process.sleep(100)
Process.sleep(500)
{:cont, acc}

_ ->
Expand All @@ -85,5 +98,37 @@ defmodule Realtime.Extensions.PostgresTest do
%{oids: oids3} = :sys.get_state(subscriber_manager_pid)
assert !Map.equal?(oids2, oids3)
end

test "Stop tenant supervisor" do
[sup, manager, poller] =
Enum.reduce_while(1..10, nil, fn x, acc ->
pids = [
:global.whereis_name({:tenant_db, :supervisor, @external_id}),
:global.whereis_name({:tenant_db, :replication, :manager, @external_id}),
:global.whereis_name({:tenant_db, :replication, :poller, @external_id})
]

pids
|> Enum.all?(&is_pid(&1))
|> case do
true ->
{:halt, pids}

false ->
Process.sleep(500)
{:cont, acc}
end
end)

assert Process.alive?(sup)
assert Process.alive?(manager)
assert Process.alive?(poller)

Postgres.stop(@external_id)

assert Process.alive?(sup) == false
assert Process.alive?(manager) == false
assert Process.alive?(poller) == false
end
end
end
23 changes: 22 additions & 1 deletion test/realtime_web/controllers/tenant_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule RealtimeWeb.TenantControllerTest do

alias Realtime.Api
alias Realtime.Api.Tenant
alias RealtimeWeb.JwtVerification
alias RealtimeWeb.{ChannelsAuthorization, JwtVerification}
import Realtime.Helpers, only: [encrypt: 2]

@external_id "test_external_id"
Expand Down Expand Up @@ -140,6 +140,27 @@ defmodule RealtimeWeb.TenantControllerTest do
end
end

describe "reload tenant" do
test "reload when tenant does exist", %{conn: conn} do
with_mocks [
{ChannelsAuthorization, [], authorize: fn _, _ -> {:ok, %{}} end},
{Api, [], get_tenant_by_external_id: fn _ -> %Tenant{} end}
] do
Routes.tenant_reload_path(conn, :reload, @external_id)
%{status: status} = post(conn, Routes.tenant_reload_path(conn, :reload, @external_id))
assert status == 204
end
end

test "reload when tenant does not exist", %{conn: conn} do
with_mock ChannelsAuthorization, authorize: fn _, _ -> {:ok, %{}} end do
Routes.tenant_reload_path(conn, :reload, @external_id)
%{status: status} = post(conn, Routes.tenant_reload_path(conn, :reload, @external_id))
assert status == 404
end
end
end

defp create_tenant(_) do
tenant = fixture(:tenant)
%{tenant: tenant}
Expand Down
1 change: 0 additions & 1 deletion test/support/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ defmodule RealtimeWeb.ConnCase do
# Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
import RealtimeWeb.ConnCase
alias RealtimeWeb.Router.Helpers, as: Routes

# The default endpoint for testing
Expand Down

0 comments on commit 89e6fbe

Please sign in to comment.