Initial commit
All checks were successful
Ansible Playbook lint / ansible-lint (push) Successful in 35s

This commit is contained in:
2024-12-19 10:25:37 +08:00
commit bf6ac11888
28 changed files with 742 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env -S bash
mapfile -t files <<< "$(ls -1 /tmp/kcw-*.zip)"
for file in ${files[@]}; do
echo "target: $file"
dir=$(cut -d'$' -f1 <<< "$file")
if ! [[ -d "$dir" ]]; then
unzip -d "$dir" "$file"
fi
cmsImportTask "$dir"
done

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env -S bash
cat > contest.yaml <<EOF
name: "con_test"
description: "Contest to test CMS"
users:
EOF
awk '{printf "- username: \"%s\"\n password: \"%s\"\n", $1, $2}' passwd >> contest.yaml
less contest.yaml

View File

@@ -0,0 +1,15 @@
---
- name: Copy users.bash
ansible.builtin.copy:
src: users.bash
dest: /srv/cms/users.bash
mode: '0755'
owner: cmsuser
group: cmsuser
- name: Copy import.bash
ansible.builtin.copy:
src: import.bash
dest: /srv/cms/import.bash
mode: '0755'
owner: cmsuser
group: cmsuser

View File

@@ -0,0 +1 @@
source "$HOME/.bashrc"

View File

@@ -0,0 +1 @@
source "$HOME/.profile"

View File

@@ -0,0 +1,4 @@
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
source "$HOME/bin/activate"

View File

@@ -0,0 +1,104 @@
---
- name: Clone repository
ansible.builtin.git:
repo: 'https://github.com/cms-dev/cms.git'
dest: /srv/cms
version: b77c87b4d60fbe7df60dc5e03d2be632a25992fe
single_branch: true
track_submodules: true
update: false
- name: Run prerequisites
ansible.builtin.command: |
python prerequisites.py -y --as-root install
args:
chdir: /srv/cms
register: ret
changed_when: ret.rc != 0
- name: Modify cmsuser
ansible.builtin.user:
name: cmsuser
shell: /usr/bin/bash
home: /srv/cms
groups: wheel
append: true
- name: Allow wheel group to sudo
community.general.sudoers:
name: wheel
group: wheel
commands: ALL
runas: ALL
nopassword: true
- name: Chown cms directory
ansible.builtin.file:
path: /srv/cms/
state: directory
owner: cmsuser
group: cmsuser
recurse: true
- name: Install pyenv
ansible.builtin.command: |
pyenv install 3.8.20 --skip-existing
args:
chdir: /srv/cms
become: true
become_user: cmsuser
register: ret
changed_when: ret.rc != 0
- name: Setup pyenv
ansible.builtin.command: |
pyenv local 3.8.20
args:
chdir: /srv/cms
become: true
become_user: cmsuser
register: ret
changed_when: ret.rc != 0
- name: Setup .profile
ansible.builtin.copy:
src: profile
dest: /srv/cms/.profile
mode: '0644'
owner: cmsuser
group: cmsuser
- name: Setup .bashrc
ansible.builtin.copy:
src: bashrc
dest: /srv/cms/.bashrc
mode: '0644'
owner: cmsuser
group: cmsuser
- name: Setup .bash_profile
ansible.builtin.copy:
src: bash_profile
dest: /srv/cms/.bash_profile
mode: '0644'
owner: cmsuser
group: cmsuser
- name: Check python version
ansible.builtin.shell: |
sudo -iu cmsuser <<EOF
python --version
EOF
register: ret
changed_when: ret.rc != 0
failed_when: ret.stdout != 'Python 3.8.20'
- name: Setup python venv
ansible.builtin.shell: |
sudo -iu cmsuser <<EOF
python -m venv .
EOF
register: ret
changed_when: ret.rc != 0
- name: Install requirements
ansible.builtin.pip:
virtualenv: /srv/cms
requirements: /srv/cms/requirements.txt
become: true
become_user: cmsuser
- name: Install cms
ansible.builtin.shell: |
sudo -iu cmsuser <<EOF
python setup.py install
EOF
register: ret
changed_when: ret.rc != 0

View File

