Co-authored-by: Franz Pletz <fpletz@fnordicwalking.de> Co-authored-by: Robin Gloster <mail@glob.in> Co-authored-by: Janne Heß <janne@hess.ooo> Co-authored-by: Florian Klink <flokli@flokli.de>wip/yesman
parent
b66d6f404e
commit
ebd38185c8
@ -0,0 +1,463 @@ |
||||
{ config, lib, pkgs, ... }@args: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
cfg = config.services.nextcloud; |
||||
|
||||
toKeyValue = generators.toKeyValue { |
||||
mkKeyValue = generators.mkKeyValueDefault {} " = "; |
||||
}; |
||||
|
||||
phpOptionsExtensions = '' |
||||
${optionalString cfg.caching.apcu "extension=${cfg.phpPackages.apcu}/lib/php/extensions/apcu.so"} |
||||
${optionalString cfg.caching.redis "extension=${cfg.phpPackages.redis}/lib/php/extensions/redis.so"} |
||||
${optionalString cfg.caching.memcached "extension=${cfg.phpPackages.memcached}/lib/php/extensions/memcached.so"} |
||||
zend_extension = opcache.so |
||||
opcache.enable = 1 |
||||
''; |
||||
phpOptions = { |
||||
upload_max_filesize = cfg.maxUploadSize; |
||||
post_max_size = cfg.maxUploadSize; |
||||
memory_limit = cfg.maxUploadSize; |
||||
} // cfg.phpOptions; |
||||
phpOptionsStr = phpOptionsExtensions + (toKeyValue phpOptions); |
||||
|
||||
occ = pkgs.writeScriptBin "nextcloud-occ" '' |
||||
#! ${pkgs.stdenv.shell} |
||||
cd ${pkgs.nextcloud} |
||||
exec /run/wrappers/bin/sudo -u nextcloud \ |
||||
NEXTCLOUD_CONFIG_DIR="${cfg.home}/config" \ |
||||
${config.services.phpfpm.phpPackage}/bin/php \ |
||||
-c ${pkgs.writeText "php.ini" phpOptionsStr}\ |
||||
occ $* |
||||
''; |
||||
|
||||
in { |
||||
options.services.nextcloud = { |
||||
enable = mkEnableOption "nextcloud"; |
||||
hostName = mkOption { |
||||
type = types.str; |
||||
description = "FQDN for the nextcloud instance."; |
||||
}; |
||||
home = mkOption { |
||||
type = types.str; |
||||
default = "/var/lib/nextcloud"; |
||||
description = "Storage path of nextcloud."; |
||||
}; |
||||
https = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = "Enable if there is a TLS terminating proxy in front of nextcloud."; |
||||
}; |
||||
|
||||
maxUploadSize = mkOption { |
||||
default = "512M"; |
||||
type = types.str; |
||||
description = '' |
||||
Defines the upload limit for files. This changes the relevant options |
||||
in php.ini and nginx if enabled. |
||||
''; |
||||
}; |
||||
|
||||
skeletonDirectory = mkOption { |
||||
default = ""; |
||||
type = types.str; |
||||
description = '' |
||||
The directory where the skeleton files are located. These files will be |
||||
copied to the data directory of new users. Leave empty to not copy any |
||||
skeleton files. |
||||
''; |
||||
}; |
||||
|
||||
nginx.enable = mkEnableOption "nginx vhost management"; |
||||
|
||||
webfinger = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable this option if you plan on using the webfinger plugin. |
||||
The appropriate nginx rewrite rules will be added to your configuration. |
||||
''; |
||||
}; |
||||
|
||||
phpPackages = mkOption { |
||||
type = types.attrs; |
||||
default = pkgs.php71Packages; |
||||
defaultText = "pkgs.php71Packages"; |
||||
description = '' |
||||
Overridable attribute of the PHP packages set to use. If any caching |
||||
module is enabled, it will be taken from here. Therefore it should |
||||
match the version of PHP given to |
||||
<literal>services.phpfpm.phpPackage</literal>. |
||||
''; |
||||
}; |
||||
|
||||
phpOptions = mkOption { |
||||
type = types.attrsOf types.str; |
||||
default = { |
||||
"short_open_tag" = "Off"; |
||||
"expose_php" = "Off"; |
||||
"error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT"; |
||||
"display_errors" = "stderr"; |
||||
"opcache.enable_cli" = "1"; |
||||
"opcache.interned_strings_buffer" = "8"; |
||||
"opcache.max_accelerated_files" = "10000"; |
||||
"opcache.memory_consumption" = "128"; |
||||
"opcache.revalidate_freq" = "1"; |
||||
"opcache.fast_shutdown" = "1"; |
||||
"openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt"; |
||||
"catch_workers_output" = "yes"; |
||||
}; |
||||
description = '' |
||||
Options for PHP's php.ini file for nextcloud. |
||||
''; |
||||
}; |
||||
|
||||
config = { |
||||
dbtype = mkOption { |
||||
type = types.enum [ "sqlite" "pgsql" "mysql" ]; |
||||
default = "sqlite"; |
||||
description = "Database type."; |
||||
}; |
||||
dbname = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = "nextcloud"; |
||||
description = "Database name."; |
||||
}; |
||||
dbuser = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = "nextcloud"; |
||||
description = "Database user."; |
||||
}; |
||||
dbpass = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
Database password. Use <literal>dbpassFile</literal> to avoid this |
||||
being world-readable in the <literal>/nix/store</literal>. |
||||
''; |
||||
}; |
||||
dbpassFile = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
The full path to a file that contains the database password. |
||||
''; |
||||
}; |
||||
dbhost = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = "localhost"; |
||||
description = "Database host."; |
||||
}; |
||||
dbport = mkOption { |
||||
type = with types; nullOr (either int str); |
||||
default = null; |
||||
description = "Database port."; |
||||
}; |
||||
dbtableprefix = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = "Table prefix in Nextcloud database."; |
||||
}; |
||||
adminuser = mkOption { |
||||
type = types.str; |
||||
default = "root"; |
||||
description = "Admin username."; |
||||
}; |
||||
adminpass = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
Database password. Use <literal>adminpassFile</literal> to avoid this |
||||
being world-readable in the <literal>/nix/store</literal>. |
||||
''; |
||||
}; |
||||
adminpassFile = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
The full path to a file that contains the admin's password. |
||||
''; |
||||
}; |
||||
|
||||
extraTrustedDomains = mkOption { |
||||
type = types.listOf types.str; |
||||
default = []; |
||||
description = '' |
||||
Trusted domains, from which the nextcloud installation will be |
||||
acessible. You don't need to add |
||||
<literal>services.nextcloud.hostname</literal> here. |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
caching = { |
||||
apcu = mkOption { |
||||
type = types.bool; |
||||
default = true; |
||||
description = '' |
||||
Whether to load the APCu module into PHP. |
||||
''; |
||||
}; |
||||
redis = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Whether to load the Redis module into PHP. |
||||
You still need to enable Redis in your config.php. |
||||
See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html |
||||
''; |
||||
}; |
||||
memcached = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Whether to load the Memcached module into PHP. |
||||
You still need to enable Memcached in your config.php. |
||||
See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html |
||||
''; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
config = mkIf cfg.enable (mkMerge [ |
||||
{ assertions = let acfg = cfg.config; in [ |
||||
{ assertion = !(acfg.dbpass != null && acfg.dbpassFile != null); |
||||
message = "Please specify no more than one of dbpass or dbpassFile"; |
||||
} |
||||
{ assertion = ((acfg.adminpass != null || acfg.adminpassFile != null) |
||||
&& !(acfg.adminpass != null && acfg.adminpassFile != null)); |
||||
message = "Please specify exactly one of adminpass or adminpassFile"; |
||||
} |
||||
]; |
||||
} |
||||
|
||||
{ systemd.timers."nextcloud-cron" = { |
||||
wantedBy = [ "timers.target" ]; |
||||
timerConfig.OnBootSec = "5m"; |
||||
timerConfig.OnUnitActiveSec = "15m"; |
||||
timerConfig.Unit = "nextcloud-cron.service"; |
||||
}; |
||||
|
||||
systemd.services = { |
||||
"nextcloud-setup" = let |
||||
overrideConfig = pkgs.writeText "nextcloud-config.php" '' |
||||
<?php |
||||
$CONFIG = [ |
||||
'apps_paths' => [ |
||||
[ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ], |
||||
[ 'path' => '${cfg.home}/store-apps', 'url' => '/store-apps', 'writable' => true ], |
||||
], |
||||
'datadirectory' => '${cfg.home}/data', |
||||
'skeletondirectory' => '${cfg.skeletonDirectory}', |
||||
${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} |
||||
'log_type' => 'syslog', |
||||
]; |
||||
''; |
||||
occInstallCmd = let |
||||
c = cfg.config; |
||||
adminpass = if c.adminpassFile != null |
||||
then ''"$(<"${toString c.adminpassFile}")"'' |
||||
else ''"${toString c.adminpass}"''; |
||||
dbpass = if c.dbpassFile != null |
||||
then ''"$(<"${toString c.dbpassFile}")"'' |
||||
else if c.dbpass != null |
||||
then ''"${toString c.dbpass}"'' |
||||
else null; |
||||
installFlags = concatStringsSep " \\\n " |
||||
(mapAttrsToList (k: v: "${k} ${toString v}") { |
||||
"--database" = ''"${c.dbtype}"''; |
||||
# The following attributes are optional depending on the type of |
||||
# database. Those that evaluate to null on the left hand side |
||||
# will be omitted. |
||||
${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"''; |
||||
${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; |
||||
${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"''; |
||||
${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; |
||||
${if (any (x: x != null) [c.dbpass c.dbpassFile]) |
||||
then "--database-pass" else null} = dbpass; |
||||
${if c.dbtableprefix != null |
||||
then "--database-table-prefix" else null} = ''"${toString c.dbtableprefix}"''; |
||||
"--admin-user" = ''"${c.adminuser}"''; |
||||
"--admin-pass" = adminpass; |
||||
"--data-dir" = ''"${cfg.home}/data"''; |
||||
}); |
||||
in '' |
||||
${occ}/bin/nextcloud-occ maintenance:install \ |
||||
${installFlags} |
||||
''; |
||||
occSetTrustedDomainsCmd = concatStringsSep "\n" (imap0 |
||||
(i: v: '' |
||||
${occ}/bin/nextcloud-occ config:system:set trusted_domains \ |
||||
${toString i} --value="${toString v}" |
||||
'') ([ cfg.hostName ] ++ cfg.config.extraTrustedDomains)); |
||||
|
||||
in { |
||||
wantedBy = [ "multi-user.target" ]; |
||||
before = [ "phpfpm-nextcloud.service" ]; |
||||
script = '' |
||||
chmod og+x ${cfg.home} |
||||
ln -sf ${pkgs.nextcloud}/apps ${cfg.home}/ |
||||
mkdir -p ${cfg.home}/config ${cfg.home}/data ${cfg.home}/store-apps |
||||
ln -sf ${overrideConfig} ${cfg.home}/config/override.config.php |
||||
|
||||
chown -R nextcloud:nginx ${cfg.home}/config ${cfg.home}/data ${cfg.home}/store-apps |
||||
|
||||
# Do not install if already installed |
||||
if [[ ! -e ${cfg.home}/config/config.php ]]; then |
||||
${occInstallCmd} |
||||
fi |
||||
|
||||
${occ}/bin/nextcloud-occ upgrade |
||||
|
||||
${occ}/bin/nextcloud-occ config:system:delete trusted_domains |
||||
${occSetTrustedDomainsCmd} |
||||
''; |
||||
serviceConfig.Type = "oneshot"; |
||||
}; |
||||
"nextcloud-cron" = { |
||||
environment.NEXTCLOUD_CONFIG_DIR = "${cfg.home}/config"; |
||||
serviceConfig.Type = "oneshot"; |
||||
serviceConfig.User = "nextcloud"; |
||||
serviceConfig.ExecStart = "${pkgs.php}/bin/php -f ${pkgs.nextcloud}/cron.php"; |
||||
}; |
||||
}; |
||||
|
||||
services.phpfpm = { |
||||
phpOptions = phpOptionsExtensions; |
||||
phpPackage = pkgs.php71; |
||||
pools.nextcloud = let |
||||
phpAdminValues = (toKeyValue |
||||
(foldr (a: b: a // b) {} |
||||
(mapAttrsToList (k: v: { "php_admin_value[${k}]" = v; }) |
||||
phpOptions))); |
||||
in { |
||||
listen = "/run/phpfpm/nextcloud"; |
||||
extraConfig = '' |
||||
listen.owner = nginx |
||||
listen.group = nginx |
||||
user = nextcloud |
||||
group = nginx |
||||
pm = dynamic |
||||
pm.max_children = 32 |
||||
pm.start_servers = 2 |
||||
pm.min_spare_servers = 2 |
||||
pm.max_spare_servers = 4 |
||||
env[NEXTCLOUD_CONFIG_DIR] = ${cfg.home}/config |
||||
env[PATH] = /run/wrappers/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin |
||||
${phpAdminValues} |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
users.extraUsers.nextcloud = { |
||||
home = "${cfg.home}"; |
||||
group = "nginx"; |
||||
createHome = true; |
||||
}; |
||||
|
||||
environment.systemPackages = [ occ ]; |
||||
} |
||||
|
||||
(mkIf cfg.nginx.enable { |
||||
services.nginx = { |
||||
enable = true; |
||||
virtualHosts = { |
||||
"${cfg.hostName}" = { |
||||
root = pkgs.nextcloud; |
||||
locations = { |
||||
"= /robots.txt" = { |
||||
priority = 100; |
||||
extraConfig = '' |
||||
allow all; |
||||
log_not_found off; |
||||
access_log off; |
||||
''; |
||||
}; |
||||
"/" = { |
||||
priority = 200; |
||||
extraConfig = "rewrite ^ /index.php$uri;"; |
||||
}; |
||||
"~ ^/store-apps" = { |
||||
priority = 201; |
||||
extraConfig = "root ${cfg.home};"; |
||||
}; |
||||
"= /.well-known/carddav" = { |
||||
priority = 210; |
||||
extraConfig = "return 301 $scheme://$host/remote.php/dav;"; |
||||
}; |
||||
"= /.well-known/caldav" = { |
||||
priority = 210; |
||||
extraConfig = "return 301 $scheme://$host/remote.php/dav;"; |
||||
}; |
||||
"~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/" = { |
||||
priority = 300; |
||||
extraConfig = "deny all;"; |
||||
}; |
||||
"~ ^/(?:\\.|autotest|occ|issue|indie|db_|console)" = { |
||||
priority = 300; |
||||
extraConfig = "deny all;"; |
||||
}; |
||||
"~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\\.php(?:$|/)" = { |
||||
priority = 500; |
||||
extraConfig = '' |
||||
include ${pkgs.nginxMainline}/conf/fastcgi.conf; |
||||
fastcgi_split_path_info ^(.+\.php)(/.*)$; |
||||
fastcgi_param PATH_INFO $fastcgi_path_info; |
||||
fastcgi_param HTTPS ${if cfg.https then "on" else "off"}; |
||||
fastcgi_param modHeadersAvailable true; |
||||
fastcgi_param front_controller_active true; |
||||
fastcgi_pass unix:/run/phpfpm/nextcloud; |
||||
fastcgi_intercept_errors on; |
||||
fastcgi_request_buffering off; |
||||
fastcgi_read_timeout 120s; |
||||
''; |
||||
}; |
||||
"~ ^/(?:updater|ocs-provider)(?:$|/)".extraConfig = '' |
||||
try_files $uri/ =404; |
||||
index index.php; |
||||
''; |
||||
"~ \\.(?:css|js|woff|svg|gif)$".extraConfig = '' |
||||
try_files $uri /index.php$uri$is_args$args; |
||||
add_header Cache-Control "public, max-age=15778463"; |
||||
add_header X-Content-Type-Options nosniff; |
||||
add_header X-XSS-Protection "1; mode=block"; |
||||
add_header X-Robots-Tag none; |
||||
add_header X-Download-Options noopen; |
||||
add_header X-Permitted-Cross-Domain-Policies none; |
||||
access_log off; |
||||
''; |
||||
"~ \\.(?:png|html|ttf|ico|jpg|jpeg)$".extraConfig = '' |
||||
try_files $uri /index.php$uri$is_args$args; |
||||
access_log off; |
||||
''; |
||||
}; |
||||
extraConfig = '' |
||||
add_header X-Content-Type-Options nosniff; |
||||
add_header X-XSS-Protection "1; mode=block"; |
||||
add_header X-Robots-Tag none; |
||||
add_header X-Download-Options noopen; |
||||
add_header X-Permitted-Cross-Domain-Policies none; |
||||
error_page 403 /core/templates/403.php; |
||||
error_page 404 /core/templates/404.php; |
||||
client_max_body_size ${cfg.maxUploadSize}; |
||||
fastcgi_buffers 64 4K; |
||||
gzip on; |
||||
gzip_vary on; |
||||
gzip_comp_level 4; |
||||
gzip_min_length 256; |
||||
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; |
||||
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; |
||||
|
||||
${optionalString cfg.webfinger '' |
||||
rewrite ^/.well-known/host-meta /public.php?service=host-meta last; |
||||
rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last; |
||||
''} |
||||
''; |
||||
}; |
||||
}; |
||||
}; |
||||
}) |
||||
]); |
||||
} |
@ -0,0 +1,56 @@ |
||||
import ../make-test.nix ({ pkgs, ...}: let |
||||
adminpass = "notproduction"; |
||||
adminuser = "root"; |
||||
in { |
||||
name = "nextcloud-basic"; |
||||
meta = with pkgs.stdenv.lib.maintainers; { |
||||
maintainers = [ globin eqyiel ]; |
||||
}; |
||||
|
||||
nodes = { |
||||
# The only thing the client needs to do is download a file. |
||||
client = { ... }: {}; |
||||
|
||||
nextcloud = { config, pkgs, ... }: { |
||||
networking.firewall.allowedTCPPorts = [ 80 ]; |
||||
|
||||
services.nextcloud = { |
||||
enable = true; |
||||
nginx.enable = true; |
||||
hostName = "nextcloud"; |
||||
config = { |
||||
# Don't inherit adminuser since "root" is supposed to be the default |
||||
inherit adminpass; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
testScript = let |
||||
withRcloneEnv = pkgs.writeScript "with-rclone-env" '' |
||||
#!${pkgs.stdenv.shell} |
||||
export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav |
||||
export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" |
||||
export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" |
||||
export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" |
||||
export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" |
||||
"''${@}" |
||||
''; |
||||
copySharedFile = pkgs.writeScript "copy-shared-file" '' |
||||
#!${pkgs.stdenv.shell} |
||||
echo 'hi' | ${withRcloneEnv} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file |
||||
''; |
||||
|
||||
diffSharedFile = pkgs.writeScript "diff-shared-file" '' |
||||
#!${pkgs.stdenv.shell} |
||||
diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) |
||||
''; |
||||
in '' |
||||
startAll(); |
||||
$nextcloud->waitForUnit("multi-user.target"); |
||||
$nextcloud->succeed("curl -sSf http://nextcloud/login"); |
||||
$nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); |
||||
$client->waitForUnit("multi-user.target"); |
||||
$client->succeed("${withRcloneEnv} ${diffSharedFile}"); |
||||
''; |
||||
}) |
@ -0,0 +1,6 @@ |
||||
{ system ? builtins.currentSystem }: |
||||
{ |
||||
basic = import ./basic.nix { inherit system; }; |
||||
with-postgresql-and-redis = import ./with-postgresql-and-redis.nix { inherit system; }; |
||||
with-mysql-and-memcached = import ./with-mysql-and-memcached.nix { inherit system; }; |
||||
} |
@ -0,0 +1,97 @@ |
||||
import ../make-test.nix ({ pkgs, ...}: let |
||||
adminpass = "hunter2"; |
||||
adminuser = "root"; |
||||
in { |
||||
name = "nextcloud-with-mysql-and-memcached"; |
||||
meta = with pkgs.stdenv.lib.maintainers; { |
||||
maintainers = [ eqyiel ]; |
||||
}; |
||||
|
||||
nodes = { |
||||
# The only thing the client needs to do is download a file. |
||||
client = { ... }: {}; |
||||
|
||||
nextcloud = { config, pkgs, ... }: { |
||||
networking.firewall.allowedTCPPorts = [ 80 ]; |
||||
|
||||
services.nextcloud = { |
||||
enable = true; |
||||
hostName = "nextcloud"; |
||||
nginx.enable = true; |
||||
https = true; |
||||
caching = { |
||||
apcu = true; |
||||
redis = false; |
||||
memcached = true; |
||||
}; |
||||
config = { |
||||
dbtype = "mysql"; |
||||
dbname = "nextcloud"; |
||||
dbuser = "nextcloud"; |
||||
dbhost = "127.0.0.1"; |
||||
dbport = 3306; |
||||
dbpass = "hunter2"; |
||||
# Don't inherit adminuser since "root" is supposed to be the default |
||||
inherit adminpass; |
||||
}; |
||||
}; |
||||
|
||||
services.mysql = { |
||||
enable = true; |
||||
bind = "127.0.0.1"; |
||||
package = pkgs.mariadb; |
||||
initialScript = pkgs.writeText "mysql-init" '' |
||||
CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'hunter2'; |
||||
CREATE DATABASE IF NOT EXISTS nextcloud; |
||||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, |
||||
CREATE TEMPORARY TABLES ON nextcloud.* TO 'nextcloud'@'localhost' |
||||
IDENTIFIED BY 'hunter2'; |
||||
FLUSH privileges; |
||||
''; |
||||
}; |
||||
|
||||
systemd.services."nextcloud-setup"= { |
||||
requires = ["mysql.service"]; |
||||
after = ["mysql.service"]; |
||||
}; |
||||
|
||||
services.memcached.enable = true; |
||||
}; |
||||
}; |
||||
|
||||
testScript = let |
||||
configureMemcached = pkgs.writeScript "configure-memcached" '' |
||||
#!${pkgs.stdenv.shell} |
||||
nextcloud-occ config:system:set memcached_servers 0 0 --value 127.0.0.1 --type string |
||||
nextcloud-occ config:system:set memcached_servers 0 1 --value 11211 --type integer |
||||
nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\APCu' --type string |
||||
nextcloud-occ config:system:set memcache.distributed --value '\OC\Memcache\Memcached' --type string |
||||
''; |
||||
withRcloneEnv = pkgs.writeScript "with-rclone-env" '' |
||||
#!${pkgs.stdenv.shell} |
||||
export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav |
||||
export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" |
||||
export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" |
||||
export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" |
||||
export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" |
||||
''; |
||||
copySharedFile = pkgs.writeScript "copy-shared-file" '' |
||||
#!${pkgs.stdenv.shell} |
||||
echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file |
||||
''; |
||||
|
||||
diffSharedFile = pkgs.writeScript "diff-shared-file" '' |
||||
#!${pkgs.stdenv.shell} |
||||
diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) |
||||
''; |
||||
in '' |
||||
startAll(); |
||||
$nextcloud->waitForUnit("multi-user.target"); |
||||
$nextcloud->succeed("${configureMemcached}"); |
||||
$nextcloud->succeed("curl -sSf http://nextcloud/login"); |
||||
$nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); |
||||
$client->waitForUnit("multi-user.target"); |
||||
$client->succeed("${withRcloneEnv} ${diffSharedFile}"); |
||||
|
||||
''; |
||||
}) |
@ -0,0 +1,130 @@ |
||||
import ../make-test.nix ({ pkgs, ...}: let |
||||
adminpass = "hunter2"; |
||||
adminuser = "custom-admin-username"; |
||||
in { |
||||
name = "nextcloud-with-postgresql-and-redis"; |
||||
meta = with pkgs.stdenv.lib.maintainers; { |
||||
maintainers = [ eqyiel ]; |
||||
}; |
||||
|
||||
nodes = { |
||||
# The only thing the client needs to do is download a file. |
||||
client = { ... }: {}; |
||||
|
||||
nextcloud = { config, pkgs, ... }: { |
||||
networking.firewall.allowedTCPPorts = [ 80 ]; |
||||
|
||||
services.nextcloud = { |
||||
enable = true; |
||||
hostName = "nextcloud"; |
||||
nginx.enable = true; |
||||
caching = { |
||||
apcu = false; |
||||
redis = true; |
||||
memcached = false; |
||||
}; |
||||
config = { |
||||
dbtype = "pgsql"; |
||||
dbname = "nextcloud"; |
||||
dbuser = "nextcloud"; |
||||
dbhost = "localhost"; |
||||
dbpassFile = toString (pkgs.writeText "db-pass-file" '' |
||||
hunter2 |
||||
''); |
||||
inherit adminuser; |
||||
adminpassFile = toString (pkgs.writeText "admin-pass-file" '' |
||||
${adminpass} |
||||
''); |
||||
}; |
||||
}; |
||||
|
||||
services.redis = { |
||||
unixSocket = "/var/run/redis/redis.sock"; |
||||
enable = true; |
||||
extraConfig = '' |
||||
unixsocketperm 770 |
||||
''; |
||||
}; |
||||
|
||||
systemd.services.redis = { |
||||
preStart = '' |
||||
mkdir -p /var/run/redis |
||||
chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis |
||||
''; |
||||
serviceConfig.PermissionsStartOnly = true; |
||||
}; |
||||
|
||||
systemd.services."nextcloud-setup"= { |
||||
requires = ["postgresql.service"]; |
||||
after = [ |
||||
"postgresql.service" |
||||
"chown-redis-socket.service" |
||||
]; |
||||
}; |
||||
|
||||
# At the time of writing, redis creates its socket with the "nobody" |
||||
# group. I figure this is slightly less bad than making the socket world |
||||
# readable. |
||||
systemd.services."chown-redis-socket" = { |
||||
enable = true; |
||||
script = '' |
||||
until ${pkgs.redis}/bin/redis-cli ping; do |
||||
echo "waiting for redis..." |
||||
sleep 1 |
||||
done |
||||
chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis/redis.sock |
||||
''; |
||||
after = [ "redis.service" ]; |
||||
requires = [ "redis.service" ]; |
||||
wantedBy = [ "redis.service" ]; |
||||
serviceConfig = { |
||||
Type = "oneshot"; |
||||
}; |
||||
}; |
||||
|
||||
services.postgresql = { |
||||
enable = true; |
||||
initialScript = pkgs.writeText "psql-init" '' |
||||
create role nextcloud with login password 'hunter2'; |
||||
create database nextcloud with owner nextcloud; |
||||
''; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
testScript = let |
||||
configureRedis = pkgs.writeScript "configure-redis" '' |
||||
#!${pkgs.stdenv.shell} |
||||
nextcloud-occ config:system:set redis 'host' --value '/var/run/redis/redis.sock' --type string |
||||
nextcloud-occ config:system:set redis 'port' --value 0 --type integer |
||||
nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string |
||||
nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string |
||||
''; |
||||
withRcloneEnv = pkgs.writeScript "with-rclone-env" '' |
||||
#!${pkgs.stdenv.shell} |
||||
export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav |
||||
export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" |
||||
export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" |
||||
export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" |
||||
export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" |
||||
"''${@}" |
||||
''; |
||||
copySharedFile = pkgs.writeScript "copy-shared-file" '' |
||||
#!${pkgs.stdenv.shell} |
||||
echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file |
||||
''; |
||||
|
||||
diffSharedFile = pkgs.writeScript "diff-shared-file" '' |
||||
#!${pkgs.stdenv.shell} |
||||
diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) |
||||
''; |
||||
in '' |
||||
startAll(); |
||||
$nextcloud->waitForUnit("multi-user.target"); |
||||
$nextcloud->succeed("${configureRedis}"); |
||||
$nextcloud->succeed("curl -sSf http://nextcloud/login"); |
||||
$nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); |
||||
$client->waitForUnit("multi-user.target"); |
||||
$client->succeed("${withRcloneEnv} ${diffSharedFile}"); |
||||
''; |
||||
}) |
Loading…
Reference in new issue