at master 6.6 kB view raw
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 12# 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{ pkgs, ... }: 23{ 24 name = "trafficserver"; 25 meta = with pkgs.lib.maintainers; { 26 maintainers = [ midchildan ]; 27 }; 28 29 nodes = { 30 ats = 31 { 32 pkgs, 33 lib, 34 config, 35 ... 36 }: 37 let 38 user = config.users.users.trafficserver.name; 39 group = config.users.groups.trafficserver.name; 40 healthchecks = pkgs.writeText "healthchecks.conf" '' 41 /status /tmp/ats.status text/plain 200 500 42 ''; 43 in 44 { 45 services.trafficserver.enable = true; 46 47 services.trafficserver.records = { 48 proxy.config.http.server_ports = "80 80:ipv6"; 49 proxy.config.hostdb.host_file.path = "/etc/hosts"; 50 proxy.config.log.max_space_mb_headroom = 0; 51 proxy.config.http.push_method_enabled = 1; 52 53 # check that cache storage is usable before accepting traffic 54 proxy.config.http.wait_for_cache = 2; 55 }; 56 57 services.trafficserver.plugins = [ 58 { 59 path = "healthchecks.so"; 60 arg = toString healthchecks; 61 } 62 { path = "xdebug.so"; } 63 ]; 64 65 services.trafficserver.remap = '' 66 map http://httpbin.test http://httpbin 67 map http://pristine-host-hdr.test http://httpbin \ 68 @plugin=conf_remap.so \ 69 @pparam=proxy.config.url_remap.pristine_host_hdr=1 70 map http://ats/tspush http://httpbin/cache \ 71 @plugin=conf_remap.so \ 72 @pparam=proxy.config.http.cache.required_headers=0 73 ''; 74 75 services.trafficserver.storage = '' 76 /dev/vdb volume=1 77 ''; 78 79 networking.firewall.allowedTCPPorts = [ 80 ]; 80 virtualisation.emptyDiskImages = [ 256 ]; 81 services.udev.extraRules = '' 82 KERNEL=="vdb", OWNER="${user}", GROUP="${group}" 83 ''; 84 }; 85 86 httpbin = 87 { pkgs, lib, ... }: 88 let 89 python = pkgs.python3.withPackages ( 90 ps: with ps; [ 91 httpbin 92 gunicorn 93 gevent 94 ] 95 ); 96 in 97 { 98 systemd.services.httpbin = { 99 enable = true; 100 after = [ "network.target" ]; 101 wantedBy = [ "multi-user.target" ]; 102 serviceConfig = { 103 ExecStart = "${python}/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent"; 104 }; 105 }; 106 107 networking.firewall.allowedTCPPorts = [ 80 ]; 108 }; 109 110 client = 111 { pkgs, lib, ... }: 112 { 113 environment.systemPackages = with pkgs; [ curl ]; 114 }; 115 }; 116 117 testScript = 118 { nodes, ... }: 119 let 120 sampleFile = pkgs.writeText "sample.txt" '' 121 It's the season of White Album. 122 ''; 123 in 124 '' 125 import json 126 import re 127 128 ats.wait_for_unit("trafficserver") 129 ats.wait_for_open_port(80) 130 httpbin.wait_for_unit("httpbin") 131 httpbin.wait_for_open_port(80) 132 client.systemctl("start network-online.target") 133 client.wait_for_unit("network-online.target") 134 135 with subtest("Traffic Server is running"): 136 out = ats.succeed("traffic_ctl server status") 137 assert out.strip() == "Proxy -- on" 138 139 with subtest("traffic_crashlog is running"): 140 ats.succeed("pgrep -f traffic_crashlog") 141 142 with subtest("basic remapping works"): 143 out = client.succeed("curl -vv -H 'Host: httpbin.test' http://ats/headers") 144 assert json.loads(out)["headers"]["Host"] == "httpbin" 145 146 with subtest("conf_remap plugin works"): 147 out = client.succeed( 148 "curl -vv -H 'Host: pristine-host-hdr.test' http://ats/headers" 149 ) 150 assert json.loads(out)["headers"]["Host"] == "pristine-host-hdr.test" 151 152 with subtest("caching works"): 153 out = client.succeed( 154 "curl -vv -D - -H 'Host: httpbin.test' -H 'X-Debug: X-Cache' http://ats/cache/60 -o /dev/null" 155 ) 156 assert "X-Cache: miss" in out 157 158 out = client.succeed( 159 "curl -vv -D - -H 'Host: httpbin.test' -H 'X-Debug: X-Cache' http://ats/cache/60 -o /dev/null" 160 ) 161 assert "X-Cache: hit-fresh" in out 162 163 with subtest("pushing to cache works"): 164 url = "http://ats/tspush" 165 166 ats.succeed(f"echo {url} > /tmp/urls.txt") 167 out = ats.succeed( 168 f"tspush -f '${sampleFile}' -u {url}" 169 ) 170 assert "HTTP/1.0 201 Created" in out, "cache push failed" 171 172 out = ats.succeed( 173 "traffic_cache_tool --spans /etc/trafficserver/storage.config find --input /tmp/urls.txt" 174 ) 175 assert "Span: /dev/vdb" in out, "cache not stored on disk" 176 177 out = client.succeed(f"curl {url}").strip() 178 expected = ( 179 open("${sampleFile}").read().strip() 180 ) 181 assert out == expected, "cache content mismatch" 182 183 with subtest("healthcheck plugin works"): 184 out = client.succeed("curl -vv http://ats/status -o /dev/null -w '%{http_code}'") 185 assert out.strip() == "500" 186 187 ats.succeed("touch /tmp/ats.status") 188 189 out = client.succeed("curl -vv http://ats/status -o /dev/null -w '%{http_code}'") 190 assert out.strip() == "200" 191 192 with subtest("logging works"): 193 access_log_path = "/var/log/trafficserver/squid.blog" 194 ats.wait_for_file(access_log_path) 195 196 out = ats.succeed(f"traffic_logcat {access_log_path}").split("\n")[0] 197 expected = "^\S+ \S+ \S+ TCP_MISS/200 \S+ GET http://httpbin/headers - DIRECT/httpbin application/json$" 198 assert re.fullmatch(expected, out) is not None, "no matching logs" 199 200 out = json.loads(ats.succeed(f"traffic_logstats -jf {access_log_path}")) 201 assert isinstance(out, dict) 202 assert out["total"]["error.total"]["req"] == "0", "unexpected log stat" 203 ''; 204}