nixos/glitchtip: fix sourcemap uploads, add stateDir option (#438957)

Sandro 6779ff00 dc85fea6

Changed files
+67 -47
nixos
modules
services
web-apps
tests
+10 -1
nixos/modules/services/web-apps/glitchtip.nix
···
default = 8000;
};
+
stateDir = lib.mkOption {
+
type = lib.types.path;
+
description = "State directory of glitchtip.";
+
default = "/var/lib/glitchtip";
+
};
+
settings = lib.mkOption {
description = ''
Configuration of GlitchTip. See <https://glitchtip.com/documentation/install#configuration> for more information.
···
StateDirectory = "glitchtip";
EnvironmentFile = cfg.environmentFiles;
WorkingDirectory = "${pkg}/lib/glitchtip";
+
BindPaths = [ "${cfg.stateDir}/uploads:${pkg}/lib/glitchtip/uploads" ];
# hardening
AmbientCapabilities = "";
···
"@system-service"
"~@privileged"
"~@resources"
+
"@chown"
];
UMask = "0077";
};
···
users.users = lib.mkIf (cfg.user == "glitchtip") {
glitchtip = {
-
home = "/var/lib/glitchtip";
group = cfg.group;
extraGroups = lib.optionals cfg.redis.createLocally [ "redis-glitchtip" ];
isSystemUser = true;
···
};
users.groups = lib.mkIf (cfg.group == "glitchtip") { glitchtip = { }; };
+
+
systemd.tmpfiles.settings.glitchtip."${cfg.stateDir}/uploads".d = { inherit (cfg) user group; };
environment.systemPackages =
let
+57 -46
nixos/tests/glitchtip.nix
···
];
};
-
testScript = ''
-
import json
-
import re
-
import time
+
testScript =
+
{ nodes, ... }: # python
+
''
+
import json
+
import re
+
import time
-
machine.wait_for_unit("glitchtip.service")
-
machine.wait_for_unit("glitchtip-worker.service")
-
machine.wait_for_open_port(8000)
+
machine.wait_for_unit("glitchtip.service")
+
machine.wait_for_unit("glitchtip-worker.service")
+
machine.wait_for_open_port(8000)
-
origin_url = "${domain}"
-
cookie_jar_path = "/tmp/cookies.txt"
-
curl = f"curl -b {cookie_jar_path} -c {cookie_jar_path} -fS -H 'Origin: {origin_url}'"
+
origin_url = "${domain}"
+
cookie_jar_path = "/tmp/cookies.txt"
+
curl = f"curl -b {cookie_jar_path} -c {cookie_jar_path} -fS -H 'Origin: {origin_url}'"
-
# create superuser account
-
machine.succeed("DJANGO_SUPERUSER_PASSWORD=password glitchtip-manage createsuperuser --no-input --email=admin@example.com")
+
# create superuser account
+
machine.succeed("DJANGO_SUPERUSER_PASSWORD=password glitchtip-manage createsuperuser --no-input --email=admin@example.com")
-
# login
-
machine.fail(f"{curl} -s {origin_url}/_allauth/browser/v1/auth/session") # get the csrf token, returns a 401
-
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
-
machine.succeed(f"{curl} {origin_url}/_allauth/browser/v1/auth/login -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"email\": \"admin@example.com\", \"password\": \"password\"}}'")
+
# login
+
machine.fail(f"{curl} -s {origin_url}/_allauth/browser/v1/auth/session") # get the csrf token, returns a 401
+
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
+
machine.succeed(f"{curl} {origin_url}/_allauth/browser/v1/auth/login -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"email\": \"admin@example.com\", \"password\": \"password\"}}'")
-
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/users/me/"))
-
assert resp["email"] == "admin@example.com"
-
assert resp["isSuperuser"] is True
+
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/users/me/"))
+
assert resp["email"] == "admin@example.com"
+
assert resp["isSuperuser"] is True
-
# create organization
-
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
-
machine.succeed(f"{curl} {origin_url}/api/0/organizations/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"name\": \"main\"}}'")
+
# create organization
+
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
+
machine.succeed(f"{curl} {origin_url}/api/0/organizations/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"name\": \"main\"}}'")
+
+
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/organizations/"))
+
assert len(resp) == 1
+
assert resp[0]["name"] == "main"
+
assert resp[0]["slug"] == "main"
+
+
# create team
+
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
+
machine.succeed(f"{curl} {origin_url}/api/0/organizations/main/teams/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"slug\": \"test\"}}'")
-
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/organizations/"))
-
assert len(resp) == 1
-
assert resp[0]["name"] == "main"
-
assert resp[0]["slug"] == "main"
+
# create project
+
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
+
machine.succeed(f"{curl} {origin_url}/api/0/teams/main/test/projects/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"name\": \"test\"}}'")
-
# create team
-
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
-
machine.succeed(f"{curl} {origin_url}/api/0/organizations/main/teams/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"slug\": \"test\"}}'")
+
# fetch dsn
+
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/projects/main/test/keys/"))
+
assert len(resp) == 1
+
assert re.match(r"^http://[\da-f]+@glitchtip\.local:8000/\d+$", dsn := resp[0]["dsn"]["public"])
-
# create project
-
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
-
machine.succeed(f"{curl} {origin_url}/api/0/teams/main/test/projects/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"name\": \"test\"}}'")
+
# send event
+
machine.succeed(f"SENTRY_DSN={dsn} sentry-cli send-event -m 'hello world'")
-
# fetch dsn
-
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/projects/main/test/keys/"))
-
assert len(resp) == 1
-
assert re.match(r"^http://[\da-f]+@glitchtip\.local:8000/\d+$", dsn := resp[0]["dsn"]["public"])
+
for _ in range(20):
+
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/organizations/main/issues/?query=is:unresolved"))
+
if len(resp) != 0: break
+
time.sleep(1)
+
assert len(resp) == 1
+
assert resp[0]["title"] == "hello world"
+
assert int(resp[0]["count"]) == 1
-
# send event
-
machine.succeed(f"SENTRY_DSN={dsn} sentry-cli send-event -m 'hello world'")
+
# create api token
+
csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut -f7").rstrip()
+
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/api-tokens/ -s -H 'X-Csrftoken: {csrf_token}' -H 'Content-Type: application/json' -d '{{\"label\":\"token\",\"scopes\":[\"project:write\"]}}'"))
+
token = resp["token"]
-
for _ in range(20):
-
resp = json.loads(machine.succeed(f"{curl} {origin_url}/api/0/organizations/main/issues/?query=is:unresolved"))
-
if len(resp) != 0: break
-
time.sleep(1)
-
assert len(resp) == 1
-
assert resp[0]["title"] == "hello world"
-
assert int(resp[0]["count"]) == 1
-
'';
+
# upload sourcemaps
+
machine.succeed(f"sentry-cli --url {origin_url} --auth-token {token} sourcemaps upload --org main --project test ${nodes.machine.services.glitchtip.package.frontend}/*.map")
+
assert machine.succeed("ls /var/lib/glitchtip/uploads/file_blobs/").strip()
+
'';
}