forked from official-stockfish/fishtest
-
Notifications
You must be signed in to change notification settings - Fork 0
Fishtest server setup
ppigazzini edited this page Sep 12, 2021
·
106 revisions
- Use a clean install of Ubuntu 18.04 (bionic) or 20.04 (focal) on a virtual machine, cloud instance etc.
- Copy the script setup-fishtest.sh:
- write your password in the variable
usr_pwd
- (optional to use https) write your fully qualified domain name in the variable
server_name
- write your password in the variable
- Run the setup script with:
sudo bash setup-fishtest.sh 2>&1 | tee setup-fishtest.sh.log
- Setup the nets workflow:
- download the nets required by the tests (e.g. the default one) from the official fishtest server NN Stats page
- open a web browser using the ip_address of the fishtest server (http://ip_address/login), login as user01 (with password user01), select the "NN Upload page" (http://ip_address/upload) and upload the net. The net is written in
/home/fishtest/post-server/nn
- (optional) use the script
/home/fishtest/post-server/set_netserver.sh
to set the server (the official server or the local development server) from which to download the nets during the tests
- Create some tests with these users:
- user00 (with password user00), to approve test
- user01 (with password user01), to create test
- Connect a worker using the ip_address of the fishtest server, to have multiple workers make some copies of the worker folder.
python3 worker.py <username> <password> --protocol <http/https> --host <ip_address> --port <80/443/custom> --concurrency <n_cores>
- Stop/start the fishtest services with:
# start the services
sudo systemctl start fishtest@{6543..6544}.service
# stop the services
sudo systemctl stop fishtest@{6543..6544}.service
- To debug the server with the Pyramid Debug Toolbar, login on Ubuntu, use the following commands to start/stop the
fishtest_dbg.service
, and open a browser using the port 6542 (http://ip_address:6542).
# start the debug session
sudo systemctl start fishtest_dbg.service
# stop the debug session
sudo systemctl stop fishtest_dbg.service
#!/bin/bash
# 210912
# to setup a fishtest server on Ubuntu 18.04 (bionic) or 20.04 (focal), simply run:
# sudo bash setup_fishtest.sh 2>&1 | tee setup_fishtest.sh.log
#
# to use fishtest connect a browser to:
# http://<ip_address> or http://<fully_qualified_domain_name>
user_name='fishtest'
user_pwd='<your_password>'
# try to find the ip address
server_name=$(hostname --all-ip-addresses)
server_name="${server_name#"${server_name%%[![:space:]]*}"}"
server_name="${server_name%"${server_name##*[![:space:]]}"}"
# use a fully qualified domain names (http/https)
# server_name='<fully_qualified_domain_name>'
git_user_name='your_name'
git_user_email='[email protected]'
# create user for fishtest
useradd -m -s /bin/bash ${user_name}
echo ${user_name}:${user_pwd} | chpasswd
usermod -aG sudo ${user_name}
sudo -i -u ${user_name} << EOF
mkdir .ssh
chmod 700 .ssh
touch .ssh/authorized_keys
chmod 600 .ssh/authorized_keys
EOF
# get the user $HOME
user_home=$(sudo -i -u ${user_name} << 'EOF'
echo ${HOME}
EOF
)
# add some bash variables
sudo -i -u ${user_name} << 'EOF'
cat << 'EOF0' >> .profile
export FISHTEST_HOST=127.0.0.1
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export VENV="$HOME/fishtest/server/env"
export PSVENV="$HOME/post-server/env"
EOF0
EOF
# set secrets
sudo -i -u ${user_name} << EOF
echo '' > fishtest.secret
echo '' > fishtest.captcha.secret
echo 'http://127.0.0.1/upload_net' > fishtest.upload
cat << EOF0 > .netrc
# GitHub authentication to raise API rate limit
# create a <personal-access-token> https://github.com/settings/tokens
#machine api.github.com
#login <personal-access-token>
#password x-oauth-basic
EOF0
chmod 600 .netrc
EOF
# install required packages
apt update && apt full-upgrade -y && apt autoremove -y && apt clean
apt purge -y apache2 apache2-data apache2-doc apache2-utils apache2-bin
apt install -y ufw git bash-completion nginx mutt curl procps
# configure ufw
ufw allow ssh
ufw allow http
ufw allow https
ufw allow 6542
ufw --force enable
ufw status verbose
# configure nginx
cat << EOF > /etc/nginx/sites-available/fishtest.conf
upstream backend_tests {
server 127.0.0.1:6543;
}
upstream backend_all {
server 127.0.0.1:6544;
}
upstream upload_server {
server 127.0.0.1:8080;
}
server {
listen 80;
listen [::]:80;
server_name ${server_name};
location ~ ^/(css|html|img|js|favicon.ico|robots.txt) {
root ${user_home}/fishtest/server/fishtest/static;
expires 1y;
add_header Cache-Control public;
access_log off;
}
location /nn/ {
root ${user_home}/post-server;
gzip_static always;
gunzip on;
}
location / {
proxy_pass http://backend_all;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host \$host:\$server_port;
proxy_set_header X-Forwarded-Port \$server_port;
client_max_body_size 120m;
client_body_buffer_size 128k;
proxy_connect_timeout 60s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
proxy_buffering off;
proxy_temp_file_write_size 64k;
proxy_redirect off;
location ~ ^/api/(active_runs|download_pgn|download_pgn_100|request_version|upload_pgn) {
proxy_pass http://backend_all;
}
location /api/ {
proxy_pass http://backend_tests;
}
location ~ ^/tests/(finished|user/) {
proxy_pass http://backend_all;
}
location /tests {
proxy_pass http://backend_tests;
}
location /upload_net/ {
proxy_pass http://upload_server;
}
}
}
EOF
unlink /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/fishtest.conf /etc/nginx/sites-enabled/fishtest.conf
systemctl enable nginx.service
systemctl restart nginx.service
# setup pyenv and install the latest python version
# https://github.com/pyenv/pyenv
apt update
apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
sudo -i -u ${user_name} << 'EOF'
cp .profile .profile.bkp
sed -i 's/^#umask 022/#umask 022\n\n# pyenv: keep before sourcing .bashrc\nexport PYENV_ROOT="$HOME\/.pyenv"\nexport PATH="$PYENV_ROOT\/bin:$PATH"/' .profile
cat << 'EOF0' >> .profile
# pyenv: keep at the end of the file
if command -v pyenv &>/dev/null; then
eval "$(pyenv init --path)"
fi
EOF0
EOF
sudo -i -u ${user_name} << 'EOF'
python_ver="3.8.12"
git clone https://github.com/pyenv/pyenv.git "${PYENV_ROOT}"
pyenv install ${python_ver}
pyenv global ${python_ver}
EOF
# install mongodb community edition for Ubuntu 18.04 (bionic) or 20.04 (focal)
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
ubuntu_release=$(lsb_release -c | awk '{print $2}')
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu ${ubuntu_release}/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
apt update
apt install -y mongodb-org
# set the cache size in /etc/mongod.conf
# wiredTiger:
# engineConfig:
# cacheSizeGB: 1.75
cp /etc/mongod.conf mongod.conf.bkp
sed -i 's/^# wiredTiger:/ wiredTiger:\n engineConfig:\n cacheSizeGB: 1.75/' /etc/mongod.conf
# setup logrotate for mongodb
sed -i '/^ logAppend: true/a\ logRotate: reopen' /etc/mongod.conf
cat << 'EOF' > /etc/logrotate.d/mongod
/var/log/mongodb/mongod.log
{
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0600 mongodb mongodb
sharedscripts
postrotate
/bin/kill -SIGUSR1 $(pgrep mongod 2>/dev/null) 2>/dev/null || true
endscript
}
EOF
# download fishtest
sudo -i -u ${user_name} << EOF
git clone --single-branch --branch master https://github.com/glinscott/fishtest.git
cd fishtest
git config user.email "${git_user_email}"
git config user.name "${git_user_name}"
EOF
# setup fishtest
sudo -i -u ${user_name} << 'EOF'
python3 -m venv ${VENV}
${VENV}/bin/python3 -m pip install --upgrade pip setuptools wheel
cd ${HOME}/fishtest/server
${VENV}/bin/python3 -m pip install -e .
EOF
# install fishtest as systemd service
cat << EOF > /etc/systemd/system/[email protected]
[Unit]
Description=Fishtest Server port %i
After=network.target mongod.service
[Service]
Type=simple
ExecStart=${user_home}/fishtest/server/env/bin/pserve production.ini http_port=%i
Restart=on-failure
RestartSec=3
User=${user_name}
WorkingDirectory=${user_home}/fishtest/server
[Install]
WantedBy=multi-user.target
EOF
# install also fishtest debug as systemd service
cat << EOF > /etc/systemd/system/fishtest_dbg.service
[Unit]
Description=Fishtest Server Debug port 6542
After=network.target mongod.service
[Service]
Type=simple
ExecStart=${user_home}/fishtest/server/env/bin/pserve development.ini --reload
User=${user_name}
WorkingDirectory=${user_home}/fishtest/server
[Install]
WantedBy=multi-user.target
EOF
# enable the autostart for mongod.service and [email protected]
# check the log with: sudo journalctl -u [email protected] --since "2 days ago"
systemctl daemon-reload
systemctl enable mongod.service
systemctl enable fishtest@{6543..6544}.service
# start fishtest server
systemctl start mongod.service
systemctl start fishtest@{6543..6544}.service
# add mongodb indexes
sudo -i -u ${user_name} << 'EOF'
${VENV}/bin/python3 ${HOME}/fishtest/server/utils/create_indexes.py actions flag_cache pgns runs users
EOF
# add some default users:
# "user00" (with password "user00"), as approver
# "user01" (with password "user01"), as normal user
sudo -i -u ${user_name} << 'EOF'
${VENV}/bin/python3 << EOF0
from fishtest.rundb import RunDb
rdb = RunDb()
rdb.userdb.create_user("user00", "user00", "[email protected]")
rdb.userdb.add_user_group("user00", "group:approvers")
user = rdb.userdb.get_user("user00")
user["blocked"] = False
user["machine_limit"] = 100
rdb.userdb.save_user(user)
rdb.userdb.create_user("user01", "user01", "[email protected]")
user = rdb.userdb.get_user("user01")
user["blocked"] = False
user["machine_limit"] = 100
rdb.userdb.save_user(user)
EOF0
EOF
sudo -i -u ${user_name} << 'EOF'
(crontab -l; cat << EOF0
VENV=${HOME}/fishtest/server/env
UPATH=${HOME}/fishtest/server/utils
# Backup mongodb database and upload to s3
# keep disabled on dev server
# 3 */6 * * * /usr/bin/cpulimit -l 50 -f -m -- sh \${UPATH}/backup.sh
# Update the users table
1,16,31,46 * * * * \${VENV}/bin/python3 \${UPATH}/delta_update_users.py
# Purge old pgn files
33 3 * * * \${VENV}/bin/python3 \${UPATH}/purge_pgn.py
# Clean up old mail (more than 9 days old)
33 5 * * * screen -D -m mutt -e 'push D~d>9d<enter>qy<enter>'
EOF0
) | crontab -
EOF
# setup post-server
sudo -i -u ${user_name} << 'EOF'
mkdir -p ${HOME}/post-server/nn
python3 -m venv ${PSVENV}
${PSVENV}/bin/python3 -m pip install --upgrade pip setuptools wheel
cat << EOF0 > ${HOME}/post-server/server.py
#!/usr/bin/env python3
# From https://gist.github.com/mdonkers/63e115cc0c79b4f6b8b3a6b797e485c7
import gzip
import logging
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
class S(BaseHTTPRequestHandler):
def _set_response(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self):
logging.info(
"GET request,\nPath: {}\nHeaders:\n{}\n".format(
str(self.path), str(self.headers)
)
)
self._set_response()
self.wfile.write("GET request for {}".format(self.path).encode("utf-8"))
def do_POST(self):
# Gets the size of data
content_length = int(self.headers["Content-Length"])
# Gets the data itself
post_data = self.rfile.read(content_length)
logging.info(
"POST request,\nPath: {}\nHeaders:\n{}\n\nBody length:\n{}\n".format(
str(self.path), str(self.headers), len(post_data)
)
)
self._set_response()
self.wfile.write("POST request for {}".format(self.path).encode("utf-8"))
file_name = Path("${HOME}/post-server/nn/") / Path(self.path).name
file_name = file_name.with_suffix(file_name.suffix + ".gz")
try:
with gzip.open(file_name, "wb") as f:
f.write(post_data)
except Exception as e:
self.wfile.write(str(e))
def run(server_class=HTTPServer, handler_class=S, port=8080):
logging.basicConfig(level=logging.INFO)
server_address = ("", port)
httpd = server_class(server_address, handler_class)
logging.info("Starting httpd...\n")
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
logging.info("Stopping httpd...\n")
if __name__ == "__main__":
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
EOF0
EOF
# script to set the server to download the nets from
sudo -i -u ${user_name} << EOF
cat << EOF0 > \${HOME}/post-server/set_netserver.sh
#!/bin/bash
_usage () {
cat << EOF1
usage: bash \\\${0} <o|l>
set the server to download the nets from:
o: the "official" server used in fishtest
l: this development "local" server
EOF1
exit
}
if [[ \\\${#} == '0' ]] ; then
_usage
fi
if [[ \\\${1} == 'l' ]]; then
sed -i 's/"https:\/\/data.stockfishchess.org\/nn\/"/"http:\/\/${server_name}\/nn\/"/' \\\${HOME}/fishtest/server/fishtest/api.py
elif [[ \\\${1} == 'o' ]]; then
sed -i 's/"http:\/\/${server_name}\/nn\/"/"https:\/\/data.stockfishchess.org\/nn\/"/' \\\${HOME}/fishtest/server/fishtest/api.py
else
_usage
fi
echo 'fishtest restart to apply the new setting:'
sudo systemctl restart fishtest@{6543..6544}
EOF0
EOF
### install post-server as systemd service
cat << EOF > /etc/systemd/system/post-server.service
[Unit]
Description=simple HTTP server in python
After=network.target
[Service]
Type=simple
ExecStart=${user_home}/post-server/env/bin/python3 ${user_home}/post-server/server.py
Restart=on-failure
RestartSec=3
User=${user_name}
WorkingDirectory=${user_home}/post-server
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable post-server.service
systemctl start post-server.service
cat << EOF
connect a browser to:
http://${server_name}
EOF
# sudo bash setup-certbot.sh 2>&1 | tee setup-certbot.sh.log
# install certbot to setup let's encrypt
# https://certbot.eff.org/
# requires a DNS and a fully qualified domain name as servername
apt update && apt install -y software-properties-common
add-apt-repository -y universe
add-apt-repository -y ppa:certbot/certbot
apt update && apt install -y certbot python-certbot-nginx
cat << EOF
to configure let's encrypt run:
sudo certbot --nginx
EOF
Use this script to test a PR/branch
#!/bin/bash
# to update a fishtest server simply run:
# sudo bash update_fishtest.sh 2>&1 | tee update_fishtest.sh.log
#
# to use fishtest connect a browser to:
# http://<ip_address>
user_name='fishtest'
# backup
sudo -i -u ${user_name} << EOF
tar -czvf fishtest.$(date +%Y%m%d%H%M%S --utc).tgz fishtest
EOF
systemctl stop cron
systemctl stop fishtest@{6543..6544}
sleep 5
# download and prepare fishtest
sudo -i -u ${user_name} << EOF
rm -rf fishtest
git clone --single-branch --branch master https://github.com/glinscott/fishtest.git
cd fishtest
git config user.email '[email protected]'
git config user.name 'your_name'
# add here the upstream branch to be tested
#git remote add <your_upstream> https://github.com/<your_username>/fishtest
#git pull --no-edit <your_upstream> <your_branch>
# add here the PR/PRs to be tested
#git pull --no-edit origin pull/<PR_number>/head
#git pull --no-edit origin pull/<PR_number>/head
# setup fishtest
sudo -i -u ${user_name} << 'EOF'
python3 -m venv ${VENV}
${VENV}/bin/python3 -m pip install --upgrade pip setuptools wheel
cd ${HOME}/fishtest/server
${VENV}/bin/python3 -m pip install -e .
EOF
# start fishtest
systemctl start cron
systemctl start fishtest@{6543..6544}
cat << EOF
connect a browser to:
http://$(hostname --all-ip-addresses)
EOF
#!/bin/bash
# requirements:
# sudo apt update && sudo apt install -y python3 python3-pip python3-venv git build-essential libnuma-dev
test_folder=/<full_path>/__test_folder
virtual_env=${test_folder}/env
rm -rf ${test_folder}
mkdir -p ${test_folder}
cd ${test_folder}
git clone --single-branch --branch master https://github.com/glinscott/fishtest.git
cd fishtest
git config user.email "[email protected]"
git config user.name "your_name"
# add here the upstream branches to be tested
#git remote add <upstream_0> https://github.com/<username_0>/fishtest
#git pull --no-edit <upstream_0> <branch_0>
#git remote add <upstream_1> https://github.com/<username_1>/fishtest
#git pull --no-edit <upstream_1> <branch_1>
# add here the PRs to be tested
#git pull --no-edit origin pull/<PR_number_0>/head
#git pull --no-edit origin pull/<PR_number_1>/head
cd worker
python3 -m venv ${virtual_env}
${virtual_env}/bin/python3 -m pip install --upgrade pip setuptools wheel
${virtual_env}/bin/python3 -m pip install requests
${virtual_env}/bin/python3 worker.py user00 user00 --protocol http --host <ip-address> --port 80 --concurrency 3
Use the mongodb tools in a temporary folder, e.g.
- backup:
mongodump --gzip && tar -cv dump | gzip -1 > dump.tar.gz && rm -rf dump
- restore:
tar -xzvf dump.tgz && mongorestore --gzip --drop && rm -rf dump
Stop fishtest and cron services before a mongodb restore:
sudo systemctl stop fishtest@{6543..6544}
sudo systemctl stop cron
Sometime a badly configured worker client may post lots of losses on time during tests, or cause some tests to stop. The best policy to follow in these cases would be:
- open a thread in the fishcooking forum, reporting about the worker anomalous behavior.
- write an email to the user asking to control the worker and to write some info in the fishcooking thread.
- in case of failure of previous steps: block the user.
Approvers (and only approvers) can now block malicious users on fishtest:
- login with your own approver username/password
- click on a run to see the test page
- all the workers' names are now a hyperlink for the approvers
- click on a worker name (field "username")
- you view some info about the user: e.g. the email
- you can block/unblock the user. If blocked, the user cannot login in fishtest (e.g. submitting a test) and the workers cannot join the framework