Skip to content

Commit

Permalink
geoserver: add service
Browse files Browse the repository at this point in the history
  • Loading branch information
rolfschr committed Oct 12, 2024
1 parent 54cce65 commit 5924550
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 72 deletions.
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2411.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@

- [Fedimint](https://github.com/fedimint/fedimint), a module based system for building federated applications (Federated E-Cash Mint). Available as [services.fedimintd](#opt-services.fedimintd).

- [Geoserver](https://geoserver.org/), a server for sharing geospatial data. Available as [services.geoserver](#opt-services.geoserver.enable).

## Backward Incompatibilities {#sec-release-24.11-incompatibilities}

- The `sound` options have been removed or renamed, as they had a lot of unintended side effects. See [below](#sec-release-24.11-migration-sound) for details.
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,7 @@
./services/web-apps/freshrss.nix
./services/web-apps/galene.nix
./services/web-apps/gancio.nix
./services/web-apps/geoserver.nix
./services/web-apps/gerrit.nix
./services/web-apps/glance.nix
./services/web-apps/gotify-server.nix
Expand Down
76 changes: 76 additions & 0 deletions nixos/modules/services/web-apps/geoserver.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
config,
lib,
pkgs,
...
}:

with lib;

let

cfg = config.services.geoserver;

in
{
options = {
services.geoserver = {
enable = mkEnableOption "Geoserver service";

package = lib.mkPackageOption pkgs "geoserver" { };

dataDir = mkOption {
type = types.path;
default = "/var/lib/geoserver";
description = ''
The data directory of the server.
When a non-default location is used, the systemd unit will get a bind
mount to it. Furthermore, the directory must exist, be writeable for
the dynamic user and outside of protected directories (e.g. /home).
'';
};

jvmOpts = mkOption {
type = types.lines;
default = "";
description = "Any options passed to the JVM.";
};

jettyOpts = mkOption {
type = types.lines;
default = "";
example = "jetty.http.port=1234";
description = "Any options passed to the Jetty web server.";
};
};
};

config = mkIf cfg.enable {

systemd.services.geoserver = {
description = "Geoserver";

wantedBy = [ "multi-user.target" ];

environment = {
GEOSERVER_HOME = "${cfg.package}/share/geoserver";
GEOSERVER_DATA_DIR = "/var/lib/geoserver";
JAVA_OPTS = "${cfg.jvmOpts}";
JETTY_OPTS = "${cfg.jettyOpts}";
};

serviceConfig = {
ExecStart = "${cfg.package}/bin/geoserver-startup";
ExecStop = "${cfg.package}/bin/geoserver-shutdown";
DynamicUser = true;
NoNewPrivileges = true;
ProtectHome = true; # true=deny access to /home, /root, /run/user
StateDirectory = "geoserver";
BindPaths = [ "${cfg.dataDir}:/var/lib/geoserver" ];
};
};
};

meta.maintainers = with lib.maintainers; [ teams.geospatial.members ];
}
83 changes: 54 additions & 29 deletions nixos/tests/geoserver.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{ pkgs, lib, ... }:

{
pkgs,
lib,
...
}:
let
geoserver = pkgs.geoserver;
geoserverWithImporterExtension = pkgs.geoserver.withExtensions (ps: with ps; [ importer ]);
Expand All @@ -8,66 +11,88 @@ let
# - wps-jdbc needs a running (Postrgres) db server.
blacklist = [ "wps-jdbc" ];

blacklistedToNull = n: v: if ! builtins.elem n blacklist then v else null;
getNonBlackistedExtensionsAsList = ps: builtins.filter (x: x != null) (lib.attrsets.mapAttrsToList blacklistedToNull ps);
geoserverWithAllExtensions = pkgs.geoserver.withExtensions (ps: getNonBlackistedExtensionsAsList ps);
blacklistedToNull = n: v: if !builtins.elem n blacklist then v else null;
getNonBlackistedExtensionsAsList =
ps: builtins.filter (x: x != null) (lib.attrsets.mapAttrsToList blacklistedToNull ps);
geoserverWithAllExtensions = pkgs.geoserver.withExtensions (
ps: getNonBlackistedExtensionsAsList ps
);
in
{

name = "geoserver";
meta = {
maintainers = with lib; [ teams.geospatial.members ];
};

nodes = {
machine = { pkgs, ... }: {
virtualisation.diskSize = 2 * 1024;

environment.systemPackages = [
geoserver
geoserverWithImporterExtension
geoserverWithAllExtensions
];
};
machine =
{ pkgs, ... }:
{
virtualisation.diskSize = 2 * 1024;

environment.systemPackages = [
geoserver
geoserverWithAllExtensions
];

services.geoserver = {
enable = true;
jettyOpts = "jetty.http.port=8090";
package = geoserverWithImporterExtension;
};
systemd.services.geoserver.wantedBy = lib.mkForce [ ]; # do not start the service at boot
};
};

testScript = ''
from contextlib import contextmanager
curl_cmd = "curl --fail --connect-timeout 2"
curl_cmd_rest = f"{curl_cmd} -u admin:geoserver -X GET"
base_url = "http://localhost:8080/geoserver"
base_url_pkg = "http://localhost:8080/geoserver"
base_url_service = "http://localhost:8090/geoserver"
log_file = "./log.txt"
@contextmanager
def running_geoserver(pkg):
def running_geoserver_pkg(pkg):
try:
print(f"Launching geoserver from {pkg}...")
machine.execute(f"{pkg}/bin/geoserver-startup > {log_file} 2>&1 &")
machine.wait_until_succeeds(f"{curl_cmd} {base_url} 2>&1", timeout=60)
machine.wait_until_succeeds(f"{curl_cmd} {base_url_pkg} 2>&1", timeout=60)
yield
finally:
# We need to wait a little bit to make sure the server is properly
# shutdown before launching a new instance.
machine.execute(f"{pkg}/bin/geoserver-shutdown; sleep 1")
@contextmanager
def running_geoserver_service(service="geoserver"):
try:
print(f"Launching service {service}...")
machine.execute(f"systemctl start {service} > tee {log_file} 2>&1")
machine.wait_until_succeeds(f"{curl_cmd} {base_url_service} 2>&1", timeout=60)
yield
finally:
# We need to wait a little bit to make sure the server is properly
# shutdown before launching a new instance.
machine.execute(f"systemctl stop {service}; sleep 1")
start_all()
with running_geoserver("${geoserver}"):
machine.succeed(f"{curl_cmd} {base_url}/ows?service=WMS&version=1.3.0&request=GetCapabilities")
with running_geoserver_pkg("${geoserver}"):
machine.succeed(f"{curl_cmd} {base_url_pkg}/ows?service=WMS&version=1.3.0&request=GetCapabilities")
# No extensions yet.
machine.fail(f"{curl_cmd_rest} {base_url}/rest/imports")
machine.fail(f"{curl_cmd_rest} {base_url}/rest/monitor/requests.csv")
machine.fail(f"{curl_cmd_rest} {base_url_pkg}/rest/imports")
machine.fail(f"{curl_cmd_rest} {base_url_pkg}/rest/monitor/requests.csv")
with running_geoserver("${geoserverWithImporterExtension}"):
machine.succeed(f"{curl_cmd_rest} {base_url}/rest/imports")
machine.fail(f"{curl_cmd_rest} {base_url}/rest/monitor/requests.csv")
with running_geoserver_service():
machine.succeed(f"{curl_cmd_rest} {base_url_service}/rest/imports")
machine.fail(f"{curl_cmd_rest} {base_url_service}/rest/monitor/requests.csv")
with running_geoserver("${geoserverWithAllExtensions}"):
machine.succeed(f"{curl_cmd_rest} {base_url}/rest/imports")
machine.succeed(f"{curl_cmd_rest} {base_url}/rest/monitor/requests.csv")
with running_geoserver_pkg("${geoserverWithAllExtensions}"):
machine.succeed(f"{curl_cmd_rest} {base_url_pkg}/rest/imports")
machine.succeed(f"{curl_cmd_rest} {base_url_pkg}/rest/monitor/requests.csv")
_, stdout = machine.execute(f"cat {log_file}")
print(stdout.replace("\\n", "\n"))
assert "GDAL Native Library loaded" in stdout, "gdal"
Expand Down
55 changes: 34 additions & 21 deletions pkgs/by-name/ge/geoserver/extensions.nix
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
# DO *NOT* MODIFY THE LINES CONTAINING "hash = ..." OR "version = ...".
# THEY ARE GENERATED. SEE ./update.sh.
{ fetchzip, libjpeg, netcdf, pkgs, stdenv }:
{
fetchzip,
libjpeg,
netcdf,
pkgs,
stdenv,
}:

let
mkGeoserverExtension = { name, version, hash, buildInputs ? [ ] }: stdenv.mkDerivation {
pname = "geoserver-${name}-extension";
inherit buildInputs version;

src = fetchzip {
url = "mirror://sourceforge/geoserver/GeoServer/${version}/extensions/geoserver-${version}-${name}-plugin.zip";
inherit hash;
# We expect several files.
stripRoot = false;
mkGeoserverExtension =
{
name,
version,
hash,
buildInputs ? [ ],
}:
stdenv.mkDerivation {
pname = "geoserver-${name}-extension";
inherit buildInputs version;

src = fetchzip {
url = "mirror://sourceforge/geoserver/GeoServer/${version}/extensions/geoserver-${version}-${name}-plugin.zip";
inherit hash;
# We expect several files.
stripRoot = false;
};

installPhase = ''
runHook preInstall
DIR=$out/share/geoserver/webapps/geoserver/WEB-INF/lib
mkdir -p $DIR
cp -r $src/* $DIR
runHook postInstall
'';
};

installPhase = ''
runHook preInstall
DIR=$out/share/geoserver/webapps/geoserver/WEB-INF/lib
mkdir -p $DIR
cp -r $src/* $DIR
runHook postInstall
'';
};
in

{
Expand Down
8 changes: 8 additions & 0 deletions pkgs/by-name/ge/geoserver/forward-jetty-opts.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--- a/bin/startup.sh
+++ b/bin/startup.sh
@@ -86,4 +86,4 @@ echo "GEOSERVER DATA DIR is ${GEOSERVER_DATA_DIR}"
#added headless to true by default, if this messes anyone up let the list
#know and we can change it back, but it seems like it won't hurt -ch
IFS=$(printf '\n\t ')
-exec "${_RUNJAVA}" ${JAVA_OPTS:--DNoJavaOpts} "${MARLIN_ENABLER:--DMarlinDisabled}" "${RENDERER:--DDefaultrenderer}" "-Djetty.base=${GEOSERVER_HOME}" "-DGEOSERVER_DATA_DIR=${GEOSERVER_DATA_DIR}" -Djava.awt.headless=true -DSTOP.PORT=8079 -DSTOP.KEY=geoserver -jar "${GEOSERVER_HOME}/start.jar"
+exec "${_RUNJAVA}" ${JAVA_OPTS:--DNoJavaOpts} "${MARLIN_ENABLER:--DMarlinDisabled}" "${RENDERER:--DDefaultrenderer}" "-Djetty.base=${GEOSERVER_HOME}" "-DGEOSERVER_DATA_DIR=${GEOSERVER_DATA_DIR}" -Djava.awt.headless=true -DSTOP.PORT=8079 -DSTOP.KEY=geoserver -jar "${GEOSERVER_HOME}/start.jar" ${JETTY_OPTS:-}
56 changes: 34 additions & 22 deletions pkgs/by-name/ge/geoserver/package.nix
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{ lib
, callPackage
, fetchurl
, makeWrapper
, nixosTests
, stdenv
, jre
, unzip
{
lib,
callPackage,
fetchurl,
makeWrapper,
nixosTests,
stdenv,
jre,
unzip,
}:
stdenv.mkDerivation (finalAttrs: rec {
pname = "geoserver";
Expand All @@ -19,10 +20,15 @@ stdenv.mkDerivation (finalAttrs: rec {
patches = [
# set GEOSERVER_DATA_DIR to current working directory if not provided
./data-dir.patch
# forward any JETTY_OPTS if that environment variable exists
./forward-jetty-opts.patch
];

sourceRoot = ".";
nativeBuildInputs = [ unzip makeWrapper ];
nativeBuildInputs = [
unzip
makeWrapper
];

installPhase =
let
Expand All @@ -48,28 +54,34 @@ stdenv.mkDerivation (finalAttrs: rec {
runHook postInstall
'';


passthru =
let
geoserver = finalAttrs.finalPackage;
extensions = lib.attrsets.filterAttrs (n: v: lib.isDerivation v) (callPackage ./extensions.nix { });
in
{
withExtensions = selector:
withExtensions =
selector:
let
selectedExtensions = selector extensions;
in
geoserver.overrideAttrs (finalAttrs: previousAttrs: {
pname = previousAttrs.pname + "-with-extensions";
buildInputs = lib.lists.unique ((previousAttrs.buildInputs or [ ]) ++ lib.lists.concatMap (drv: drv.buildInputs) selectedExtensions);
postInstall = (previousAttrs.postInstall or "") + ''
for extension in ${builtins.toString selectedExtensions} ; do
cp -r $extension/* $out
# Some files are the same for all/several extensions. We allow overwriting them again.
chmod -R +w $out
done
'';
});
geoserver.overrideAttrs (
finalAttrs: previousAttrs: {
pname = previousAttrs.pname + "-with-extensions";
buildInputs = lib.lists.unique (
(previousAttrs.buildInputs or [ ]) ++ lib.lists.concatMap (drv: drv.buildInputs) selectedExtensions
);
postInstall =
(previousAttrs.postInstall or "")
+ ''
for extension in ${builtins.toString selectedExtensions} ; do
cp -r $extension/* $out
# Some files are the same for all/several extensions. We allow overwriting them again.
chmod -R +w $out
done
'';
}
);
tests.geoserver = nixosTests.geoserver;
updateScript = ./update.sh;
};
Expand Down

0 comments on commit 5924550

Please sign in to comment.