123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- -module(epgsql_cth).
- -export([
- init/2,
- terminate/1,
- pre_init_per_suite/3
- ]).
- -include_lib("common_test/include/ct.hrl").
- -include("epgsql_tests.hrl").
- init(_Id, State) ->
- Start = os:timestamp(),
- PgConfig = start_postgres(),
- ok = create_testdbs(PgConfig),
- ct:pal(info, "postgres started in ~p ms\n",
- [timer:now_diff(os:timestamp(), Start) / 1000]),
- [{pg_config, PgConfig}|State].
- pre_init_per_suite(_SuiteName, Config, State) ->
- {Config ++ State, State}.
- terminate(State) ->
- ok = stop_postgres(?config(pg_config, State)).
- create_testdbs(Config) ->
- PgHost = ?config(host, Config),
- PgPort = ?config(port, Config),
- PgUser = ?config(user, Config),
- Utils = ?config(utils, Config),
- Psql = ?config(psql, Utils),
- CreateDB = ?config(createdb, Utils),
- Opts = lists:concat([" -h ", PgHost, " -p ", PgPort, " "]),
- Cmds = [
- [CreateDB, Opts, PgUser],
- [Psql, Opts, "template1 < ", filename:join(?TEST_DATA_DIR, "test_schema.sql")]
- ],
- lists:foreach(fun(Cmd) ->
- {ok, []} = exec:run(lists:flatten(Cmd), [sync])
- end, Cmds).
- %% =============================================================================
- %% start postgresql
- %% =============================================================================
- -define(PG_TIMEOUT, 30000).
- start_postgres() ->
- ok = application:start(erlexec),
- pipe([
- fun find_utils/1,
- fun init_database/1,
- fun get_version/1,
- fun write_postgresql_config/1,
- fun copy_certs/1,
- fun write_pg_hba_config/1,
- fun start_postgresql/1
- ], []).
- stop_postgres(Config) ->
- PgProc = ?config(proc, Config),
- PgProc ! stop,
- ok.
- find_utils(State) ->
- Utils = [initdb, createdb, postgres, psql],
- UtilsConfig = lists:foldl(fun(U, Acc) ->
- UList = atom_to_list(U),
- Path = case os:find_executable(UList) of
- false ->
- case filelib:wildcard("/usr/lib/postgresql/**/bin/" ++ UList) of
- [] ->
- ct:pal(error, "~s not found", [U]),
- throw({util_no_found, U});
- List -> lists:last(lists:sort(List))
- end;
- P -> P
- end,
- [{U, Path}|Acc]
- end, [], Utils),
- [{utils, UtilsConfig}|State].
- start_postgresql(Config) ->
- PgDataDir = ?config(datadir, Config),
- Utils = ?config(utils, Config),
- Postgres = ?config(postgres, Utils),
- PgHost = "localhost",
- PgPort = get_free_port(),
- SocketDir = "/tmp",
- Command = lists:concat(
- [Postgres,
- " -k ", SocketDir,
- " -D ", PgDataDir,
- " -h ", PgHost,
- " -p ", PgPort]),
- ct:pal(info, ?HI_IMPORTANCE, "Starting Postgresql: `~s`", [Command]),
- Pid = proc_lib:spawn(fun() ->
- {ok, _, I} = exec:run_link(Command,
- [{stderr,
- fun(_, _, Msg) ->
- ct:pal(info, "postgres: ~s", [Msg])
- end},
- {env, [{"LANGUAGE", "en"}]}]),
- loop(I)
- end),
- ConfigR = [
- {host, PgHost},
- {port, PgPort},
- {proc, Pid}
- | Config
- ],
- wait_postgresql_ready(SocketDir, ConfigR).
- loop(I) ->
- receive
- stop -> exec:kill(I, 0);
- _ -> loop(I)
- end.
- wait_postgresql_ready(SocketDir, Config) ->
- PgPort = ?config(port, Config),
- PgFile = lists:concat([".s.PGSQL.", PgPort]),
- Path = filename:join(SocketDir, PgFile),
- WaitUntil = ts_add(os:timestamp(), ?PG_TIMEOUT),
- case wait_(Path, WaitUntil) of
- true -> ok;
- false -> throw(<<"Postgresql init timeout">>)
- end,
- Config.
- wait_(Path, Until) ->
- case file:read_file_info(Path) of
- {error, enoent} ->
- case os:timestamp() > Until of
- true -> false;
- _ ->
- timer:sleep(300),
- wait_(Path, Until)
- end;
- _ -> true
- end.
- init_database(Config) ->
- Utils = ?config(utils, Config),
- Initdb = ?config(initdb, Utils),
- {ok, Cwd} = file:get_cwd(),
- PgDataDir = filename:append(Cwd, "datadir"),
- {ok, _} = exec:run(Initdb ++ " --locale en_US.UTF8 " ++ PgDataDir, [sync,stdout,stderr]),
- [{datadir, PgDataDir}|Config].
- get_version(Config) ->
- Datadir = ?config(datadir, Config),
- VersionFile = filename:join(Datadir, "PG_VERSION"),
- {ok, VersionFileData} = file:read_file(VersionFile),
- VersionBin = list_to_binary(string:strip(binary_to_list(VersionFileData), both, $\n)),
- Version = lists:map(fun erlang:binary_to_integer/1,
- binary:split(VersionBin, <<".">>, [global])),
- [{version, Version} | Config].
- write_postgresql_config(Config) ->
- PgDataDir = ?config(datadir, Config),
- PGConfig = [
- "ssl = on\n",
- "ssl_ca_file = 'root.crt'\n",
- "lc_messages = 'en_US.UTF-8'\n",
- "fsync = off\n",
- "wal_level = 'logical'\n",
- "max_replication_slots = 15\n",
- "max_wal_senders = 15"
- ],
- FilePath = filename:join(PgDataDir, "postgresql.conf"),
- ok = file:write_file(FilePath, PGConfig),
- Config.
- copy_certs(Config) ->
- PgDataDir = ?config(datadir, Config),
- Files = [
- {"server.crt", "server.crt", 8#00660},
- {"server.key", "server.key", 8#00600},
- {"root.crt", "root.crt", 8#00660},
- {"root.key", "root.key", 8#00660}
- ],
- lists:foreach(fun({From, To, Mode}) ->
- FromPath = filename:join(?TEST_DATA_DIR, From),
- ToPath = filename:join(PgDataDir, To),
- {ok, _} = file:copy(FromPath, ToPath),
- ok = file:change_mode(ToPath, Mode)
- end, Files),
- Config.
- write_pg_hba_config(Config) ->
- PgDataDir = ?config(datadir, Config),
- Version = ?config(version, Config),
- User = os:getenv("USER"),
- PGConfig = [
- "local all ", User, " trust\n",
- "host template1 ", User, " 127.0.0.1/32 trust\n",
- "host ", User, " ", User, " 127.0.0.1/32 trust\n",
- "hostssl postgres ", User, " 127.0.0.1/32 trust\n",
- "host epgsql_test_db1 ", User, " 127.0.0.1/32 trust\n",
- "host epgsql_test_db1 epgsql_test 127.0.0.1/32 trust\n",
- "host epgsql_test_db1 epgsql_test_md5 127.0.0.1/32 md5\n",
- "host epgsql_test_db1 epgsql_test_cleartext 127.0.0.1/32 password\n",
- "hostssl epgsql_test_db1 epgsql_test_cert 127.0.0.1/32 cert clientcert=1\n",
- "host template1 ", User, " ::1/128 trust\n",
- "host ", User, " ", User, " ::1/128 trust\n",
- "hostssl postgres ", User, " ::1/128 trust\n",
- "host epgsql_test_db1 ", User, " ::1/128 trust\n",
- "host epgsql_test_db1 epgsql_test ::1/128 trust\n",
- "host epgsql_test_db1 epgsql_test_md5 ::1/128 md5\n",
- "host epgsql_test_db1 epgsql_test_cleartext ::1/128 password\n",
- "hostssl epgsql_test_db1 epgsql_test_cert ::1/128 cert clientcert=1\n" |
- case Version >= [10] of
- true ->
- %% See
- %% https://www.postgresql.org/docs/10/static/release-10.html
- %% "Change how logical replication uses pg_hba.conf"
- ["host epgsql_test_db1 epgsql_test_replication 127.0.0.1/32 trust\n",
- %% scram auth method only available on PG >= 10
- "host epgsql_test_db1 epgsql_test_scram 127.0.0.1/32 scram-sha-256\n"];
- false ->
- ["host replication epgsql_test_replication 127.0.0.1/32 trust\n"]
- end
- ],
- FilePath = filename:join(PgDataDir, "pg_hba.conf"),
- ok = file:write_file(FilePath, PGConfig),
- [{user, User}|Config].
- %% =============================================================================
- %% Internal functions
- %% =============================================================================
- get_free_port() ->
- {ok, Listen} = gen_tcp:listen(0, []),
- {ok, Port} = inet:port(Listen),
- ok = gen_tcp:close(Listen),
- Port.
- pipe(Funs, Config) ->
- lists:foldl(fun(F, S) -> F(S) end, Config, Funs).
- ts_add({Mega, Sec, Micro}, Timeout) ->
- V = (Mega * 1000000 + Sec)*1000000 + Micro + Timeout * 1000,
- {V div 1000000000000,
- V div 1000000 rem 1000000,
- V rem 1000000}.
|