@@ -0,0 +1,22 @@
[Unit]
Description=CMS Log Server Daemon
After=network.target
[Service]
Type=simple
User=cmsuser
Group=cmsuser
WorkingDirectory=/srv/cms
Environment=PYENV_SHELL=bash
Environment=PYENV_ROOT=/srv/cms/.pyenv
Environment=LOGNAME=cmsuser
Environment=VIRTUAL_ENV=/srv/cms
ExecStart=/srv/cms/bin/cmsLogService 0
ExecStop=/usr/bin/kill $MAINPID
Restart=on-failure
StandardInput=null
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,22 @@
[Unit]
Description=CMS Ranking Server Daemon
After=network.target
[Service]
Type=simple
User=cmsuser
Group=cmsuser
WorkingDirectory=/srv/cms
Environment=PYENV_SHELL=bash
Environment=PYENV_ROOT=/srv/cms/.pyenv
Environment=LOGNAME=cmsuser
Environment=VIRTUAL_ENV=/srv/cms
ExecStart=/srv/cms/bin/cmsRankingWebServer
ExecStop=/usr/bin/kill $MAINPID
Restart=on-failure
StandardInput=null
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,24 @@
[Unit]
Description=CMS Resources Service Daemon for Contest ID %i
Requires=cms-log.service postgresql.service
After=network.target cms-log.service postgresql.service
[Service]
Type=simple
User=cmsuser
Group=cmsuser
WorkingDirectory=/srv/cms
Environment=PYENV_SHELL=bash
Environment=PYENV_ROOT=/srv/cms/.pyenv
Environment=LOGNAME=cmsuser
Environment=VIRTUAL_ENV=/srv/cms
ExecStart=/srv/cms/bin/cmsResourceService -a %i
ExecStop=/usr/bin/kill $MAINPID
Restart=on-failure
RestartSec=60s
StandardInput=null
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,46 @@
---
- name: Install cms.conf
ansible.builtin.template:
src: cms.conf.jinja
dest: /usr/local/etc/cms.conf
mode: '0600'
owner: cmsuser
group: cmsuser
- name: Install cms.ranking.conf
ansible.builtin.template:
src: cms.ranking.conf.jinja
dest: /usr/local/etc/cms.ranking.conf
mode: '0600'
owner: cmsuser
group: cmsuser
- name: Copy cms-log.service
ansible.builtin.copy:
src: cms-log.service
dest: /etc/systemd/system/cms-log.service
mode: '0644'
owner: root
group: root
- name: Enable and start cms-log.service
ansible.builtin.systemd_service:
name: cms-log.service
state: started
enabled: true
- name: Copy cms-rank.service
ansible.builtin.copy:
src: cms-rank.service
dest: /etc/systemd/system/cms-rank.service
mode: '0644'
owner: root
group: root
- name: Enable and start cms-rank.service
ansible.builtin.systemd_service:
name: cms-rank.service
state: started
enabled: true
- name: Copy cms@.service
ansible.builtin.copy:
src: cms@.service
dest: /etc/systemd/system/cms@.service
mode: '0644'
owner: root
group: root

View File

