Skip to content

Commit 25c1eb9

Browse files
committed
fix: add nixos module test
1 parent 3408746 commit 25c1eb9

File tree

8 files changed

+304
-13
lines changed

8 files changed

+304
-13
lines changed

.commitlintrc.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
# The rules below have been manually copied from @commitlint/config-conventional
3+
# and match the v1.0.0 specification:
4+
# https://www.conventionalcommits.org/en/v1.0.0/#specification
5+
#
6+
# You can remove them and uncomment the config below when the following issue is
7+
# fixed: https://github.com/conventional-changelog/commitlint/issues/613
8+
#
9+
# extends:
10+
# - '@commitlint/config-conventional'
11+
rules:
12+
body-leading-blank: [1, always]
13+
body-max-line-length: [2, always, 100]
14+
footer-leading-blank: [1, always]
15+
footer-max-line-length: [2, always, 100]
16+
header-max-length: [2, always, 100]
17+
subject-case:
18+
- 2
19+
- never
20+
- [sentence-case, start-case, pascal-case, upper-case]
21+
subject-empty: [2, never]
22+
subject-full-stop: [2, never, "."]
23+
type-case: [2, always, lower-case]
24+
type-empty: [2, never]
25+
type-enum:
26+
- 2
27+
- always
28+
- [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]

