diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 5b0fc467ea4..4ab7307c3b6 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -4,56 +4,102 @@ with lib;
let
cfg = config.services.traefik;
- configFile =
- if cfg.configFile == null then
- pkgs.runCommand "config.toml" {
- buildInputs = [ pkgs.remarshal ];
- preferLocalBuild = true;
- } ''
- remarshal -if json -of toml \
- < ${pkgs.writeText "config.json" (builtins.toJSON cfg.configOptions)} \
- > $out
- ''
- else cfg.configFile;
-
+ jsonValue = with types;
+ let
+ valueType = nullOr (oneOf [
+ bool
+ int
+ float
+ str
+ (lazyAttrsOf valueType)
+ (listOf valueType)
+ ]) // {
+ description = "JSON value";
+ emptyValue.value = { };
+ };
+ in valueType;
+ dynamicConfigFile = if cfg.dynamicConfigFile == null then
+ pkgs.runCommand "config.toml" {
+ buildInputs = [ pkgs.remarshal ];
+ preferLocalBuild = true;
+ } ''
+ remarshal -if json -of toml \
+ < ${
+ pkgs.writeText "dynamic_config.json"
+ (builtins.toJSON cfg.dynamicConfigOptions)
+ } \
+ > $out
+ ''
+ else
+ cfg.dynamicConfigFile;
+ staticConfigFile = if cfg.staticConfigFile == null then
+ pkgs.runCommand "config.toml" {
+ buildInputs = [ pkgs.yj ];
+ preferLocalBuild = true;
+ } ''
+ yj -jt -i \
+ < ${
+ pkgs.writeText "static_config.json" (builtins.toJSON
+ (recursiveUpdate cfg.staticConfigOptions {
+ providers.file.filename = "${dynamicConfigFile}";
+ }))
+ } \
+ > $out
+ ''
+ else
+ cfg.staticConfigFile;
in {
options.services.traefik = {
enable = mkEnableOption "Traefik web server";
- configFile = mkOption {
+ staticConfigFile = mkOption {
default = null;
- example = literalExample "/path/to/config.toml";
+ example = literalExample "/path/to/static_config.toml";
type = types.nullOr types.path;
description = ''
- Path to verbatim traefik.toml to use.
- (Using that option has precedence over configOptions)
+ Path to traefik's static configuration to use.
+ (Using that option has precedence over staticConfigOptions and dynamicConfigOptions)
'';
};
- configOptions = mkOption {
+ staticConfigOptions = mkOption {
description = ''
- Config for Traefik.
+ Static configuration for Traefik.
'';
- type = types.attrs;
- default = {
- defaultEntryPoints = ["http"];
- entryPoints.http.address = ":80";
- };
+ type = jsonValue;
+ default = { entryPoints.http.address = ":80"; };
example = {
- defaultEntrypoints = [ "http" ];
- web.address = ":8080";
+ entryPoints.web.address = ":8080";
entryPoints.http.address = ":80";
- file = {};
- frontends = {
- frontend1 = {
- backend = "backend1";
- routes.test_1.rule = "Host:localhost";
- };
- };
- backends.backend1 = {
- servers.server1.url = "http://localhost:8000";
+ api = { };
+ };
+ };
+
+ dynamicConfigFile = mkOption {
+ default = null;
+ example = literalExample "/path/to/dynamic_config.toml";
+ type = types.nullOr types.path;
+ description = ''
+ Path to traefik's dynamic configuration to use.
+ (Using that option has precedence over dynamicConfigOptions)
+ '';
+ };
+
+ dynamicConfigOptions = mkOption {
+ description = ''
+ Dynamic configuration for Traefik.
+ '';
+ type = jsonValue;
+ default = { };
+ example = {
+ http.routers.router1 = {
+ rule = "Host(`localhost`)";
+ service = "service1";
};
+
+ http.services.service1.loadBalancer.servers =
+ [{ url = "http://localhost:8080"; }];
};
};
@@ -61,7 +107,7 @@ in {
default = "/var/lib/traefik";
type = types.path;
description = ''
- Location for any persistent data traefik creates, ie. acme
+ Location for any persistent data traefik creates, ie. acme
'';
};
@@ -84,16 +130,15 @@ in {
};
config = mkIf cfg.enable {
- systemd.tmpfiles.rules = [
- "d '${cfg.dataDir}' 0700 traefik traefik - -"
- ];
+ systemd.tmpfiles.rules = [ "d '${cfg.dataDir}' 0700 traefik traefik - -" ];
systemd.services.traefik = {
description = "Traefik web server";
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
- ExecStart = ''${cfg.package.bin}/bin/traefik --configfile=${configFile}'';
+ ExecStart =
+ "${cfg.package}/bin/traefik --configfile=${staticConfigFile}";
Type = "simple";
User = "traefik";
Group = cfg.group;
@@ -120,6 +165,6 @@ in {
isSystemUser = true;
};
- users.groups.traefik = {};
+ users.groups.traefik = { };
};
}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 5819879b30e..4f60892488a 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -303,6 +303,8 @@ in
timezone = handleTest ./timezone.nix {};
tinydns = handleTest ./tinydns.nix {};
tor = handleTest ./tor.nix {};
+ # traefik test relies on docker-containers
+ traefik = handleTestOn ["x86_64-linux"] ./traefik.nix {};
transmission = handleTest ./transmission.nix {};
trac = handleTest ./trac.nix {};
trilium-server = handleTestOn ["x86_64-linux"] ./trilium-server.nix {};
diff --git a/nixos/tests/traefik.nix b/nixos/tests/traefik.nix
new file mode 100644
index 00000000000..0e21a7cf843
--- /dev/null
+++ b/nixos/tests/traefik.nix
@@ -0,0 +1,87 @@
+# Test Traefik as a reverse proxy of a local web service
+# and a Docker container.
+import ./make-test-python.nix ({ pkgs, ... }: {
+ name = "traefik";
+ meta = with pkgs.stdenv.lib.maintainers; {
+ maintainers = [ joko ];
+ };
+
+ nodes = {
+ client = { config, pkgs, ... }: {
+ environment.systemPackages = [ pkgs.curl ];
+ };
+ traefik = { config, pkgs, ... }: {
+ docker-containers.nginx = {
+ extraDockerOptions = [
+ "-l" "traefik.enable=true"
+ "-l" "traefik.http.routers.nginx.entrypoints=web"
+ "-l" "traefik.http.routers.nginx.rule=Host(`nginx.traefik.test`)"
+ ];
+ image = "nginx-container";
+ imageFile = pkgs.dockerTools.examples.nginx;
+ };
+
+ networking.firewall.allowedTCPPorts = [ 80 ];
+
+ services.traefik = {
+ enable = true;
+
+ dynamicConfigOptions = {
+ http.routers.simplehttp = {
+ rule = "Host(`simplehttp.traefik.test`)";
+ entryPoints = [ "web" ];
+ service = "simplehttp";
+ };
+
+ http.services.simplehttp = {
+ loadBalancer.servers = [{
+ url = "http://127.0.0.1:8000";
+ }];
+ };
+ };
+
+ staticConfigOptions = {
+ global = {
+ checkNewVersion = false;
+ sendAnonymousUsage = false;
+ };
+
+ entryPoints.web.address = ":80";
+
+ providers.docker.exposedByDefault = false;
+ };
+ };
+
+ systemd.services.simplehttp = {
+ script = "${pkgs.python3}/bin/python -m http.server 8000";
+ serviceConfig.Type = "simple";
+ wantedBy = [ "multi-user.target" ];
+ };
+
+ users.users.traefik.extraGroups = [ "docker" ];
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ traefik.wait_for_unit("docker-nginx.service")
+ traefik.wait_until_succeeds("docker ps | grep nginx-container")
+ traefik.wait_for_unit("simplehttp.service")
+ traefik.wait_for_unit("traefik.service")
+ traefik.wait_for_open_port(80)
+ traefik.wait_for_unit("multi-user.target")
+
+ client.wait_for_unit("multi-user.target")
+
+ with subtest("Check that a container can be reached via Traefik"):
+ assert "Hello from NGINX" in client.succeed(
+ "curl -sSf -H Host:nginx.traefik.test http://traefik/"
+ )
+
+ with subtest("Check that dynamic configuration works"):
+ assert "Directory listing for " in client.succeed(
+ "curl -sSf -H Host:simplehttp.traefik.test http://traefik/"
+ )
+ '';
+})
diff --git a/pkgs/servers/traefik/default.nix b/pkgs/servers/traefik/default.nix
index d2a784cdf13..0487ed177d0 100644
--- a/pkgs/servers/traefik/default.nix
+++ b/pkgs/servers/traefik/default.nix
@@ -1,39 +1,37 @@
-{ stdenv, buildGoPackage, fetchFromGitHub, bash, go-bindata}:
+{ stdenv, buildGoModule, fetchFromGitHub, go-bindata, nixosTests }:
-buildGoPackage rec {
+buildGoModule rec {
pname = "traefik";
- version = "1.7.14";
-
- goPackagePath = "github.com/containous/traefik";
+ version = "2.2.0";
src = fetchFromGitHub {
owner = "containous";
repo = "traefik";
rev = "v${version}";
- sha256 = "1j3p09j8rpdkp8v4d4mz224ddakkvhzchvccm9qryrqc2fq4022v";
+ sha256 = "1dcazssabqxr9wv3dds3z7ks3y628qa07vgnn3hpdwxzm2b2ma92";
};
- nativeBuildInputs = [ go-bindata bash ];
+ modSha256 = "0w3ssxvsmq8i6hbfmn4ig2x13i2nlqy5q1khcblf9pq5vhk202qx";
+ subPackages = [ "cmd/traefik" ];
- buildPhase = ''
- runHook preBuild
- (
- cd go/src/github.com/containous/traefik
- bash ./script/make.sh generate
+ nativeBuildInputs = [ go-bindata ];
- CODENAME=$(awk -F "=" '/CODENAME=/ { print $2}' script/binary)
- go build -ldflags "\
- -X github.com/containous/traefik/version.Version=${version} \
- -X github.com/containous/traefik/version.Codename=$CODENAME \
- " -a -o $bin/bin/traefik ./cmd/traefik
- )
- runHook postBuild
+ passthru.tests = { inherit (nixosTests) traefik; };
+
+ preBuild = ''
+ go generate
+
+ CODENAME=$(awk -F "=" '/CODENAME=/ { print $2}' script/binary)
+
+ makeFlagsArray+=("-ldflags=\
+ -X github.com/containous/traefik/version.Version=${version} \
+ -X github.com/containous/traefik/version.Codename=$CODENAME")
'';
meta = with stdenv.lib; {
homepage = "https://traefik.io";
description = "A modern reverse proxy";
license = licenses.mit;
- maintainers = with maintainers; [ hamhut1066 vdemeester ];
+ maintainers = with maintainers; [ vdemeester ];
};
}