@@ -0,0 +1,193 @@
{
"_help": "There is no way to put comments in a JSON file; the",
"_help": "fields starting with '_' are meant to be comments.",
"_section": "System-wide configuration",
"temp_dir": "/tmp",
"_help": "Whether to have a backdoor (see doc for the risks).",
"backdoor": false,
"_help": "The user/group that CMS will be run as.",
"cmsuser": "cmsuser",
"_section": "AsyncLibrary",
"core_services":
{
"LogService": [["localhost", 29000]],
"ResourceService": [["localhost", 28000]],
"ScoringService": [["localhost", 28500]],
"Checker": [["localhost", 22000]],
"EvaluationService": [["localhost", 25000]],
"Worker": [["localhost", 26000],
["localhost", 26001],
["localhost", 26002],
["localhost", 26003],
["localhost", 26004],
["localhost", 26005],
["localhost", 26006],
["localhost", 26007],
["localhost", 26008],
["localhost", 26009],
["localhost", 26010],
["localhost", 26011],
["localhost", 26012],
["localhost", 26013],
["localhost", 26014],
["localhost", 26015]],
"ContestWebServer": [["localhost", 21000]],
"AdminWebServer": [["localhost", 21100]],
"ProxyService": [["localhost", 28600]],
"PrintingService": [["localhost", 25123]]
},
"other_services":
{
"TestFileCacher": [["localhost", 27501]]
},
"_section": "Database",
"_help": "Connection string for the database.",
"database": "postgresql+psycopg2://cmsuser:{{ password }}@localhost:5432/cmsdb",
"_help": "Whether SQLAlchemy prints DB queries on stdout.",
"database_debug": false,
"_help": "Whether to use two-phase commit.",
"twophase_commit": false,
"_section": "Worker",
"_help": "Don't delete the sandbox directory under /tmp/ when they",
"_help": "are not needed anymore. Warning: this can easily eat GB",
"_help": "of space very soon.",
"keep_sandbox": false,
"_section": "Sandbox",
"_help": "Do not allow contestants' solutions to write files bigger",
"_help": "than this size (expressed in KB; defaults to 1 GB).",
"max_file_size": 1048576,
"_section": "WebServers",
"_help": "This key is used to encode information that can be seen",
"_help": "by the user, namely cookies and auto-incremented",
"_help": "numbers. It should be changed for each",
"_help": "contest. Particularly, you should not use this example",
"_help": "for other than testing. It must be a 16 bytes long",
"_help": "hexadecimal number. You can easily create a key calling:",
"_help": "python -c 'from cmscommon import crypto; print(crypto.get_hex_random_key())'",
"secret_key": "8e045a51e4b102ea803c06f92841a1fb",
"_help": "Whether Tornado prints debug information on stdout.",
"tornado_debug": false,
"_section": "ContestWebServer",
"_help": "Listening HTTP addresses and ports for the CWSs listed above",
"_help": "in core_services. If you access them through a proxy (acting",
"_help": "as a load balancer) running on the same host you could put",
"_help": "127.0.0.1 here for additional security.",
"contest_listen_address": [""],
"contest_listen_port": [8888],
"_help": "Login cookie duration in seconds. The duration is refreshed",
"_help": "on every manual request.",
"cookie_duration": 10800,
"_help": "If CWSs write submissions to disk before storing them in",
"_help": "the DB, and where to save them. %s = DATA_DIR.",
"submit_local_copy": true,
"submit_local_copy_path": "%s/submissions/",
"_help": "The number of proxies that will be crossed before CWSs get",
"_help": "the request. This is used to decide whether to assume that",
"_help": "the real source IP address is the one listed in the request",
"_help": "headers or not. For example, if you're using nginx as a load",
"_help": "balancer, you will likely want to set this value to 1.",
"num_proxies_used": 0,
"_help": "Maximum size of a submission in bytes. If you use a proxy",
"_help": "and set these sizes to large values remember to change",
"_help": "client_max_body_size in nginx.conf too.",
"max_submission_length": 100000,
"max_input_length": 5000000,
"_help": "STL documentation path in the system (exposed in CWS).",
"stl_path": "/usr/share/cppreference/doc/html/",
"_section": "AdminWebServer",
"_help": "Listening HTTP address and port for the AWS. If you access",
"_help": "it through a proxy running on the same host you could put",
"_help": "127.0.0.1 here for additional security.",
"admin_listen_address": "",
"admin_listen_port": 8889,
"_help": "Login cookie duration for admins in seconds.",
"_help": "The duration is refreshed on every manual request.",
"admin_cookie_duration": 36000,
"_help": "The number of proxies that will be crossed before AWS gets",
"_help": "the request. This is used to determine the request's real",
"_help": "source IP address. For example, if you're using nginx as",
"_help": "a proxy, you will likely want to set this value to 1.",
"admin_num_proxies_used": 0,
"_section": "ScoringService",
"_help": "List of URLs (with embedded username and password) of the",
"_help": "RWSs where the scores are to be sent. Don't include the",
"_help": "load balancing proxy (if any), just the backends. If any",
"_help": "of them uses HTTPS specify a file with the certificates",
"_help": "you trust.",
"rankings": ["http://{{ username }}:{{ password }}@localhost:8890/"],
"https_certfile": null,
"_section": "PrintingService",
"_help": "Maximum size of a print job in bytes.",
"max_print_length": 10000000,
"_help": "Printer name (can be found out using 'lpstat -p';",
"_help": "if null, printing is disabled)",
"printer": null,
"_help": "Output paper size (probably A4 or Letter)",
"paper_size": "A4",
"_help": "Maximum number of pages a user can print per print job",
"_help": "(excluding the title page). Text files are cropped to this",
"_help": "length. Too long pdf files are rejected.",
"max_pages_per_job": 10,
"max_jobs_per_user": 10,
"pdf_printing_allowed": false,
"_help": "This is the end of this file."
}

View File

@@ -0,0 +1,16 @@
{
"_help": "There is no way to put comments in a JSON file; the",
"_help": "fields starting with '_' are meant to be comments.",
"_help": "Listening address for RankingWebServer.",
"bind_address": "",
"_help": "Listening port for RankingWebServer.",
"http_port": 8890,
"_help": "Login information for adding and editing data.",
"username": "{{ username }}",
"password": "{{ password }}",
"_help": "This is the end of this file."
}

View File

@@ -0,0 +1,5 @@
---
- name: Reload haproxy
ansible.builtin.systemd_service:
name: haproxy
state: reloaded

View File

