python310Packages.pydeconz: 104 -> 105
[NixPkgs.git] / nixos / tests / trafficserver.nix
blob983ded4f172e21bdaa304202b9b4ef1292998bf0
1 # verifies:
2 #   1. Traffic Server is able to start
3 #   2. Traffic Server spawns traffic_crashlog upon startup
4 #   3. Traffic Server proxies HTTP requests according to URL remapping rules
5 #      in 'services.trafficserver.remap'
6 #   4. Traffic Server applies per-map settings specified with the conf_remap
7 #      plugin
8 #   5. Traffic Server caches HTTP responses
9 #   6. Traffic Server processes HTTP PUSH requests
10 #   7. Traffic Server can load the healthchecks plugin
11 #   8. Traffic Server logs HTTP traffic as configured
13 # uses:
14 #   - bin/traffic_manager
15 #   - bin/traffic_server
16 #   - bin/traffic_crashlog
17 #   - bin/traffic_cache_tool
18 #   - bin/traffic_ctl
19 #   - bin/traffic_logcat
20 #   - bin/traffic_logstats
21 #   - bin/tspush
22 import ./make-test-python.nix ({ pkgs, ... }: {
23   name = "trafficserver";
24   meta = with pkgs.lib.maintainers; {
25     maintainers = [ midchildan ];
26   };
28   nodes = {
29     ats = { pkgs, lib, config, ... }: let
30       user = config.users.users.trafficserver.name;
31       group = config.users.groups.trafficserver.name;
32       healthchecks = pkgs.writeText "healthchecks.conf" ''
33         /status /tmp/ats.status text/plain 200 500
34       '';
35     in {
36       services.trafficserver.enable = true;
38       services.trafficserver.records = {
39         proxy.config.http.server_ports = "80 80:ipv6";
40         proxy.config.hostdb.host_file.path = "/etc/hosts";
41         proxy.config.log.max_space_mb_headroom = 0;
42         proxy.config.http.push_method_enabled = 1;
44         # check that cache storage is usable before accepting traffic
45         proxy.config.http.wait_for_cache = 2;
46       };
48       services.trafficserver.plugins = [
49         { path = "healthchecks.so"; arg = toString healthchecks; }
50         { path = "xdebug.so"; }
51       ];
53       services.trafficserver.remap = ''
54         map http://httpbin.test http://httpbin
55         map http://pristine-host-hdr.test http://httpbin \
56           @plugin=conf_remap.so \
57           @pparam=proxy.config.url_remap.pristine_host_hdr=1
58         map http://ats/tspush http://httpbin/cache \
59           @plugin=conf_remap.so \
60           @pparam=proxy.config.http.cache.required_headers=0
61       '';
63       services.trafficserver.storage = ''
64         /dev/vdb volume=1
65       '';
67       networking.firewall.allowedTCPPorts = [ 80 ];
68       virtualisation.emptyDiskImages = [ 256 ];
69       services.udev.extraRules = ''
70         KERNEL=="vdb", OWNER="${user}", GROUP="${group}"
71       '';
72     };
74     httpbin = { pkgs, lib, ... }: let
75       python = pkgs.python3.withPackages
76         (ps: with ps; [ httpbin gunicorn gevent ]);
77     in {
78       systemd.services.httpbin = {
79         enable = true;
80         after = [ "network.target" ];
81         wantedBy = [ "multi-user.target" ];
82         serviceConfig = {
83           ExecStart = "${python}/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent";
84         };
85       };
87       networking.firewall.allowedTCPPorts = [ 80 ];
88     };
90     client = { pkgs, lib, ... }: {
91       environment.systemPackages = with pkgs; [ curl ];
92     };
93   };
95   testScript = { nodes, ... }: let
96     sampleFile = pkgs.writeText "sample.txt" ''
97       It's the season of White Album.
98     '';
99   in ''
100     import json
101     import re
103     ats.wait_for_unit("trafficserver")
104     ats.wait_for_open_port(80)
105     httpbin.wait_for_unit("httpbin")
106     httpbin.wait_for_open_port(80)
107     client.wait_for_unit("network-online.target")
109     with subtest("Traffic Server is running"):
110         out = ats.succeed("traffic_ctl server status")
111         assert out.strip() == "Proxy -- on"
113     with subtest("traffic_crashlog is running"):
114         ats.succeed("pgrep -f traffic_crashlog")
116     with subtest("basic remapping works"):
117         out = client.succeed("curl -vv -H 'Host: httpbin.test' http://ats/headers")
118         assert json.loads(out)["headers"]["Host"] == "httpbin"
120     with subtest("conf_remap plugin works"):
121         out = client.succeed(
122             "curl -vv -H 'Host: pristine-host-hdr.test' http://ats/headers"
123         )
124         assert json.loads(out)["headers"]["Host"] == "pristine-host-hdr.test"
126     with subtest("caching works"):
127         out = client.succeed(
128             "curl -vv -D - -H 'Host: httpbin.test' -H 'X-Debug: X-Cache' http://ats/cache/60 -o /dev/null"
129         )
130         assert "X-Cache: miss" in out
132         out = client.succeed(
133             "curl -vv -D - -H 'Host: httpbin.test' -H 'X-Debug: X-Cache' http://ats/cache/60 -o /dev/null"
134         )
135         assert "X-Cache: hit-fresh" in out
137     with subtest("pushing to cache works"):
138         url = "http://ats/tspush"
140         ats.succeed(f"echo {url} > /tmp/urls.txt")
141         out = ats.succeed(
142             f"tspush -f '${sampleFile}' -u {url}"
143         )
144         assert "HTTP/1.0 201 Created" in out, "cache push failed"
146         out = ats.succeed(
147             "traffic_cache_tool --spans /etc/trafficserver/storage.config find --input /tmp/urls.txt"
148         )
149         assert "Span: /dev/vdb" in out, "cache not stored on disk"
151         out = client.succeed(f"curl {url}").strip()
152         expected = (
153             open("${sampleFile}").read().strip()
154         )
155         assert out == expected, "cache content mismatch"
157     with subtest("healthcheck plugin works"):
158         out = client.succeed("curl -vv http://ats/status -o /dev/null -w '%{http_code}'")
159         assert out.strip() == "500"
161         ats.succeed("touch /tmp/ats.status")
163         out = client.succeed("curl -vv http://ats/status -o /dev/null -w '%{http_code}'")
164         assert out.strip() == "200"
166     with subtest("logging works"):
167         access_log_path = "/var/log/trafficserver/squid.blog"
168         ats.wait_for_file(access_log_path)
170         out = ats.succeed(f"traffic_logcat {access_log_path}").split("\n")[0]
171         expected = "^\S+ \S+ \S+ TCP_MISS/200 \S+ GET http://httpbin/headers - DIRECT/httpbin application/json$"
172         assert re.fullmatch(expected, out) is not None, "no matching logs"
174         out = json.loads(ats.succeed(f"traffic_logstats -jf {access_log_path}"))
175         assert out["total"]["error.total"]["req"] == "0", "unexpected log stat"
176   '';