Apache Traffic Serverのautest.sh
はじめに
trafficserver 9.2.0-rc0のtestsディレクトリのautest.shを実行する際のメモです。
Apache Traffic ServerのThe ASF Slackでmasaori335さんにいろいろ教えていただきました。ありがとうございました!
Docker上のUbuntu 22.04 LTSと20.04 LTSでautest.shを動かす手順を GitHub - hnakamur/trafficserver-run-autest-docker においています。
DockerでIPv6を使う
autestのテストでIPv6が必要になるので、DockerでIPv6を使えるようにする必要があります。以下のページを参考にしました。
- Enable IPv6 support | Docker Documentation
- Enable IPv6 for Docker containers on Ubuntu 18.04 | Medium 3, DockerでIPv6化 – mahori blog
1の公式ドキュメントではIPv6アドレス - Wikipediaの文書記述用アドレスプレフィックスのアドレスの/32
を/64
にしたアドレスが使われています。
実際にどういうアドレスを使えばよいかですが、2のドキュメントによるとグローバルのIPv6アドレスを使うと設定は簡単ですが、インターネットからDockerコンテナにアクセスできてしまうとのことでした。
そこで2と3のページを参考に/etc/docker/daemon.json
は以下のようにしました。
{
"ipv6": true,
"fixed-cidr-v6": "fd6d:6168:6f72:6900::/64"
}
その後dockerサービスを再起動します。
sudo systemctl restart docker
最後にIPv6のNATを設定します。
ip6tables -t nat -A POSTROUTING -s fd6d:6168:6f72:6900::/64 ! -o docker0 -j MASQUERADE
その後
docker network inspect bridge
で確認するとIPAMのConfigにIPv4とIPv6のエントリが出てきました。
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
},
{
"Subnet": "fd6d:6168:6f72:6900::/64"
}
]
この後、コンテナを作成して docker inspect <コンテナID>
で確認するとNetworkSettingsのGlobalIPv6AddressにIPv6アドレスがついていました。
autestのレポジトリ
PyPIのプロジェクトはautest · PyPIで、ソースレポジトリはautestsuite / reusable-gold-testing-system — Bitbucketです。
今回はautestのソースまでは見ていませんが、一応メモ。
trafficserverのJenkinsでのautest
apache/trafficserver-ciにCI用のレポジトリがあります。
9.2.xのautestの実行結果は https://ci.trafficserver.apache.org/view/9.2.x/job/9.2.x/job/autest/ で見られます。
実行するテストを分けて4つのジョブを並列で実行しています。左のジョブ一覧で9.2.x 0of4
のリンクをクリック後、左メニューのPipeline Steps
を押すとパイプラインが見られます。configureとmakeをしている箇所を見てapache/trafficserver-ciで検索すると
jenkins/github/autest.pipelineに対応するようです。
先頭のagentのdockerの箇所を見るとimage 'ci.trafficserver.apache.org/ats/rockylinux:8'
となっていて、
docker/rockylinux8/のdocker/rockylinux8/Dockerfileに対応しているようです。
autest.shの–sandboxオプションを指定すると失敗したテストのログなどが残る
-C none
(Command Line options — AuTest 1.4.0 documentation参照)をつけると成功したテストのログなども残ります。
-f テスト名
で実行するテストをフィルタリングできます。テスト名はファイル名に対応しています。例えばchunked_encoding
というテストなら
~/ghq/github.com/apache/trafficserver-9.2.0-rc0$ find ./tests -name 'chunked_encoding.test.py'
./tests/gold_tests/chunked_encoding/chunked_encoding.test.py
のように探します。
テスト失敗のログ
例としてchunked_encodingが失敗したログを見てみます。
テストのファイルはtests/gold_tests/chunked_encoding/chunked_encoding.test.pyです。tr = Test.AddTestRun()
が5回出てくるので5つのステップが追加されています(Local objects typesのTestRun
がステップに対応)。
テストのログで見ると以下のようになっていて.
が成功したステップでF
が失敗したステップです。
Running Test chunked_encoding:....F Failed
ログの後ろの方に成功/失敗の詳細が出力されています。
Test: chunked_encoding: Failed
File: chunked_encoding.test.py
Directory: /src/trafficserver/tests/gold_tests/chunked_encoding
Setting up : recycling port: 61178, queue size: 999 - Passed
…(略)…
Run: 4-tr: Failed
Setting up : Copying 'server4.sh' to 'None' - Passed
Setting up : Copying 'case4.sh' to 'None' - Passed
Time-Out : TestRun finishes within expected time - Passed
Reason: Returned value: 1.1100995540618896 < 5.0
Starting TestRun 4-tr : No Issues found - Passed
Reason: Started!
Process: Default: Failed
Test : Checking that ReturnCode == 0 - Failed
Reason: Returned Value 1 != 0
Time-Out : Process finishes within expected time - Passed
Reason: Returned value: 1.0053956508636475 < 600.0
file /src/autest-sandbox/chunked_encoding/_output/4-tr-Default/stream.all.txt : Response should not include content length - Passed
Reason: Contents of /src/autest-sandbox/chunked_encoding/_output/4-tr-Default/stream.all.txt excludes expression
今回はRun: 4-tr
がFailedで、詳細はTest : Checking that ReturnCode == 0
であるべきところが1が返ってきたため失敗となったことがわかります。
sandbox内のディレクトリ・ファイル構成
例えば --sandbox /src/autest-sandbox -C none -f chunked_encoding
をつけて実行した場合、/src/autest-sandbox/chunked_encoding/
に以下のようにディレクトリが残ります。
# ls -F /src/autest-sandbox/chunked_encoding/
_output/ case4.sh outserver4 server/ server2/ server3/ server4.sh smuggle-client* ts/
server
, server2
, server3
, ts
フォルダはtests/gold_tests/chunked_encoding/chunked_encoding.test.py#L104-L108のStartBefore
で起動しているオリジンサーバとtrafficserverにプロセスに対応します。
tr.Processes.Default.StartBefore(server)
tr.Processes.Default.StartBefore(server2)
tr.Processes.Default.StartBefore(server3)
# Delay on readiness of our ssl ports
tr.Processes.Default.StartBefore(Test.Processes.ts)
ts/
フォルダにはtrafficserverの設定ファイルやログファイルが含まれています。
# ls -F /src/autest-sandbox/chunked_encoding/ts/
bin/ cache/ config/ log/ plugin/ runtime/ ssl/ storage/
_output/
フォルダは以下のようになっています。
# # ls -F /src/autest-sandbox/chunked_encoding/_output/
0-tr-Default/ 4-tr-Default/ chunked_encoding-ts/
1-tr-Default/ chunked_encoding-server/ condition-CheckOutput-86fe/
2-tr-Default/ chunked_encoding-server2/
3-tr-Default/ chunked_encoding-server3/
例えば0-tr-Default
フォルダの下は以下のようなファイルが入っています。
# ls /src/autest-sandbox/chunked_encoding/_output/0-tr-Default/
command.txt stream.all.txt stream.error.txt stream.stdout.txt stream.warning.txt
replay.sh stream.debug.txt stream.stderr.txt stream.verbose.txt
sandboxのディレクトリでのテストの再実行
replay.sh
でリプレイ実行できます。実行パーミションがついていないのでbash
をつけて実行する必要があります。
まずdocker run --rm -it イメージ名 bash
でコンテナを起動しつつ入って
cd /src/autest-sandbox/chunked_encoding
bash _output/chunked_encoding-server/replay.sh
でオリジンサーバserver
を起動します。
別の端末でdocker exec -it $(docker ps -lq) bash
で同じコンテナに入って
cd /src/autest-sandbox/chunked_encoding
bash _output/chunked_encoding-server2/replay.sh
でserver2
を起動します。
別の端末でdocker exec -it $(docker ps -lq) bash
で同じコンテナに入って
cd /src/autest-sandbox/chunked_encoding
bash _output/chunked_encoding-server3/replay.sh
でserver3
を起動します。
別の端末でdocker exec -it $(docker ps -lq) bash
で同じコンテナに入って
cd /src/autest-sandbox/chunked_encoding
bash _output/chunked_encoding-ts/replay.sh
でtrafficserverを起動します。
さらに別の端末でdocker exec -it $(docker ps -lq) bash
で同じコンテナに入って
cd /src/autest-sandbox/chunked_encoding
seq 0 4 | xargs -I {} bash _output/{}-tr-Default/replay.sh
でテストを実行します。
以下のような出力になりました。
# seq 0 4 | xargs -I {} bash _output/{}-tr-Default/replay.sh
…(略)…
microserverapachetrafficserver* Trying 127.0.0.1:61179...
* Connected to 127.0.0.1 (127.0.0.1) port 61179 (#0)
> POST / HTTP/1.1
> Host: www.yetanotherexample.com
> User-Agent: curl/7.81.0
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: ATS/9.2.0
< Date: Mon, 16 Jan 2023 11:18:25 GMT
< Age: 0
< Transfer-Encoding: chunked
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
microserverapachetrafficserverNcat: bind to :::61178: Address already in use. QUITTING.
using address: 127.0.0.1 and port: 61181
Send request
Received 128 bytes HTTP/1.1 200
Date: Mon, 16 Jan 2023 11:18:27 GMT
Age: 1
Transfer-Encoding: chunked
Connection: close
Server: ATS/9.2.0
Received 25 bytes F
123456789012345
0
./case4.sh: 20: kill: No such process
bind to :::61178: Address already in use. QUITTING.
のエラーが問題のようです。
61178で検索してみると
# grep -r 61178 .
./ts/config/remap.config:map / http://127.0.0.1:61178
./ts/log/traffic.out:[Jan 16 08:47:38.876] [ET_NET 15] DEBUG: <URL.cc:1730 (url_describe)> (http) PORT: "61178", PORT_LEN: 5, PORT_NUM: 61178
…(略)…
./ts/log/traffic.out:[Jan 16 11:18:27.004] [ET_NET 0] DEBUG: <HttpSessionManager.cc:491 (removeSession)> (http_ss) Remove session 0x7f51680b9740 127.0.0.1:61178 m_fqdn_pool size=1 m_ip_p
ool_size=1
./outserver4:Host: 127.0.0.1:61178
./_output/4-tr-Default/command.txt:Command= sh ./case4.sh 61181 61178
./_output/4-tr-Default/replay.sh:sh ./case4.sh 61181 61178
case4.sh
の中身は以下のようになっていました。
# cat case4.sh
…(略)…
nc -l ${2} -o outserver4 -c "sh ./server4.sh" &
sleep 1
./smuggle-client 127.0.0.1 ${1}
kill %1
ここでncが61178番ポートでリッスンするようです。
しばらく待ってから再度テストのステップを実行してみると、出力が変わりました。
# seq 0 4 | xargs -I {} bash _output/{}-tr-Default/replay.sh
…(略)…
microserverapachetrafficserver* Trying 127.0.0.1:61179...
* Connected to 127.0.0.1 (127.0.0.1) port 61179 (#0)
> POST / HTTP/1.1
> Host: www.yetanotherexample.com
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Length: 11
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: ATS/9.2.0
< Date: Mon, 16 Jan 2023 11:22:39 GMT
< Age: 0
< Transfer-Encoding: chunked
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
microserverapachetrafficserver* Trying 127.0.0.1:61179...
* Connected to 127.0.0.1 (127.0.0.1) port 61179 (#0)
> POST / HTTP/1.1
> Host: www.yetanotherexample.com
> User-Agent: curl/7.81.0
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: ATS/9.2.0
< Date: Mon, 16 Jan 2023 11:22:39 GMT
< Age: 0
< Transfer-Encoding: chunked
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
microserverapachetrafficserverusing address: 127.0.0.1 and port: 61181
Send request
Received 128 bytes HTTP/1.1 200
Date: Mon, 16 Jan 2023 11:22:40 GMT
Age: 0
Transfer-Encoding: chunked
Connection: close
Server: ATS/9.2.0
Received 25 bytes F
123456789012345
0
./case4.sh: 20: kill: No such process
root@f16ca3320b70:/src/autest-sandbox/chunked_encoding# echo $?
123
# pgrep -f '(traffic_server|microserver)' | xargs -r ps uwwf -p
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
nobody 145 3.1 0.1 2024184 85840 pts/4 Sl+ 11:18 0:21 traffic_server --bind_stderr /src/autest-sandbox/chunked_encoding/ts/log/traffic.out --bind_stdout /src/autest-sandbox/chunked_encoding/ts/log/traffic.out
root 94 0.0 0.0 173012 15824 pts/2 Sl+ 11:13 0:00 /root/.local/share/virtualenvs/tests-L5-scKRl/bin/python /root/.local/share/virtualenvs/tests-L5-scKRl/bin/microserver --data-dir /src/autest-sandbox/chunked_encoding/server3 --ip_address 127.0.0.1 --lookupkey {PATH} --port 61187
root 79 0.0 0.0 173456 17316 pts/1 Sl+ 11:12 0:00 /root/.local/share/virtualenvs/tests-L5-scKRl/bin/python /root/.local/share/virtualenvs/tests-L5-scKRl/bin/microserver --data-dir /src/autest-sandbox/chunked_encoding/server2 --ip_address 127.0.0.1 --lookupkey {PATH} --ssl --key /src/trafficserver/tests/gold_tests/autest-site/../../tools/microserver/ssl/server.pem --cert /src/trafficserver/tests/gold_tests/autest-site/../../tools/microserver/ssl/server.crt --s_port 61186
root 76 0.0 0.0 173012 15364 pts/0 Sl+ 11:12 0:00 /root/.local/share/virtualenvs/tests-L5-scKRl/bin/python /root/.local/share/virtualenvs/tests-L5-scKRl/bin/microserver --data-dir /src/autest-sandbox/chunked_encoding/server --ip_address 127.0.0.1 --lookupkey {PATH} --port 61185
そこで
# nc -l 61178 -o outserver4 -c "sh ./server4.sh"
と実行して、さらに別の端末を起動して./smuggle-client 127.0.0.1 61181
を実行すると
# ./smuggle-client 127.0.0.1 61181
using address: 127.0.0.1 and port: 61181
Send request
Received 128 bytes HTTP/1.1 200
Date: Mon, 16 Jan 2023 11:32:27 GMT
Age: 0
Transfer-Encoding: chunked
Connection: close
Server: ATS/9.2.0
Received 25 bytes F
123456789012345
0
となり、nc
のほうは終了していました。どちらもecho $?
で確認すると終了コードは0でした。
ncの引数に指定しているserver4.sh
とoutserver4
は以下のような内容でした。
# tail -2 server4.sh
printf "HTTP/1.1 200\r\nTransfer-encoding: chunked\r\n\r\n"
printf "F\r\n123456789012345\r\n0\r\n\r\n"
# cat outserver4
GET / HTTP/1.1
Host: 127.0.0.1:61178
Client-ip: 127.0.0.1
X-Forwarded-For: 127.0.0.1
Via: https/1.1 traffic_server[7bb817a4-f398-4af8-b7a7-529823a85330] (ApacheTrafficServer/9.2.0)
Transfer-Encoding: chunked
0
HTTP/1.1 200
Transfer-encoding: chunked
F
123456789012345
0
# cat case4.sh
…(略)…
nc -l ${2} -o outserver4 -c "sh ./server4.sh" &
sleep 1
./smuggle-client 127.0.0.1 ${1}
kill %1
を見返すと、smuggle-clientが終了してkillする前にncが終了してkill: No such process
になったようです。
以下細かいメモ
ncはncatパッケージのを使う
今日知ったのですがUbuntuのncは、netcat-openbsd、netcat-traditional、ncatというパッケージがあり、-w
オプションの挙動が違います。
trafficserverのautestではncatを使う必要があります。
ncatでは-w
は接続タイムアウトですが、他2つでは接続と通信合わせてのタイムアウトです。
-w 1
として標準入力から送信するリクエストを流し込むとncatはレスポンスを受け取るとすぐ終了しますが、他2つではレスポンスを受け取っても1秒経たないと終了しませんでした(テストではサーバ側はConnection: keep-alive
を返すようになっていました)。
ncatを使え、以上なのですが、ついでにメモ。
netcat-openbsdだと-N
(Shutdown the network socket after EOF stdin)を指定すればすぐ終了しました。netcat-traditionalだと-q secs
(quit after EOF on stdin and delay of secs)というのがありました。試してみると-q 0
だとレスポンスが全く表示されず、-q 1
だとレスポンスは表示されるが1秒待つことなくすぐ終了しました。
trafficserverのautestではncの-o
を使いますが、netcat-openbsdのncは-o
は非対応です。netcat-traditionalにはあります。
pipenvはautestではrootユーザで実行
Pipenv: Python Dev Workflow for Humans — pipenv 2022.12.20.dev0 documentationによると
pip install --user pipenv
と一般ユーザでインストールするのが推奨とのことです。
ただ、それでautestを実行するとroot権限が必要なテストをスキップする旨のメッセージが出たためrootでインストールしてrootで実行するようにしました。