@@ -0,0 +1,14 @@
---
- name: Copy haproxy.cfg
ansible.builtin.copy:
src: haproxy.cfg.jinja
dest: /etc/haproxy/haproxy.cfg
mode: '0600'
owner: root
group: root
notify: Reload haproxy
- name: Enable and start haproxy
ansible.builtin.systemd_service:
name: haproxy.service
state: started
enabled: true

View File

@@ -0,0 +1,69 @@
#---------------------------------------------------------------------
# Example configuration. See the full configuration manual online.
#
# http://www.haproxy.org/download/2.5/doc/configuration.txt
#
#---------------------------------------------------------------------
global
maxconn 20000
log 127.0.0.1 local0
user haproxy
pidfile /run/haproxy.pid
daemon
userlist creds
user {{ username }} insecure-password {{ password }}
frontend secure
bind :8080
http-request auth unless { http_auth(creds) }
mode http
log global
option httplog
option dontlognull
option forwardfor except 127.0.0.0/8
maxconn 8000
timeout client 30s
use_backend rank if { hdr(host) -i {{ dns_prefix }}-ranking.{{ dns_suffix }} }
use_backend admin if { hdr(host) -i {{ dns_prefix }}-admin.{{ dns_suffix }} }
default_backend contest
frontend main
bind :80
mode http
log global
option httplog
option dontlognull
option forwardfor except 127.0.0.0/8
maxconn 8000
timeout client 30s
use_backend rank if { hdr(host) -i {{ dns_prefix }}-ranking.{{ dns_suffix }} }
use_backend admin if { hdr(host) -i {{ dns_prefix }}-admin.{{ dns_suffix }} }
default_backend contest
backend contest
mode http
balance roundrobin
timeout connect 5s
timeout server 30s
timeout queue 30s
server contest1 127.0.0.1:8888 check
backend admin
mode http
balance roundrobin
timeout connect 5s
timeout server 30s
timeout queue 30s
server admin1 127.0.0.1:8889 check
backend rank
mode http
balance roundrobin
timeout connect 5s
timeout server 30s
timeout queue 30s
server rank1 127.0.0.1:8890 check

View File

@@ -0,0 +1,4 @@
---
- name: Upgrade packages
community.general.pacman:
upgrade: true

View File

@@ -0,0 +1,34 @@
---
- name: Update package cache
community.general.pacman:
update_cache: true
- name: Install cms dependencies
community.general.pacman:
pkg:
- base-devel
- jdk8-openjdk
- fpc
- postgresql
- python
- libcap
- git
notify: Upgrade packages
- name: Install cms optional dependencies
community.general.pacman:
pkg:
- postgresql-libs
- libcups
- libyaml
- python-virtualenv
- rust
notify: Upgrade packages
- name: Install additional packages
community.general.pacman:
pkg:
- pyenv
- haproxy
- python-psycopg2
notify: Upgrade packages

View File

@@ -0,0 +1,3 @@
---
- name: Reboot
ansible.builtin.reboot:

View File

@@ -0,0 +1,13 @@
---
- name: Setup boot entry facts
ansible.builtin.set_fact:
bootconf: "{{ setup_cgroupsv1_bootconf | default('/boot/loader/entries/arch.conf') }}"
- name: Append cgroupsv1 boot option
ansible.builtin.lineinfile:
path: "{{ bootconf }}"
regexp: '^(options.*rw)'
line: '\1 SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 systemd.unified_cgroup_hierarchy=0'
backrefs: true
notify: Reboot
- name: Flush handlers
ansible.builtin.meta: flush_handlers

View File

@@ -0,0 +1,42 @@
---
- name: Init PostgreSQL
ansible.builtin.command: |
initdb --locale=C.UTF-8 --encoding=UTF8 -D /var/lib/postgres/data
args:
creates: /var/lib/postgres/data/PG_VERSION
become: true
become_user: postgres
- name: Start and enable postgres
ansible.builtin.systemd_service:
name: postgresql.service
state: started
enabled: true
- name: Setup postgres
become: true
become_user: postgres
block:
- name: Create cmsuser
community.postgresql.postgresql_user:
name: cmsuser
password: "{{ password }}"
environment:
PGOPTION: '-c password_encryption=scram-sha-256'
- name: Create cmsdb
community.postgresql.postgresql_db:
db: cmsdb
owner: cmsuser
- name: Configure privileges
community.postgresql.postgresql_privs:
db: cmsdb
role: cmsuser
privs: ALL
objs: ALL_IN_SCHEMA
- name: CMS init DB
ansible.builtin.shell: |
sudo -iu cmsuser <<EOF
cmsInitDB
EOF
become: true
become_user: cmsuser
register: ret
changed_when: ret.rc != 0