erpnext starts in the VM

This commit is contained in:
Akshay Mankar 2023-06-05 19:28:33 +02:00
parent cb9630c787
commit 5d27509f50
Signed by untrusted user: axeman
GPG key ID: CA08F3AB62369B89
2 changed files with 111 additions and 79 deletions

View file

@ -1,5 +1,5 @@
self: super: { self: super: {
python3 = super.python3.override { python3-erpnext = super.python3.override {
packageOverrides = pyself: pysuper: { packageOverrides = pyself: pysuper: {
bench = pyself.callPackage ./python/bench.nix {}; bench = pyself.callPackage ./python/bench.nix {};
erpnext = pyself.callPackage ./python/erpnext.nix {}; erpnext = pyself.callPackage ./python/erpnext.nix {};

View file

@ -1,5 +1,4 @@
{ pkgs, lib, config, modulesPath, ... }: { pkgs, lib, config, modulesPath, ... }:
with lib;
{ {
imports = [ imports = [
"${modulesPath}/profiles/minimal.nix" "${modulesPath}/profiles/minimal.nix"
@ -9,6 +8,7 @@ with lib;
config = { config = {
services.qemuGuest.enable = true; services.qemuGuest.enable = true;
system.stateVersion = "23.05";
fileSystems."/" = { fileSystems."/" = {
device = "/dev/disk/by-label/nixos"; device = "/dev/disk/by-label/nixos";
@ -28,6 +28,14 @@ with lib;
# We don't want to use tmpfs, otherwise the nix store's size will be bounded # We don't want to use tmpfs, otherwise the nix store's size will be bounded
# by a fraction of available RAM. # by a fraction of available RAM.
writableStoreUseTmpfs = false; writableStoreUseTmpfs = false;
forwardPorts = [{
guest.port = 22;
host.port = 2222;
} {
guest.port = 9090;
host.port = 9090;
}];
}; };
# So that we can ssh into the VM, see e.g. # So that we can ssh into the VM, see e.g.
@ -36,7 +44,11 @@ with lib;
services.openssh.settings.PermitRootLogin = "yes"; services.openssh.settings.PermitRootLogin = "yes";
# Give root an empty password to ssh in. # Give root an empty password to ssh in.
users.extraUsers.root.password = ""; users.extraUsers.root.password = "";
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMNeQYLFauAbzDyIbKC86NUh9yZfiyBm/BtIdkcpZnSU"
];
users.mutableUsers = false; users.mutableUsers = false;
networking.firewall.enable = false;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
git git
@ -47,6 +59,13 @@ with lib;
services.mysql = { services.mysql = {
enable = true; enable = true;
package = pkgs.mariadb; package = pkgs.mariadb;
ensureUsers = [{
name = "root";
ensurePermissions = {
"*.*" = "ALL PRIVILEGES";
};
}];
ensureDatabases = [ "root" ];
}; };
services.redis.servers = { services.redis.servers = {
@ -63,69 +82,61 @@ with lib;
description = "User to run erpnext"; description = "User to run erpnext";
group = "erpnext"; group = "erpnext";
isSystemUser = true; isSystemUser = true;
home = "/var/lib/erpnext";
createHome = true;
};
systemd.services.setup-mysql = {
enable = true;
before = [ "erpnext.service" ];
after = [ "mysql.service" ];
wantedBy = [ "erpnext.service" ];
partOf = [ "erpnext.service" ];
script = ''
${pkgs.mariadb-client}/bin/mysql -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('password')";
'';
serviceConfig = {
RemainAfterExit = true;
Type = "oneshot";
};
};
systemd.services.ensure-bench-dir = {
enable = true;
before = [ "erpnext.service" ];
wantedBy = [ "erpnext.service" ];
partOf = [ "erpnext.service" ];
script = ''
cd /var/lib/erpnext
mkdir bench
cd bench
mkdir -p apps sites config/pids logs
'';
serviceConfig = {
RemainAfterExit = true;
Type = "oneshot";
User = "erpnext";
};
}; };
#users = {
# users.${user} = {
# uid = 327;
# group = group;
# home = server.workDir;
# };
# groups.${group}.gid = 327;
#};
systemd.services.erpnext = systemd.services.erpnext =
let
name = "worker1";
user = "erpnext";
group = "erpnext";
server = {
bind = "127.0.0.1:9090";
workDir = "/var/lib/erpnext";
};
in {
enable = true;
wantedBy = [ "multi-user.target" ];
after = [ "mysql.service" "redis.service" "redis-socketio.service" ];
description = "ERPNext";
environment =
let let
penv = pkgs.python3.buildEnv.override { penv = pkgs.python3-erpnext.buildEnv.override {
extraLibs = [ extraLibs = [
pkgs.python3.pkgs.frappe pkgs.python3-erpnext.pkgs.frappe
pkgs.python3.pkgs.erpnext pkgs.python3-erpnext.pkgs.erpnext
pkgs.python3.pkgs.bench pkgs.python3-erpnext.pkgs.bench
]; ];
}; };
in { appsFile = pkgs.writeText "erpnext-apps.txt" ''
PYTHONPATH = "${penv}/${pkgs.python3.sitePackages}/";
};
#confinement = {
# enable = true;
# packages = [ ];
#};
serviceConfig = {
#User = "erpnext";
#NoNewPrivileges = true;
Type = "simple";
BindReadOnlyPaths = [
"${pkgs.frappe-app}/share/apps/frappe:/frappe-bench/apps/frappe"
"${pkgs.erpnext-app}/share/apps/erpnext:/frappe-bench/apps/erpnext"
"${pkgs.frappe-erpnext-assets}/share/sites/assets:/frappe-bench/sites/assets"
# "${penv}:/frappe-bench/env"
];
ExecStartPre = pkgs.writeScript "erpnext-server.${name}-init" ''
#!/bin/sh
mkdir -p ${server.workDir}/sites
chown ${user}:${group} ${server.workDir}
cat > ${server.workDir}/sites/apps.txt <<EOF
frappe frappe
erpnext erpnext
EOF '';
cat > ${server.workDir}/sites/common_site_config.json <<EOF # In a module, this could be provided by a use as a file as it could
# contain secrets and we don't want this in the nix-store. But here it
# is OK.
commonSiteConfig = pkgs.writeText "erpnext-common_site_config.json" ''
{ {
"db_host": "localhost", "db_host": "localhost",
"db_port": 3306, "db_port": 3306,
@ -134,32 +145,53 @@ with lib;
"redis_cache": "redis://localhost:6379?db=0", "redis_cache": "redis://localhost:6379?db=0",
"redis_queue": "redis://localhost:6379?db=1", "redis_queue": "redis://localhost:6379?db=1",
"redis_socketio": "redis://localhost:6379?db=2", "redis_socketio": "redis://localhost:6379?db=2",
"socketio_port": 12311 "socketio_port": 3000
} }
EOF
cd "${server.workDir}/sites"
# Upstream initializes the DB with this command
bench new-site localhost --mariadb-root-password password --admin-password admin
bench --site localhost install-app erpnext
node $tmp/apps/frappe/socketio.js &
''; '';
ExecStart = '' in
${pkgs.python3Packages.gunicorn}/bin/gunicorn frappe.app:application --name ${name} \ {
--chdir="${server.workDir}/sites" \ enable = true;
--user ${user} \ wantedBy = [ "multi-user.target" ];
--group ${group} \ after = [ "mysql.service" "redis.service" "redis-socketio.service" ];
--bind=${server.bind} \ description = "ERPNext";
--pid ${server.workDir}/gunicorn-${name}.pid \ confinement = {
--threads=4 \ enable = true;
--workers=2 \ packages = [ pkgs.mariadb-client pkgs.nodejs penv ];
--worker-class=gthread \ };
--worker-tmp-dir=/dev/shm \ script = ''
--timeout=120 \ export PYTHON_PATH=${penv}/${pkgs.python3-erpnext.sitePackages}
--preload export PATH="${pkgs.mariadb-client}/bin:${pkgs.nodejs}/bin:${penv}/bin:$PATH"
'';
};
};
system.stateVersion = "23.11"; cat /etc/hosts
${pkgs.nettools}/bin/netstat -nptel
# Upstream initializes the DB with this command
# TODO: Make this idempotent
cd /var/lib/erpnext/bench/sites
bench new-site localhost --mariadb-root-password password --admin-password admin
bench --site localhost install-app erpnext
# TODO: Run these as systemd units
node /var/lib/erpnext/bench/apps/frappe/socketio.js &
gunicorn --chdir="/var/lib/erpnext/bench/sites" --bind=0.0.0.0:9090 --threads=4 --workers=2 --worker-class=gthread --worker-tmp-dir=/dev/shm --timeout=120 --preload frappe.app:application
'';
serviceConfig = {
User = "erpnext";
NoNewPrivileges = true;
Type = "simple";
BindReadOnlyPaths = [
"/etc/hosts:/etc/hosts"
"${pkgs.frappe-app}/share/apps/frappe:/var/lib/erpnext/bench/apps/frappe"
"${pkgs.erpnext-app}/share/apps/erpnext:/var/lib/erpnext/bench/apps/erpnext"
"${pkgs.frappe-erpnext-assets}/share/sites/assets:/var/lib/erpnext/bench/sites/assets"
"${appsFile}:/var/lib/erpnext/bench/sites/apps.txt"
"${commonSiteConfig}:/var/lib/erpnext/bench/sites/common_site_config.json"
"${penv}:/var/lib/erpnext/bench/env"
];
BindPaths = [
"/var/lib/erpnext:/var/lib/erpnext"
];
};
};
}; };
} }