flake.nix

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@
176176
export DATABASE_URL="postgres://phone_db:your_secure_password_here@localhost:${
177177
toString postgres_port
178178
}/phone_db"
179-
export IMAGE_DIR="/tmp/images"
180179
181180
export LDAP_SERVER="localhost"
182181
export LDAP_PORT="${toString ldap_port}"
@@ -221,21 +220,162 @@
221220
};
222221
services.postgres = {
223222
enable = true;
224-
package = pkgs.postgresql_15.withPackages (ps: [ps.postgis]);
223+
package = pkgs.postgresql_15;
225224
listen_addresses = "127.0.0.1";
226225
port = postgres_port;
227226
initialDatabases = [{name = "phone_db";}];
228227
initialScript = ''
229228
\c phone_db;
230229
CREATE USER phone_db with encrypted password 'your_secure_password_here';
231230
GRANT ALL PRIVILEGES ON DATABASE phone_db TO phone_db;
232-
ALTER USER phone_db WITH SUPERUSER;
233231
'';
234232
};
235233
}
236234
];
237235
};
236+
237+
test = pkgs.nixosTest {
238+
name = "phone_db";
239+
nodes.machine = {...}: {
240+
imports = [
241+
self.nixosModules.default
242+
];
243+
services.phone_db = {
244+
enable = true;
245+
http_url = "http://localhost:4000";
246+
port = 4000;
247+
secrets = pkgs.writeText "secrets.txt" ''
248+
export RELEASE_COOKIE="12345678901234567890123456789012345678901234567890123456"
249+
export DATABASE_URL="postgres://phone_db:your_secure_password_here@localhost/phone_db"
250+
export GUARDIAN_SECRET="1234567890123456789012345678901234567890123456789012345678901234"
251+
export SECRET_KEY_BASE="1234567890123456789012345678901234567890123456789012345678901234"
252+
export SIGNING_SALT="12345678901234567890123456789012"
253+
export OIDC_DISCOVERY_URL="http://localhost"
254+
export OIDC_CLIENT_ID="photos"
255+
export OIDC_CLIENT_SECRET="12345678901234567890123456789012"
256+
export OIDC_AUTH_SCOPE="openid profile groups"
257+
258+
export DATABASE_URL_TEST="postgres://phone_db:your_secure_password_here@localhost:${
259+
toString postgres_port
260+
}/phone_db_test"
261+
export DATABASE_URL="postgres://phone_db:your_secure_password_here@localhost:${
262+
toString postgres_port
263+
}/phone_db"
264+
265+
export LDAP_SERVER="localhost"
266+
export LDAP_PORT="${toString ldap_port}"
267+
export LDAP_BASE_DN="${dn_suffix}"
268+
export LDAP_USERNAME="root"
269+
export LDAP_USER_PASSWORD="your_secure_password_here"
270+
271+
export PHONE_USERNAME="${phone_username}"
272+
export PHONE_PASSWORD="${phone_password}"
273+
'';
274+
};
275+
system.stateVersion = "24.05";
276+
277+
services.postgresql = {
278+
enable = true;
279+
package = pkgs.postgresql_15;
280+
settings.port = postgres_port;
281+
initialScript = pkgs.writeText "init.psql" ''
282+
CREATE DATABASE phone_db;
283+
CREATE USER phone_db with encrypted password 'your_secure_password_here';
284+
ALTER DATABASE phone_db OWNER TO phone_db;
285+
'';
286+
};
287+
288+
services.openldap = {
289+
enable = true;
290+
291+
/*
292+
enable plain and secure connections
293+
*/
294+
urlList = [ldap_url];
295+
296+
settings = {
297+
attrs = {
298+
olcLogLevel = "conns config";
299+
};
300+
301+
children = {
302+
"cn=schema".includes = [
303+
"${pkgs.openldap}/etc/schema/core.ldif"
304+
"${pkgs.openldap}/etc/schema/cosine.ldif"
305+
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
306+
];
307+
308+
"olcDatabase={1}mdb" = {
309+
attrs = {
310+
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
311+
312+
olcDatabase = "{1}mdb";
313+
olcDbDirectory = "/var/lib/openldap/test";
314+
315+
olcSuffix = dn_suffix;
316+
317+
/*
318+
your admin account, do not use writeText on a production system
319+
*/
320+
olcRootDN = root_dn;
321+
olcRootPW.path = pkgs.writeText "olcRootPW" "your_secure_password_here";
322+
323+
olcAccess = [
324+
/*
325+
custom access rules for userPassword attributes
326+
*/
327+
''
328+
{0}to attrs=userPassword
329+
by self write
330+
by anonymous auth
331+
by * none
332+
''
333+
334+
/*
335+
allow read on anything else
336+
*/
337+
''
338+
{1}to *
339+
by * read
340+
''
341+
];
342+
};
343+
children = {
344+
"olcOverlay={2}ppolicy".attrs = {
345+
objectClass = ["olcOverlayConfig" "olcPPolicyConfig" "top"];
346+
olcOverlay = "{2}ppolicy";
347+
olcPPolicyHashCleartext = "TRUE";
348+
};
349+
"olcOverlay={3}memberof".attrs = {
350+
objectClass = ["olcOverlayConfig" "olcMemberOf" "top"];
351+
olcOverlay = "{3}memberof";
352+
olcMemberOfRefInt = "TRUE";
353+
olcMemberOfDangling = "ignore";
354+
olcMemberOfGroupOC = "groupOfNames";
355+
olcMemberOfMemberAD = "member";
356+
olcMemberOfMemberOfAD = "memberOf";
357+
};
358+
"olcOverlay={4}refint".attrs = {
359+
objectClass = ["olcOverlayConfig" "olcRefintConfig" "top"];
360+
olcOverlay = "{4}refint";
361+
olcRefintAttribute = ["memberof" "member" "manager" "owner"];
362+
};
363+
};
364+
};
365+
};
366+
};
367+
};
368+
};
369+
370+
testScript = ''
371+
machine.wait_for_unit("phone_db.service")
372+
machine.wait_for_open_port(4000)
373+
machine.succeed("${pkgs.curl}/bin/curl --fail -v http://localhost:4000/_health")
374+
machine.succeed("${test_phone_call}/bin/test_phone_call")
375+
'';
376+
};
238377
in {
378+
checks.nixosModules = test;
239379
packages = {
240380
devenv-up = devShell.config.procfileScript;
241381
default = pkg;

lib/phone_db/contacts/ldap.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ defmodule PhoneDb.Contacts.Ldap do
5555
end
5656
end
5757

58+
def get_contact(%Contact{} = contact) do
59+
authenticate()
60+
61+
case Paddle.get(%PhoneDb.Contacts.Ldap.Person{telephoneNumber: contact.phone_number}, nil) do
62+
{:error, :noSuchObject} -> nil
63+
{:ok, [person]} -> person
64+
end
65+
end
66+
5867
def update_contact(%Contact{} = contact) do
5968
authenticate()
6069

lib/phone_db/release.ex

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
defmodule PhoneDb.Release do
2+
@moduledoc """
3+
Functions for managing released code
4+
"""
5+
@app :phone_db
6+
7+
require Logger
8+
9+
alias Ecto.Adapters.SQL
10+
import Ecto.Query
11+
alias PhoneDb.Contacts.Contact
12+
alias PhoneDb.Repo
13+
14+
def migrate do
15+
for repo <- repos() do
16+
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
17+
end
18+
end
19+
20+
def rollback(repo, version) do
21+
for r <- repos(), r == repo do
22+
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
23+
end
24+
end
25+
26+
def seconds_since_last_migration do
27+
Repo.one(
28+
from m in "schema_migrations",
29+
select: fragment("EXTRACT(EPOCH FROM age(NOW(), ?::timestamp))::BIGINT", m.inserted_at),
30+
order_by: [desc: m.inserted_at],
31+
limit: 1
32+
)
33+
end
34+
35+
def migrations_check do
36+
repos = Application.fetch_env!(@app, :ecto_repos)
37+
38+
migrations =
39+
repos
40+
|> Enum.map(&Ecto.Migrator.migrations/1)
41+
|> List.flatten()
42+
|> Enum.filter(fn
43+
{:up, _, _} -> false
44+
{_, _, _} -> true
45+
end)
46+
47+
if Enum.empty?(migrations) do
48+
:ok
49+
else
50+
Logger.error("Migrations pending: #{inspect(migrations)}")
51+
{:error, "Migrations pending: #{inspect(migrations)}"}
52+
end
53+
end
54+
55+
def database_check do
56+
repos = Application.fetch_env!(@app, :ecto_repos)
57+
58+
Enum.reduce_while(repos, :ok, fn item, _acc ->
59+
case SQL.query(item, "SELECT 1") do
60+
{:ok, %{num_rows: 1, rows: [[1]]}} ->
61+
{:cont, :ok}
62+
63+
{:error, reason} ->
64+
Logger.error("Database error: #{inspect(reason)}")
65+
{:halt, {:error, inspect(reason)}}
66+
end
67+
end)
68+
rescue
69+
e ->
70+
Logger.error("Database error: #{inspect(e)}")
71+
{:error, inspect(e)}
72+
end
73+
74+
def ldap_check do
75+
contact = %Contact{phone_number: "000"}
76+
# This probably will fail because the contact doesn't exist.
77+
PhoneDb.Contacts.Ldap.get_contact(contact)
78+
:ok
79+
rescue
80+
e ->
81+
Logger.error("Database error: #{inspect(e)}")
82+
{:error, inspect(e)}
83+
end
84+
85+
def health_check do
86+
with :ok <- database_check(),
87+
:ok <- ldap_check() do
88+
:ok
89+
else
90+
{:error, reason} -> {:error, reason}
91+
end
92+
end
93+
94+
defp repos do
95+
Application.ensure_all_started(:ssl)
96+
Application.load(@app)
97+
Application.fetch_env!(@app, :ecto_repos)
98+
end
99+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule PhoneDbWeb.HealthCheckController do
2+
use PhoneDbWeb, :controller
3+
4+
alias PhoneDb.Release
5+
6+
def index(conn, _params) do
7+
case Release.health_check() do
8+
:ok ->
9+
text(conn, "HEALTHY")
10+
11+
{:error, _reason} ->
12+
conn |> put_status(500) |> text("ERROR")
13+
end
14+
end
15+
end

lib/phone_db_web/controllers/page_controller.ex

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,4 @@ defmodule PhoneDbWeb.PageController do
2525
def login(conn, _params) do
2626
redirect(conn, to: Routes.page_path(conn, :index))
2727
end
28-
29-
@spec health(Plug.Conn.t(), map()) :: Plug.Conn.t()
30-
def health(conn, _params) do
31-
send_resp(conn, 200, "healthy")
32-
end
3328
end

lib/phone_db_web/router.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ defmodule PhoneDbWeb.Router do
5353
plug :phone_auth
5454
end
5555

56-
scope "/health" do
57-
get "/", PhoneDbWeb.PageController, :health
56+
scope "/", PhoneDbWeb do
57+
get "/_health", HealthCheckController, :index
5858
end
5959

6060
live_session :default, on_mount: PhoneDbWeb.InitAssigns do

module.nix

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
pkgs,
44
config,
55
...
6-
}:
7-
with lib; let
6+
}: let
7+
inherit (lib) mkEnableOption mkOption types mkIf;
8+
89
cfg = config.services.phone_db;
910

1011
system = pkgs.stdenv.system;
@@ -26,7 +27,10 @@ in {
2627
enable = mkEnableOption "phone_db service";
2728
secrets = mkOption {type = types.path;};
2829
http_url = mkOption {type = types.str;};
29-
port = mkOption {type = types.int;};
30+
port = mkOption {
31+
type = types.int;
32+
default = 4000;
33+
};
3034
data_dir = mkOption {
3135
type = types.str;
3236
default = "/var/lib/phone_db";
@@ -49,6 +53,7 @@ in {
4953
after = ["network.target" "postgresql.service" "openldap.service"];
5054
serviceConfig = {
5155
User = "phone_db";
56+
ExecStartPre = ''${wrapper}/bin/phone_db eval "PhoneDb.Release.migrate"'';
5257
ExecStart = "${wrapper}/bin/phone_db start";
5358
ExecStop = "${wrapper}/bin/phone_db stop";
5459
ExecReload = "${wrapper}/bin/phone_db reload";

0 commit comments

Comments
 (0)