nixos/zammad: refactor package, module and nixos-test (#277456)

This commit is contained in:
Jenny
2024-11-25 19:45:58 +01:00
committed by GitHub
4 changed files with 70 additions and 110 deletions

View File

@@ -30,6 +30,8 @@
- Support for CUDA 10 has been dropped, as announced in the 24.11 release notes. - Support for CUDA 10 has been dropped, as announced in the 24.11 release notes.
- `zammad` has had its support for MySQL removed, since it was never working correctly and is now deprecated upstream. Check the [migration guide](https://docs.zammad.org/en/latest/appendix/migrate-to-postgresql.html) for how to convert your database to PostgreSQL.
- `kanata` was updated to v1.7.0, which introduces several breaking changes. - `kanata` was updated to v1.7.0, which introduces several breaking changes.
See the release notes of See the release notes of
[v1.7.0](https://github.com/jtroo/kanata/releases/tag/v1.7.0) [v1.7.0](https://github.com/jtroo/kanata/releases/tag/v1.7.0)

View File

@@ -1,4 +1,5 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.services.zammad; cfg = config.services.zammad;
settingsFormat = pkgs.formats.yaml { }; settingsFormat = pkgs.formats.yaml { };
@@ -7,11 +8,11 @@ let
Type = "simple"; Type = "simple";
Restart = "always"; Restart = "always";
User = "zammad"; User = cfg.user;
Group = "zammad"; Group = cfg.group;
PrivateTmp = true; PrivateTmp = true;
StateDirectory = "zammad"; StateDirectory = "zammad";
WorkingDirectory = cfg.dataDir; WorkingDirectory = package;
}; };
environment = { environment = {
RAILS_ENV = "production"; RAILS_ENV = "production";
@@ -21,6 +22,9 @@ let
REDIS_URL = "redis://${cfg.redis.host}:${toString cfg.redis.port}"; REDIS_URL = "redis://${cfg.redis.host}:${toString cfg.redis.port}";
}; };
databaseConfig = settingsFormat.generate "database.yml" cfg.database.settings; databaseConfig = settingsFormat.generate "database.yml" cfg.database.settings;
package = cfg.package.override {
dataDir = cfg.dataDir;
};
in in
{ {
@@ -30,6 +34,22 @@ in
package = lib.mkPackageOption pkgs "zammad" { }; package = lib.mkPackageOption pkgs "zammad" { };
user = lib.mkOption {
type = lib.types.str;
default = "zammad";
description = ''
Name of the Zammad user.
'';
};
group = lib.mkOption {
type = lib.types.str;
default = "zammad";
description = ''
Name of the Zammad group.
'';
};
dataDir = lib.mkOption { dataDir = lib.mkOption {
type = lib.types.path; type = lib.types.path;
default = "/var/lib/zammad"; default = "/var/lib/zammad";
@@ -94,25 +114,9 @@ in
}; };
database = { database = {
type = lib.mkOption {
type = lib.types.enum [ "PostgreSQL" "MySQL" ];
default = "PostgreSQL";
example = "MySQL";
description = "Database engine to use.";
};
host = lib.mkOption { host = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.str;
default = { default = "/run/postgresql";
PostgreSQL = "/run/postgresql";
MySQL = "localhost";
}.${cfg.database.type};
defaultText = lib.literalExpression ''
{
PostgreSQL = "/run/postgresql";
MySQL = "localhost";
}.''${config.services.zammad.database.type};
'';
description = '' description = ''
Database host address. Database host address.
''; '';
@@ -195,13 +199,9 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.zammad.database.settings = { services.zammad.database.settings = {
production = lib.mapAttrs (_: v: lib.mkDefault v) (filterNull { production = lib.mapAttrs (_: v: lib.mkDefault v) (filterNull {
adapter = { adapter = "postgresql";
PostgreSQL = "postgresql";
MySQL = "mysql2";
}.${cfg.database.type};
database = cfg.database.name; database = cfg.database.name;
pool = 50; pool = 50;
timeout = 5000; timeout = 5000;
@@ -217,13 +217,12 @@ in
config.services.zammad.websocketPort config.services.zammad.websocketPort
]; ];
users.users.zammad = { users.users.${cfg.user} = {
group = "${cfg.group}";
isSystemUser = true; isSystemUser = true;
home = cfg.dataDir;
group = "zammad";
}; };
users.groups.zammad = { }; users.groups.${cfg.group} = { };
assertions = [ assertions = [
{ {
@@ -240,19 +239,7 @@ in
} }
]; ];
services.mysql = lib.optionalAttrs (cfg.database.createLocally && cfg.database.type == "MySQL") { services.postgresql = lib.optionalAttrs (cfg.database.createLocally) {
enable = true;
package = lib.mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{
name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
services.postgresql = lib.optionalAttrs (cfg.database.createLocally && cfg.database.type == "PostgreSQL") {
enable = true; enable = true;
ensureDatabases = [ cfg.database.name ]; ensureDatabases = [ cfg.database.name ];
ensureUsers = [ ensureUsers = [
@@ -278,54 +265,52 @@ in
}; };
after = [ after = [
"network.target" "network.target"
"systemd-tmpfiles-setup.service"
] ++ lib.optionals (cfg.database.createLocally) [
"postgresql.service" "postgresql.service"
] ++ lib.optionals cfg.redis.createLocally [ ] ++ lib.optionals cfg.redis.createLocally [
"redis-${cfg.redis.name}.service" "redis-${cfg.redis.name}.service"
]; ];
requires = [ requires = lib.optionals (cfg.database.createLocally) [
"postgresql.service" "postgresql.service"
]; ];
description = "Zammad web"; description = "Zammad web";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
preStart = '' preStart = ''
# Blindly copy the whole project here.
chmod -R +w .
rm -rf ./public/assets/
rm -rf ./tmp/*
rm -rf ./log/*
cp -r --no-preserve=owner ${cfg.package}/* .
chmod -R +w .
# config file # config file
cp ${databaseConfig} ./config/database.yml cat ${databaseConfig} > ${cfg.dataDir}/config/database.yml
chmod -R +w .
${lib.optionalString (cfg.database.passwordFile != null) '' ${lib.optionalString (cfg.database.passwordFile != null) ''
{ {
echo -n " password: " echo -n " password: "
cat ${cfg.database.passwordFile} cat ${cfg.database.passwordFile}
} >> ./config/database.yml } >> ${cfg.dataDir}/config/database.yml
''} ''}
${lib.optionalString (cfg.secretKeyBaseFile != null) '' ${lib.optionalString (cfg.secretKeyBaseFile != null) ''
{ {
echo "production: " echo "production: "
echo -n " secret_key_base: " echo -n " secret_key_base: "
cat ${cfg.secretKeyBaseFile} cat ${cfg.secretKeyBaseFile}
} > ./config/secrets.yml } > ${cfg.dataDir}/config/secrets.yml
''} ''}
if [ `${config.services.postgresql.package}/bin/psql \ # needed for cleanup
--host ${cfg.database.host} \ shopt -s extglob
${lib.optionalString
(cfg.database.port != null) # cleanup state directory from module before refactoring in
"--port ${toString cfg.database.port}"} \ # https://github.com/NixOS/nixpkgs/pull/277456
--username ${cfg.database.user} \ if [[ -e ${cfg.dataDir}/node_modules ]]; then
--dbname ${cfg.database.name} \ rm -rf ${cfg.dataDir}/!("tmp"|"config"|"log"|"state_dir_migrated"|"db_seeded")
--command "SELECT COUNT(*) FROM pg_class c \ rm -rf ${cfg.dataDir}/config/!("database.yml"|"secrets.yml")
JOIN pg_namespace s ON s.oid = c.relnamespace \ # state directory cleanup required --> zammad was already installed --> do not seed db
WHERE s.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') \ echo true > ${cfg.dataDir}/db_seeded
AND s.nspname NOT LIKE 'pg_temp%';" | sed -n 3p` -eq 0 ]; then fi
SEEDED=$(cat ${cfg.dataDir}/db_seeded)
if [[ $SEEDED != "true" ]]; then
echo "Initialize database" echo "Initialize database"
./bin/rake --no-system db:migrate ./bin/rake --no-system db:migrate
./bin/rake --no-system db:seed ./bin/rake --no-system db:seed
echo true > ${cfg.dataDir}/db_seeded
else else
echo "Migrate database" echo "Migrate database"
./bin/rake --no-system db:migrate ./bin/rake --no-system db:migrate
@@ -335,6 +320,16 @@ in
script = "./script/rails server -b ${cfg.host} -p ${toString cfg.port}"; script = "./script/rails server -b ${cfg.host} -p ${toString cfg.port}";
}; };
systemd.tmpfiles.rules = [
"d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/config 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/tmp 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/log 0750 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/config/secrets.yml 0640 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/config/database.yml 0640 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/db_seeded 0640 ${cfg.user} ${cfg.group} - -"
];
systemd.services.zammad-websocket = { systemd.services.zammad-websocket = {
inherit serviceConfig environment; inherit serviceConfig environment;
after = [ "zammad-web.service" ]; after = [ "zammad-web.service" ];

View File

@@ -15,34 +15,6 @@ import ./make-test-python.nix (
services.zammad.secretKeyBaseFile = pkgs.writeText "secret" '' services.zammad.secretKeyBaseFile = pkgs.writeText "secret" ''
52882ef142066e09ab99ce816ba72522e789505caba224a52d750ec7dc872c2c371b2fd19f16b25dfbdd435a4dd46cb3df9f82eb63fafad715056bdfe25740d6 52882ef142066e09ab99ce816ba72522e789505caba224a52d750ec7dc872c2c371b2fd19f16b25dfbdd435a4dd46cb3df9f82eb63fafad715056bdfe25740d6
''; '';
systemd.services.zammad-locale-cheat =
let cfg = config.services.zammad; in
{
serviceConfig = {
Type = "oneshot";
Restart = "on-failure";
User = "zammad";
Group = "zammad";
PrivateTmp = true;
StateDirectory = "zammad";
WorkingDirectory = cfg.dataDir;
};
wantedBy = [ "zammad-web.service" ];
description = "Hack in the locale files so zammad doesn't try to access the internet";
script = ''
mkdir -p ./config/translations
VERSION=$(cat ${cfg.package}/VERSION)
# If these files are not in place, zammad will try to access the internet.
# For the test, we only need to supply en-us.
echo '[{"locale":"en-us","alias":"en","name":"English (United States)","active":true,"dir":"ltr"}]' \
> ./config/locales-$VERSION.yml
echo '[{"locale":"en-us","format":"time","source":"date","target":"mm/dd/yyyy","target_initial":"mm/dd/yyyy"},{"locale":"en-us","format":"time","source":"timestamp","target":"mm/dd/yyyy HH:MM","target_initial":"mm/dd/yyyy HH:MM"}]' \
> ./config/translations/en-us-$VERSION.yml
'';
};
}; };
testScript = '' testScript = ''

View File

@@ -7,7 +7,6 @@
, bundlerEnv , bundlerEnv
, defaultGemConfig , defaultGemConfig
, callPackage , callPackage
, writeText
, procps , procps
, ruby , ruby
, postgresql , postgresql
@@ -19,6 +18,7 @@
, yarn2nix-moretea , yarn2nix-moretea
, cacert , cacert
, redis , redis
, dataDir ? "/var/lib/zammad"
}: }:
let let
@@ -26,9 +26,7 @@ let
version = "6.3.1"; version = "6.3.1";
src = applyPatches { src = applyPatches {
src = fetchFromGitHub (lib.importJSON ./source.json); src = fetchFromGitHub (lib.importJSON ./source.json);
patches = [ patches = [
./fix-sendmail-location.diff ./fix-sendmail-location.diff
]; ];
@@ -41,16 +39,6 @@ let
''; '';
}; };
databaseConfig = writeText "database.yml" ''
production:
url: <%= ENV['DATABASE_URL'] %>
'';
secretsConfig = writeText "secrets.yml" ''
production:
secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
'';
rubyEnv = bundlerEnv { rubyEnv = bundlerEnv {
name = "${pname}-gems-${version}"; name = "${pname}-gems-${version}";
inherit version; inherit version;
@@ -156,9 +144,12 @@ stdenv.mkDerivation {
installPhase = '' installPhase = ''
cp -R . $out cp -R . $out
cp ${databaseConfig} $out/config/database.yml rm -rf $out/config/database.yml $out/config/secrets.yml $out/tmp $out/log
cp ${secretsConfig} $out/config/secrets.yml # dataDir will be set in the module, and the package gets overriden there
sed -i -e "s|info|debug|" $out/config/environments/production.rb ln -s ${dataDir}/config/database.yml $out/config/database.yml
ln -s ${dataDir}/config/secrets.yml $out/config/secrets.yml
ln -s ${dataDir}/tmp $out/tmp
ln -s ${dataDir}/log $out/log
''; '';
passthru = { passthru = {