Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ priv/native/*
/.pre-commit-config.yaml
*.coverdata
/tmp

.devenv/
89 changes: 89 additions & 0 deletions test/integration/authentication_methods_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
defmodule Supavisor.Integration.AuthenticationMethodsTest do
use SupavisorWeb.ConnCase, async: false
use Supavisor.PostgresCase, async: false

import Supavisor.Support.Tenants

@moduletag integration_docker: true

@auth_configs %{
"scram-sha-256": [
hostname: "localhost",
port: 6433,
database: "postgres",
username: "postgres",
password: "postgres"
],
password: [
hostname: "localhost",
port: 6434,
database: "postgres",
username: "postgres",
password: "postgres",
volume: "./dev/postgres/password/etc/postgresql/pg_hba.conf:/etc/postgresql/pg_hba.conf",
environment: "--auth-host=password"
]
# md5: [
# hostname: "localhost",
# port: 6434,
# database: "postgres",
# username: "postgres",
# password: "postgres",
# volume: "./dev/postgres/md5/etc/postgresql/pg_hba.conf:/etc/postgresql/pg_hba.conf",
# environment: "--auth-host=md5"
# ]
}

for {key, auth_config} <- @auth_configs do
describe "#{key}" do
setup %{conn: conn} do
external_id = unquote(key)
container_name = container_name(external_id)

cleanup_containers(container_name)

jwt = gen_token()

conn =
conn
|> put_req_header("accept", "application/json")
|> put_req_header("authorization", "Bearer " <> jwt)

on_exit(fn -> cleanup_containers(container_name) end)

{:ok, conn: conn, container_name: container_name, external_id: external_id}
end

test "starts postgres and connects through proxy", %{
conn: conn,
container_name: container_name,
external_id: external_id
} do
opts =
Keyword.merge(unquote(auth_config),
container_name: container_name,
external_id: external_id
)

start_postgres_container(opts)
create_tenant(conn, opts)

assert :ok = test_connection(opts)

stop_postgres_container(container_name)
terminate_tenant(conn, external_id)
end
end
end

defp test_connection(opts) do
connection_opts = connection_opts(opts)

assert {:ok, conn} = Postgrex.start_link(connection_opts)
assert {:ok, %{rows: [[_version_string]]}} = Postgrex.query(conn, "SELECT version();", [])

:ok
end

defp container_name(internal_id), do: "supavisor-db-#{internal_id}"
end
158 changes: 32 additions & 126 deletions test/integration/postgres_switching_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
defmodule Supavisor.Integration.PostgresSwitchingTest do
use SupavisorWeb.ConnCase, async: false
use Supavisor.PostgresCase, async: false

alias Supavisor.Jwt.Token
import Supavisor.Support.Tenants

@moduletag integration_docker: true

Expand All @@ -13,7 +14,8 @@ defmodule Supavisor.Integration.PostgresSwitchingTest do
@db_host "localhost"

setup %{conn: conn} do
cleanup_containers()
containers = [container_name(15), container_name(16)]
cleanup_containers(containers)

jwt = gen_token()

Expand All @@ -22,17 +24,19 @@ defmodule Supavisor.Integration.PostgresSwitchingTest do
|> put_req_header("accept", "application/json")
|> put_req_header("authorization", "Bearer " <> jwt)

on_exit(fn -> cleanup_containers() end)
on_exit(fn -> cleanup_containers(containers) end)

{:ok, conn: conn}
end

test "PostgreSQL upgrade scenario: 15 -> 16", %{conn: conn} do
start_postgres_container(15)
create_tenant(conn)
assert :ok = test_connection()
opts = build_opts(15, @tenant_name)

stop_postgres_container(15)
start_postgres_container(opts)
create_tenant(conn, opts)
assert :ok = test_connection(opts)

stop_postgres_container(opts[:container_name])

# Ideally, we shouldn't need to terminate the tenant manually here.
#
Expand All @@ -43,139 +47,41 @@ defmodule Supavisor.Integration.PostgresSwitchingTest do
#
# Currently, if we don't terminate the tenant (or restart supavisor),
# we get authentication errors.
terminate_tenant(conn)
terminate_tenant(conn, @tenant_name)
Process.sleep(2000)
start_postgres_container(16)

assert :ok = test_connection()
end

defp start_postgres_container(version) do
container_name = container_name(version)

{_output, 0} =
System.cmd("docker", [
"run",
"-d",
"--name",
container_name,
"-e",
"POSTGRES_USER=#{@postgres_user}",
"-e",
"POSTGRES_PASSWORD=#{@postgres_password}",
"-e",
"POSTGRES_DB=#{@postgres_db}",
"-p",
"#{@postgres_port}:5432",
"postgres:#{version}"
])

wait_for_postgres()
end

defp wait_for_postgres(max_attempts \\ 30) do
wait_for_postgres(1, max_attempts)
end

defp wait_for_postgres(attempt, max_attempts) when attempt != max_attempts do
case System.cmd("pg_isready", [
"-h",
"localhost",
"-p",
to_string(@postgres_port),
"-U",
@postgres_user,
"-d",
@postgres_db
]) do
{_, 0} ->
:ok

_ ->
Process.sleep(1000)
wait_for_postgres(attempt + 1, max_attempts)
end
end

defp wait_for_postgres(_attempt, max_attempts) do
raise "PostgreSQL failed to start within #{max_attempts} seconds"
end
opts = build_opts(16, @tenant_name)
start_postgres_container(opts)

defp stop_postgres_container(version) do
System.cmd("docker", ["stop", container_name(version)])
System.cmd("docker", ["rm", container_name(version)])
assert :ok = test_connection(opts)
end

defp create_tenant(conn) do
tenant_attrs = %{
db_host: @db_host,
db_port: @postgres_port,
db_database: @postgres_db,
external_id: @tenant_name,
ip_version: "auto",
enforce_ssl: false,
require_user: false,
auth_query: "SELECT rolname, rolpassword FROM pg_authid WHERE rolname=$1;",
users: [
%{
db_user: @postgres_user,
db_password: @postgres_password,
pool_size: 20,
mode_type: "transaction",
is_manager: true
}
]
}

conn = put(conn, Routes.tenant_path(conn, :update, @tenant_name), tenant: tenant_attrs)

case conn.status do
status when status in 200..201 ->
:ok

_status ->
:ok
end
defp build_opts(version, external_id) do
Keyword.merge(postgres_container_opts(version),
container_name: container_name(version),
external_id: external_id
)
end

defp terminate_tenant(conn) do
_conn = get(conn, Routes.tenant_path(conn, :terminate, @tenant_name))
:ok
end

defp test_connection do
proxy_port = Application.fetch_env!(:supavisor, :proxy_port_transaction)

connection_opts = [
hostname: @db_host,
port: proxy_port,
database: @postgres_db,
username: "#{@postgres_user}.#{@tenant_name}",
defp postgres_container_opts(version) do
[
image: "postgres:#{version}",
port: @postgres_port,
user: @postgres_user,
password: @postgres_password,
# This is important as otherwise Postgrex may try to reconnect in case of errors.
# We want to avoid that, as it hides connection errors.
backoff: nil
database: @postgres_db,
hostname: @db_host
]
end

defp test_connection(opts) do
connection_opts = connection_opts(opts)

assert {:ok, conn} = Postgrex.start_link(connection_opts)
assert {:ok, %{rows: [[_version_string]]}} = Postgrex.query(conn, "SELECT version();", [])

:ok
end

defp container_name(version) do
"test_postgres_#{version}_switching"
end

defp cleanup_containers do
[15, 16]
|> Enum.each(fn version ->
System.cmd("docker", ["rm", "-f", container_name(version)], stderr_to_stdout: true)
end)
end

defp gen_token do
secret = Application.fetch_env!(:supavisor, :api_jwt_secret)
Token.gen!(secret)
end
defp container_name(version), do: "test_postgres_#{version}_switching"
end
4 changes: 0 additions & 4 deletions test/supavisor_web/controllers/metrics_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,4 @@ defmodule SupavisorWeb.MetricsControllerTest do
defp auth(conn, bearer \\ gen_token()) do
put_req_header(conn, "authorization", "Bearer " <> bearer)
end

defp gen_token(secret \\ Application.fetch_env!(:supavisor, :metrics_jwt_secret)) do
Supavisor.Jwt.Token.gen!(secret)
end
end
4 changes: 0 additions & 4 deletions test/supavisor_web/controllers/tenant_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,4 @@ defmodule SupavisorWeb.TenantControllerTest do

assert {:ok, nil} = Cachex.get(Supavisor.Cache, {:tenant_cache, external_id, nil})
end

defp gen_token(secret \\ Application.fetch_env!(:supavisor, :metrics_jwt_secret)) do
Supavisor.Jwt.Token.gen!(secret)
end
end
6 changes: 6 additions & 0 deletions test/support/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ defmodule SupavisorWeb.ConnCase do

use ExUnit.CaseTemplate

alias Supavisor.Jwt.Token

using do
quote do
# Import conveniences for testing with connections
Expand All @@ -35,4 +37,8 @@ defmodule SupavisorWeb.ConnCase do
Supavisor.DataCase.setup_sandbox(tags)
{:ok, conn: Phoenix.ConnTest.build_conn()}
end

def gen_token(secret \\ Application.fetch_env!(:supavisor, :api_jwt_secret)) do
Token.gen!(secret)
end
end
Loading
Loading